Go web 简单开发 demo 以及时间类型转换踩坑经理-gin框架、gorm框架

我的代码实现(包括6.824-2022分布式实验一、二)

直达gitee链接
https://gitee.com/zhou-xiujun/xun-go/tree/pet-store/

Go web 简单开发 demo

后端使用go语言,gin框架开发以及gorm操作数据库。

以一个宠物表的单表增删改查为例。

1、建立项目 pet-store

2、项目下建立static文件夹存储前端静态文件

为了简单,只用简单的 html 静态页面,并且不关注样式,只需要起到发送请求的作用。

在static文件夹下建立 hello.html 文件:

<html>
<head>
    <title>
        Hello! Static Website
    </title>
</head>
<body>
<h2>Hello! Static Website</h2>
</body>
</html>

在static文件夹下建立 index.html 文件:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Pet Management System</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 20px;
        }
        input, select, textarea {
            margin: 5px 0;
        }
    </style>
</head>
<body>
<h1>Pet Management System</h1>

<h2>Create Pet</h2>
<form id="createPetForm">
    宠物的名字:
    <input type="text" id="name" placeholder="Name" required><br>
    宠物的生物学名称:
    <input type="text" id="scientific_name" placeholder="Scientific Name"><br>
    宠物的来源地:
    <input type="text" id="origin" placeholder="Origin"><br>
    宠物的出生日期:
    <input type="datetime-local" id="birth_date" placeholder="Birth Date"><br>
    宠物的平均寿命(年):
    <input type="number" id="average_lifespan" placeholder="Average Lifespan (years)"><br>
    宠物的颜色:
    <input type="text" id="color" placeholder="Color"><br>
    宠物是否患有疾病:
    <select id="has_disease">
        <option value="">Has Disease?</option>
        <option value="true">Yes</option>
        <option value="false">No</option>
    </select><br>
    关联的疾病ID:
    <input type="number" id="disease_id" placeholder="Disease ID"><br>
    患病日期:
    <input type="datetime-local" id="disease_date" placeholder="Disease Date"><br>
    宠物的体重(公斤):
    <input type="number" step="0.01" id="weight" placeholder="Weight (kg)"><br>
    体重的更新时间:
    <input type="datetime-local" id="weight_update_time" placeholder="Weight Update Time"><br>
    宠物的价格:
    <input type="number" step="0.01" id="price" placeholder="Price"><br>
    宠物的购买日期:
    <input type="datetime-local" id="purchase_date" placeholder="Purchase Date"><br>
    购买宠物的商店名称:
    <input type="text" id="purchase_store" placeholder="Purchase Store"><br>
    宠物的食物信息:
    <textarea id="food" placeholder="Food"></textarea><br>
    其他额外信息:
    <textarea id="additional_info" placeholder="Additional Info"></textarea><br>
    <button type="button" onclick="createPet()">Create Pet</button>
</form>
<pre id="createPetResult"></pre>

<h2>Get Pet By ID</h2>
<input type="number" id="get_pet_id" placeholder="Pet ID">
<button type="button" onclick="getPet()">Get Pet</button>
<pre id="petResult"></pre>

<h2>Get Pets By Name</h2>
<input type="text" id="get_pets_name" placeholder="Pet Name">
<button type="button" onclick="getPetsByName()">Get Pets</button>
<pre id="petsResult"></pre>

<h2>Update Pet</h2>
<form id="updatePetForm">
    <input type="number" id="update_pet_id" placeholder="Pet ID" required><br>
    <input type="text" id="update_name" placeholder="Name"><br>
    <input type="text" id="update_scientific_name" placeholder="Scientific Name"><br>
    <input type="text" id="update_origin" placeholder="Origin"><br>
    <input type="datetime-local" id="update_birth_date" placeholder="Birth Date"><br>
    <input type="number" id="update_average_lifespan" placeholder="Average Lifespan (years)"><br>
    <input type="text" id="update_color" placeholder="Color"><br>
    <select id="update_has_disease">
        <option value="">Has Disease?</option>
        <option value="true">Yes</option>
        <option value="false">No</option>
    </select><br>
    <input type="number" id="update_disease_id" placeholder="Disease ID"><br>
    <input type="datetime-local" id="update_disease_date" placeholder="Disease Date"><br>
    <input type="number" step="0.01" id="update_weight" placeholder="Weight (kg)"><br>
    <input type="datetime-local" id="update_weight_update_time" placeholder="Weight Update Time"><br>
    <input type="number" step="0.01" id="update_price" placeholder="Price"><br>
    <input type="datetime-local" id="update_purchase_date" placeholder="Purchase Date"><br>
    <input type="text" id="update_purchase_store" placeholder="Purchase Store"><br>
    <textarea id="update_food" placeholder="Food"></textarea><br>
    <textarea id="update_additional_info" placeholder="Additional Info"></textarea><br>
    <button type="button" onclick="updatePet()">Update Pet</button>
</form>
<pre id="updatePetResult"></pre>

<h2>Delete Pet</h2>
<input type="number" id="delete_pet_id" placeholder="Pet ID">
<button type="button" onclick="deletePet()">Delete Pet</button>
<pre id="deleteResult"></pre>

<script>
    const baseURL = 'http://localhost:8080';

    async function createPet() {
        const pet = {
            name: document.getElementById('name').value,
            scientific_name: document.getElementById('scientific_name').value,
            origin: document.getElementById('origin').value ? document.getElementById('origin').value : "",
            birth_date: document.getElementById('birth_date').value ? new Date(document.getElementById('birth_date').value).toISOString() : null,
            average_lifespan: document.getElementById('average_lifespan').value ? parseInt(document.getElementById('average_lifespan').value) : 0,
            color: document.getElementById('color').value ? document.getElementById('color').value : "",
            has_disease: document.getElementById('has_disease').value === "" ? false : document.getElementById('has_disease').value === "true",
            disease_id: document.getElementById('disease_id').value ? parseInt(document.getElementById('disease_id').value) : 0,
            disease_date: document.getElementById('disease_date').value ? new Date(document.getElementById('disease_date').value).toISOString() : null,
            weight: document.getElementById('weight').value ? parseFloat(document.getElementById('weight').value) : 0,
            weight_update_time: document.getElementById('weight_update_time').value ? new Date(document.getElementById('weight_update_time').value).toISOString() : null,
            price: document.getElementById('price').value ? parseFloat(document.getElementById('price').value) : 0,
            purchase_date: document.getElementById('purchase_date').value ? new Date(document.getElementById('purchase_date').value).toISOString() : null,
            purchase_store: document.getElementById('purchase_store').value ? document.getElementById('purchase_store').value : "",
            food: document.getElementById('food').value ? document.getElementById('food').value : "",
            additional_info: document.getElementById('additional_info').value ? document.getElementById('additional_info').value : ""
        };

        const response = await fetch(`${baseURL}/createPet`, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(pet)
        });

        const data = await response.json();
        document.getElementById('createPetResult').innerText = JSON.stringify(data, null, 2);
        console.log(data);
    }


    async function getPet() {
        const petId = document.getElementById('get_pet_id').value;
        const response = await fetch(`${baseURL}/getPetById/${petId}`);
        const data = await response.json();
        document.getElementById('petResult').innerText = JSON.stringify(data, null, 2);
    }

    async function getPetsByName() {
        const petName = document.getElementById('get_pets_name').value;
        const response = await fetch(`${baseURL}/getPetsByName/${petName}`);
        const data = await response.json();
        document.getElementById('petsResult').innerText = JSON.stringify(data, null, 2);
    }

    async function updatePet() {
        const petId = document.getElementById('update_pet_id').value;
        const pet = {
            name: document.getElementById('update_name').value,
            scientific_name: document.getElementById('update_scientific_name').value,
            origin: document.getElementById('update_origin').value ? document.getElementById('update_origin').value : "",
            birth_date: document.getElementById('update_birth_date').value ? new Date(document.getElementById('update_birth_date').value).toISOString() : null,
            average_lifespan: document.getElementById('update_average_lifespan').value ? parseInt(document.getElementById('update_average_lifespan').value) : 0,
            color: document.getElementById('update_color').value ? document.getElementById('update_color').value : "",
            has_disease: document.getElementById('update_has_disease').value === "" ? false : document.getElementById('update_has_disease').value === "true",
            disease_id: document.getElementById('update_disease_id').value ? parseInt(document.getElementById('update_disease_id').value) : 0,
            disease_date: document.getElementById('update_disease_date').value ? new Date(document.getElementById('update_disease_date').value).toISOString() : null,
            weight: document.getElementById('update_weight').value ? parseFloat(document.getElementById('update_weight').value) : 0,
            weight_update_time: document.getElementById('update_weight_update_time').value ? new Date(document.getElementById('update_weight_update_time').value).toISOString() : null,
            price: document.getElementById('update_price').value ? parseFloat(document.getElementById('update_price').value) : 0,
            purchase_date: document.getElementById('update_purchase_date').value ? new Date(document.getElementById('update_purchase_date').value).toISOString() : null,
            purchase_store: document.getElementById('update_purchase_store').value ? document.getElementById('update_purchase_store').value : "",
            food: document.getElementById('update_food').value ? document.getElementById('update_food').value : "",
            additional_info: document.getElementById('update_additional_info').value ? document.getElementById('update_additional_info').value : ""
        };
        const response = await fetch(`${baseURL}/savePetById/${petId}`, {
            method: 'PUT',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(pet)
        });
        const data = await response.json();
        document.getElementById('updatePetResult').innerText = JSON.stringify(data, null, 2);
        console.log(data);
    }

    async function deletePet() {
        const petId = document.getElementById('delete_pet_id').value;
        const response = await fetch(`${baseURL}/deletePetById/${petId}`, {
            method: 'DELETE'
        });
        const data = await response.json();
        document.getElementById('deleteResult').innerText = JSON.stringify(data, null, 2);
    }
</script>
</body>
</html>

3、建立数据库表

建立宠物信息表:

CREATE TABLE `pet` (
  `id` int NOT NULL AUTO_INCREMENT COMMENT '自动递增的主键',
  `name` varchar(100) COLLATE utf8mb4_0900_as_cs NOT NULL COMMENT '''宠物的名字''',
  `scientific_name` varchar(150) COLLATE utf8mb4_0900_as_cs DEFAULT NULL COMMENT '''宠物的生物学名称''',
  `origin` varchar(100) COLLATE utf8mb4_0900_as_cs DEFAULT NULL COMMENT '''宠物的来源地''',
  `birth_date` date DEFAULT NULL COMMENT '''宠物的出生日期''',
  `average_lifespan` bigint DEFAULT '0' COMMENT '''宠物的平均寿命(年)''',
  `color` varchar(50) COLLATE utf8mb4_0900_as_cs DEFAULT NULL COMMENT '''宠物的颜色''',
  `has_disease` tinyint(1) DEFAULT NULL COMMENT '''宠物是否患有疾病''',
  `disease_id` bigint DEFAULT '0' COMMENT '''关联的疾病ID''',
  `disease_date` date DEFAULT NULL COMMENT '''患病日期''',
  `weight` decimal(5,2) DEFAULT NULL COMMENT '''宠物的体重(公斤)''',
  `weight_update_time` datetime DEFAULT NULL COMMENT '''体重的更新时间''',
  `price` decimal(10,2) DEFAULT NULL COMMENT '''宠物的价格''',
  `purchase_date` date DEFAULT NULL COMMENT '''宠物的购买日期''',
  `purchase_store` varchar(100) COLLATE utf8mb4_0900_as_cs DEFAULT NULL COMMENT '''购买宠物的商店名称''',
  `food` text COLLATE utf8mb4_0900_as_cs COMMENT '''宠物的食物信息''',
  `additional_info` text COLLATE utf8mb4_0900_as_cs COMMENT '''其他额外信息''',
  `is_delete` tinyint(1) DEFAULT '0' COMMENT '''是否删除''',
  `updated_at` datetime DEFAULT NULL COMMENT '''更新时间''',
  `deleted_at` datetime DEFAULT NULL COMMENT '''删除时间''',
  `created_at` datetime DEFAULT NULL COMMENT '''创建时间''',
  PRIMARY KEY (`id`),
  KEY `idx_name` (`name`) COMMENT '名字索引',
  KEY `idx_scientific_name` (`scientific_name`) COMMENT '生物学名称索引',
  KEY `idx_color` (`color`) COMMENT '颜色索引',
  KEY `idx_pet_deleted_at` (`deleted_at`)
) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_as_cs COMMENT='水生宠物信息表';

还有一个疾病信息表:

这个可以自行设计。。。

4、定义表对应的结构体

创建models文件夹,底下创建pet.go文件:

package models

import (
	"time"
)

// Pet 表示水生宠物的信息
// 对于不需要的值,比如DeletedAt有时不需要传入,可以使用*time.Time或者加入gorm标签default:null
// 建议使用后者
// 对于前端传入的时间类型与time.Time之间转换的冲突问题
type Pet struct {
	ID               int64     `gorm:"primaryKey;autoIncrement;comment:'宠物的ID'" json:"id"`
	Name             string    `gorm:"column:name;size:100;not null;comment:'宠物的名字'" json:"name"`
	ScientificName   string    `gorm:"column:scientific_name;default:null;size:150;comment:'宠物的生物学名称'" json:"scientific_name"`
	Origin           string    `gorm:"column:origin;size:100;default:null;comment:'宠物的来源地'" json:"origin"`
	BirthDate        time.Time `gorm:"column:birth_date;type:datetime;default:null;comment:'宠物的出生日期'" json:"birth_date"`
	AverageLifespan  uint      `gorm:"column:average_lifespan;type:int;default:0;comment:'宠物的平均寿命(年)'" json:"average_lifespan"`
	Color            string    `gorm:"column:color;size:50;default:null;comment:'宠物的颜色'" json:"color"`
	HasDisease       bool      `gorm:"column:has_disease;type:tinyint(1);default:null;comment:'宠物是否患有疾病'" json:"has_disease"`
	DiseaseID        uint      `gorm:"column:disease_id;type:int;default:0;comment:'关联的疾病ID'" json:"disease_id"`
	DiseaseDate      time.Time `gorm:"column:disease_date;type:datetime;default:null;comment:'患病日期'" json:"disease_date"`
	Weight           float64   `gorm:"column:weight;type:decimal(5,2);default:null;comment:'宠物的体重(公斤)'" json:"weight"`
	WeightUpdateTime time.Time `gorm:"column:weight_update_time;type:datetime;default:null;comment:'体重的更新时间'" json:"weight_update_time"`
	Price            float64   `gorm:"column:price;type:decimal(10,2);default:null;comment:'宠物的价格'" json:"price"`
	PurchaseDate     time.Time `gorm:"column:purchase_date;type:datetime;default:null;comment:'宠物的购买日期'" json:"purchase_date"`
	PurchaseStore    string    `gorm:"column:purchase_store;size:100;default:null;comment:'购买宠物的商店名称'" json:"purchase_store"`
	Food             string    `gorm:"column:food;type:text;default:null;comment:'宠物的食物信息'" json:"food"`
	AdditionalInfo   string    `gorm:"column:additional_info;type:text;default:null;comment:'其他额外信息'" json:"additional_info"`
	CreatedAt        time.Time `gorm:"column:created_at;type:datetime;comment:'创建时间'" json:"created_at"`
	UpdatedAt        time.Time `gorm:"column:updated_at;type:datetime;comment:'更新时间'" json:"updated_at"`
	DeletedAt        time.Time `gorm:"column:deleted_at;type:datetime;default:null;comment:'删除时间'" json:"deleted_at"`
	IsDelete         bool      `gorm:"column:is_delete;type:tinyint(1);default:0;comment:'是否删除'" json:"is_delete"`
}

// TableName 定义数据库表名
func (Pet) TableName() string {
	return "pet"
}

5、使用GORM的AutoMigrate功能匹配指定模型的结构

package database

import (
	"gorm.io/gorm"
	"xun-go/com/xun/pet-store/models"
)

// Migrate 对数据库模式进行自动迁移
// 该函数使用GORM的AutoMigrate功能来自动创建或更新数据库表,以匹配指定模型的结构。
// 参数:
//   db *gorm.DB - 数据库连接实例,用于执行模式迁移操作。
func Migrate(db *gorm.DB) {
    // 自动迁移Pet模型对应的数据库表,更新表结构以匹配当前模型定义
    db.AutoMigrate(&models.Pet{})
    // 自动迁移Disease模型对应的数据库表,更新表结构以匹配当前模型定义
    db.AutoMigrate(&models.Disease{})
}

6、建立config文件夹,创建ConnectDB.go文件,用于连接数据库

package config

import (
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"log"
)

var DB *gorm.DB

// ConnectDB 初始化并返回一个与数据库的连接。
// 该函数使用GORM库来打开与MySQL数据库的连接,并配置相应的连接参数。
// 如果连接失败,将记录错误并终止程序。
// 返回值是一个指向gorm.DB类型的指针,用于后续的数据库操作。
func ConnectDB() *gorm.DB {
    // 定义数据库的DSN(Data Source Name),包括用户名、密码、主机、端口和数据库名称等信息。
    dsn := "username:password@tcp(127.0.0.1:3306)/pet_store?charset=utf8mb4&parseTime=True&loc=Local"
    
    // 声明一个错误变量,用于捕获打开数据库连接时可能出现的错误。
    var err error
    
    // 使用GORM的Open函数打开数据库连接,并配置GORM的行为。
    // 如果打开数据库连接失败,将记录错误信息并使用log.Fatal退出程序。
    DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
    if err != nil {
        log.Fatal("Failed to connect to database:", err)
    }
    
    // 返回成功打开的数据库连接。
    return DB
}

7、创建controller

创建controllers文件夹,底下创建 PetController.go文件:

package controllers

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"gorm.io/gorm"
	"net/http"
	"strconv"
	"time"

	"xun-go/com/xun/pet-store/models"
)


// PetController 定义了宠物控制器的结构体
// 该控制器用于处理与宠物相关的业务逻辑和数据操作。
//
// 属性:
//   DB *gorm.DB - 数据库连接实例,用于执行数据库操作。
type PetController struct {
	DB *gorm.DB
}


// NewPetController 创建一个新的PetController实例。
// 这个函数接收一个gorm.DB类型的参数db,用于初始化PetController内的DB字段。
// 返回一个指向PetController实例的指针。
func NewPetController(db *gorm.DB) *PetController {
    // 返回一个新的PetController实例,其中DB字段被初始化为传入的db参数。
	return &PetController{DB: db}
}

// CreatePet 通过HTTP POST请求创建一个新的宠物记录。
// 请求体中应包含新宠物的所有相关信息。
// 参数:
//   c *gin.Context: Gin框架的上下文对象,用于处理HTTP请求和响应。
func (pc *PetController) CreatePet(c *gin.Context) {
	var pet models.Pet
	// 解析请求体中的JSON数据,并将其映射到pet变量中。
	// 如果解析失败,将返回HTTP 400错误(Bad Request)。
	if err := c.ShouldBindJSON(&pet); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	// 将新宠物添加到数据库中。
	// 如果添加失败,将返回HTTP 500错误(Internal Server Error)。
	if err := pc.DB.Create(&pet).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusCreated, pet)
}

// TestCreate 测试创建函数
func (pc *PetController) TestCreate() {
	pet := models.Pet{
		Name:             "Test Pet",
		Color:            "Black",
		Price:            100.00,
		Weight:           10.00,
		BirthDate:        time.Now(),
		DiseaseDate:      time.Now(),
		WeightUpdateTime: time.Now(),
		PurchaseDate:     time.Now(),
		PurchaseStore:    "Test Store",
		Origin:           "Test Origin",
		ScientificName:   "Test Scientific Name",
		AdditionalInfo:   "Test Additional Info",
		AverageLifespan:  10,
		HasDisease:       false,
	}
	if err := pc.DB.Create(&pet).Error; err != nil {
		return
	}
}

// GetPetById 根据ID获取宠物信息。
// 该方法处理GET请求,路径为/createPet/{petId},其中{petId}为宠物的唯一标识。
// 请求成功时,返回具体的宠物信息;失败时,返回相应的错误信息。
func (pc *PetController) GetPetById(c *gin.Context) {
	// 从请求路径中提取petId
	id := c.Param("id")
	var pet models.Pet
	// 根据petId从数据库中查询未被删除的宠物信息
	if err := pc.DB.Where("is_delete = 0").First(&pet, id).Error; err != nil {
		// 如果查询出错,返回错误信息
		c.JSON(http.StatusNotFound, gin.H{"error": "Pet not found"})
		fmt.Println("GetPet: " + err.Error())
		return
	}
	// 如果查询成功,返回宠物信息
	c.JSON(http.StatusOK, pet)
}

// GetPetsByName 根据名字查询宠物
func (pc *PetController) GetPetsByName(c *gin.Context) {
	name := c.Param("name")
	// 声明一个切片,用于存储查询结果
	pets := make([]models.Pet, 0)

	// 使用Find方法查询所有名字为$name$的宠物
	if err := pc.DB.Where("is_delete = 0").Where("name = ?", name).Find(&pets).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	// 如果没有找到任何记录
	if len(pets) == 0 {
		c.JSON(http.StatusNotFound, gin.H{"error": "No pets found with the given name"})
		return
	}

	c.JSON(http.StatusOK, pets)
}

// GetAllPets 获取所有宠物
func (pc *PetController) GetAllPets(c *gin.Context) {
	var pets []models.Pet
	if err := pc.DB.Find(&pets).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, pets)
}

// UpdatePet handles updating an existing pet.
// 对于时间为null的会解析报错
//func (pc *PetController) UpdatePet(c *gin.Context) {
//	id := c.Param("id")
//	var pet models.Pet
//
//	// 设置pet对象的ID字段
//	pet.ID, _ = strconv.ParseInt(id, 10, 64)
//	if err := c.ShouldBindJSON(&pet); err != nil {
//		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
//		fmt.Println("ShouldBindJSON:" + err.Error())
//		return
//	}
//	if err := pc.DB.Save(&pet).Error; err != nil {
//		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
//		fmt.Println("Save:" + err.Error())
//		return
//	}
//	c.JSON(http.StatusOK, pet)
//}

// UpdatePet 更新宠物信息
// 此方法接收HTTP PUT或PATCH请求,并更新数据库中特定ID的宠物信息。
func (pc *PetController) UpdatePet(c *gin.Context) {
	// 获取URL中的宠物ID参数
	id := c.Param("id")
	var updates map[string]interface{}

	// 解析请求体中的JSON数据到updates map中
	if err := c.ShouldBindJSON(&updates); err != nil {
		// 如果解析失败,返回错误信息并终止函数执行
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		fmt.Println("ShouldBindJSON:" + err.Error())
		return
	}

	// 将字符串ID转换为int64类型
	parsedID, err := strconv.ParseInt(id, 10, 64)
	if err != nil {
		// 如果转换失败,返回错误信息并终止函数执行
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid ID"})
		return
	}

	// 删除updates中的'id'键值对,因为ID不应该被更新
	delete(updates, "id")

	// 清理updates,移除所有空值或nil值的键值对
	for key, value := range updates {
		if value == nil || value == "" {
			delete(updates, key)
		}
	}

	// 使用gorm更新数据库中的宠物信息
	if err := pc.DB.Where("is_delete = 0").Model(&models.Pet{ID: parsedID}).Updates(updates).Error; err != nil {
		// 如果更新失败,返回错误信息并终止函数执行
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		fmt.Println("Updates:" + err.Error())
		return
	}

	// 从数据库中获取更新后的宠物信息
	var pet models.Pet
	if err := pc.DB.Where("is_delete = 0").First(&pet, parsedID).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	// 返回更新后的宠物信息
	c.JSON(http.StatusOK, pet)
}

// DeletePet 通过ID删除宠物记录
// 该方法处理HTTP DELETE请求,路径参数中包含宠物的ID。
func (pc *PetController) DeletePet(c *gin.Context) {
	id := c.Param("id")
	// 物理删除
	if err := pc.DB.Delete(&models.Pet{}, id).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{"message": "Pet deleted successfully"})
}

// SoftDeletePet 执行软删除操作
func (pc *PetController) SoftDeletePet(c *gin.Context) {
	id := c.Param("id")

	// 使用Updates方法更新is_delete字段为1
	if err := pc.DB.Model(&models.Pet{}).Where("id = ?", id).Update("is_delete", 1).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"message": "Pet soft deleted successfully"})
}

8、创建路由 routes

创建routes文件夹,底下创建 routes.go:

package routes

import (
	"github.com/gin-gonic/gin"
	"gorm.io/gorm"
	"net/http"
	"xun-go/com/xun/pet-store/controllers"
)

// RegisterRoutes 注册应用程序的所有路由
// 参数:
//
//	r - Gin引擎实例,用于注册路由
//	db - GORM数据库连接实例,提供数据访问能力
func RegisterRoutes(r *gin.Engine, db *gorm.DB) {
	// 创建PetController实例,传入数据库连接
	petController := controllers.NewPetController(db)

	// 设置静态文件目录
	r.LoadHTMLGlob("com/xun/pet-store/static/*")

	// 配置路由
	// GET "/hello" - 渲染并返回"hello.html"页面
	r.GET("/hello", func(c *gin.Context) {
		c.HTML(http.StatusOK, "hello.html", gin.H{
			"title": "hello",
		})
	})

	// GET "/index" - 渲染并返回"index.html"页面
	r.GET("/index", func(c *gin.Context) {
		c.HTML(http.StatusOK, "index.html", gin.H{
			"title": "index",
		})
	})

	// 配置 CRUD 路由
	r.POST("/createPet", petController.CreatePet)
	r.GET("/getPetById/:id", petController.GetPetById)
	r.GET("/getPetsByName/:name", petController.GetPetsByName)
	r.PUT("/savePetById/:id", petController.UpdatePet)
	r.DELETE("/deletePetById/:id", petController.SoftDeletePet)
}

解释:

r.POST("/createPet", petController.CreatePet)

这一行代码做了以下几件事:

  • 定义了一个路由:“/createPet” 这个 URL 路径被绑定到一个特定的处理函数上。这意味着当有 HTTP POST 请求发送到这个路径时,Gin 框架会调用相应的处理函数。
  • 指定了处理函数:petController.CreatePet 是一个处理函数,它将被调用来处理所有针对 /createPet 路径的 POST 请求。这个函数通常位于 petController 类型的一个实例中,它是一个实现了创建宠物逻辑的函数。
    • petController 可能是一个实现了多个与宠物相关的操作的控制器对象,如创建、读取、更新和删除(CRUD)操作。
    • CreatePet 方法应该接受一个 *gin.Context 参数,这是 Gin 中每个请求的标准上下文对象,包含了请求和响应的信息,以及一些辅助方法来处理请求和构建响应。
  • 处理请求:当一个 POST 请求到达 /createPet 路径时,Gin 会调用 petController.CreatePet 函数,并将 *gin.Context 对象作为参数传递。在这个函数中,你可以从请求中读取数据(例如,从请求体中解析 JSON 数据),执行业务逻辑(例如,创建一个新的宠物记录),然后使用 Context 对象构建和发送响应。
  • 另外,还可以利用 Gin 的中间件系统:Gin 提供了强大的中间件支持,可以在处理请求之前或之后执行额外的操作,比如身份验证、日志记录、错误处理等。这些中间件可以被添加到整个应用、一组路由或单个路由上。

总的来说,r.POST 这一行代码是在设置一个具体的路由和处理函数,使得应用程序能够响应特定类型的 HTTP 请求,并执行相应的业务逻辑。

9、最后,编写main方法

在pet-store目录下创建main.go:

package main

import (
	"github.com/gin-gonic/gin"
	"log"
	"xun-go/com/xun/pet-store/config"
	"xun-go/com/xun/pet-store/database"
	"xun-go/com/xun/pet-store/routes"
)

func main() {
	// 初始化Gin框架,默认配置包含了logger和recovery中间件
	r := gin.Default()

	// 连接到数据库,这里假设config.ConnectDB()是数据库连接的封装函数
	db := config.ConnectDB()

	// 执行数据库迁移,这里假设database.Migrate()是用于同步数据库模型到实际数据库的函数
	database.Migrate(config.DB)

	// 测试代码,用于插入数据到数据库
	// petController := controllers.NewPetController(db)
	// petController.TestCreate()

	// 注册路由,将Gin引擎和数据库连接传递给RegisterRoutes函数
	// 这里会设置所有API的路由和对应的处理函数
	routes.RegisterRoutes(r, db)

	// 启动HTTP服务,监听8080端口
	// 如果启动过程中出现错误,将错误信息输出到标准错误流并终止程序
	if err := r.Run(":8080"); err != nil {
		log.Fatal(err)
	}
}

这段代码的主要功能如下:

  • 初始化Gin框架并配置默认的中间件。
  • 建立数据库连接。
  • 进行数据库迁移,确保数据库结构与代码中的模型一致。
  • 注册所有应用程序的路由。
  • 启动HTTP服务器,监听8080端口,处理所有传入的HTTP请求。

踩坑经历

  1. 前后端时间类型转换错误
{ "error": "parsing time \"2024-07-06T23:16\" as \"2006-01-02T15:04:05Z07:00

前端类型是:

<input type="datetime-local" id="birth_date" placeholder="Birth Date">

后端类型是:

time.Time

解决办法:

birth_date: document.getElementById('birth_date').value ? new Date(document.getElementById('birth_date').value).toISOString() : null,

在前端进行转换。

  1. 查询数据库时,is_delete字段的数据库bit类型转换结构体bool错误
sql: Scan error on column index 17, name "is_delete": sql/driver: couldn't convert "\x00" into type bool

解决办法:

将数据库表的is_delete字段类型改为 tinyint(1)

其他内容(6.824-2022分布式实验)

Lab1:MapReduce

实验一链接

Lab2:Raft

实验二Raft链接

Lab2A:Leader Election

lab2A链接

Lab2B:日志复制

lab2B链接

Lab2C :持久化机制 persistence

lab2C链接

Lab2D:日志压缩 log compaction

lab2D链接

我的代码实现

直达gitee链接
https://gitee.com/zhou-xiujun/xun-go/tree/pet-store/

  • 16
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值