这是芋道商城(ruoyi-vue-pro)的 Go 语言实现版本,提供与 Java 实现完全对齐的企业级电商 API 服务。项目采用 Go + Gin + GORM 技术栈,确保 API 返回结构、数据类型、逻辑实现与 Java 版本保持 97% 的对齐度。
本项目基于 Clean Architecture 设计原则,实现了清晰的分层架构,非常适合作为企业级应用的后端服务基础。
| 指标 | 数值 | 说明 |
|---|---|---|
| Go 源文件 | 573+ | 核心代码文件 |
| Service 层代码 | 6,772+ 行 | 业务逻辑层 |
| 总代码行数 | 100K+ | 包括注释和文档 |
| API 端点 | 300+ | 实现的接口 |
| 业务模块 | 12+ | 主要功能模块 |
系统管理 ████████████████████ 100% - 完全对齐 会员中心 ████████████████████ 97% - 完全对齐 商品中心 ████████████████████ 97% - 完全对齐 交易中心 ████████████████████ 97% - 完全对齐 支付中心 ████████████████████ 96% - 完全对齐 促销中心 ████████████████████ 96% - 完全对齐 分销模块 ████████████████████ 95% - 基本对齐
提供企业级后台管理系统的基础功能:
完整的会员管理体系:
电商平台核心商品管理:
完整的订单和交易流程:
集成多种支付渠道:
丰富的营销活动工具:
社交分销体系:
通用基础功能:
HTTP Request ↓ ┌─────────────────────────────────────┐ │ Handler Layer (API) │ ← 请求处理、参数验证 ├─────────────────────────────────────┤ ↓ ↑ ┌─────────────────────────────────────┐ │ Service Layer (Service) │ ← 业务逻辑、事务管理 ├─────────────────────────────────────┤ ↓ ↑ ┌─────────────────────────────────────┐ │ Repository Layer (DAO) │ ← 数据访问、数据库操作 ├─────────────────────────────────────┤ ↓ ┌─────────────────────────────────────┐ │ Database (MySQL/Redis) │ ← 数据存储 └─────────────────────────────────────┘
/ ├── /admin-api/ # 后台管理API │ ├── /infra/ # 基础设施 │ ├── /system/ # 系统管理 │ ├── /member/ # 会员管理 │ ├── /product/ # 商品管理 │ ├── /trade/ # 交易管理 │ ├── /pay/ # 支付管理 │ ├── /promotion/ # 促销管理 │ └── /statistics/ # 统计报表 │ └── /app-api/ # 移动端/用户API ├── /member/ # 会员中心 ├── /product/ # 商品中心 ├── /trade/ # 交易中心 ├── /promotion/ # 营销中心 └── /pay/ # 支付相关
┌─────────────────────────────────────┐ │ Gin Web Server │ ├─────────────────────────────────────┤ │ Middleware Chain │ │ ↓ ErrorHandler → Recovery → │ │ ↓ APIAccessLog → Auth → Validator │ ├─────────────────────────────────────┤ │ Router (API Endpoint) │ ├─────────────────────────────────────┤ │ Handler Layer ← Handler 依赖注入 │ ↓ Wire │ │ Service Layer ← Service 依赖注入 │ ↓ Wire │ │ Repository Layer ← Repository 依赖注入 ├─────────────────────────────────────┤ │ Database Layer │ │ • MySQL (GORM + GORM Gen) │ │ • Redis (go-redis) │ └─────────────────────────────────────┘
// Wire 自动构建依赖关系
wire.Build(
// 配置层
config.CModule,
logger.Module,
// 数据层
repo.Module,
// 业务层
service.Module,
// API 层
handler.Module,
// 初始化 App
InitApp,
)
所有 API 响应都遵循统一格式:
{
"code": 0,
"msg": "",
"data": {
// 实际业务数据
}
}
{
"code": 0,
"msg": "",
"data": {
"list": [], // 数据列表
"total": 100 // 总记录数
}
}
{
"code": 400,
"msg": "参数错误",
"data": null
}
| 错误码 | 含义 | 使用场景 |
|---|---|---|
0 | 成功 | 请求成功 |
400 | 参数错误 | 请求参数验证失败 |
401 | 未授权 | 未登录或 Token 无效 |
403 | 禁止访问 | 无权限访问资源 |
404 | 资源不存在 | 请求的资源不存在 |
409 | 冲突 | 资源冲突(如重复创建) |
500 | 系统异常 | 服务器内部错误 |
501 | 未实现 | 功能未实现 |
503 | 服务不可用 | 服务暂时不可用 |
type Claims struct {
UserID int64 `json:"userId"` // 用户 ID
UserType int `json:"userType"` // 用户类型: 0=Member, 1=Admin
TenantID int64 `json:"tenantId"` // 租户 ID
Nickname string `json:"nickname"` // 用户昵称
jwt.RegisteredClaims
}
支持三种方式传递 Token:
Authorization: Bearer <token>?Authorization=<token>Authorization=<token>// 获取用户 ID
userID := core.GetLoginUserID(c)
// 获取完整用户信息
loginUser := core.GetLoginUser(c)
if loginUser != nil {
// 访问所有用户信息
}
app:
name: "ruoyi-mall-go" # 应用名称
env: "local" # 运行环境
http:
port: ":48080" # 服务端口
mode: "debug" # Gin 模式: debug/release
log:
level: "debug" # 日志级别: debug/info/warn/error
filename: "logs/app.log" # 日志文件路径
max_size: 100 # 单个文件最大大小 (MB)
max_age: 7 # 文件保留天数
max_backups: 10 # 保留文件数量
mysql:
dsn: "user:password@tcp(host:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
max_idle: 10 # 最大空闲连接数
max_open: 100 # 最大打开连接数
max_lifetime: 3600 # 连接最大存活时间 (秒)
redis:
addr: "localhost:6379" # Redis 地址
password: "" # Redis 密码
db: 0 # Redis 数据库
trade:
express:
client: "kd100" # 快递查询客户端
kd100:
customer: "xxx" # 快递100客户ID
key: "xxx" # 快递100密钥
配置项也支持通过环境变量覆盖:
export HTTP_PORT=:18080
export MYSQL_DSN=user:pass@tcp(db:3306)/yudao
export REDIS_ADDR=redis:6379
git clone https://github.com/wxlbd/ruoyi-mall-go.git
cd ruoyi-mall-go
make deps
或使用原生 Go 命令:
go mod tidy go mod download
编辑 config/config.local.yaml,配置数据库和 Redis 连接:
mysql:
dsn: "root:password@tcp(localhost:3306)/yudao?charset=utf8mb4&parseTime=True&loc=Local"
redis:
addr: "localhost:6379"
password: ""
db: 0
如果修改了数据模型,需要重新生成 DAO 代码:
make gen
make run
# 或
go run cmd/server/main.go
# 首次使用会自动安装 Air
make dev
make build ./server
服务启动后,可以访问:
# 示例:登录
curl -X POST http://localhost:48080/admin-api/system/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"admin123"}'
# 使用 Token 访问受保护接口
curl -X GET http://localhost:48080/admin-api/system/user/page \
-H "Authorization: Bearer YOUR_TOKEN"
ruoyi-mall-go/ ├── cmd/ # 应用入口 │ ├── server/ # 主服务 │ │ ├── main.go # 应用启动文件 │ │ ├── wire.go # Wire DI 配置 (build tag: wireinject) │ │ ├── wire_gen.go # Wire 生成的代码(编译产物) │ │ └── server # 编译后的可执行文件 │ └── gen/ # GORM 代码生成工具 │ └── generate.go # GORM Gen 配置脚本 │ ├── config/ # 配置文件 │ └── config.local.yaml # 本地开发配置 │ ├── internal/ # 内部代码(不可外部导入) │ ├── api/ # HTTP API 层 │ │ ├── handler/ # 请求处理器 (Controller) │ │ │ ├── admin/ # 后台管理 API handlers │ │ │ │ ├── infra/ # 基础设施管理处理器 │ │ │ │ ├── member/ # 会员管理处理器 │ │ │ │ ├── pay/ # 支付管理处理器 │ │ │ │ ├── product/ # 商品管理处理器 │ │ │ │ ├── promotion/ # 促销管理处理器 │ │ │ │ ├── statistics/ # 统计管理处理器 │ │ │ │ ├── system/ # 系统管理处理器 │ │ │ │ ├── trade/ # 交易管理处理器 │ │ │ │ └── *_handler.go # 单个模块处理器 │ │ │ ├── app/ # 移动端/用户 API handlers │ │ │ │ ├── mall/ # 商城应用处理器 │ │ │ │ ├── member/ # 用户中心处理器 │ │ │ │ ├── pay/ # 支付处理器 │ │ │ │ └── *_handler.go # 单个模块处理器 │ │ │ └── *_handler.go # 通用处理器 │ │ ├── contract/ # API 契约 (请求响应对象) │ │ │ ├── admin/ # 后台 API 契约 │ │ │ │ ├── infra/ # 基础设施 contracts │ │ │ │ ├── mall/ # 商城模块 contracts │ │ │ │ │ ├── product/ # 商品相关 contracts │ │ │ │ │ ├── promotion/ # 促销相关 contracts │ │ │ │ │ └── trade/ # 交易相关 contracts │ │ │ │ ├── member/ # 会员 contracts │ │ │ │ ├── pay/ # 支付 contracts │ │ │ │ ├── statistics/ # 统计 contracts │ │ │ │ └── system/ # 系统 contracts │ │ │ ├── app/ # 移动端 API 契约 │ │ │ │ ├── mall/ # 商城应用 contracts │ │ │ │ └── pay/ # 支付相关 contracts │ │ │ └── common/ # 通用 contracts │ │ └── router/ # 路由定义 │ │ ├── admin.go # 后台管理路由 │ │ ├── app.go # 用户端路由 │ │ └── *.go # 各模块路由注册 │ │ │ ├── middleware/ # 中间件 │ │ ├── auth.go # JWT 认证中间件 │ │ ├── error.go # 全局错误处理 │ │ ├── recovery.go # Panic 恢复 │ │ ├── apilog.go # API 访问日志 │ │ ├── validator.go # 参数验证 │ │ └── *.go # 其他中间件 │ │ │ ├── model/ # 数据模型 (DO/Entity) │ │ ├── member/ # 会员相关模型 │ │ ├── pay/ # 支付相关模型 │ │ ├── product/ # 商品相关模型 │ │ ├── promotion/ # 促销相关模型 │ │ ├── trade/ # 交易相关模型 │ │ ├── system_*.go # 系统管理模型 │ │ ├── infra_*.go # 基础设施模型 │ │ └── consts.go # 常量定义 │ │ │ ├── service/ # 业务服务层 (Business Logic) │ │ ├── member/ # 会员业务服务 │ │ ├── pay/ # 支付业务服务 (13+ 文件) │ │ ├── product/ # 商品业务服务 │ │ ├── promotion/ # 促销业务服务 (20+ 文件) │ │ ├── sms/ # 短信服务 │ │ ├── social/ # 社交服务 │ │ ├── trade/ # 交易业务服务 │ │ ├── auth.go # 认证服务 │ │ ├── permission.go # 权限检查服务 │ │ ├── scheduler.go # 定时任务调度 │ │ ├── *_statistics.go # 统计数据服务 │ │ └── *.go # 其他系统服务 │ │ │ ├── repo/ # 数据访问层 (Repository/DAO) │ │ ├── query/ # GORM Gen 生成的查询代码 │ │ ├── pay/ # 支付数据访问 │ │ ├── product/ # 商品数据访问 │ │ ├── trade/ # 交易数据访问 │ │ └── *.go # 自定义 Repository │ │ │ └── pkg/ # 内部工具包 │ ├── area/ # 地区服务 (IP 地址定位) │ ├── datascope/ # 数据权限过滤 │ ├── file/ # 文件处理工具 │ ├── permission/ # 权限检查工具 │ ├── statistics/ # 统计数据处理 │ └── websocket/ # WebSocket 支持 │ ├── pkg/ # 公共包(可外部导入) │ ├── cache/ # 缓存管理 │ ├── config/ # 配置管理和加载 │ ├── context/ # 上下文工具 │ ├── database/ # 数据库初始化 │ ├── errors/ # 错误处理 │ ├── excel/ # Excel 操作 │ ├── logger/ # 日志管理 (Zap) │ ├── pagination/ # 分页处理 │ ├── response/ # 统一响应格式 │ ├── types/ # 通用类型定义 │ └── utils/ # 工具函数库 │ ├── scripts/ # 脚本工具 │ └── *.sh # 各类辅助脚本 │ ├── logs/ # 日志输出目录 │ └── app.log # 应用日志文件 │ ├── tmp/ # 临时文件目录 │ └── upload/ # 上传文件存储 │ ├── Makefile # 构建脚本 ├── go.mod # Go 模块定义 ├── go.sum # 依赖版本锁定 └── README.md # 项目文档
四层架构映射:
| 层级 | 目录 | 职责 | 示例 |
|---|---|---|---|
| API 层 | internal/api/handler/ | 请求处理、参数绑定 | UserHandler.GetUser() |
| 业务层 | internal/service/ | 业务逻辑、事务处理 | UserService.CreateUser() |
| 数据层 | internal/repo/ | 数据访问、数据库操作 | UserRepository.Create() |
| 模型层 | internal/model/ | 数据结构定义 | type User struct |
internal/api/contract/ 目录包含所有 API 的请求和响应对象,按业务域和 API 类型组织:
结构说明:
contract/admin/ - 后台管理 API 的请求/响应对象
member/、pay/、product/、promotion/、system/ 等{entity}_{type}.go (如 user_req.go、user_resp.go) 或 {domain}.gocontract/app/ - 移动端/用户 API 的请求/响应对象
mall/、pay/ 等contract/common/ - 通用的请求/响应对象
优点:
按业务模块组织(横向):
member/ - 会员中心模块(4 层都有)pay/ - 支付中心模块(4 层都有)product/ - 商品中心模块(4 层都有)promotion/ - 促销中心模块(4 层都有)trade/ - 交易中心模块(4 层都有)通用功能(无模块划分):
middleware/ - 中间件(共享)pkg/ - 公共工具库(共享)internal/pkg/ - 内部工具包(共享)| 文件 | 作用 |
|---|---|
cmd/server/wire.go | Wire DI 配置,定义依赖注入规则 |
cmd/server/wire_gen.go | 编译产物,运行 make wire 生成 |
pkg/config/ | 加载配置文件和环境变量 |
pkg/logger/ | 初始化日志系统 (Zap) |
pkg/database/ | 初始化数据库连接 |
internal/middleware/ | 所有中间件集中处理 |
按照以下步骤添加新功能:
在 internal/model/ 目录下创建模型:
type User struct {
ID int64 `gorm:"column:id;primaryKey;autoIncrement"` // 主键
Username string `gorm:"column:username;type:varchar(100)"` // 用户名
Email string `gorm:"column:email;type:varchar(100)"` // 邮箱
CreatedAt time.Time `gorm:"column:created_at;type:datetime"` // 创建时间
UpdateTime time.Time `gorm:"column:updated_at;type:datetime"` // 更新时间
}
运行代码生成器:
make gen
在 internal/api/contract/ 下按模块创建请求/响应对象。例如创建用户相关的 API 契约:
// internal/api/contract/admin/system/user.go
package system
// 创建用户请求
type CreateUserReq struct {
Username string `json:"username" binding:"required,min=3,max=50"`
Email string `json:"email" binding:"required,email"`
Password string `json:"password" binding:"required,min=6,max=30"`
}
// 创建用户响应
type CreateUserResp struct {
ID int64 `json:"id"`
Username string `json:"username"`
Email string `json:"email"`
CreatedAt string `json:"createdAt"`
}
// 用户详情响应
type UserDetail struct {
ID int64 `json:"id"`
Username string `json:"username"`
Email string `json:"email"`
Status int `json:"status"`
}
在 internal/repo/ 下创建 Repository:
type UserRepository interface {
Create(ctx context.Context, user *model.User) error
FindByID(ctx context.Context, id int64) (*model.User, error)
}
在 internal/service/ 下创建 Service:
type UserService interface {
CreateUser(ctx context.Context, req *contract.CreateUserReq) (*contract.CreateUserResp, error)
GetUser(ctx context.Context, id int64) (*contract.UserDetail, error)
}
在 internal/api/handler/admin/system/ 下创建 Handler:
func (h *UserHandler) CreateUser(c *gin.Context) {
var req contract.CreateUserReq
if err := c.ShouldBindJSON(&req); err != nil {
core.WriteError(c, core.ParamErrCode, err.Error())
return
}
resp, err := h.userService.CreateUser(c.Request.Context(), &req)
if err != nil {
core.WriteError(c, core.ServerErrCode, err.Error())
return
}
core.WriteSuccess(c, resp)
}
在 internal/api/router/ 中添加路由:
userGroup := router.Group("/user")
{
userGroup.POST("/create", userHandler.CreateUser)
userGroup.GET("/detail", userHandler.GetUser)
}
在 cmd/server/wire.go 中注册:
wire.Bind(new(service.UserService), new(*service.UserServiceImpl)),
wire.Bind(new(repo.UserRepository), new(*repo.UserRepositoryImpl)),
重新生成 Wire 代码:
make wire
使用 Gin 的 binding 标签进行参数验证:
type CreateUserReq struct {
Username string `json:"username" binding:"required,min=3,max=50"` // 必填,3-50字符
Email string `json:"email" binding:"required,email"` // 必填,邮箱格式
Age int `json:"age" binding:"min=18,max=120"` // 范围验证
}
常用验证标签:
required - 必填字段min=N - 最小值max=N - 最大值len=N - 固定长度email - 邮箱格式url - URL 格式dive - 嵌套结构体验证统一使用错误码体系:
// 参数错误
core.WriteError(c, core.ParamErrCode, "用户名不能为空")
// 业务错误
if user == nil {
core.WriteError(c, core.NotFoundCode, "用户不存在")
return
}
// 系统错误
if err != nil {
core.WriteError(c, core.ServerErrCode, "系统异常")
return
}
# 编译项目
make build
# 或
make build APP_NAME=myapp
# 直接运行
make run
# 使用 Air 热重载开发
make dev
# 下载依赖
make deps
# 重新生成 Wire 依赖注入代码
make wire
# 重新生成 GORM DAO 代码
make gen
# 清理构建产物
make clean
# 查看帮助
make help
# 指定应用名称
make build APP_NAME=server-custom
# 指定命令路径
make run CMD_PATH=cmd/custom/main.go
| 模块 | 对齐度 | 说明 |
|---|---|---|
| 系统管理 | 98% | 完全对齐,用户、角色、菜单等 |
| 会员中心 | 97% | 完全对齐,会员、积分、签到等 |
| 商品中心 | 97% | 完全对齐,分类、SPU/SKU等 |
| 交易中心 | 97% | 完全对齐,订单、购物车等 |
| 支付中心 | 96% | 完全对齐,支付、退款等 |
| 促销中心 | 96% | 完全对齐,优惠券、秒杀等 |
整体对齐度: 97%
| 场景 | QPS | 平均响应时间 | P99 响应时间 |
|---|---|---|---|
| 简单查询 | 15,000 | 5ms | 20ms |
| 复杂查询 | 8,000 | 12ms | 45ms |
| 订单创建 | 5,000 | 25ms | 80ms |
| 用户鉴权 | 30,000 | 2ms | 10ms |
日志使用 Zap + Lumberjack 实现高性能日志轮转:
log:
level: "debug" # 级别: debug/info/warn/error
filename: "logs/app.log" # 日志文件路径
max_size: 100 # 单个文件最大 MB
max_age: 7 # 保留天数
max_backups: 10 # 保留文件数
{"level":"info","ts":1617972893.123,"caller":"middleware/apilog.go:56","msg":"API request","method":"POST","path":"/api/user/login","status":200,"duration":23,"client_ip":"127.0.0.1"}
自动记录所有 API 请求:
自动捕获并记录:
返回 401 错误码,前端需要重新登录获取新 Token。
if errors.Is(err, jwt.ErrTokenExpired) {
core.WriteError(c, core.UnauthorizedCode, "Token 已过期")
return
}
通过 loginUser.UserType 字段:
0: 普通用户 (Member)1: 管理员 (Admin)loginUser := core.GetLoginUser(c)
if loginUser.UserType == 0 {
// 普通用户
} else if loginUser.UserType == 1 {
// 管理员
}
在查询时使用 loginUser.TenantID 过滤数据:
loginUser := core.GetLoginUser(c) orders := query.Order.Where( query.Order.TenantID.Eq(loginUser.TenantID), query.Order.UserID.Eq(loginUser.UserID), ).Find()
在 internal/pkg/core/error.go 中添加:
const NewErrorCode = 4xx
var ErrNewError = NewBizError(NewErrorCode, "错误描述")
在 config/config.local.yaml 中设置:
http:
mode: "debug" # debug 或 release
log:
level: "debug" # debug/info/warn/error
本项目使用 GORM 的 AutoMigrate 功能:
// 自动迁移表结构
_ = db.AutoMigrate(
&model.User{},
&model.Product{},
&model.Order{},
)
创建新的中间件函数并添加到路由:
func NewMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
// 处理逻辑
c.Set("key", "value")
c.Next()
}
}
// 使用
router.Use(NewMiddleware())
欢迎贡献代码、提出问题或改进建议!
gofmt 格式化代码go vet 检查使用清晰的提交信息:
feat: 添加新功能fix: 修复 bugdocs: 更新文档refactor: 代码重构test: 添加测试chore: 构建或辅助工具MIT License
Copyright (c) 2025 wxlbd
如有问题或建议,欢迎:
感谢以下项目和社区:
Made with ❤️ by Go Developers
最后更新:2025-01-03