br-addon 0.1.73

This is an addon
Documentation
# PROJECT KNOWLEDGE BASE

Rust 插件系统框架,提供 `Addon → Module → Action` 三层运行时分发架构。
API 通过字符串名称 `{addon}.{module}.{action}` 动态路由(如 `dms.task.table`),非编译时绑定。

## COMMANDS

```bash
# 构建
cargo build                                    # 默认全 feature
cargo build --features "mysql"                 # 指定单个 feature
cargo build --no-default-features --features "mysql,cache"

# 测试
cargo test                                     # 全部测试
cargo test -- --nocapture                      # 带 stdout 输出
cargo test create_plugin                       # 运行单个测试(按函数名匹配)
cargo test test_empty -- --nocapture           # 单个测试带输出

# 检查(提交前必须通过)
cargo fmt -- --check
cargo clippy --all-targets --all-features -- -D warnings

# 发布
cargo package --list && cargo publish --dry-run && cargo publish
```

**Features**: `mysql` `sqlite` `mssql` `pgsql` `cache` `email`(默认全启用)

## STRUCTURE

```
src/
├── lib.rs        # Plugin trait, ApiResponse, ApiType, 全局状态
├── addon.rs      # Addon trait + addon_create() 脚手架生成 + to_pascal_case()
├── module.rs     # Module trait + DB helpers (db_find/db_select/db_insert/db_update/db_delete)
├── action.rs     # Action trait + check() 参数验证 + Btn/BtnType/BtnColor/Dashboard UI组件
├── request.rs    # Request struct + Method/ContentType 枚举
├── tools.rs      # Tools 工具集(db/cache/email) + ToolsConfig
├── tables.rs     # Tables 表格查询构建器(分页/排序/联表/树形) [feature-gated]
└── swagger.rs    # Swagger OpenAPI 3.0 文档生成器(Swagger/Server/Api/RequestBody)
temp/             # 代码生成模板(action_table/action_add/action_del/action_get 等)
examples/addon/   # 完整 Addon→Module→Action 实现参考
tests/main.rs     # 脚手架生成测试
```

## CODE STYLE

### 命名规范
| 类型 | 风格 | 示例 |
|------|------|------|
| Struct/Enum/Trait | PascalCase | `ApiResponse`, `BtnType`, `DashboardModel` |
| 函数/方法 | snake_case | `module_name()`, `table_key()` |
| 常量/静态变量 | SCREAMING_SNAKE | `GLOBAL_DATA`, `PLUGIN_TOOLS` |
| 私有/内部方法 | 下划线前缀 | `_name()`, `_table_name()`, `_load_apis()` |

### 注释语言
本项目使用**中文注释**,保持一致性。用 `///` 写文档注释。

### Import 顺序
1. `crate::` 内部模块(按字母序)
2. 条件编译的外部 crate(`#[cfg(...)]``br_db`/`br_cache`/`br_email`3. 外部 crate(`json`, `lazy_static`, `log`, `serde`, `std`,按字母序)

```rust
use crate::action::Action;
use crate::addon::Addon;
use crate::module::Module;
#[cfg(any(feature = "mysql", feature = "sqlite", feature = "mssql", feature = "pgsql"))]
use br_db::Db;
use json::{array, object, JsonValue};
use std::collections::HashMap;
```

### Feature 条件编译(必须)
所有数据库相关代码必须用 feature gate 包裹:
```rust
#[cfg(any(feature = "mysql", feature = "sqlite", feature = "mssql", feature = "pgsql"))]
fn db_operation() { }
```
cache 用 `#[cfg(feature = "cache")]`,email 用 `#[cfg(feature = "email")]`。

### JSON 操作
使用 `json` crate(**非 serde_json**)。核心宏和类型:
```rust
use json::{object, array, JsonValue};
let obj = object! { "key" => "value", "num" => 42 };  // 创建对象
let arr = array!["a", "b"];                             // 创建数组
obj["key"].as_str().unwrap_or("");                      // 安全取值
obj.has_key("key");                                     // 检查 key
obj.is_empty() / obj.is_null();                         // 空值判断
for (k, v) in obj.entries() { }                         // 遍历对象
for item in arr.members() { }                           // 遍历数组
```

### 错误处理
- Trait 方法返回 `Result<T, String>`,错误信息用中文
- API 层用 `ApiResponse::fail(code, "消息")``ApiResponse::error(data, "消息")`
- 错误码规范:`900_xxx` 系列为参数验证错误,`-1` 为通用错误
-`.unwrap_or()` / `.unwrap_or_default()` 处理用户输入,**禁止裸 `.unwrap()`**
- `lock()` 可用 `.expect("描述")` 因为锁失败是不可恢复错误

```rust
// 正确
fn module(name: &str) -> Result<Box<dyn Module>, String> {
    Err(format!("模型格式不正确: {name}"))
}
ApiResponse::fail(900_001, "参数错误")

// 错误
request[name].as_str().unwrap()  // 禁止
```

### 类型名称推导
通过 `std::any::type_name::<Self>()` 解析模块路径自动推导名称:
- Action: `t[2].t[3].t[4]``addon.module.action`
- Module: `t[2].t[3]``addon.module`
- 表名: `{addon}_{module}` 自动推导

### `Box::leak` 模式
Module/Addon trait 中用 `Box::leak(s.into_boxed_str())` 返回 `&'static str`。
每个具体类型只泄漏一次,这是有意设计。新增类似方法时保持此模式并添加注释。

### Builder 模式
`Tables`、`Swagger`、`Btn`、`Dashboard` 均使用 builder 模式,方法返回 `&mut Self`:
```rust
self.table()
    .main_table_fields(table_name, fields, hidd_field, show_field)
    .search_fields(search_fields)
    .params(request)
    .get_table()
```

### 全局状态
- `PLUGIN_TOOLS`: `OnceLock<Tools>` 写入一次后只读,无锁访问
- `CONFIG`: `lazy_static! Mutex<HashMap>` 存储配置
- `GLOBAL_DATA`: `thread_local! RefCell<JsonValue>` 线程局部变量
- `GLOBAL_HANDLE`: `LazyLock<Mutex<HashMap>>` 监听线程注册
- `GLOBAL_ADDONS/MODULE/ACTION`: `OnceLock<Vec<String>>` 一次性初始化

### 标注规范
- 构造函数和返回新值的方法加 `#[must_use]`(如 `Swagger::new()``Btn::new()`- `tables.rs` 整个模块允许 `#[allow(clippy::too_many_arguments)]`

## ANTI-PATTERNS(禁止)

- ❌ 非 feature-gated 代码中直接使用 `self.tools().db`
- ❌ 添加新的全局 `lazy_static!` 变量(新增全局状态用 `LazyLock``OnceLock`- ❌ Action 中直接 `panic!`,应使用 `ApiResponse::fail()`
- ❌ 用 `.unwrap()` 处理用户输入
- ❌ 直接 `.leak()` 泄漏字符串,应使用 `Box::leak()` 并添加注释说明
- ❌ 在 `check()` 验证中遗漏 `require` 检查
- ❌ 使用 `serde_json`,本项目统一用 `json` crate

## GIT COMMIT

格式:`<类型> <描述>`(中文描述)

| 前缀 | 含义 | 示例 |
|------|------|------|
| A | 新增 | `A fields` |
| U | 更新/优化 | `U 优化addon` |
| F | 修复 | `F 修复参数验证` |
| D | 删除 | `D 删除废弃模板` |
| R | 重构 | `R 重构Tables构建器` |

## KEY PATTERNS

### 实现新插件
参考 `examples/addon/` 目录,需实现三层 trait:
1. `Addon` trait → `fn module(&mut self, name: &str) -> Result<Box<dyn Module>, String>`
2. `Module` trait → `fn action(&mut self, name: &str) -> Result<Box<dyn Action>, String>`
3. `Action` trait → `fn title()` + `fn index(&mut self, request: Request) -> ApiResponse`

### Trait Bounds
- `Addon: Send + Sync + 'static`
- `Module: Send + Sync + 'static`
- `Action` 无额外 bounds(但通过 `Box<dyn Action>` 传递)

实现 struct 通常用 `#[derive(Debug, Clone)]`,内部持有对应 Module struct 引用。

### Trait 必须实现 vs 可选方法
大部分 trait 方法有默认实现,只需覆盖必要的:

| Trait | 必须实现 | 常用覆盖 | 其余有默认值 |
|-------|---------|---------|-------------|
| `Addon` | `title()`, `module()` | `icon()`, `sort()`, `description()` | `name()` 自动推导 |
| `Module` | `title()`, `action()` | `fields()`, `table()`, `table_key()`, `table_unique()`, `table_index()` | `_name()`, `_table_name()` 自动推导 |
| `Action` | `title()`, `index()` | `params()`, `method()`, `tags()`, `auth()`, `public()`, `description()` | `api()`, `module_name()` 自动推导 |

### Action struct 惯用写法
每个 Action struct 持有对应 Module struct 引用,这是固定模式:
```rust
#[derive(Debug, Clone)]
pub struct DmsTaskTable {
    pub module: DmsTask,       // 持有 Module struct
}
impl Action for DmsTaskTable {
    fn title(&self) -> &'static str { "表格" }
    fn index(&mut self, request: Request) -> ApiResponse {
        // 通过 self.module 访问 Module 层方法
        // 通过 self.tools() 访问 DB/Cache/Email
    }
}
```
Module struct 在 `mod.rs` 中定义,所有同模块 Action 共享同一个 Module struct 类型。

### Action 请求生命周期
`run()` 是外部入口,`index()` 是业务逻辑入口。流程:
```
run(request)
  → 检查 HTTP method 是否匹配 self.method()
  → 如果 params_check() == true:
      → check(&mut request.query, self.query())   // 验证地址参数
      → check(&mut request.body, self.params())    // 验证请求体参数
  → index(request)                                 // 执行业务逻辑
  → 根据 ApiResponse.success 转为 Ok/Err 返回
```
`check()` 会自动移除 params 中未定义的字段,并对缺失字段填充 `field["def"]` 默认值。

### ApiResponse 双态返回
`index()` 返回 `ApiResponse`,`run()` 根据 `success` 字段拆分:
```rust
// index() 内部 — 直接返回 ApiResponse
fn index(&mut self, request: Request) -> ApiResponse {
    ApiResponse::success(data, "获取完成")   // success=true  → run() 返回 Ok(...)
    ApiResponse::fail(1000, "参数错误")      // success=false → run() 返回 Err(...)
}
```

### br_fields 字段定义
`params()` 中用 `br_fields` 定义请求参数,`fields()` 中定义数据库字段:
```rust
fn params(&mut self) -> JsonValue {
    let mut fields = object! {};
    // Str::new(require, name, title, max_len, default)
    fields["name"] = br_fields::str::Str::new(true, "name", "名称", 50, "").field();
    // Int::new(require, name, title, max, default)
    fields["age"] = br_fields::int::Int::new(false, "age", "年龄", 200, 0).field();
    // Switch::new(require, name, title, default)
    fields["active"] = br_fields::int::Switch::new(false, "active", "启用", true).field();
    fields
}
```
`field()` 生成 check 验证用的 JSON,`swagger()` 生成 API 文档用的 JSON。

### Tools 访问与 DB 查询链
通过 `self.tools()` 获取 Tools 实例,DB 操作使用链式调用:
```rust
// Module 层快捷方法
self.db_find("id_value");                              // 按 id 查单条
self.db_insert(data);                                  // 插入
self.db_update("id_value", data);                      // 按 id 更新
self.db_delete("id_value");                            // 按 id 删除

// Action 层完整链式查询
let mut binding = self.tools();
let db = binding.db.table("table_name");
db.where_and("field", "=", value.into());
db.where_and("status", "in", "a,b,c".into());
let result = db.find();     // 单条
let list = db.select();     // 多条
let count = db.count();     // 计数
let col = db.column("field"); // 单列
```
**注意**:`self.tools()` 每次调用都会 clone 整个 Tools,频繁调用时先绑定到变量。

### 脚手架生成
`addon_create()` 从 `temp/` 模板生成代码,模板按 action 名称匹配:
table/add/del/get/put/select/select_tree/tree/menu/down/import

### 参数验证字段类型
`check()` 支持:key, text, table, tree, file, int, timestamp, yearmonth, float,
string, url, time, code, pass, email, location, color, date, barcode, datetime,
editor, tel, dict, switch, select, radio, array, polygon, object

## NOTES

- `dev-dependencies` 使用本地路径 `../br-web-server`,CI 需特殊处理
- `br-db` 当前使用本地路径 `../br-db`(Cargo.toml 中有注释掉的 crates.io 版本)
- 无 rustfmt.toml / clippy.toml,使用默认配置
- 无 CI 配置文件,依赖手动执行 fmt + clippy