feat: initialize ERP base platform (extracted from HMS)

- Stripped 11 business crates (health, ai, dialysis, plugins)
- Cleaned AppState, AppConfig, main.rs from business coupling
- Reduced migrations from 169 to 53 (base-only)
- Removed health_provider trait from erp-core
- Removed business integration tests
- Removed gateway rate limiting middleware
- Base capabilities: auth, RBAC, JWT, config, workflow, message, plugin, audit, crypto, RLS, multi-tenant

Cargo check: OK
Cargo test: OK
This commit is contained in:
iven
2026-05-31 20:35:57 +08:00
commit 59856ac2fc
639 changed files with 124710 additions and 0 deletions

View File

@@ -0,0 +1,143 @@
use erp_core::error::AppError;
/// Config module error types.
#[derive(Debug, thiserror::Error)]
pub enum ConfigError {
#[error("验证失败: {0}")]
Validation(String),
#[error("资源未找到: {0}")]
NotFound(String),
#[error("键已存在: {0}")]
DuplicateKey(String),
#[error("编号序列耗尽: {0}")]
NumberingExhausted(String),
#[error("版本冲突: 数据已被其他操作修改,请刷新后重试")]
VersionMismatch,
}
impl From<sea_orm::TransactionError<ConfigError>> for ConfigError {
fn from(err: sea_orm::TransactionError<ConfigError>) -> Self {
match err {
sea_orm::TransactionError::Connection(err) => ConfigError::Validation(err.to_string()),
sea_orm::TransactionError::Transaction(inner) => inner,
}
}
}
impl From<sea_orm::DbErr> for ConfigError {
fn from(err: sea_orm::DbErr) -> Self {
ConfigError::Validation(err.to_string())
}
}
impl From<ConfigError> for AppError {
fn from(err: ConfigError) -> Self {
match err {
ConfigError::Validation(s) => AppError::Validation(s),
ConfigError::NotFound(s) => AppError::NotFound(s),
ConfigError::DuplicateKey(s) => AppError::Conflict(s),
ConfigError::NumberingExhausted(s) => AppError::Internal(s),
ConfigError::VersionMismatch => AppError::VersionMismatch,
}
}
}
pub type ConfigResult<T> = Result<T, ConfigError>;
#[cfg(test)]
mod tests {
use super::*;
use erp_core::error::AppError;
#[test]
fn config_error_validation_maps_to_app_validation() {
let app: AppError = ConfigError::Validation("字段不能为空".to_string()).into();
match app {
AppError::Validation(msg) => assert_eq!(msg, "字段不能为空"),
other => panic!("期望 Validation实际得到 {:?}", other),
}
}
#[test]
fn config_error_not_found_maps_to_app_not_found() {
let app: AppError = ConfigError::NotFound("字典不存在".to_string()).into();
match app {
AppError::NotFound(msg) => assert_eq!(msg, "字典不存在"),
other => panic!("期望 NotFound实际得到 {:?}", other),
}
}
#[test]
fn config_error_duplicate_key_maps_to_app_conflict() {
let app: AppError = ConfigError::DuplicateKey("编码已存在".to_string()).into();
match app {
AppError::Conflict(msg) => assert_eq!(msg, "编码已存在"),
other => panic!("期望 Conflict实际得到 {:?}", other),
}
}
#[test]
fn config_error_numbering_exhausted_maps_to_app_internal() {
let app: AppError = ConfigError::NumberingExhausted("序列已耗尽".to_string()).into();
match app {
AppError::Internal(msg) => assert!(msg.contains("序列已耗尽")),
other => panic!("期望 Internal实际得到 {:?}", other),
}
}
#[test]
fn config_error_version_mismatch_maps_to_app_version_mismatch() {
let app: AppError = ConfigError::VersionMismatch.into();
match app {
AppError::VersionMismatch => {}
other => panic!("期望 VersionMismatch实际得到 {:?}", other),
}
}
#[test]
fn config_error_display_messages() {
// 验证各变体的 Display 输出包含中文描述
assert!(
ConfigError::Validation("test".into())
.to_string()
.contains("验证失败")
);
assert!(
ConfigError::NotFound("test".into())
.to_string()
.contains("资源未找到")
);
assert!(
ConfigError::DuplicateKey("test".into())
.to_string()
.contains("键已存在")
);
assert!(
ConfigError::NumberingExhausted("test".into())
.to_string()
.contains("编号序列耗尽")
);
assert!(
ConfigError::VersionMismatch
.to_string()
.contains("版本冲突")
);
}
#[test]
fn transaction_error_connection_maps_to_validation() {
// TransactionError::Connection 应该转换为 ConfigError::Validation
let config_err: ConfigError = sea_orm::TransactionError::Connection(sea_orm::DbErr::Conn(
sea_orm::RuntimeErr::Internal("连接失败".to_string()),
))
.into();
match config_err {
ConfigError::Validation(msg) => assert!(msg.contains("连接失败")),
other => panic!("期望 Validation实际得到 {:?}", other),
}
}
}