Gin-grom-mysql后端学习项目
main.go:程序入口、加载配置config-初始化数据库?、创建路由router、启动服务,使用gin+grom+mysql生成一个学习用的小web记账项目,简单先行,便于学习。internal:是什么意思,私有应用代码(外部项目无法导入)handler:接收请求,返回响应,调用service层。dasebase/db.go ,初始化数据库连接四大参数。service:调用repository
·
基本要求
使用gin+grom+mysql生成一个学习用的小web记账项目,简单先行,便于学习
暂无前端,使用postman测试接口?
数据库
用户表、消费表、统计表
不要外键
sql语句
-- 用户表
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY COMMENT '用户ID',
name VARCHAR(255) NOT NULL UNIQUE COMMENT '用户名(唯一)',
password VARCHAR(255) NOT NULL COMMENT '密码(存储哈希值)',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
) COMMENT='用户信息表';
-- 消费记录表(使用数字分类)
CREATE TABLE expenses (
id INT AUTO_INCREMENT PRIMARY KEY COMMENT '消费ID',
user_id INT NOT NULL COMMENT '关联用户ID',
note VARCHAR(255) NOT NULL COMMENT '消费说明',
amount DECIMAL(10, 2) NOT NULL COMMENT '消费金额',
category TINYINT NOT NULL COMMENT '消费分类: 1-餐饮, 2-交通, 3-娱乐, 4-购物, 5-住房, 6-其他',
expense_date DATE NOT NULL COMMENT '消费日期',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
) COMMENT='消费记录表';
-- 统计表
CREATE TABLE statistics (
id INT AUTO_INCREMENT PRIMARY KEY COMMENT '统计ID',
user_id INT NOT NULL COMMENT '关联用户ID',
day_total DECIMAL(10, 2) DEFAULT 0.00 COMMENT '当日总消费',
week_total DECIMAL(10, 2) DEFAULT 0.00 COMMENT '本周总消费',
month_total DECIMAL(10, 2) DEFAULT 0.00 COMMENT '本月总消费',
year_total DECIMAL(10, 2) DEFAULT 0.00 COMMENT '本年总消费',
last_updated DATE NOT NULL COMMENT '最后统计日期'
) COMMENT='用户消费统计表';
项目结构
DayCost
├── cmd/
│ └── main.go # 应用入口:初始化配置、路由和服务器
├── config/
│ └── config.go # 配置加载(支持.env/env变量)
├── internal/
│ ├── model/ # ORM模型定义
│ │ ├── user.go # User模型 struct
│ │ ├── expense.go # Expense模型 struct
│ │ └── statistic.go # Statistic模型 struct
│ ├── handler/ # HTTP请求处理器
│ │ ├── user_handler.go # 用户相关路由处理
│ │ ├── expense_handler.go # 消费记录CRUD处理
│ │ └── stat_handler.go # 统计数据处理
│ ├── repository/ # 数据库操作层
│ │ ├── user_repo.go # 用户数据操作
│ │ ├── expense_repo.go # 消费记录操作
│ │ └── stat_repo.go # 统计数据操作
│ └── service/ # 业务逻辑层
│ ├── auth_service.go # 认证相关逻辑
│ ├── expense_service.go # 消费业务逻辑
│ └── stat_service.go # 统计计算逻辑
├── pkg/
│ ├── database/
│ │ └── db.go # 数据库连接初始化
│ └── util/
│ └── response.go # 统一响应格式工具
├── routes/
│ └── router.go # 路由定义和中间件注册
├── scripts/
│ └── init_db.sql # 数据库初始化脚本
├── go.mod
├── go.sum
└── .env.example # 环境变量示例文件
项目结构详细描述
main.go:程序入口、加载配置config-初始化数据库?、创建路由router、启动服务,
congig.go:配置,
internal:是什么意思,私有应用代码(外部项目无法导入)
model层:定义结构体
repository:封装crud
service:调用repository,复杂业务实现
handler:接收请求,返回响应,调用service层
pkg是什么意思
dasebase/db.go ,初始化数据库连接四大参数
util/response.go,统一响应体Result
router.go,路由
程序入口main.go
- 初始化连接数据库,db.go
- 初始化路由并启动服务,router.go
package main
import (
"DayCost/pkg/database"
"DayCost/routes"
"log"
)
func main() {
// 初始化数据库
if err := database.InitDB(); err != nil {
log.Fatalf("数据库初始化失败: %v", err)
}
// 创建路由
r := routes.SetupRouter()
// 启动服务
if err := r.Run(":8080"); err != nil {
log.Fatalf("服务启动失败: %v", err)
}
}
数据库连接,生成一个连接好的DB对象
- 数据库连接参数 - 后续可写在env环境中,这里直接写入
- 数据库连接字符串dns
- gorm.Open() //驱动+参数
package database
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
var DB *gorm.DB
type Config struct {
Host string
Port string
User string
Password string
DBName string
}
func InitDB() error {
// 简化配置(实际项目中应从环境变量读取)
cfg := Config{
Host: "localhost",
Port: "3306",
User: "root",
Password: "123456",
DBName: "daycost",
}
dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",
cfg.User, cfg.Password, cfg.Host, cfg.Port, cfg.DBName)
var err error
DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{}) //数据库驱动 //
if err != nil {
return err
}
return nil
}
用户登录功能实现,数据层-curd层-server层-统一响应体-handle控制层-路由层
model/user.go
package model
import "time"
type User struct {
ID uint `gorm:"primaryKey;autoIncrement"`
Name string `gorm:"unique;not null"`
Password string `gorm:"not null"`
CreatedAt time.Time `gorm:"autoCreateTime"`
UpdatedAt time.Time `gorm:"autoUpdateTime"`
}
数据类型和grom注解详解
repository/user_repo.go
在Gin或其他现代Web框架中,repository作为DAO层的命名源于领域驱动设计(DDD)模式。DDD将数据访问逻辑抽象为“仓储”(Repository),强调对领域对象的集合式操作,而非单纯数据库表操作。
package repository
import (
"DayCost/internal/model"
"DayCost/pkg/database"
)
// 用户仓库结构体
type UserRepository struct{}
// 根据用户名查询用户
func (r *UserRepository) FindUserByName(name string) (*model.User, error) {
var user model.User
// Where 条件查询 + First 获取第一条记录
result := database.DB.Where("name = ?", name).First(&user)
return &user, result.Error
}
// 创建用户
func (r *UserRepository) CreateUser(user *model.User) error {
// Create 插入记录
result := database.DB.Create(user)
return result.Error
}
Gorm 完成 CRUD 操作
service/auth_service.go
package service
import (
"DayCost/internal/model"
"DayCost/internal/repository"
)
type AuthService struct {
userRepo *repository.UserRepository
}
func NewAuthService() *AuthService {
return &AuthService{
userRepo: &repository.UserRepository{},
}
}
func (s *AuthService) Register(user *model.User) error {
return s.userRepo.CreateUser(user)
}
func (s *AuthService) Login(name, password string) (*model.User, bool) {
user, err := s.userRepo.FindUserByName(name)
if err != nil {
return nil, false
}
// 简化密码验证(实际应使用bcrypt)
if user.Password != password {
return nil, false
}
return user, true
}
handle/user_handle.go
package handler
import (
"DayCost/internal/service"
"DayCost/pkg/util"
"github.com/gin-gonic/gin"
)
type AuthHandler struct {
authService *service.AuthService
}
func NewAuthHandler() *AuthHandler {
return &AuthHandler{
authService: service.NewAuthService(),
}
}
func (h *AuthHandler) Login(c *gin.Context) {
var req struct {
Name string `json:"name" binding:"required"`
Password string `json:"password" binding:"required"`
}
if err := c.ShouldBindJSON(&req); err != nil {
util.SendBadRequest(c, "无效的请求格式")
return
}
user, ok := h.authService.Login(req.Name, req.Password)
if !ok {
util.SendUnauthorized(c, "用户名或密码错误")
return
}
// 简化响应(实际应返回JWT token)
util.SendSuccess(c, gin.H{
"id": user.ID,
"name": user.Name,
})
}
response.go
package util
import "github.com/gin-gonic/gin"
func SendSuccess(c *gin.Context, data interface{}) {
c.JSON(200, gin.H{
"code": 0,
"msg": "success",
"data": data,
})
}
func SendBadRequest(c *gin.Context, message string) {
c.JSON(400, gin.H{
"code": 400,
"msg": message,
})
}
func SendUnauthorized(c *gin.Context, message string) {
c.JSON(401, gin.H{
"code": 401,
"msg": message,
})
}
router.go
package routes
import (
"DayCost/internal/handler"
"github.com/gin-gonic/gin"
)
func SetupRouter() *gin.Engine {
r := gin.Default()
authHandler := handler.NewAuthHandler()
// 用户路由
userGroup := r.Group("/api/user")
{
userGroup.POST("/login", authHandler.Login)
}
return r
}
更多推荐
所有评论(0)