gin-base/db/README.md

948 lines
24 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# Magic-ORM 自主 ORM 框架架构文档
## 📋 目录
- [概述](#概述)
- [核心特性](#核心特性)
- [架构设计](#架构设计)
- [技术栈](#技术栈)
- [核心接口设计](#核心接口设计)
- [快速开始](#快速开始)
- [详细功能说明](#详细功能说明)
- [最佳实践](#最佳实践)
---
## 概述
Magic-ORM 是一个完全自主研发的企业级 Go 语言 ORM 框架,不依赖任何第三方 ORM 库。框架基于 `database/sql` 标准库构建,提供了全自动化事务管理、面向接口设计、智能字段映射等高级特性。支持 MySQL、SQLite 等主流数据库,内置完整的迁移管理和可观测性支持,帮助开发者快速构建高质量的数据访问层。
**设计理念:**
- 零依赖:仅依赖 Go 标准库 `database/sql`
- 高性能:优化的查询执行器和连接池管理
- 易用性:简洁的 API 设计和智能默认行为
- 可扩展:面向接口的设计,支持自定义驱动扩展
- **内置驱动**:框架自带所有主流数据库驱动,无需额外安装
---
## 核心特性
- **全自动化嵌套事务支持**:无需手动管理事务传播行为
- **面向接口化设计**:核心功能均通过接口暴露,便于 Mock 与扩展
- **内置主流数据库驱动**:开箱即用,并支持自定义驱动扩展
- **统一配置组件**:与框架配置体系无缝集成
- **单例模式数据库对象**:同一分组配置仅初始化一次
- **双模式操作**:原生 SQL + ORM 链式操作
- **OpenTelemetry 可观测性**:完整支持 Tracing、Logging、Metrics
- **智能结果映射**`Scan` 自动识别 Map/Struct/Slice无需 `sql.ErrNoRows` 判空
- **全自动字段映射**:无需结构体标签,自动匹配数据库字段
- **参数智能过滤**:自动识别并过滤无效/空值字段
- **Model/DAO 代码生成器**:一键生成全量数据访问代码
- **高级特性**调试模式、DryRun、自定义 Handler、软删除、时间自动更新、模型关联、主从集群等
- **自动化数据库迁移**:支持自动迁移、增量迁移、回滚迁移等完整迁移管理
---
## 架构设计
### 整体架构图
```mermaid
graph TB
A[应用层] --> B[Magic-ORM 框架]
B --> C[配置中心]
B --> D[数据库连接池]
B --> E[事务管理器]
B --> F[迁移管理器]
C --> C1[统一配置组件]
C --> C2[环境配置]
D --> D1[MySQL 驱动]
D --> D2[SQLite 驱动]
D --> D3[自定义驱动]
E --> E1[自动嵌套事务]
E --> E2[事务传播控制]
F --> F1[自动迁移]
F --> F2[增量迁移]
F --> F3[回滚迁移]
B --> G[观测性组件]
G --> G1[Tracing]
G --> G2[Logging]
G --> G3[Metrics]
B --> H[工具组件]
H --> H1[字段映射器]
H --> H2[参数过滤器]
H --> H3[结果映射器]
H --> H4[代码生成器]
```
### 目录结构
```
magic-orm/
├── core/ # 核心实现
│ ├── database.go # 数据库连接管理
│ ├── transaction.go # 事务管理
│ ├── query.go # 查询构建器
│ └── mapper.go # 字段映射器
├── migrate/ # 迁移管理
│ └── migrator.go # 自动迁移实现
├── generator/ # 代码生成器
│ ├── model.go # Model 生成
│ └── dao.go # DAO 生成
├── tracing/ # OpenTelemetry 集成
│ └── tracer.go # 链路追踪
└── driver/ # 数据库驱动适配(已内置)
├── mysql.go # MySQL 驱动(内置)
├── sqlite.go # SQLite 驱动(内置)
├── postgres.go # PostgreSQL 驱动(内置)
├── sqlserver.go # SQL Server 驱动(内置)
├── oracle.go # Oracle 驱动(内置)
└── clickhouse.go # ClickHouse 驱动(内置)
```
### 核心组件说明
#### 1. 数据库连接管理 (`core/database.go`)
- **单例模式**:全局唯一的 `DB` 实例,确保资源高效利用
- **多数据库支持**:支持 MySQL、SQLite、PostgreSQL、SQL Server、Oracle、ClickHouse 等
- **驱动内置**:所有主流数据库驱动已预装在框架中
- **连接池优化**:内置 sql.DB 连接池管理
- **健康检查**:启动时自动执行 `Ping()` 验证连接
**核心配置项:**
```go
Config{
DriverName: "mysql", // 驱动名称
DataSource: "dns", // 数据源连接字符串
MaxIdleConns: 10, // 最大空闲连接数
MaxOpenConns: 100, // 最大打开连接数
Debug: true, // 调试模式
}
```
#### 2. 查询构建器 (`core/query.go`)
提供流畅的链式查询接口:
- **条件查询**: Where, Or, And
- **字段选择**: Select, Omit
- **排序分页**: Order, Limit, Offset
- **分组统计**: Group, Having, Count
- **连接查询**: Join, LeftJoin, RightJoin
- **预加载**: Preload
**示例:**
```go
var users []model.User
db.Model(&model.User{}).
Where("status = ?", 1).
Select("id", "username").
Order("id DESC").
Limit(10).
Find(&users)
```
#### 3. 事务管理器 (`core/transaction.go`)
提供完整的事务管理能力:
- **自动嵌套事务**: 自动管理事务传播
- **保存点支持**: 支持部分回滚
- **生命周期回调**: Before/After 钩子
#### 4. 字段映射器 (`core/mapper.go`)
智能字段映射系统:
- **驼峰转下划线**: UserName -> user_name
- **标签解析**: 支持 db, json 标签
- **类型转换**: Go 类型与数据库类型自动转换
- **零值过滤**: 自动过滤空值和零值
#### 5. 迁移管理 (`migrate/migrator.go`)
完整的数据库迁移方案:
- **自动迁移**: 根据模型自动创建/修改表结构
- **增量迁移**: 支持添加字段、索引等
- **回滚支持**: 支持迁移回滚
- **版本管理**: 迁移版本记录和管理
#### 6. 驱动管理器 (`driver/manager.go`)
统一的驱动管理和注册中心:
- **驱动注册**: 自动注册所有内置驱动
- **驱动选择**: 根据配置自动选择合适的驱动
- **驱动扩展**: 支持用户自定义驱动注册
- **版本检测**: 自动检测数据库版本并适配特性
```go
// 驱动管理器会自动处理
var supportedDrivers = map[string]driver.Driver{
"mysql": &MySQLDriver{},
"sqlite": &SQLiteDriver{},
"postgres": &PostgresDriver{},
"sqlserver": &SQLServerDriver{},
"oracle": &OracleDriver{},
"clickhouse": &ClickHouseDriver{},
}
```
---
## 技术栈
### 核心依赖
| 组件 | 版本 | 说明 |
|------|------|------|
| Go | 1.25+ | 编程语言 |
| database/sql | stdlib | Go 标准库 |
| driver-go | Latest | 数据库驱动接口规范 |
| OpenTelemetry | Latest | 可观测性框架 |
| **内置驱动集合** | Latest | **包含所有主流数据库驱动** |
### 支持的数据库驱动
框架已内置以下数据库驱动,**无需额外安装**
- **MySQL**: 内置驱动(基于 `github.com/go-sql-driver/mysql`
- **SQLite**: 内置驱动(基于 `github.com/mattn/go-sqlite3`
- **PostgreSQL**: 内置驱动(基于 `github.com/lib/pq`
- **SQL Server**: 内置驱动(基于 `github.com/denisenkom/go-mssqldb`
- **Oracle**: 内置驱动(基于 `github.com/godror/godror`
- **ClickHouse**: 内置驱动(基于 `github.com/ClickHouse/clickhouse-go`
- **自定义驱动**: 实现 `driver.Driver` 接口即可扩展
> 💡 **说明**:框架在编译时已将所有主流数据库驱动打包,用户只需引入 `magic-orm` 即可完成所有数据库操作,无需单独安装各数据库驱动。
---
## 核心接口设计
### 1. 数据库连接接口
```go
// IDatabase 数据库连接接口
type IDatabase interface {
// 基础操作
DB() *sql.DB
Close() error
Ping() error
// 事务管理
Begin() (ITx, error)
Transaction(fn func(ITx) error) error
// 查询构建器
Model(model interface{}) IQuery
Table(name string) IQuery
Query(result interface{}, query string, args ...interface{}) error
Exec(query string, args ...interface{}) (sql.Result, error)
// 迁移管理
Migrate(models ...interface{}) error
// 配置
SetDebug(bool)
SetMaxIdleConns(int)
SetMaxOpenConns(int)
SetConnMaxLifetime(time.Duration)
}
```
### 2. 事务接口
```go
// ITx 事务接口
type ITx interface {
// 基础操作
Commit() error
Rollback() error
// 查询操作
Model(model interface{}) IQuery
Table(name string) IQuery
Insert(model interface{}) (int64, error)
BatchInsert(models interface{}, batchSize int) error
Update(model interface{}, data map[string]interface{}) error
Delete(model interface{}) error
// 原生 SQL
Query(result interface{}, query string, args ...interface{}) error
Exec(query string, args ...interface{}) (sql.Result, error)
}
```
### 3. 查询构建器接口
```go
// IQuery 查询构建器接口
type IQuery interface {
// 条件查询
Where(query string, args ...interface{}) IQuery
Or(query string, args ...interface{}) IQuery
And(query string, args ...interface{}) IQuery
// 字段选择
Select(fields ...string) IQuery
Omit(fields ...string) IQuery
// 排序
Order(order string) IQuery
OrderBy(field string, direction string) IQuery
// 分页
Limit(limit int) IQuery
Offset(offset int) IQuery
Page(page, pageSize int) IQuery
// 分组
Group(group string) IQuery
Having(having string, args ...interface{}) IQuery
// 连接
Join(join string, args ...interface{}) IQuery
LeftJoin(table, on string) IQuery
RightJoin(table, on string) IQuery
InnerJoin(table, on string) IQuery
// 预加载
Preload(relation string, conditions ...interface{}) IQuery
// 执行查询
First(result interface{}) error
Find(result interface{}) error
Count(count *int64) IQuery
Exists() (bool, error)
// 更新和删除
Updates(data interface{}) error
UpdateColumn(column string, value interface{}) error
Delete() error
// 特殊模式
Unscoped() IQuery
DryRun() IQuery
Debug() IQuery
// 构建 SQL不执行
Build() (string, []interface{})
}
```
### 4. 模型接口
```go
// IModel 模型接口
type IModel interface {
// 表名映射
TableName() string
// 生命周期回调(可选)
BeforeCreate(tx ITx) error
AfterCreate(tx ITx) error
BeforeUpdate(tx ITx) error
AfterUpdate(tx ITx) error
BeforeDelete(tx ITx) error
AfterDelete(tx ITx) error
BeforeSave(tx ITx) error
AfterSave(tx ITx) error
}
```
### 5. 字段映射器接口
```go
// IFieldMapper 字段映射器接口
type IFieldMapper interface {
// 结构体字段转数据库列
StructToColumns(model interface{}) (map[string]interface{}, error)
// 数据库列转结构体字段
ColumnsToStruct(row *sql.Rows, model interface{}) error
// 获取表名
GetTableName(model interface{}) string
// 获取主键字段
GetPrimaryKey(model interface{}) string
// 获取字段信息
GetFields(model interface{}) []FieldInfo
}
// FieldInfo 字段信息
type FieldInfo struct {
Name string // 字段名
Column string // 列名
Type string // Go 类型
DbType string // 数据库类型
Tag string // 标签
IsPrimary bool // 是否主键
IsAuto bool // 是否自增
}
```
### 6. 迁移管理器接口
```go
// IMigrator 迁移管理器接口
type IMigrator interface {
// 自动迁移
AutoMigrate(models ...interface{}) error
// 表操作
CreateTable(model interface{}) error
DropTable(model interface{}) error
HasTable(model interface{}) (bool, error)
RenameTable(oldName, newName string) error
// 列操作
AddColumn(model interface{}, field string) error
DropColumn(model interface{}, field string) error
HasColumn(model interface{}, field string) (bool, error)
RenameColumn(model interface{}, oldField, newField string) error
// 索引操作
CreateIndex(model interface{}, field string) error
DropIndex(model interface{}, field string) error
HasIndex(model interface{}, field string) (bool, error)
}
```
### 7. 代码生成器接口
```go
// ICodeGenerator 代码生成器接口
type ICodeGenerator interface {
// 生成 Model 代码
GenerateModel(table string, outputDir string) error
// 生成 DAO 代码
GenerateDAO(table string, outputDir string) error
// 生成完整代码
GenerateAll(tables []string, outputDir string) error
// 从数据库读取表结构
InspectTable(tableName string) (*TableSchema, error)
}
// TableSchema 表结构信息
type TableSchema struct {
Name string
Columns []ColumnInfo
Indexes []IndexInfo
}
```
### 8. 配置结构
```go
// Config 数据库配置
type Config struct {
DriverName string // 驱动名称
DataSource string // 数据源连接字符串
MaxIdleConns int // 最大空闲连接数
MaxOpenConns int // 最大打开连接数
ConnMaxLifetime time.Duration // 连接最大生命周期
Debug bool // 调试模式
// 主从配置
Replicas []string // 从库列表
ReadPolicy ReadPolicy // 读负载均衡策略
// OpenTelemetry
EnableTracing bool
ServiceName string
}
// ReadPolicy 读负载均衡策略
type ReadPolicy int
const (
Random ReadPolicy = iota
RoundRobin
LeastConn
)
```
---
## 快速开始
### 1. 安装 Magic-ORM
```bash
# 仅需安装 magic-orm所有数据库驱动已内置
go get github.com/your-org/magic-orm
```
> ✅ **无需单独安装数据库驱动!** 所有驱动已包含在 magic-orm 中。
### 2. 配置数据库
在配置文件中设置数据库参数:
```yaml
database:
type: mysql # 或 sqlite, postgres
dns: "user:password@tcp(localhost:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
debug: true
max_idle_conns: 10
max_open_conns: 100
```
### 3. 定义模型
```go
package model
import "time"
type User struct {
ID int64 `json:"id" db:"id"`
Username string `json:"username" db:"username"`
Password string `json:"-" db:"password"`
Email string `json:"email" db:"email"`
Status int `json:"status" db:"status"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
}
// 表名映射
func (User) TableName() string {
return "user"
}
```
### 4. 初始化数据库连接
```go
package main
import (
"database/sql"
_ "github.com/go-sql-driver/mysql"
"your-project/orm"
)
func main() {
// 初始化数据库连接
db, err := orm.NewDatabase(&orm.Config{
DriverName: "mysql",
DataSource: "user:password@tcp(localhost:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local",
MaxIdleConns: 10,
MaxOpenConns: 100,
Debug: true,
})
if err != nil {
panic(err)
}
defer db.Close()
// 执行迁移
orm.Migrate(db, &model.User{})
}
```
### 5. CRUD 操作
```go
// 创建
user := &model.User{Username: "admin", Password: "123456", Email: "admin@example.com"}
id, err := db.Insert(user)
// 查询单个
var user model.User
err := db.Model(&model.User{}).Where("id = ?", 1).First(&user)
// 查询多个
var users []model.User
err := db.Model(&model.User{}).Where("status = ?", 1).Order("id DESC").Find(&users)
// 更新
err := db.Model(&model.User{}).Where("id = ?", 1).Updates(map[string]interface{}{
"email": "new@example.com",
})
// 删除
err := db.Model(&model.User{}).Where("id = ?", 1).Delete()
// 原生 SQL
var results []model.User
err := db.Query(&results, "SELECT * FROM user WHERE status = ?", 1)
```
### 6. 事务操作
```go
// 自动嵌套事务
err := db.Transaction(func(tx *orm.Tx) error {
// 创建用户
user := &model.User{Username: "test", Email: "test@example.com"}
_, err := tx.Insert(user)
if err != nil {
return err
}
// 创建关联数据(自动加入同一事务)
profile := &model.Profile{UserID: user.ID, Avatar: "default.png"}
_, err = tx.Insert(profile)
if err != nil {
return err
}
return nil
})
```
---
## 详细功能说明
### 1. 全自动化嵌套事务
框架自动管理事务的传播行为,支持以下场景:
- **REQUIRED**: 如果当前存在事务,则加入该事务;否则创建新事务
- **REQUIRES_NEW**: 无论当前是否存在事务,都创建新事务
- **NESTED**: 在当前事务中创建嵌套事务(使用保存点)
**示例:**
```go
// 外层事务
db.Transaction(func(tx *orm.Tx) error {
// 内层自动加入同一事务
userService.CreateUser(tx, user)
orderService.CreateOrder(tx, order)
return nil
})
```
### 2. 智能结果映射
无需手动处理 `sql.ErrNoRows`,框架自动识别返回类型:
```go
// 自动识别 Struct
var user model.User
db.Model(&model.User{}).Where("id = ?", 1).First(&user) // 不存在时返回零值,不报错
// 自动识别 Slice
var users []model.User
db.Model(&model.User{}).Where("status = ?", 1).Find(&users) // 空结果返回空切片,而非 nil
// 自动识别 Map
var result map[string]interface{}
db.Table("user").Where("id = ?", 1).First(&result)
```
### 3. 全自动字段映射
无需结构体标签,框架自动匹配字段:
```go
// 驼峰命名自动转下划线
type UserInfo struct {
UserName string // 自动映射到 user_name 字段
UserAge int // 自动映射到 user_age 字段
CreatedAt string // 自动映射到 created_at 字段
}
```
### 4. 参数智能过滤
自动过滤零值和空指针:
```go
// 仅更新非零值字段
updateData := &model.User{
Username: "newname", // 会被更新
Email: "", // 空值,自动过滤
Status: 0, // 零值,自动过滤
}
db.Model(&user).Updates(updateData)
```
### 5. OpenTelemetry 可观测性
完整支持分布式追踪:
```go
// 自动注入 Span
ctx, span := otel.Tracer("gin-base").Start(context.Background(), "DB Query")
defer span.End()
// 自动记录 SQL 执行时间、错误信息等
db.WithContext(ctx).Find(&users)
```
### 6. 数据库迁移管理
#### 自动迁移
```go
database.SetAutoMigrate(&model.User{}, &model.Order{})
```
#### 增量迁移
```go
// 添加新字段
type UserV2 struct {
model.User
Phone string ` + "`" + `json:"phone" gorm:"column:phone;type:varchar(20)"` + "`" + `
}
database.SetAutoMigrate(&UserV2{})
```
#### 字段操作
```go
// 重命名字段
database.RenameColumn(&model.User{}, "UserName", "Nickname")
// 删除字段
database.DropColumn(&model.User{}, "OldField")
```
### 7. 高级特性
#### 软删除
```go
type User struct {
ID int64 `json:"id" db:"id"`
DeletedAt *time.Time `json:"deleted_at" db:"deleted_at"` // 软删除标记
}
// 自动过滤已删除记录
db.Model(&model.User{}).Find(&users) // WHERE deleted_at IS NULL
// 强制包含已删除记录
db.Unscoped().Model(&model.User{}).Find(&users)
```
#### 调试模式
```yaml
database:
debug: true # 输出所有 SQL 日志
```
#### DryRun 模式
```go
// 生成 SQL 但不执行
sql, args := db.Model(&model.User{}).DryRun().Insert(&user)
fmt.Println(sql, args)
```
#### 自定义 Handler
```go
// 注册回调函数
db.Callback().Before("insert").Register("custom_before_insert", func(ctx context.Context, db *orm.DB) error {
// 自定义逻辑
return nil
})
```
#### 主从集群
```go
// 配置读写分离
db, err := orm.NewDatabase(&orm.Config{
DriverName: "mysql",
DataSource: "master_dsn",
Replicas: []string{"slave1_dsn", "slave2_dsn"},
})
```
---
## 最佳实践
### 1. 模型设计规范
```go
// ✅ 推荐:使用 db 标签明确字段映射
type User struct {
ID int64 `json:"id" db:"id"`
Username string `json:"username" db:"username"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
}
// ❌ 不推荐:缺少字段映射标签
type User struct {
Id int64 // 无法自动映射到 id 列
UserName string // 可能映射错误
CreatedAt time.Time // 时间格式可能不匹配
}
```
### 2. 事务使用规范
```go
// ✅ 推荐:使用闭包自动管理事务
err := db.Transaction(func(tx *orm.Tx) error {
// 业务逻辑
return nil
})
// ❌ 不推荐:手动管理事务
tx, err := db.Begin()
if err != nil {
panic(err)
}
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
```
### 3. 查询优化
```go
// ✅ 推荐:使用 Select 指定字段
db.Model(&model.User{}).Select("id", "username").Find(&users)
// ✅ 推荐:使用 Index 加速查询
// 在数据库层面创建索引
// CREATE INDEX idx_username ON user(username);
// ✅ 推荐:批量操作
users := []model.User{{}, {}, {}}
db.BatchInsert(&users, 100) // 每批 100 条
// ❌ 避免N+1 查询问题
for _, user := range users {
db.Model(&model.Order{}).Where("user_id = ?", user.ID).Find(&orders) // 循环查询
}
// ✅ 使用 Join 或预加载
db.Query(&results, "SELECT u.*, o.* FROM user u LEFT JOIN orders o ON u.id = o.user_id")
```
### 4. 错误处理
```go
// ✅ 推荐:统一错误处理
if err := db.Insert(&user); err != nil {
log.Error("创建用户失败", "error", err)
return err
}
// ✅ 使用 errors 包判断特定错误
if errors.Is(err, sql.ErrNoRows) {
// 记录不存在
}
```
### 5. 性能优化
```go
// 连接池配置
sqlDB := db.DB()
sqlDB.SetMaxIdleConns(10) // 最大空闲连接数
sqlDB.SetMaxOpenConns(100) // 最大打开连接数
sqlDB.SetConnMaxLifetime(time.Hour) // 连接最大生命周期
// 使用 Scan 替代 Find 提升性能
type Result struct {
ID int64 `db:"id"`
Username string `db:"username"`
}
var results []Result
db.Model(&model.User{}).Select("id", "username").Scan(&results)
```
---
## 常见问题
### Q: 如何处理并发写入?
A: 使用事务 + 乐观锁:
```go
type Product struct {
ID int64 `db:"id"`
Version int `db:"version"` // 版本号
}
// 更新时检查版本号
rows, err := db.Exec(
"UPDATE product SET version = ?, stock = ? WHERE id = ? AND version = ?",
newVersion, newStock, id, oldVersion,
)
count, _ := rows.RowsAffected()
if count == 0 {
return errors.New("乐观锁冲突,数据已被其他事务修改")
}
```
### Q: 如何实现读写分离?
A: 配置主从数据库连接:
```go
db, err := orm.NewDatabase(&orm.Config{
DriverName: "mysql",
DataSource: "master_dsn",
Replicas: []string{"slave1_dsn", "slave2_dsn"},
ReadPolicy: orm.RoundRobin, // 负载均衡策略
})
```
### Q: 如何批量插入大量数据?
A: 使用 `BatchInsert`
```go
users := make([]model.User, 10000)
// ... 填充数据 ...
db.BatchInsert(&users, 1000) // 每批 1000 条,共 10 批
```
### Q: 如何实现字段自动映射?
A: 框架会自动将驼峰命名转换为下划线命名:
```go
type UserInfo struct {
UserName string `db:"user_name"` // 自动映射到 user_name 字段
UserAge int `db:"user_age"` // 自动映射到 user_age 字段
CreatedAt string `db:"created_at"` // 自动映射到 created_at 字段
}
```
### Q: 如何处理时间字段?
A: 使用 `time.Time` 类型,框架会自动处理时区转换:
```go
type Event struct {
ID int64 `db:"id"`
StartTime time.Time `db:"start_time"`
EndTime time.Time `db:"end_time"`
}
```
---
## 更新日志
- **v1.0.0**: 初始版本发布
- 完全自主研发,零依赖第三方 ORM
- 基于 database/sql 标准库
- 全自动化事务管理
- 智能字段映射
- OpenTelemetry 集成
- 支持 MySQL、SQLite、PostgreSQL
---
## 贡献指南
欢迎提交 Issue 和 Pull Request
---
## 许可证
MIT License