feat: initialize Nuanji (Warm Notes) project

- Base platform from base.git (ERP base: auth, core, config, message, workflow, plugin)
- Created erp-diary module skeleton (lib.rs, dto.rs, error.rs, event.rs, state.rs)
- Integrated erp-diary into workspace and erp-server
- Added DiaryModule registration in main.rs
- Added DiaryState FromRef in state.rs
- Diary routes mounted (empty routes, ready for implementation)
- Product design spec v1.2 preserved in docs/
- Implementation plan preserved in plans/

Cargo check: OK
Cargo test: OK (78+ base tests passing)
This commit is contained in:
iven
2026-05-31 20:52:19 +08:00
commit c539e6fd83
285 changed files with 59156 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),
}
}
}