项目使用 rustfmt 进行代码格式化,配置文件为 rustfmt.toml:
edition = "2024"
style_edition = "2024"
tab_spaces = 2
hard_tabs = false
newline_style = "Unix"
imports_granularity = "Crate"
group_imports = "StdExternalCrate"
reorder_modules = true
reorder_impl_items = true
use_field_init_shorthand = true
use_try_shorthand = true
wrap_comments = true
normalize_comments = true强制要求:
- 使用 2 个空格缩进
- 导入语句按组排序:标准库 → 外部 crate → 内部模块
- 模块声明需要重新排序
- 使用字段初始化简写语法
- 使用 try 简写语法(
?操作符) - 注释自动换行和规范化
使用方法:
cargo fmt前端 JavaScript 相关文件推荐使用 Prettier 进行格式化,建议配置文件为 .prettierrc:
{
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5",
"printWidth": 100
}强制要求:
- 使用 2 个空格缩进
- 语句末尾添加分号
- 字符串使用单引号
- 行宽限制 100 字符
使用方法:
# 安装 Prettier
npm install --save-dev prettier
# 格式化 JavaScript 文件
npx prettier --write static/js/-
变量名:使用
snake_case(蛇形命名法)let file_id = 123; let group_name = "Documents";
-
函数名:使用
snake_casepub fn create_group() { } pub fn delete_file_by_id() { }
-
类型和结构体:使用
PascalCase(驼峰命名法)pub struct File { } pub struct CreateGroupDTO { }
-
枚举:使用
PascalCasepub enum GroupCondition { } pub enum OrderDirection { }
-
常量:使用
SCREAMING_SNAKE_CASEpub const RELATION_TYPE_PARENT_CHILD: i32 = 1;
-
Rust 源文件:小写字母 + 下划线分隔
- ✅
file_groups.rs,group_relations.rs - ❌
FileGroups.rs,filegroups.rs
- ✅
-
测试文件:与被测试模块同名,放在
tests/目录file_classification_cli/ ├── src/ │ └── cli.rs └── tests/ └── cli.rs
每个模块文件开头必须包含模块级文档注释,说明模块用途:
// groups.rs
//! 分组服务模块
//!
//! 提供分组相关的业务逻辑处理,包括分组的创建、删除、查询和更新操作,
//! 并处理分组与其关联文件、标签等资源的引用计数和级联删除。
公共函数必须包含完整的文档注释:
/// 通过名称创建分组
///
/// 参数:
/// - `conn`: 数据库连接对象
/// - `create_group_dto`: 包含分组信息的 DTO 对象
///
/// 返回值:
/// 成功时返回插入记录的 ID,失败则返回相应的错误
pub fn create_group(
conn: &mut AnyConnection,
create_group_dto: &CreateGroupDTO,
) -> Result<i32, Error> {
// ...
}- 使用
//添加单行注释,注释前留一个空格 - 复杂逻辑需要添加注释说明意图
- 避免无意义的注释(代码已经很清晰的情况)
// ✅ 好的注释
// 对于非主组,需要先减少关联文件的引用计数
for file in &files_associated_with_group {
files_dao::decrease_file_reference_count_by_id(conn, file.id)?;
}
// ❌ 避免的注释
// 循环遍历文件
for file in &files {
// 删除文件
delete_file(file)?;
}- 所有注释必须使用中文(特殊业务逻辑可用英文术语)
- 文档字符串保持中英文双语版本(如 README)
项目采用清晰的三层架构:
┌─────────────────────────────────────┐
│ Presentation Layer (Web/CLI) │
│ - handlers/ (API 请求处理器) │
│ - cli.rs (命令行解析) │
│ - repl.rs (交互式界面) │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ Business Logic Layer (Core) │
│ - service/ (业务逻辑层) │
│ - model/ (数据模型层) │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ Data Access Layer (Core) │
│ - internal/ (DAO 数据访问层) │
│ - utils/database.rs (数据库工具) │
└─────────────────────────────────────┘
层级依赖规则:
- ✅ Web/CLI → Core (service → model → internal)
- ❌ internal → service (禁止反向依赖)
- ❌ CLI ↔ WebAPI (禁止横向依赖)
file_classification_core/
├── src/
│ ├── internal/ # 数据访问层(私有)
│ │ ├── files.rs # 文件 DAO
│ │ ├── groups.rs # 分组 DAO
│ │ ├── tags.rs # 标签 DAO
│ │ ├── file_group.rs # 文件 - 分组关联 DAO
│ │ ├── group_tag.rs # 分组 - 标签关联 DAO
│ │ └── group_relations.rs # 分组关系 DAO
│ ├── model/ # 数据模型层(公开)
│ │ ├── models.rs # 所有模型定义
│ │ └── schema.rs # Diesel Schema
│ ├── service/ # 业务逻辑层(公开)
│ │ ├── files.rs # 文件服务
│ │ ├── groups.rs # 分组服务
│ │ └── ...
│ ├── utils/ # 工具模块
│ │ ├── database.rs # 数据库连接
│ │ └── errors.rs # 错误处理
│ └── lib.rs # 库入口
internal/模块标记为mod(私有),不对外暴露model/和service/标记为pub mod(公开)- 外部只能通过
service/提供的接口访问数据
// lib.rs
mod internal; // ❌ 外部不可见
pub mod model; // ✅ 外部可见
pub mod service; // ✅ 外部可见
pub mod utils; // ✅ 外部可见[workspace]
resolver = "2"
members = ["file_classification_*", "common"]统一在根 Cargo.toml 的 [workspace.dependencies] 中定义:
[workspace.dependencies]
diesel = { version = "2.2", features = ["chrono", "r2d2"] }
diesel_migrations = "2.2"
dotenvy = "0.15"
chrono = { version = "0.4", features = ["serde"] }
serde = { version = "1.0.219", features = ["derive"] }子项目引用方式:
[dependencies]
diesel.workspace = true
chrono.workspace = true- ❌ 禁止在子项目中直接指定版本
- ❌ 禁止循环依赖
- ❌ 禁止引入未使用的依赖
main/master: 主分支,用于发布feature/*: 功能分支git checkout -b feature/add-file-upload git checkout -b feature/group-hierarchy
bugfix/*: 修复分支git checkout -b bugfix/primary-group-delete
release/*: 发布分支git checkout -b release/v1.2.0
# 1. 从主分支创建功能分支
git checkout main
git pull origin main
git checkout -b feature/your-feature
# 2. 开发并提交(遵循提交规范)
git add .
git commit -m "feat: add new file upload endpoint"
# 3. 同步主分支变更
git fetch origin
git rebase origin/main
# 4. 推送并创建 Pull Request
git push origin feature/your-feature采用 Conventional Commits 规范:
<type>(<scope>): <subject>
<body>
<footer>
feat: 新功能fix: 修复 bugdocs: 文档更新style: 代码格式调整(不影响功能)refactor: 重构(既不是新功能也不是修复)test: 添加或修改测试chore: 构建过程或辅助工具变动
core: 核心库cli: 命令行工具webapi: Web APIdb: 数据库相关ui: 前端界面
# ✅ 好的提交
feat(core): add group tree structure support
fix(cli): resolve primary group deletion issue
docs(readme): update Chinese translation
refactor(webapi): optimize error handling logic
test(core): add unit tests for file service
# ❌ 不好的提交
update code
fix bug
add new feature为所有公共函数编写单元测试:
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_create_group() {
// Arrange
let mut conn = setup_test_db();
let dto = CreateGroupDTO { name: "Test".to_string() };
// Act
let result = create_group(&mut conn, &dto);
// Assert
assert!(result.is_ok());
assert_eq!(result.unwrap(), 1);
}
}在 tests/ 目录添加集成测试:
// tests/cli.rs
#[test]
fn test_file_commands() {
assert!(true)
}# 运行所有测试
cargo test
# 运行特定模块测试
cargo test --package file_classification_core
# 运行带过滤的测试
cargo test test_create_group所有 PR 必须通过 GitHub Actions 的自动化测试:
- ✅ Linux (Ubuntu) 测试通过
- ✅ Windows 测试通过
- ✅ macOS 测试通过
- ✅ 代码格式化检查通过
支持多种数据库,每个数据库有独立的 migration 目录:
migrations/ # Diesel 默认(SQLite)
migrations_mysql/ # MySQL
migrations_postgres/ # PostgreSQL
migrations_sqlite/ # SQLite
格式:YYYY-MM-DD-HHMMSS_description
✅ 2024-10-01-193345_FileClassification
✅ 2025-10-20-000000_update_group_hierarchy
✅ 2025-11-02-000000_add_description_fields每个 migration 必须包含:
up.sql: 升级脚本down.sql: 降级脚本(可回滚)
-- up.sql
CREATE TABLE IF NOT EXISTS `groups` (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL UNIQUE
);
-- down.sql
DROP TABLE IF EXISTS `groups`;不同数据库的 SQL 语法差异需要在对应目录分别维护:
# SQLite (migrations_sqlite/)
CREATE TABLE IF NOT EXISTS `groups` (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT
);
# MySQL (migrations_mysql/)
CREATE TABLE IF NOT EXISTS `groups` (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
);
# PostgreSQL (migrations_postgres/)
CREATE TABLE IF NOT EXISTS groups (
id SERIAL PRIMARY KEY
);- 使用
snake_case - 表名使用复数形式
- 外键使用单数形式 +
_id后缀
-- ✅ 好的命名
CREATE TABLE files (
id INTEGER PRIMARY KEY,
type TEXT NOT NULL,
group_id INTEGER NOT NULL,
FOREIGN KEY (group_id) REFERENCES groups(id)
);
-- ❌ 避免的命名
CREATE TABLE FileList (
FileID INTEGER PRIMARY KEY,
FileType TEXT,
GroupID INTEGER
);为频繁查询的字段添加索引:
CREATE INDEX idx_files_group_id ON files(group_id);
CREATE INDEX idx_groups_name ON groups(name);SQL 文件中添加表和字段注释:
CREATE TABLE `groups` (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL UNIQUE
);
-- COMMENT ON TABLE `groups` IS '组信息表';
-- COMMENT ON COLUMN `groups`.id IS '组 ID';
-- COMMENT ON COLUMN `groups`.name IS '组名称';- 使用名词复数形式
- 使用小写字母 + 连字符
✅ /api/files
✅ /api/groups
✅ /api/file-groups
✅ /api/group-tags
❌ /api/file
❌ /api/createGroup
❌ /api/getFiles
GET /api/groups // 获取分组列表
GET /api/groups/{id} // 获取单个分组
POST /api/groups // 创建分组
PUT /api/groups/{id} // 更新分组
DELETE /api/groups/{id} // 删除分组使用查询选项对象:
// ✅ 好的设计
GET /api/groups/search/by-filter-with-options?
name=Documents&
page=1&
page_size=20&
order_by=name_asc
// ❌ 避免的设计
GET /api/getGroupsByNameAndPage?name=Documents&page=1#[get("/api/groups")]
async fn api_list_groups(pool: web::Data<DbPool>) -> Result<HttpResponse> { }
#[get("/api/groups/{id}")]
async fn api_get_group_by_id(path: web::Path<i32>) -> Result<HttpResponse> { }
#[post("/api/groups")]
async fn api_create_group(payload: web::Json<CreateGroupDTO>) -> Result<HttpResponse> { }
#[put("/api/groups/update/by-conditions")]
async fn api_update_groups_by_conditions(
payload: web::Json<(Vec<GroupCondition>, UpdateGroupDTO)>
) -> Result<HttpResponse> { }// 成功响应
{
"success": true,
"data": { /* 数据内容 */ },
"count": 10 // 可选,列表查询时返回
}
// 错误响应
{
"success": false,
"error": {
"code": "GROUP_NOT_FOUND",
"message": "分组未找到"
}
}使用大写字母 + 下划线分隔:
// 分组相关
GROUP_NOT_FOUND
CREATE_GROUP_FAILED
DELETE_GROUP_FAILED
// 文件相关
FILE_NOT_FOUND
CREATE_FILE_FAILED
// 验证错误
VALIDATION_ERROR
// 数据库错误
DATABASE_ERROR#[derive(Debug)]
pub enum AppError {
GroupNotFound,
CreateGroupFailed(String),
ValidationError(String),
DieselError(DieselError),
}
impl Display for AppError {
fn fmt(&self, f: &mut Formatter) -> Result {
write!(f, "{}", self.message())
}
}项目根目录和维护中英双语版本:
README.md- 英文版本README_cn.md- 中文版本
所有文档放在 docs/ 目录:
docs/
├── README.md # 英文概述
├── README_cn.md # 中文概述
├── project_structure.md # 英文项目结构
├── project_structure_cn.md # 中文项目结构
└── CONTRIBUTING.md # 贡献指南(本文件)
使用 OpenAPI/Swagger 规范:
use utoipa::OpenApi;
#[derive(OpenApi)]
#[openapi(
paths(
handlers::api_list_groups,
handlers::api_get_group_by_id,
),
components(
schemas(Group, CreateGroupDTO, UpdateGroupDTO)
)
)]
pub struct ApiDoc;访问路径:/api-docs/openapi.json
在提交代码前,请确认以下事项:
- 代码已通过
cargo fmt格式化 - 代码已通过
cargo clippy检查 - 所有单元测试通过 (
cargo test) - 没有编译警告
- 新功能已添加单元测试
- 已有功能的回归测试通过
- 错误处理完善(考虑了各种异常情况)
- 事务使用正确(保证数据一致性)
- 遵循命名规范(snake_case / PascalCase)
- 添加了必要的注释
- 公共函数有完整的文档注释
- 模块有模块级文档注释
- SQL 注入防护(使用参数化查询)
- 输入验证完整
- 敏感信息未硬编码(使用环境变量)
- 引用计数更新正确(无内存泄漏风险)
- 更新了相关文档(如适用)
- 提交了中英文文档(如适用)
- API 变更已更新 OpenAPI 文档
- 提交信息符合 Conventional Commits 规范
- 分支命名正确
- 已与主分支同步
# 格式化代码
cargo fmt
stylua static/js/
# 运行测试
cargo test
# 代码检查
cargo clippy -- -D warnings
# 构建项目
cargo build --release
# 运行 CLI
cargo run --bin file_classification_cli -- repl
# 运行 WebAPI
cargo run --bin file_classification_webapi
# 数据库迁移
diesel migration runFileClassificationSolutions/
├── common/ # 公共工具库
├── file_classification_core/ # 核心业务逻辑
│ ├── internal/ # 数据访问层(私有)
│ ├── model/ # 数据模型(公开)
│ ├── service/ # 业务服务(公开)
│ └── utils/ # 工具函数
├── file_classification_cli/ # 命令行工具
├── file_classification_webapi/ # Web API
│ ├── handlers/ # API 处理器
│ └── static/ # 前端静态资源
└── migrations_*/ # 数据库迁移
如有问题,请通过以下方式联系:
- 提交 Issue: https://github.com/DataEraserC/FileClassificationSolutions/issues
- 讨论区:https://github.com/DataEraserC/FileClassificationSolutions/discussions
最后更新日期: 2026-03-25
文档版本: 1.0.0