derive-usage
Guide for using genies_derive procedural macros. Use when implementing DDD aggregates with
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Guide for using genies_derive procedural macros. Use when implementing DDD aggregates with
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
Guide for using RBatis v4 Rust ORM framework. Use when implementing database CRUD operations, writing dynamic SQL with py_sql or html_sql macros, configuring database connections, managing transactions, implementing interceptors, syncing table structures, or integrating RBatis into Genies microservices. Also use when the user asks about RBatis usage patterns, query building, or database operations in Rust.
Genies 前后端接口规范。Use when designing API contracts, defining field naming conventions, date/time formats, response models, pagination, error handling, or ID strategies between Genies backend and frontend (Web/Android/OHOS).
Guide for using flyway-rs database migration framework with RBatis. Use when implementing database schema migrations, managing SQL changelog files, configuring migration runners, handling multi-database migrations, or integrating database versioning into Genies microservices.
Guide for developing Rust microservices using Genies framework following Java DDD layering principles. Use when creating new microservices, designing aggregate roots, implementing domain events, organizing service layers, setting up Flyway migrations, or structuring a DDD-based Genies project.
Genies framework unified skill hub. Use when you need to find the right skill for any Genies framework task, including authentication, authorization, caching, configuration, database, DDD microservices, Dapr messaging, macros, K8s deployment, testing, API conventions, gateway proxy, or Salvo web framework features. Also use when the user asks about Genies framework capabilities, asks which skill to use, or wants a quick overview of available Genies skills.
Guide for using the Genies Rust microservice framework with DDD and Dapr. Use when developing with Genies, creating aggregates, domain events, Dapr subscriptions, Casbin field-level permissions, configuration management, or when the user asks about Genies framework usage patterns.
| name | derive-usage |
| description | Guide for using genies_derive procedural macros. Use when implementing DDD aggregates with |
genies_derive 提供 7 个核心过程宏,用于简化 DDD + Dapr + Casbin 应用开发。纯过程宏库,无运行时依赖。
核心宏:
#[derive(Aggregate)] - 聚合根派生,实现 AggregateType/WithAggregateId/InitializeAggregate#[derive(DomainEvent)] - 领域事件派生,实现 DomainEvent trait#[derive(Config)] - 配置派生,支持 YAML + ENV 加载#[derive(ConfigCore)] - 内部配置派生(避免循环依赖)#[topic(...)] - Dapr topic 消费,Redis 幂等#[remote(...)] - HTTP 请求包装,JWT 自动刷新#[casbin] - 字段级权限控制,自动嵌套检测[dependencies]
genies_derive = { workspace = true }
genies = { workspace = true } # for runtime support
聚合根派生,实现 DDD 聚合类型标识和 ID 访问。
| Attribute | Required | Description |
|---|---|---|
#[aggregate_type("Name")] | No | 覆盖聚合类型名(默认:struct 名) |
#[id_field(field)] | No | 指定 ID 字段,生成 WithAggregateId |
#[initialize_with_defaults] | No | 生成 InitializeAggregate(需要 id_field) |
use genies_derive::Aggregate;
use serde::{Deserialize, Serialize};
#[derive(Aggregate, Serialize, Deserialize, Default)]
#[aggregate_type("Order")]
#[id_field(id)]
#[initialize_with_defaults]
pub struct Order {
pub id: String, // 使用 genies::next_id() 生成(新项目优先雪花 ID)
pub status: String,
pub total: f64,
}
// Generated:
// impl AggregateType for Order {
// fn aggregate_type(&self) -> String { "Order".to_string() }
// fn atype() -> String { "Order".to_string() }
// }
// impl WithAggregateId for Order {
// type Id = String;
// fn aggregate_id(&self) -> &Self::Id { &self.id }
// }
// impl InitializeAggregate for Order {
// fn initialize(id: String) -> Self {
// Self { id, status: Default::default(), total: Default::default() }
// }
// }
领域事件派生,支持 struct 和 enum。
| Attribute | Required | Description |
|---|---|---|
#[event_type("Name")] | No | 事件类型名(默认:struct/variant 名) |
#[event_type_version("V1")] | No | 版本(默认:"V0") |
#[event_source("service")] | No | 来源标识(默认:"") |
use genies_derive::DomainEvent;
use serde::{Deserialize, Serialize};
#[derive(DomainEvent, Serialize, Deserialize, Default)]
#[event_type("OrderCreated")]
#[event_type_version("V1")]
#[event_source("order-service")]
pub struct OrderCreatedEvent {
pub order_id: String,
pub customer_id: String,
}
// Generated: DomainEvent trait
// fn event_type(&self) -> String { "OrderCreated".to_string() }
// fn event_type_version(&self) -> String { "V1".to_string() }
// fn event_source(&self) -> String { "order-service".to_string() }
// fn json(&self) -> String { serde_json::to_string(self).unwrap() }
#[derive(DomainEvent, Serialize, Deserialize)]
#[event_type_version("V1")]
pub enum OrderEvent {
#[event_type("OrderCreated")]
Created { order_id: String },
#[event_type("OrderShipped")]
Shipped { tracking: String },
}
配置派生,从 YAML + 环境变量加载。
| Attribute | Description |
|---|---|
#[config(default = "value")] | 默认值 |
use genies_derive::Config;
use serde::{Deserialize, Serialize};
#[derive(Config, Debug, Deserialize, Serialize)]
pub struct AppConfig {
#[config(default = "localhost")]
pub host: String,
#[config(default = "8080")]
pub port: u16,
#[config(default = "t1,t2")]
pub topics: Vec<String>,
pub db_url: Option<String>,
}
// Usage:
let config = AppConfig::from_sources("application.yml")?;
default() - 使用默认值创建from_file(path) - 从 YAML 加载from_sources(path) - YAML + ENV 综合加载(推荐)load_env(&mut self) - 从环境变量覆盖merge(&mut self, other) - 合并配置validate(&self) - 验证配置# 支持两种格式
export host="prod.example.com" # 原名
export HOST="prod.example.com" # SCREAMING_SNAKE
与 Config 相同,但使用 genies_core::error::ConfigError。用于框架内部避免循环依赖。
Dapr topic 消费宏,自动生成 Salvo handler + Redis 幂等。
| Attribute | Required | Default | Description |
|---|---|---|---|
name = "..." | No | aggregate type | Topic 名称 |
pubsub = "..." | No | "messagebus" | PubSub 组件名 |
metadata = "k=v,..." | No | - | Topic 元数据 |
use genies_derive::topic;
use rbatis::executor::Executor;
#[topic(name = "order-events", pubsub = "messagebus")]
pub async fn handle_order_created(
tx: &mut dyn Executor,
event: OrderCreatedEvent,
) -> anyhow::Result<u64> {
Order::insert(tx, &order).await?;
Ok(1)
}
handle_order_created(tx, event) - 业务逻辑handle_order_created_hoop - Salvo handler(#[handler])handle_order_created_dapr() - 返回 DaprTopicSubscriptionhandle_order_created_hoop_router() - Salvo RouterMessage → CloudEvent 解析 → event_type 匹配
↓
Redis key: {server}-{handler}-{event_type}-{id}
↓
SET NX (原子) → 处理 → SET CONSUMED
↓
失败自动 rollback,删除 key,Dapr 重发
feignhttp 请求包装,自动管理 Keycloak JWT Token(401 时自动刷新并重试)。类似 Java 的 @FeignClient。
remote 模块独立于 DDD 四层架构,按外部服务分文件:
src/remote/
├── mod.rs # 模块导出
├── patient_service.rs # 患者服务远程调用
├── baseinfo_service.rs # 基础信息服务远程调用
└── his_service.rs # HIS 系统远程调用
在 lib.rs 中声明 pub mod remote;。
use once_cell::sync::Lazy;
use genies_derive::remote;
use serde::{Deserialize, Serialize};
/// 使用 config_gateway! 宏定义外部服务的基础 URL
pub static BaseInfo: Lazy<String> = genies::config_gateway!("/baseinfo");
pub static Patient: Lazy<String> = genies::config_gateway!("/patient");
/// 远程调用返回的数据模型
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct CustomConfigModel {
pub id: Option<String>,
pub name: Option<String>,
}
| 注解 | 说明 | 示例 |
|---|---|---|
#[query] | 查询参数(?key=value) | #[query] name: &str |
#[path] | 路径参数(/api/{id}) | #[path] id: &str |
#[body] | 请求体(JSON) | #[body] body: UserDTO |
#[remote]
#[get(url = BaseInfo, path = "/customconfig/depttypename")]
pub async fn findByDepartmentIdAndTypeName(
#[query] departmentId: &str,
#[query] typeName: &str,
) -> feignhttp::Result<Vec<CustomConfigModel>> { impled!() }
#[remote]
#[get(url = Patient, path = "/api/patient/id/{id}")]
pub async fn get_patient_by_id(
#[path] id: &str,
) -> feignhttp::Result<PatientInfo> { impled!() }
#[remote]
#[post(url = Patient, path = "/api/patient/create")]
pub async fn create_patient(
#[body] patient: PatientCreateDTO,
) -> feignhttp::Result<String> { impled!() }
config_gateway! 宏:genies::config_gateway!("/service-prefix") 生成 Lazy<String>,值为 ${gateway}/service-prefix,gateway 从 application.yml 配置读取url + path 分离:url 引用 Lazy<String> 静态变量(服务基础路径),path 是具体端点路径impled!()(feignhttp 宏要求)&str 而非 String// 原函数重命名为 _feignhttp 后缀,增加 Authorization header 参数
pub async fn get_patient_by_id_feignhttp(
#[header] Authorization: &str,
#[path] id: &str,
) -> feignhttp::Result<PatientInfo>
// 包装函数(自动 token 管理)
pub async fn get_patient_by_id(id: &str) -> feignhttp::Result<PatientInfo> {
// 1. 从 REMOTE_TOKEN 获取 Bearer token
// 2. 调用 get_patient_by_id_feignhttp
// 3. 如果 401,从 Keycloak 刷新 token 并重试
}
| Java FeignClient | Rust/Genies #[remote] |
|---|---|
@FeignClient(name = "patient-service") | pub static Patient: Lazy<String> = genies::config_gateway!("/patient"); |
@GetMapping("/api/patient/{id}") | #[get(url = Patient, path = "/api/patient/{id}")] |
@RequestParam String name | #[query] name: &str |
@PathVariable String id | #[path] id: &str |
@RequestBody UserDTO body | #[body] body: UserDTO |
| Spring Security OAuth2 Token | #[remote] 自动管理 Keycloak Token |
字段级权限控制,自动生成 Serialize + Writer。
use genies_derive::casbin;
use serde::Deserialize;
use salvo::oapi::ToSchema;
#[casbin]
#[derive(Deserialize, ToSchema)]
pub struct User {
pub id: u64,
pub name: String,
pub email: String, // 可被 deny
pub address: Address, // 自动嵌套检测
pub accounts: Vec<BankAccount>, // Vec 自动检测
}
宏自动识别非原始类型:
Address → 递归过滤Option<Address> → 非 null 时递归Vec<BankAccount> → 遍历每项递归impl User {
/// 对 JSON Value 树进行递归字段权限过滤
pub fn casbin_filter(
value: &mut serde_json::Value,
enforcer: &casbin::Enforcer,
subject: &str,
) { ... }
}
impl salvo::writing::Writer for User {
// 从 Depot 提取 enforcer/subject,过滤后渲染 JSON 响应
}
casbin_filter 方法签名:
pub fn casbin_filter(value: &mut serde_json::Value, enforcer: &casbin::Enforcer, subject: &str)
value — 待过滤的 JSON Value(会被原地修改,deny 的字段被移除)enforcer — Casbin Enforcer 引用subject — 当前用户标识(对应 casbin_rules 中的 v0)Writer trait 只在 handler 直接返回 Json<T> 或 T 时自动触发。当返回 Json<RespVO<T>> 时,RespVO 的 Writer 接管序列化,T 的 Writer 不会被调用,字段过滤不会自动生效。
手动调用模式:
use salvo::prelude::*;
use genies_core::RespVO;
#[endpoint]
async fn get_user(depot: &mut Depot) -> Json<RespVO<User>> {
let user = fetch_user().await;
// 从 Depot 获取 enforcer 和 subject(由 casbin_auth 中间件注入)
let enforcer = depot.obtain::<std::sync::Arc<casbin::Enforcer>>().ok();
let subject = depot.get::<String>("subject").ok();
// 手动调用 casbin_filter
let mut value = serde_json::to_value(&user).unwrap();
if let (Some(e), Some(s)) = (&enforcer, &subject) {
User::casbin_filter(&mut value, e.as_ref(), s.as_str());
}
let filtered: User = serde_json::from_value(value).unwrap();
Json(RespVO::from(&Ok::<_, String>(filtered)))
}
参数类型转换:
e.as_ref() — Arc<Enforcer> → &Enforcers.as_str() — String → &str-- 禁止 bob 看 email
INSERT INTO casbin_rules (ptype,v0,v1,v2,v3)
VALUES ('p','bob','User.email','read','deny');
-- 禁止 guest 看 phone
INSERT INTO casbin_rules (ptype,v0,v1,v2,v3)
VALUES ('p','guest','User.phone','read','deny');
启用 debug_mode 打印生成代码:
[dependencies]
genies_derive = { path = "...", features = ["debug_mode"] }