我的代码实现(包括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请求。
踩坑经历
- 前后端时间类型转换错误
{ "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,
在前端进行转换。
- 查询数据库时,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
Lab2A:Leader Election
Lab2B:日志复制
Lab2C :持久化机制 persistence
Lab2D:日志压缩 log compaction
我的代码实现
直达gitee链接
https://gitee.com/zhou-xiujun/xun-go/tree/pet-store/