# AetherGo **Repository Path**: devAether666/AetherGo ## Basic Information - **Project Name**: AetherGo - **Description**: 基于go敏捷开发平台 - **Primary Language**: Go - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2025-07-24 - **Last Updated**: 2025-07-24 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ## Aether Go 敏捷开发平台 系统目前已经完成以下功能: - 基于 `casbin`、`jwt` 的权限管理模块,支持数据权限,权限粒度细化至按钮(方法)级; - 支持模块化开发、模块自动加载、完善的模块生命周期管理; - 验证器、控制器、模型、服务、接口层高度解耦; - 自研发的 `ORM` 增强功能,支持链式调用,所见即所得,具体使用示例见下方说明; - 简洁优雅的项目结构和最佳实践方案,项目持续优化更新中; - 内置完善的基础模型,通过组合的方式实现强大的功能扩展和可插拔; - 前端项目正在进行迭代(后面也会同步开源出来),使用最新技术栈 `vue3` `vite` `unocss` 等,力求全面简洁优雅但不臃肿; ![image-20250724200454223](/home/halo/.config/Typora/typora-user-images/image-20250724200454223.png) ## 项目环境 后端:`go1.22+` `mysql5.7+` `redis7+` 前端:`node20.0+` 推荐使用 `pnpm` 或 `yarn` 作为包管理工具 ## 运行项目 1. 环境准备 ```shell go mod tidy ``` 配置 `config.yml` 2. 开发发布 - `Windows` 开发 ```shell go run main.go # go run . ``` - `Windows` 编译 ```shell go build -o myapp.exe # 生成 Windows 可执行文件 ``` - `Windows` 交叉编译到 `Linux` `amd64` - CMD ```shell set GOOS=linux set GOARCH=amd64 set CGO_ENABLED=0 go build -o myapp-linux ./main.go # 生成 Linux 可执行文件 myapp-linux ``` - PowerShell ```shell $env:GOOS = "linux" $env:GOARCH = "amd64" $env:CGO_ENABLED = "0" go build -o myapp-linux ./main.go ``` - `Windows` 交叉编译到 `MacOs` `amd64` ```shell # 编译到 macOS(Intel 芯片,amd64 架构) $env:GOOS = "darwin" $env:GOARCH = "amd64" $env:CGO_ENABLED = "0" go build -o myapp-mac-intel ./main.go # 编译到 macOS(M1/M2 芯片,arm64 架构) $env:GOOS = "darwin" $env:GOARCH = "arm64" $env:CGO_ENABLED = "0" go build -o myapp-mac-m1 ./main.go ``` - `Linux` 编译 ```shell go build -o myapp # 生成名为 myapp 的可执行文件 ``` ## Aether Query 基于 `gorm` 的增强型 Query 查询器(这个是亮点,用好了事半功倍,以下是详细用法) ```go package main import ( "fmt" "net/http" "aether-go/frame/db" "gorm.io/driver/sqlite" "gorm.io/gorm" ) // ------------------------------ 1. 定义业务模型 ------------------------------ // User 用户模型(嵌入BaseModel继承默认实现) type User struct { db.AetherQuery // 继承默认接口实现 Name string `gorm:"column:name"` Age int `gorm:"column:age"` RoleID int64 `gorm:"column:role_id"` // 关联角色ID } // TableName 重写必要方法(按需自定义) func (u *User) TableName() string { return "users" // 自定义表名 } func (u *User) Fields() []string { // 明确列出所有字段(用于WithoutField等功能) return []string{"id", "name", "age", "role_id", "sort", "weight", "delete_at"} } func (u *User) SearchableFields() map[string]string { // 定义可搜索字段及操作符 return map[string]string{ "name": "like", // 姓名支持模糊查询 "age": "op", // 年龄支持精确匹配 "role_id": "in", // 角色ID支持IN查询 } } // SearchAttr 自定义搜索逻辑(覆盖默认实现) func (u *User) SearchAttr(field string, eq *db.EnhancedDB, value interface{}, params map[string]interface{}) error { switch field { case "name": // 自定义姓名搜索:同时匹配name和nickname(假设存在nickname字段) eq.Where("name LIKE ? OR nickname LIKE ?", fmt.Sprintf("%%%v%%", value), fmt.Sprintf("%%%v%%", value)) case "age_range": // 支持年龄范围搜索(如?age_range=18,30) if rangeVal, ok := value.(string); ok { parts := strings.Split(rangeVal, ",") if len(parts) == 2 { eq.Where("age BETWEEN ? AND ?", parts[0], parts[1]) } } } return nil } // Role 角色模型(用于JOIN示例) type Role struct { db.AetherQuery RoleName string `gorm:"column:role_name"` } func (r *Role) TableName() string { return "roles" } func (r *Role) Fields() []string { return []string{"id", "role_name", "sort", "delete_at"} } // ------------------------------ 2. 初始化数据库 ------------------------------ func initDB() *gorm.DB { db, err := gorm.Open(sqlite.Open("demo.db"), &gorm.Config{ DisableForeignKeyConstraintWhenMigrating: true, // 简化示例:禁用外键约束 }) if err != nil { panic("数据库连接失败: " + err.Error()) } // 自动迁移表结构 db.AutoMigrate(&User{}, &Role{}) return db } // ------------------------------ 3. 核心查询示例 ------------------------------ func main() { db := initDB() // 示例1:基础查询(列表+分页) fmt.Println("----- 示例1:用户列表分页 -----") var users []User req, _ := http.NewRequest("GET", "/users?page=1&limit=10&name=test", nil) // 模拟请求 err := query.New(db, &User{}). Without("delete_at", true). // 排除软删除字段 QuickSearch(map[string]interface{}{"name": "test"}). // 快速搜索 Order("desc"). // 自动排序 Paginate(req, 10). // 分页(默认10条/页) Find(&users).Error if err != nil { panic(err) } fmt.Printf("查询结果: %+v\n", users) // 示例2:JOIN查询(用户+角色) fmt.Println("\n----- 示例2:用户+角色JOIN -----") var userWithRoles []struct { User RoleName string `gorm:"column:role_name"` // 角色名(来自JOIN) } err = query.New(db, &User{}). LeftJoin([2]interface{}{&Role{}, "r"}, "id", "role_id", []string{"role_name"}). // 左连接角色表 Without("weight", true). // 排除权重字段 Search(map[string]interface{}{"age_range": "18,30"}). // 自定义搜索(年龄范围) OrderByDesc("users.id"). // 按用户ID降序 Find(&userWithRoles).Error if err != nil { panic(err) } fmt.Printf("JOIN结果: %+v\n", userWithRoles) // 示例3:子查询+字段自增 fmt.Println("\n----- 示例3:子查询+自增 -----") // 子查询:统计每个用户的角色数量 subQuery := db.Model(&Role{}). Select("COUNT(*)"). Where("id = users.role_id") // 执行更新:用户年龄+1 err = query.New(db, &User{}). AddSubSelect(subQuery, "role_count"). // 添加子查询字段 Where("age > ?", 18). Inc("age", 1) // 年龄自增1 if err != nil { panic(err) } fmt.Println("更新成功") } ``` 更多高级使用等待你的探索。 ## 事件系统 ```go // 事件系统用法 // UserLoginEvent 用户登录事件 type UserLoginEvent struct { UserID string Username string LoginTime time.Time } // Name 实现Event接口 func (e *UserLoginEvent) Name() string { return "user.login" } // OrderPaidEvent 订单支付事件 type OrderPaidEvent struct { OrderID string UserID string Amount float64 PaymentTime time.Time } // Name 实现Event接口 func (e *OrderPaidEvent) Name() string { return "order.paid" } func main() { // 创建事件调度器 dispatcher := event.NewDispatcher() // 注册事件监听器 registerListeners(dispatcher) // 注册事件订阅者 registerSubscribers(dispatcher) // 创建Redis客户端 (用于队列) redisClient := redis.NewClient(&redis.Options{ Addr: "localhost:6379", }) // 创建基于队列的调度器 queueDispatcher := event.NewQueueDispatcher(dispatcher, redisClient, "events", 3) queueDispatcher.StartWorkers() defer queueDispatcher.StopWorkers() // 创建Gin引擎 r := gin.Default() // 注册路由 registerRoutes(r, dispatcher, queueDispatcher) // 启动服务器 srv := &http.Server{ Addr: ":8080", Handler: r, } go func() { if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { log.Fatalf("listen: %s\n", err) } }() // 优雅关闭 quit := make(chan os.Signal, 1) signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) <-quit log.Println("Shutting down server...") ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() if err := srv.Shutdown(ctx); err != nil { log.Fatal("Server forced to shutdown:", err) } log.Println("Server exiting") } // 注册事件监听器 func registerListeners(dispatcher *event.Dispatcher) { // 用户登录监听器 dispatcher.On("user.login", event.ListenerFunc(func(ctx context.Context, event event.Event) error { loginEvent, ok := event.(*UserLoginEvent) if !ok { return fmt.Errorf("invalid event type: %T", event) } log.Printf("用户 %s (ID: %s) 登录时间: %s", loginEvent.Username, loginEvent.UserID, loginEvent.LoginTime.Format(time.RFC3339)) return nil })) // 订单支付监听器 dispatcher.On("order.paid", event.ListenerFunc(func(ctx context.Context, event event.Event) error { orderEvent, ok := event.(*OrderPaidEvent) if !ok { return fmt.Errorf("invalid event type: %T", event) } log.Printf("订单 %s 支付成功,金额: %.2f,用户ID: %s", orderEvent.OrderID, orderEvent.Amount, orderEvent.UserID) return nil })) } // 注册事件订阅者 func registerSubscribers(dispatcher *event.Dispatcher) { // 用户相关事件订阅者 userSubscriber := event.SubscriberFunc(func(registry event.EventRegistry) { registry.On("user.register", event.ListenerFunc(func(ctx context.Context, event event.Event) error { // 处理用户注册逻辑 return nil })) registry.On("user.logout", event.ListenerFunc(func(ctx context.Context, event event.Event) error { // 处理用户登出逻辑 return nil })) }) // 注册订阅者 userSubscriber.Subscribe(dispatcher) } // 注册路由 func registerRoutes(r *gin.Engine, dispatcher *event.Dispatcher, queueDispatcher *event.QueueDispatcher) { r.POST("/login", func(c *gin.Context) { var loginData struct { Username string `json:"username" binding:"required"` Password string `json:"password" binding:"required"` } if err := c.ShouldBindJSON(&loginData); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // 验证逻辑... // 触发同步事件 event := &UserLoginEvent{ UserID: "12345", // 实际应用中从数据库获取 Username: loginData.Username, LoginTime: time.Now(), } // 同步触发 if err := dispatcher.Emit(c.Request.Context(), event); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to process event"}) return } // 异步触发 (使用队列) if err := queueDispatcher.EmitToQueue(c.Request.Context(), event); err != nil { log.Printf("Failed to queue event: %v", err) } c.JSON(http.StatusOK, gin.H{"message": "Login successful"}) }) r.POST("/orders/:id/pay", func(c *gin.Context) { orderID := c.Param("id") var payData struct { Amount float64 `json:"amount" binding:"required"` } if err := c.ShouldBindJSON(&payData); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // 支付处理逻辑... // 触发订单支付事件 event := &OrderPaidEvent{ OrderID: orderID, UserID: "12345", // 实际应用中从会话获取 Amount: payData.Amount, PaymentTime: time.Now(), } // 同步触发 if err := dispatcher.Emit(c.Request.Context(), event); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to process event"}) return } c.JSON(http.StatusOK, gin.H{"message": "Payment successful"}) }) } ``` ## TODO 后期会加入依赖注入(DI)、全面的自动化构建功能,你只需要在平台动动手指,专注你的业务即可!打包构建发布,可以轻松管理。 - 终端 - 热重启 - 热更新 - 全局异常 - 接口文档 - 依赖注入 - 插件模式 - ...... 未来还会继续支持更多功能,欢迎提出宝贵意见。 如果你觉得还不错或者有一点感兴趣,希望给个 star !谢谢!