feat: 初始化ERP平台底座项目结构

- 添加基础crate结构(erp-core, erp-common)
- 实现核心模块trait和事件总线
- 配置Docker开发环境(PostgreSQL+Redis)
- 添加Tauri桌面端基础框架
- 设置CI/CD工作流
- 编写项目协作规范文档(CLAUDE.md)
This commit is contained in:
iven
2026-04-10 23:40:38 +08:00
commit eb856b1d73
32 changed files with 8049 additions and 0 deletions

View File

@@ -0,0 +1,25 @@
[package]
name = "erp-server"
version.workspace = true
edition.workspace = true
[[bin]]
name = "erp-server"
path = "src/main.rs"
[dependencies]
erp-core.workspace = true
erp-common.workspace = true
tokio.workspace = true
axum.workspace = true
tower.workspace = true
tower-http.workspace = true
tracing.workspace = true
tracing-subscriber.workspace = true
config.workspace = true
sea-orm.workspace = true
redis.workspace = true
utoipa.workspace = true
utoipa-swagger-ui.workspace = true
serde_json.workspace = true
serde.workspace = true

View File

@@ -0,0 +1,19 @@
[server]
host = "0.0.0.0"
port = 3000
[database]
url = "postgres://erp:erp_dev_2024@localhost:5432/erp"
max_connections = 20
min_connections = 5
[redis]
url = "redis://localhost:6379"
[jwt]
secret = "change-me-in-production"
access_token_ttl = "15m"
refresh_token_ttl = "7d"
[log]
level = "info"

View File

@@ -0,0 +1,50 @@
use serde::Deserialize;
#[derive(Debug, Deserialize)]
pub struct AppConfig {
pub server: ServerConfig,
pub database: DatabaseConfig,
pub redis: RedisConfig,
pub jwt: JwtConfig,
pub log: LogConfig,
}
#[derive(Debug, Deserialize)]
pub struct ServerConfig {
pub host: String,
pub port: u16,
}
#[derive(Debug, Deserialize)]
pub struct DatabaseConfig {
pub url: String,
pub max_connections: u32,
pub min_connections: u32,
}
#[derive(Debug, Deserialize)]
pub struct RedisConfig {
pub url: String,
}
#[derive(Debug, Deserialize)]
pub struct JwtConfig {
pub secret: String,
pub access_token_ttl: String,
pub refresh_token_ttl: String,
}
#[derive(Debug, Deserialize)]
pub struct LogConfig {
pub level: String,
}
impl AppConfig {
pub fn load() -> anyhow::Result<Self> {
let config = config::Config::builder()
.add_source(config::File::with_name("config/default"))
.add_source(config::Environment::with_prefix("ERP").separator("__"))
.build()?;
Ok(config.try_deserialize()?)
}
}

View File

@@ -0,0 +1,16 @@
use sea_orm::{ConnectOptions, Database, DatabaseConnection};
use std::time::Duration;
use crate::config::DatabaseConfig;
pub async fn connect(config: &DatabaseConfig) -> anyhow::Result<DatabaseConnection> {
let mut opt = ConnectOptions::new(&config.url);
opt.max_connections(config.max_connections)
.min_connections(config.min_connections)
.connect_timeout(Duration::from_secs(10))
.idle_timeout(Duration::from_secs(600));
let db = Database::connect(opt).await?;
tracing::info!("Database connected successfully");
Ok(db)
}

View File

@@ -0,0 +1,41 @@
mod config;
mod db;
use axum::Router;
use config::AppConfig;
use tracing_subscriber::EnvFilter;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Load config
let config = AppConfig::load()?;
// Initialize tracing
tracing_subscriber::fmt()
.with_env_filter(
EnvFilter::try_from_default_env()
.unwrap_or_else(|_| EnvFilter::new(&config.log.level)),
)
.json()
.init();
tracing::info!("ERP Server starting...");
// Connect to database
let db = db::connect(&config.database).await?;
// Connect to Redis
let _redis_client = redis::Client::open(&config.redis.url[..])?;
tracing::info!("Redis client created");
// Build app
let app = Router::new()
.fallback(|| async { axum::Json(serde_json::json!({"error": "Not found"})) });
let addr = format!("{}:{}", config.server.host, config.server.port);
let listener = tokio::net::TcpListener::bind(&addr).await?;
tracing::info!("Server listening on {}", addr);
axum::serve(listener, app).await?;
Ok(())
}