🚀 Go-Spring 框架学习笔记
📑 目录
1. 🌱 Go-Spring 简介
1.1 什么是 Go-Spring
Go-Spring 是一个借鉴 Java Spring 框架设计思想的 Go 语言应用框架,提供了 IoC 容器、依赖注入、AOP、Web 开发等核心功能。
┌──────────────────────────────────────────────────────────────────┐│ Go-Spring 架构概览 │├──────────────────────────────────────────────────────────────────┤│ ││ ┌─────────────────────────────────────────────────────────┐ ││ │ Application Layer │ ││ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ ││ │ │ Web │ │ Data │ │ Cache │ │ MQ │ │ ││ │ │ Module │ │ Module │ │ Module │ │ Module │ │ ││ │ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ │ ││ └────────┼────────────┼────────────┼────────────┼────────┘ ││ │ │ │ │ ││ ┌────────┴────────────┴────────────┴────────────┴────────┐ ││ │ Spring Core (IoC/DI) │ ││ │ Bean 管理 · 依赖注入 · 生命周期控制 │ ││ └─────────────────────────┬──────────────────────────────┘ ││ │ ││ ┌─────────────────────────┴──────────────────────────────┐ ││ │ Configuration Layer │ ││ │ 配置文件 · 环境变量 · 热更新配置 │ ││ └────────────────────────────────────────────────────────┘ ││ │└──────────────────────────────────────────────────────────────────┘
1.2 Go-Spring 的核心特性
| |
|---|
| |
| |
| |
| 类似 Spring Boot 的 Web 开发体验 |
| |
| |
| |
1.3 与 Java Spring 的对比
2. ⚙️ 环境搭建与项目初始化
2.1 安装 Go-Spring
# 初始化项目go mod init hospital-management# 安装 Go-Spring 核心库go get github.com/go-spring/spring-core# 安装 Web 模块go get github.com/go-spring/spring-web# 安装配置模块go get github.com/go-spring/spring-boot
2.2 项目结构建议
hospital-management/├── cmd/│ └── main.go # 程序入口├── config/│ ├── application.yaml # 主配置文件│ └── application-dev.yaml # 开发环境配置├── internal/│ ├── controller/ # 控制器层│ │ ├── hospital_controller.go│ │ └── doctor_controller.go│ ├── service/ # 业务逻辑层│ │ ├── hospital_service.go│ │ └── doctor_service.go│ ├── repository/ # 数据访问层│ │ └── hospital_repo.go│ ├── model/ # 数据模型│ │ └── hospital.go│ └── config/ # 配置类│ └── app_config.go├── go.mod└── go.sum
2.3 第一个 Go-Spring 程序
// cmd/main.gopackage mainimport ("fmt""github.com/go-spring/spring-core/gs""github.com/go-spring/spring-core/gs/cond")// MessageService 定义服务接口type MessageService interface { GetMessage() string}// HelloService 实现 MessageServicetype HelloService struct { Name string`value:"${app.name:HospitalSystem}"`}// GetMessage 实现接口方法func(s *HelloService)GetMessage()string {return fmt.Sprintf("Hello from %s!", s.Name)}// 注册为 Beanfuncinit() {// 注册 HelloService gs.Object(new(HelloService)).Export((*MessageService)(nil))}funcmain() {// 启动 IoC 容器if err := gs.Run(); err != nil {panic(err) }// 获取 Beanvar msgService MessageServiceif err := gs.GetBean(&msgService); err != nil {panic(err) } fmt.Println(msgService.GetMessage())}
2.4 配置文件
# config/application.yamlapp:name:"医院管理系统"version:"1.0.0"port:8080server:host:"0.0.0.0"port:8080read-timeout:30swrite-timeout:30sdatabase:driver:mysqlhost:localhostport:3306username:rootpassword:123456database:hospital_dbmax-open-conns:100max-idle-conns:10log:level:infoformat:json
3. 🏗️ 核心概念:IoC 容器
3.1 什么是 IoC 容器
IoC (Inversion of Control) 即控制反转,是一种设计原则,将对象的创建和管理交给容器,而不是由对象自己控制。
┌──────────────────────────────────────────────────────────────────┐│ IoC 容器工作原理 │├──────────────────────────────────────────────────────────────────┤│ ││ 传统方式: ││ ┌─────────┐ new ┌─────────┐ ││ │ Service │ ───────────→ │ Repo │ ││ └─────────┘ └─────────┘ ││ ↑ 强耦合 ││ ││ IoC 方式: ││ ┌─────────┐ ┌─────────┐ ││ │ Service │ ←─────────── │ Repo │ ││ └─────────┘ 注入 └─────────┘ ││ ↑ ↑ ││ └───────────┬─────────────┘ ││ ↓ ││ ┌─────────┐ ││ │ IoC容器 │ 统一管理 Bean 的创建和依赖 ││ └─────────┘ ││ │└──────────────────────────────────────────────────────────────────┘
3.2 Bean 的注册方式
package mainimport ("github.com/go-spring/spring-core/gs")// ========== 方式1:使用 gs.Object 注册实例 ==========type UserService struct { Name string}funcinit() {// 直接注册实例 gs.Object(&UserService{Name: "default"})}// ========== 方式2:使用 gs.Bean 注册构造函数 ==========type OrderService struct { UserSvc *UserService}// NewOrderService 构造函数funcNewOrderService(userSvc *UserService) *OrderService {return &OrderService{UserSvc: userSvc}}funcinit() {// 注册构造函数,自动注入依赖 gs.Bean(NewOrderService)}// ========== 方式3:使用结构体标签注册 ==========type HospitalService struct { Repo *HospitalRepo `autowire:""` Config *AppConfig `autowire:""`}type HospitalRepo struct { DB *gorm.DB `autowire:""`}funcinit() {// 自动扫描并注册 gs.Object(new(HospitalRepo)) gs.Object(new(HospitalService))}
3.3 Bean 的作用域
package mainimport ("github.com/go-spring/spring-core/gs")// Singleton(默认)- 全局唯一实例type DatabaseService struct{}funcinit() { gs.Object(new(DatabaseService)).WithName("dbService")// 或显式指定 gs.Object(new(DatabaseService)).Singleton()}// Prototype - 每次获取创建新实例type RequestHandler struct{}funcinit() { gs.Object(new(RequestHandler)).Prototype()}
4. 💉 依赖注入(DI)详解
4.1 构造函数注入(推荐)
package serviceimport ("hospital-management/internal/repository")// HospitalService 医院服务type HospitalService struct { repo *repository.HospitalRepo cache *CacheService config *AppConfig}// NewHospitalService 构造函数注入funcNewHospitalService( repo *repository.HospitalRepo, cache *CacheService, config *AppConfig,) *HospitalService {return &HospitalService{ repo: repo, cache: cache, config: config, }}// GetHospital 获取医院信息func(s *HospitalService)GetHospital(id uint)(*Hospital, error) {// 先查缓存if cached := s.cache.Get(id); cached != nil {return cached, nil }// 查数据库 hospital, err := s.repo.FindByID(id)if err != nil {returnnil, err }// 写入缓存 s.cache.Set(id, hospital)return hospital, nil}funcinit() {// 注册到 IoC 容器 gs.Bean(NewHospitalService)}
4.2 属性注入(标签方式)
package serviceimport ("github.com/go-spring/spring-core/gs")// DoctorService 医生服务type DoctorService struct {// 通过 autowire 标签注入 Repo *DoctorRepo `autowire:""` HospitalSvc *HospitalService `autowire:""`// 通过 value 标签注入配置 PageSize int`value:"${pagination.page-size:20}"` MaxLimit int`value:"${pagination.max-limit:100}"`}// ListDoctors 查询医生列表func(s *DoctorService)ListDoctors(page int)([]Doctor, error) {if page < 1 { page = 1 } offset := (page - 1) * s.PageSizereturn s.Repo.FindAll(offset, s.PageSize)}funcinit() { gs.Object(new(DoctorService))}
4.3 接口注入
package mainimport ("github.com/go-spring/spring-core/gs")// Cache 缓存接口type Cache interface { Get(key string) (interface{}, bool) Set(key string, value interface{}) Delete(key string)}// RedisCache Redis 实现type RedisCache struct { Addr string`value:"${redis.addr:localhost:6379}"`}func(c *RedisCache)Get(key string)(interface{}, bool) {// Redis 实现returnnil, false}func(c *RedisCache)Set(key string, value interface{}) {// Redis 实现}func(c *RedisCache)Delete(key string) {// Redis 实现}// MemoryCache 内存实现type MemoryCache struct { data map[string]interface{}}func(c *MemoryCache)Get(key string)(interface{}, bool) { v, ok := c.data[key]return v, ok}func(c *MemoryCache)Set(key string, value interface{}) { c.data[key] = value}func(c *MemoryCache)Delete(key string) {delete(c.data, key)}// DataService 使用接口注入type DataService struct { Cache Cache `autowire:""`// 自动注入 Cache 接口的实现}funcinit() {// 注册实现并导出接口 gs.Object(new(RedisCache)).Export((*Cache)(nil))// 或注册内存缓存// gs.Object(new(MemoryCache)).Export((*Cache)(nil)) gs.Object(new(DataService))}
4.4 条件注入
package configimport ("github.com/go-spring/spring-core/gs""github.com/go-spring/spring-core/gs/cond")// MySQLConfig MySQL 配置type MySQLConfig struct { Host string`value:"${mysql.host}"` Port int`value:"${mysql.port}"` Database string`value:"${mysql.database}"`}// PostgresConfig PostgreSQL 配置type PostgresConfig struct { Host string`value:"${postgres.host}"` Port int`value:"${postgres.port}"` Database string`value:"${postgres.database}"`}funcinit() {// 条件注入:当 mysql.enabled=true 时注册 gs.Object(new(MySQLConfig)). Cond(cond.OnProperty("mysql.enabled", cond.HavingValue("true")))// 条件注入:当 postgres.enabled=true 时注册 gs.Object(new(PostgresConfig)). Cond(cond.OnProperty("postgres.enabled", cond.HavingValue("true")))// 条件注入:根据环境变量 gs.Object(new(DevConfig)). Cond(cond.OnProfile("dev")) gs.Object(new(ProdConfig)). Cond(cond.OnProfile("prod"))}
5. 🎯 控制反转(IoC)实战
5.1 医院管理系统的依赖关系
┌──────────────────────────────────────────────────────────────────┐│ 医院管理系统依赖关系图 │├──────────────────────────────────────────────────────────────────┤│ ││ ┌─────────────────┐ ││ │ HospitalController │ ← HTTP 请求入口 ││ └────────┬────────┘ ││ │ 依赖 ││ ▼ ││ ┌─────────────────┐ ┌─────────────────┐ ││ │ HospitalService │ ───→│ CacheService │ ││ └────────┬────────┘ └─────────────────┘ ││ │ ││ ▼ ││ ┌─────────────────┐ ┌─────────────────┐ ││ │ HospitalRepo │ ───→│ *gorm.DB │ ││ └─────────────────┘ └─────────────────┘ ││ ││ 所有依赖由 IoC 容器自动注入 ││ │└──────────────────────────────────────────────────────────────────┘
5.2 完整的依赖注入示例
package mainimport ("github.com/go-spring/spring-core/gs""gorm.io/driver/mysql""gorm.io/gorm")// ========== 配置类 ==========type DBConfig struct { Host string`value:"${database.host:localhost}"` Port int`value:"${database.port:3306}"` Username string`value:"${database.username:root}"` Password string`value:"${database.password:}"` Database string`value:"${database.database:hospital_db}"`}// ========== 基础设施 ==========// NewGormDB 创建数据库连接funcNewGormDB(config *DBConfig)(*gorm.DB, error) { dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local", config.Username, config.Password, config.Host, config.Port, config.Database) db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})if err != nil {returnnil, err }return db, nil}// ========== Repository 层 ==========type HospitalRepo struct { DB *gorm.DB `autowire:""`}func(r *HospitalRepo)FindByID(id uint)(*Hospital, error) {var hospital Hospitalif err := r.DB.First(&hospital, id).Error; err != nil {returnnil, err }return &hospital, nil}func(r *HospitalRepo)FindAll(offset, limit int)([]Hospital, error) {var hospitals []Hospitalif err := r.DB.Offset(offset).Limit(limit).Find(&hospitals).Error; err != nil {returnnil, err }return hospitals, nil}// ========== Service 层 ==========type CacheService struct { cache map[uint]*Hospital mu sync.RWMutex}funcNewCacheService() *CacheService {return &CacheService{ cache: make(map[uint]*Hospital), }}func(s *CacheService)Get(id uint) *Hospital { s.mu.RLock()defer s.mu.RUnlock()return s.cache[id]}func(s *CacheService)Set(id uint, hospital *Hospital) { s.mu.Lock()defer s.mu.Unlock() s.cache[id] = hospital}type HospitalService struct { repo *HospitalRepo cache *CacheService}funcNewHospitalService(repo *HospitalRepo, cache *CacheService) *HospitalService {return &HospitalService{ repo: repo, cache: cache, }}func(s *HospitalService)GetHospital(id uint)(*Hospital, error) {// 先查缓存if cached := s.cache.Get(id); cached != nil {return cached, nil }// 查数据库 hospital, err := s.repo.FindByID(id)if err != nil {returnnil, err }// 写入缓存 s.cache.Set(id, hospital)return hospital, nil}// ========== Controller 层 ==========type HospitalController struct { Service *HospitalService `autowire:""`}func(c *HospitalController)GetHospital(ctx gs.Context) { id := ctx.Param("id") hospitalID, _ := strconv.ParseUint(id, 10, 32) hospital, err := c.Service.GetHospital(uint(hospitalID))if err != nil { ctx.JSON(404, gs.Map{"code": 404,"msg": "医院不存在", })return } ctx.JSON(200, gs.Map{"code": 0,"data": hospital, })}// ========== 注册所有 Bean ==========funcinit() {// 配置 gs.Object(new(DBConfig))// 基础设施 gs.Bean(NewGormDB)// Repository gs.Object(new(HospitalRepo))// Service gs.Bean(NewCacheService) gs.Bean(NewHospitalService)// Controller gs.Object(new(HospitalController))}funcmain() {if err := gs.Run(); err != nil {panic(err) }}
6. 📦 Bean 的生命周期
6.1 生命周期回调
package serviceimport ("fmt""github.com/go-spring/spring-core/gs")// DatabaseService 数据库服务type DatabaseService struct { Host string`value:"${database.host}"` Port int`value:"${database.port}"`}// OnInit 初始化回调(Bean 创建后调用)func(s *DatabaseService)OnInit()error { fmt.Printf("🚀 DatabaseService 初始化: %s:%d\n", s.Host, s.Port)// 连接数据库returnnil}// OnDestroy 销毁回调(容器关闭时调用)func(s *DatabaseService)OnDestroy() { fmt.Println("🛑 DatabaseService 销毁,关闭连接")// 关闭数据库连接}// ========== 使用 Init 和 Destroy 标签 ==========type CacheManager struct { MaxSize int`value:"${cache.max-size:1000}"`}// Init 初始化方法func(c *CacheManager)Init()error { fmt.Printf("📦 CacheManager 初始化,最大缓存: %d\n", c.MaxSize)returnnil}// Destroy 销毁方法func(c *CacheManager)Destroy() { fmt.Println("🗑️ CacheManager 销毁,清理缓存")}funcinit() {// 方式1:自动识别 OnInit/OnDestroy gs.Object(new(DatabaseService))// 方式2:显式指定生命周期方法 gs.Object(new(CacheManager)). Init(func(c *CacheManager)error { return c.Init() }). Destroy(func(c *CacheManager) { c.Destroy() })}
6.2 启动和关闭顺序
package mainimport ("fmt""github.com/go-spring/spring-core/gs")// 启动顺序:1type ConfigLoader struct{}func(c *ConfigLoader)OnInit()error { fmt.Println("1️⃣ ConfigLoader: 加载配置")returnnil}// 启动顺序:2type DatabaseManager struct { Config *ConfigLoader `autowire:""`}func(d *DatabaseManager)OnInit()error { fmt.Println("2️⃣ DatabaseManager: 连接数据库")returnnil}// 启动顺序:3type CacheInitializer struct { DB *DatabaseManager `autowire:""`}func(c *CacheInitializer)OnInit()error { fmt.Println("3️⃣ CacheInitializer: 初始化缓存")returnnil}// 启动顺序:4type HTTPServer struct { Cache *CacheInitializer `autowire:""`}func(h *HTTPServer)OnInit()error { fmt.Println("4️⃣ HTTPServer: 启动 HTTP 服务")returnnil}func(h *HTTPServer)OnDestroy() { fmt.Println("🛑 HTTPServer: 关闭 HTTP 服务")}funcinit() { gs.Object(new(ConfigLoader)) gs.Object(new(DatabaseManager)) gs.Object(new(CacheInitializer)) gs.Object(new(HTTPServer))}
7. 🌐 Web 开发(Spring Web)
7.1 基础路由配置
package controllerimport ("github.com/go-spring/spring-core/gs""github.com/go-spring/spring-web")// HospitalController 医院控制器type HospitalController struct { Service *HospitalService `autowire:""`}funcinit() { gs.Object(new(HospitalController))}// InitRouter 初始化路由func(c *HospitalController)InitRouter(r web.Router) {// 基础 CRUD r.GetMapping("/api/hospitals", c.ListHospitals) r.GetMapping("/api/hospitals/:id", c.GetHospital) r.PostMapping("/api/hospitals", c.CreateHospital) r.PutMapping("/api/hospitals/:id", c.UpdateHospital) r.DeleteMapping("/api/hospitals/:id", c.DeleteHospital)// 搜索 r.GetMapping("/api/hospitals/search", c.SearchHospitals)}// GetHospital 获取单个医院func(c *HospitalController)GetHospital(ctx web.Context) { id := ctx.PathParam("id") hospitalID, _ := strconv.ParseUint(id, 10, 32) hospital, err := c.Service.GetHospital(uint(hospitalID))if err != nil { ctx.JSON(404, web.Map{"code": 404,"msg": "医院不存在", })return } ctx.JSON(200, web.Map{"code": 0,"data": hospital, })}// ListHospitals 获取医院列表func(c *HospitalController)ListHospitals(ctx web.Context) { page := ctx.QueryParamInt("page", 1) size := ctx.QueryParamInt("size", 10) hospitals, total, err := c.Service.ListHospitals(page, size)if err != nil { ctx.JSON(500, web.Map{"code": 500,"msg": err.Error(), })return } ctx.JSON(200, web.Map{"code": 0,"data": web.Map{"list": hospitals,"total": total,"page": page,"size": size, }, })}// CreateHospital 创建医院func(c *HospitalController)CreateHospital(ctx web.Context) {var req CreateHospitalRequestif err := ctx.Bind(&req); err != nil { ctx.JSON(400, web.Map{"code": 400,"msg": "参数错误: " + err.Error(), })return } hospital, err := c.Service.CreateHospital(&req)if err != nil { ctx.JSON(500, web.Map{"code": 500,"msg": err.Error(), })return } ctx.JSON(201, web.Map{"code": 0,"data": hospital, })}
7.2 中间件使用
package middlewareimport ("fmt""time""github.com/go-spring/spring-web")// LoggerMiddleware 日志中间件funcLoggerMiddleware()web.Filter {returnfunc(ctx web.Context, chain web.FilterChain) { start := time.Now() fmt.Printf("[%s] %s %s\n", start.Format("2006-01-02 15:04:05"), ctx.Method(), ctx.Path()) chain.Next(ctx) duration := time.Since(start) fmt.Printf("[%s] 响应时间: %v\n", time.Now().Format("2006-01-02 15:04:05"), duration) }}// AuthMiddleware 认证中间件funcAuthMiddleware()web.Filter {returnfunc(ctx web.Context, chain web.FilterChain) { token := ctx.Header("Authorization")if token == "" { ctx.JSON(401, web.Map{"code": 401,"msg": "未授权", })return }// 验证 tokenif !validateToken(token) { ctx.JSON(401, web.Map{"code": 401,"msg": "无效的 token", })return } chain.Next(ctx) }}// CORS 跨域中间件funcCORSMiddleware()web.Filter {returnfunc(ctx web.Context, chain web.FilterChain) { ctx.Header("Access-Control-Allow-Origin", "*") ctx.Header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS") ctx.Header("Access-Control-Allow-Headers", "Content-Type, Authorization")if ctx.Method() == "OPTIONS" { ctx.Status(200)return } chain.Next(ctx) }}// 注册中间件funcinit() { gs.SetRouterFilter(LoggerMiddleware()) gs.SetRouterFilter(CORSMiddleware())}
7.3 请求参数绑定
package controllerimport ("github.com/go-spring/spring-web")// CreateHospitalRequest 创建医院请求type CreateHospitalRequest struct { Name string`json:"name" binding:"required"`// 必填 Code string`json:"code" binding:"required"`// 必填 Level string`json:"level" binding:"oneof=三甲 三乙 二甲 二乙"` Province string`json:"province"` City string`json:"city"` Address string`json:"address"` Phone string`json:"phone" binding:"omitempty,len=11"` BedCount int`json:"bed_count" binding:"gte=0"`// 大于等于0 IsPublic bool`json:"is_public"`}// SearchHospitalRequest 搜索请求type SearchHospitalRequest struct { Name string`query:"name"`// Query 参数 City string`query:"city"` Level string`query:"level"` Page int`query:"page" default:"1"` PageSize int`query:"page_size" default:"10"`}// GetHospitalByID 路径参数示例func(c *HospitalController)GetHospitalByID(ctx web.Context) {// 路径参数 /api/hospitals/:id id := ctx.PathParam("id")// Query 参数 /api/hospitals/1?include_dept=true includeDept := ctx.QueryParamBool("include_dept", false)// Header userID := ctx.Header("X-User-ID") fmt.Printf("ID: %s, IncludeDept: %v, UserID: %s\n", id, includeDept, userID)}// CreateWithFile 文件上传示例func(c *HospitalController)CreateWithFile(ctx web.Context) {// 获取表单数据 name := ctx.FormParam("name")// 获取上传的文件 file, err := ctx.FormFile("logo")if err != nil { ctx.JSON(400, web.Map{"msg": "上传文件失败"})return }// 保存文件 ctx.SaveFile(file, "./uploads/"+file.Filename) ctx.JSON(200, web.Map{"name": name,"file": file.Filename, })}
8. 💾 数据库集成(Spring Data)
8.1 配置数据源
package configimport ("gorm.io/driver/mysql""gorm.io/gorm""gorm.io/gorm/logger")// DatabaseConfig 数据库配置type DatabaseConfig struct { Driver string`value:"${database.driver:mysql}"` Host string`value:"${database.host:localhost}"` Port int`value:"${database.port:3306}"` Username string`value:"${database.username}"` Password string`value:"${database.password}"` Database string`value:"${database.database}"` MaxOpenConns int`value:"${database.max-open-conns:100}"` MaxIdleConns int`value:"${database.max-idle-conns:10}"` LogLevel string`value:"${database.log-level:info}"`}// NewGormDB 创建 GORM 连接funcNewGormDB(config *DatabaseConfig)(*gorm.DB, error) { dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local", config.Username, config.Password, config.Host, config.Port, config.Database)var logLevel logger.LogLevelswitch config.LogLevel {case"silent": logLevel = logger.Silentcase"error": logLevel = logger.Errorcase"warn": logLevel = logger.Warndefault: logLevel = logger.Info } db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{ Logger: logger.Default.LogMode(logLevel), })if err != nil {returnnil, err }// 配置连接池 sqlDB, err := db.DB()if err != nil {returnnil, err } sqlDB.SetMaxOpenConns(config.MaxOpenConns) sqlDB.SetMaxIdleConns(config.MaxIdleConns)return db, nil}funcinit() { gs.Object(new(DatabaseConfig)) gs.Bean(NewGormDB)}
8.2 Repository 模式
package repositoryimport ("gorm.io/gorm")// BaseRepository 基础仓库type BaseRepository struct { DB *gorm.DB `autowire:""`}// Transaction 执行事务func(r *BaseRepository)Transaction(fn func(tx *gorm.DB)error) error {return r.DB.Transaction(fn)}// HospitalRepo 医院仓库type HospitalRepo struct { BaseRepository}// NewHospitalRepo 创建仓库实例funcNewHospitalRepo(db *gorm.DB) *HospitalRepo {return &HospitalRepo{ BaseRepository: BaseRepository{DB: db}, }}// Create 创建医院func(r *HospitalRepo)Create(hospital *Hospital)error {return r.DB.Create(hospital).Error}// FindByID 根据ID查询func(r *HospitalRepo)FindByID(id uint)(*Hospital, error) {var hospital Hospitalif err := r.DB.First(&hospital, id).Error; err != nil {returnnil, err }return &hospital, nil}// FindByCode 根据编码查询func(r *HospitalRepo)FindByCode(code string)(*Hospital, error) {var hospital Hospitalif err := r.DB.Where("code = ?", code).First(&hospital).Error; err != nil {returnnil, err }return &hospital, nil}// List 分页查询func(r *HospitalRepo)List(page, size int, conditions map[string]interface{})([]Hospital, int64, error) {var hospitals []Hospitalvar total int64 query := r.DB.Model(&Hospital{})// 动态条件for key, value := range conditions {if value != "" && value != 0 { query = query.Where(key+" = ?", value) } }// 统计总数if err := query.Count(&total).Error; err != nil {returnnil, 0, err }// 分页查询 offset := (page - 1) * sizeif err := query.Offset(offset).Limit(size).Find(&hospitals).Error; err != nil {returnnil, 0, err }return hospitals, total, nil}// Update 更新医院func(r *HospitalRepo)Update(id uint, updates map[string]interface{})error {return r.DB.Model(&Hospital{}).Where("id = ?", id).Updates(updates).Error}// Delete 删除医院(软删除)func(r *HospitalRepo)Delete(id uint)error {return r.DB.Delete(&Hospital{}, id).Error}// Search 搜索医院func(r *HospitalRepo)Search(keyword string, page, size int)([]Hospital, int64, error) {var hospitals []Hospitalvar total int64 query := r.DB.Model(&Hospital{}). Where("name LIKE ? OR address LIKE ?", "%"+keyword+"%", "%"+keyword+"%") query.Count(&total) offset := (page - 1) * size err := query.Offset(offset).Limit(size).Find(&hospitals).Errorreturn hospitals, total, err}funcinit() { gs.Bean(NewHospitalRepo)}
8.3 事务管理
package serviceimport ("gorm.io/gorm")// HospitalService 医院服务type HospitalService struct { repo *HospitalRepo deptRepo *DepartmentRepo db *gorm.DB `autowire:""`}// CreateHospitalWithDepartments 创建医院及科室(事务)func(s *HospitalService)CreateHospitalWithDepartments( hospital *Hospital, departments []Department,)error {// 使用事务return s.db.Transaction(func(tx *gorm.DB)error {// 创建医院if err := tx.Create(hospital).Error; err != nil {return err }// 创建科室for i := range departments { departments[i].HospitalID = hospital.ID }if err := tx.Create(&departments).Error; err != nil {return err }returnnil })}// TransferDoctor 医生调动(跨医院事务)func(s *HospitalService)TransferDoctor( doctorID uint, fromHospitalID, toHospitalID uint,)error {return s.db.Transaction(func(tx *gorm.DB)error {// 1. 更新医生所属医院if err := tx.Model(&Doctor{}). Where("id = ?", doctorID). Update("hospital_id", toHospitalID).Error; err != nil {return err }// 2. 减少原医院医生数if err := tx.Model(&Hospital{}). Where("id = ?", fromHospitalID). UpdateColumn("doctor_count", gorm.Expr("doctor_count - 1")).Error; err != nil {return err }// 3. 增加目标医院医生数if err := tx.Model(&Hospital{}). Where("id = ?", toHospitalID). UpdateColumn("doctor_count", gorm.Expr("doctor_count + 1")).Error; err != nil {return err }returnnil })}
9. 🔒 配置管理与热更新
9.1 多环境配置
# config/application.yaml (默认配置)app:name:医院管理系统version:1.0.0server:port:8080database:driver:mysqlhost:localhostport:3306database:hospital_db---# config/application-dev.yaml (开发环境)spring:profiles:devdatabase:host:localhostusername:rootpassword:dev123log:level:debug---# config/application-prod.yaml (生产环境)spring:profiles:prodserver:port:80database:host:prod-db.internalusername:${DB_USERNAME}password:${DB_PASSWORD}log:level:warn
9.2 配置热更新
package configimport ("github.com/go-spring/spring-core/gs")// DynamicConfig 动态配置type DynamicConfig struct {// 使用 dynamic 标签支持热更新 MaxUploadSize int64`value:"${app.max-upload-size:10485760}" dynamic:"true"` EnableCache bool`value:"${app.enable-cache:true}" dynamic:"true"` CacheTTL int`value:"${app.cache-ttl:300}" dynamic:"true"`}// OnConfigChange 配置变更回调func(c *DynamicConfig)OnConfigChange(key string, value interface{}) { fmt.Printf("⚙️ 配置变更: %s = %v\n", key, value)switch key {case"app.enable-cache":if c.EnableCache { fmt.Println("✅ 缓存已启用") } else { fmt.Println("❌ 缓存已禁用") }case"app.cache-ttl": fmt.Printf("🕐 缓存 TTL 更新为: %d 秒\n", c.CacheTTL) }}// ConfigWatcher 配置观察者type ConfigWatcher struct { Config *DynamicConfig `autowire:""`}func(w *ConfigWatcher)OnInit()error {// 监听配置变更 gs.SubscribeConfigChange(func(event gs.ConfigChangeEvent) { fmt.Printf("📝 配置变更事件: %s\n", event.Key) })returnnil}funcinit() { gs.Object(new(DynamicConfig)) gs.Object(new(ConfigWatcher))}
9.3 配置验证
package configimport ("errors""fmt")// ServerConfig 服务器配置type ServerConfig struct { Host string`value:"${server.host:0.0.0.0}"` Port int`value:"${server.port:8080}"`}// Validate 配置验证func(c *ServerConfig)Validate()error {if c.Port < 1 || c.Port > 65535 {return fmt.Errorf("端口号必须在 1-65535 之间: %d", c.Port) }returnnil}// DatabaseConfig 数据库配置type DatabaseConfig struct { Host string`value:"${database.host}"` Port int`value:"${database.port:3306}"` Username string`value:"${database.username}"` Password string`value:"${database.password}"` Database string`value:"${database.database}"`}// Validate 配置验证func(c *DatabaseConfig)Validate()error {if c.Host == "" {return errors.New("数据库主机不能为空") }if c.Username == "" {return errors.New("数据库用户名不能为空") }if c.Password == "" {return errors.New("数据库密码不能为空") }if c.Database == "" {return errors.New("数据库名不能为空") }returnnil}// OnInit 初始化时验证func(c *DatabaseConfig)OnInit()error {return c.Validate()}
10. 💼 实战:医院管理系统
10.1 完整项目结构
hospital-management/├── cmd/│ └── main.go├── config/│ ├── application.yaml│ └── application-dev.yaml├── internal/│ ├── model/│ │ ├── hospital.go│ │ ├── department.go│ │ └── doctor.go│ ├── repository/│ │ ├── hospital_repo.go│ │ ├── department_repo.go│ │ └── doctor_repo.go│ ├── service/│ │ ├── hospital_service.go│ │ ├── department_service.go│ │ └── doctor_service.go│ ├── controller/│ │ ├── hospital_controller.go│ │ ├── department_controller.go│ │ └── doctor_controller.go│ └── config/│ └── app_config.go├── pkg/│ ├── response/│ │ └── response.go│ └── utils/│ └── utils.go├── go.mod└── go.sum
10.2 完整代码实现
// ========== internal/model/hospital.go ==========package modelimport ("time""gorm.io/gorm")// Hospital 医院模型type Hospital struct { ID uint`gorm:"primaryKey" json:"id"` Name string`gorm:"size:100;not null;index" json:"name"` Code string`gorm:"size:20;uniqueIndex" json:"code"` Level string`gorm:"size:10" json:"level"` Province string`gorm:"size:50" json:"province"` City string`gorm:"size:50;index" json:"city"` Address string`gorm:"size:255" json:"address"` Phone string`gorm:"size:20" json:"phone"` BedCount int`gorm:"default:0" json:"bed_count"` DoctorCount int`gorm:"default:0" json:"doctor_count"` IsPublic bool`gorm:"default:true" json:"is_public"` Status int8`gorm:"default:1" json:"status"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`}// TableName 指定表名func(Hospital)TableName()string {return"hospitals"}// ========== internal/service/hospital_service.go ==========package serviceimport ("errors""hospital-management/internal/model""hospital-management/internal/repository")// HospitalService 医院服务type HospitalService struct { repo *repository.HospitalRepo cache *CacheService}// NewHospitalService 构造函数funcNewHospitalService( repo *repository.HospitalRepo, cache *CacheService,) *HospitalService {return &HospitalService{ repo: repo, cache: cache, }}// CreateHospitalRequest 创建请求type CreateHospitalRequest struct { Name string`json:"name" binding:"required"` Code string`json:"code" binding:"required"` Level string`json:"level"` Province string`json:"province"` City string`json:"city"` Address string`json:"address"` Phone string`json:"phone"` BedCount int`json:"bed_count"` IsPublic bool`json:"is_public"`}// CreateHospital 创建医院func(s *HospitalService)CreateHospital(req *CreateHospitalRequest)(*model.Hospital, error) {// 检查编码是否已存在 existing, _ := s.repo.FindByCode(req.Code)if existing != nil {returnnil, errors.New("医院编码已存在") } hospital := &model.Hospital{ Name: req.Name, Code: req.Code, Level: req.Level, Province: req.Province, City: req.City, Address: req.Address, Phone: req.Phone, BedCount: req.BedCount, IsPublic: req.IsPublic, Status: 1, }if err := s.repo.Create(hospital); err != nil {returnnil, err }return hospital, nil}// GetHospital 获取医院func(s *HospitalService)GetHospital(id uint)(*model.Hospital, error) {// 先查缓存if cached := s.cache.GetHospital(id); cached != nil {return cached, nil } hospital, err := s.repo.FindByID(id)if err != nil {returnnil, err }// 写入缓存 s.cache.SetHospital(id, hospital)return hospital, nil}// ListHospitals 列表查询func(s *HospitalService)ListHospitals(page, size int, city, level string)([]model.Hospital, int64, error) { conditions := make(map[string]interface{})if city != "" { conditions["city"] = city }if level != "" { conditions["level"] = level }return s.repo.List(page, size, conditions)}// UpdateHospital 更新医院func(s *HospitalService)UpdateHospital(id uint, updates map[string]interface{})error {if err := s.repo.Update(id, updates); err != nil {return err }// 清除缓存 s.cache.DeleteHospital(id)returnnil}// DeleteHospital 删除医院func(s *HospitalService)DeleteHospital(id uint)error {if err := s.repo.Delete(id); err != nil {return err }// 清除缓存 s.cache.DeleteHospital(id)returnnil}// ========== internal/controller/hospital_controller.go ==========package controllerimport ("strconv""github.com/go-spring/spring-core/gs""github.com/go-spring/spring-web""hospital-management/internal/service""hospital-management/pkg/response")// HospitalController 医院控制器type HospitalController struct { Service *service.HospitalService `autowire:""`}funcinit() { gs.Object(new(HospitalController))}// InitRouter 注册路由func(c *HospitalController)InitRouter(r web.Router) { r.GetMapping("/api/hospitals", c.List) r.GetMapping("/api/hospitals/:id", c.Get) r.PostMapping("/api/hospitals", c.Create) r.PutMapping("/api/hospitals/:id", c.Update) r.DeleteMapping("/api/hospitals/:id", c.Delete)}// Get 获取单个医院func(c *HospitalController)Get(ctx web.Context) { id, _ := strconv.ParseUint(ctx.PathParam("id"), 10, 32) hospital, err := c.Service.GetHospital(uint(id))if err != nil { response.Error(ctx, 404, "医院不存在")return } response.Success(ctx, hospital)}// List 获取列表func(c *HospitalController)List(ctx web.Context) { page := ctx.QueryParamInt("page", 1) size := ctx.QueryParamInt("size", 10) city := ctx.QueryParam("city") level := ctx.QueryParam("level") hospitals, total, err := c.Service.ListHospitals(page, size, city, level)if err != nil { response.Error(ctx, 500, err.Error())return } response.Page(ctx, hospitals, total, page, size)}// Create 创建医院func(c *HospitalController)Create(ctx web.Context) {var req service.CreateHospitalRequestif err := ctx.Bind(&req); err != nil { response.Error(ctx, 400, "参数错误: "+err.Error())return } hospital, err := c.Service.CreateHospital(&req)if err != nil { response.Error(ctx, 500, err.Error())return } response.Created(ctx, hospital)}// Update 更新医院func(c *HospitalController)Update(ctx web.Context) { id, _ := strconv.ParseUint(ctx.PathParam("id"), 10, 32)var updates map[string]interface{}if err := ctx.Bind(&updates); err != nil { response.Error(ctx, 400, "参数错误")return }if err := c.Service.UpdateHospital(uint(id), updates); err != nil { response.Error(ctx, 500, err.Error())return } response.Success(ctx, nil)}// Delete 删除医院func(c *HospitalController)Delete(ctx web.Context) { id, _ := strconv.ParseUint(ctx.PathParam("id"), 10, 32)if err := c.Service.DeleteHospital(uint(id)); err != nil { response.Error(ctx, 500, err.Error())return } response.Success(ctx, nil)}// ========== pkg/response/response.go ==========package responseimport"github.com/go-spring/spring-web"// Success 成功响应funcSuccess(ctx web.Context, data interface{}) { ctx.JSON(200, web.Map{"code": 0,"msg": "success","data": data, })}// Created 创建成功funcCreated(ctx web.Context, data interface{}) { ctx.JSON(201, web.Map{"code": 0,"msg": "created","data": data, })}// Error 错误响应funcError(ctx web.Context, code int, msg string) { ctx.JSON(code, web.Map{"code": code,"msg": msg,"data": nil, })}// Page 分页响应funcPage(ctx web.Context, list interface{}, total int64, page, size int) { ctx.JSON(200, web.Map{"code": 0,"msg": "success","data": web.Map{"list": list,"total": total,"page": page,"page_size": size, }, })}// ========== cmd/main.go ==========package mainimport ("github.com/go-spring/spring-core/gs" _ "hospital-management/internal/config" _ "hospital-management/internal/controller" _ "hospital-management/internal/repository" _ "hospital-management/internal/service")funcmain() {// 启动应用if err := gs.Run(); err != nil {panic(err) }}
10.3 运行效果
# 启动应用go run cmd/main.go# 输出🚀 Go-Spring 启动中...📦 加载配置文件: config/application.yaml📦 加载配置文件: config/application-dev.yaml✅ DatabaseService 初始化: localhost:3306✅ CacheService 初始化✅ HospitalService 初始化✅ HospitalController 初始化,注册路由🌐 HTTP 服务启动: http://0.0.0.0:8080# 测试 APIcurl http://localhost:8080/api/hospitals# 响应{"code": 0,"msg": "success","data": {"list": [...],"total": 10,"page": 1,"page_size": 10 }}