package main import ( "database/sql" "encoding/json" "fmt" _ "github.com/go-sql-driver/mysql" // MySQL 驱动 "log" "math/rand" "net/http" "strconv" "time" ) func initweb() { var err error db, err = sql.Open("mysql", "root:43994399@tcp(192.168.2.19:3306)/brotato") /*******************/ if err != nil { log.Fatal("数据库连接失败: ", err) } defer db.Close() err = db.Ping() if err != nil { log.Fatal("数据库连接不可用: ", err) } log.Println("数据库连接") // 创建 ServeMux 实例 mux := http.NewServeMux() // 注册 /login 路由,绑定到 loginHandler mux.HandleFunc("/loginHandler", loginHandler) //登录与注册 //mux.HandleFunc("/getPerson", getPerson) mux.HandleFunc("/showDailyTaskHandler", showDailyTaskHandler) //每日任务 mux.HandleFunc("/inviteeListHandler", inviteeListHandler) //返回邀请好友列表(和邀请奖励) mux.HandleFunc("/userInfoHandler", userInfoHandler) //以openId为查询返回所有用户数据的方法(会有用的) mux.HandleFunc("/goldCommodityHandler", goldCommodityHandler) //金币商店列表 mux.HandleFunc("/rankingListHandler", rankingListHandler) //排行榜 mux.HandleFunc("/saveHandler", saveHandler) // 注册存档路由 mux.HandleFunc("/loadHandler", loadHandler) // 注册加载路由 log.Println("服务器启动,监听端口: 8080") //openFileDate() // 打开 json 文件并解析数据 err = http.ListenAndServe(":8080", mux) // 使用 mux 作为路由器 if err != nil { log.Fatal("ListenAndServe: ", err) } } // 打开json文件函数 /*func openFileDate() { // 打开 JSON 文件 jsonFile, err := os.Open("playerInfo.json") if err != nil { log.Fatalf("无法打开文件: %v", err) } defer jsonFile.Close() // 读取文件的内容 byteValue, _ := ioutil.ReadAll(jsonFile) // 解析 JSON 数据到结构体 err = json.Unmarshal(byteValue, &person) if err != nil { log.Fatalf("解析 JSON 数据失败: %v", err) } }*/ /*************************************************一个方法,以open Id为查询,能返回当前用户所有数据***************************************************/ // User 结构体用于存储查询结果 type User struct { Id int `json:"id"` UserName sql.NullString `json:"-"` // 允许 NULL 值 Url sql.NullString `json:"-"` Gold int `json:"gold"` RandCode sql.NullString `json:"-"` OpenId string `json:"openId"` ParentAddress sql.NullString `json:"-"` Language sql.NullString `json:"-"` TgUserId sql.NullString `json:"-"` LastLoginAt sql.NullString `json:"-"` RegisteredAt sql.NullString `json:"-"` InviteReward int `json:"inviteReward"` InviteCode sql.NullString `json:"-"` NickName sql.NullString `json:"-"` Ranks sql.NullInt64 `json:"-"` TopScore sql.NullInt64 `json:"-"` } // JSONUser 结构体用于返回 JSON 数据 type JSONUser struct { Id int `json:"id"` UserName string `json:"userName"` Url string `json:"url"` Gold int `json:"gold"` RandCode string `json:"randCode"` OpenId string `json:"openId"` ParentAddress string `json:"parentAddress"` Language string `json:"language"` TgUserId string `json:"tg_userid"` LastLoginAt string `json:"lastLoginAt"` RegisteredAt string `json:"registeredAt"` InviteReward int `json:"inviteReward"` InviteCode string `json:"inviteCode"` NickName string `json:"nickName"` Ranks int `json:"ranking"` TopScore int `json:"topScore"` } // 转换函数,将 User 转换为 JSONUser,并处理 NULL 值 func (u *User) toJSONUser() JSONUser { return JSONUser{ Id: u.Id, UserName: nullStringToString(u.UserName), Url: nullStringToString(u.Url), Gold: u.Gold, RandCode: nullStringToString(u.RandCode), OpenId: u.OpenId, ParentAddress: nullStringToString(u.ParentAddress), Language: nullStringToString(u.Language), TgUserId: nullStringToString(u.TgUserId), LastLoginAt: nullStringToString(u.LastLoginAt), RegisteredAt: nullStringToString(u.RegisteredAt), InviteReward: u.InviteReward, InviteCode: nullStringToString(u.InviteCode), NickName: nullStringToString(u.NickName), Ranks: nullIntToInt(u.Ranks), TopScore: nullIntToInt(u.TopScore), } } // 辅助函数,将 sql.NullString 转为字符串 func nullStringToString(ns sql.NullString) string { if ns.Valid { return ns.String } return "" } // 辅助函数,将 sql.NullInt64 转为 int func nullIntToInt(ni sql.NullInt64) int { if ni.Valid { return int(ni.Int64) } return 0 } // 查询并返回用户信息 func getUserInfoByOpenId(db *sql.DB, openId string) (User, error) { var user User query := ` SELECT id, userName, url, gold, randCode, openId, parentAddress, language, tg_userid, lastLoginAt, registeredAt, inviteReward, inviteCode, nickName, ranks, topScore FROM users WHERE openId = ?` err := db.QueryRow(query, openId).Scan( &user.Id, &user.UserName, &user.Url, &user.Gold, &user.RandCode, &user.OpenId, &user.ParentAddress, &user.Language, &user.TgUserId, &user.LastLoginAt, &user.RegisteredAt, &user.InviteReward, &user.InviteCode, &user.NickName, &user.Ranks, &user.TopScore, ) if err == sql.ErrNoRows { // 返回自定义错误消息,指示未找到记录 return user, fmt.Errorf("用户不存在,openId: %s", openId) } else if err != nil { // 返回其他查询错误 return user, fmt.Errorf("查询出错: %v", err) } return user, nil } // 处理请求,返回用户信息 func userInfoHandler(w http.ResponseWriter, r *http.Request) { var req struct { OpenId string `json:"openId"` } err := json.NewDecoder(r.Body).Decode(&req) if err != nil { log.Printf("Error decoding request body: %v\n", err) http.Error(w, "Invalid request body", http.StatusBadRequest) return } user, err := getUserInfoByOpenId(db, req.OpenId) if err != nil { if err.Error() == fmt.Sprintf("用户不存在,openId: %s", req.OpenId) { log.Printf("User not found for openId: %s\n", req.OpenId) http.Error(w, "User not found", http.StatusNotFound) } else { log.Printf("Error retrieving user info: %v\n", err) http.Error(w, "Error retrieving user info", http.StatusInternalServerError) } return } // 返回 JSON 格式的用户信息 w.Header().Set("Content-Type", "application/json") if err := json.NewEncoder(w).Encode(user.toJSONUser()); err != nil { log.Printf("Error encoding user info to JSON: %v\n", err) http.Error(w, "Error encoding user info to JSON", http.StatusInternalServerError) } } /**********************************************************登录与注册********************************************/ var person Person // 定义玩家,接收所有玩家信息 type Person struct { Id int `json:"id"` //数据库中的标识,会自增 UserName string `json:"userName"` //昵称(甲方要求每次登录刷新) Url string `json:"url"` //头像的URL Gold int `json:"gold"` //金币 RandCode string `json:"randCode"` //随机码,注册时生成,登录会刷新 OpenId string `json:"openId"` //openid ParentAddress string `json:"parentAddress"` //上级地址 Language string `json:"language"` //语言(甲方要求每次登录刷新)(刷新功能未完成) Tg_userid string `json:"tg_userid"` //tgid InviteCode string `json:"inviteCode"` //玩家注册时生成并插入的邀请码,不会刷新 } // 检查用户是否存在 func checkUserExists(db *sql.DB, openId string) (bool, error) { log.Println("传入的 openId: ", openId) /**************************************************/ var exists bool // 查询数据库中是否存在传入的 openid query := "SELECT COUNT(*) FROM users WHERE openId = ?" // 使用 db.QueryRow() 执行查询,查询结果存储在 exists 变量中 err := db.QueryRow(query, openId).Scan(&exists) if err != nil { return false, err // 如果查询出错,返回错误 } return exists, nil // 返回查询结果,存在则为 true,不存在为 false } // 登录或注册函数 func loginOrRegister(db *sql.DB, openid, userName, url, parentAddress, language, tg_userid string) (string, error) { // 调用 checkUserExists() 函数,检查用户是否存在 exists, err := checkUserExists(db, openid) if err != nil { log.Printf("Error checking user exists: %v\n", err) return "500", err // 如果查询出错,返回错误 } // 每次登录生成新的随机码 person.RandCode = generateRandomCode() //甲方要求的每次登录刷新userName person.UserName = generateRandomCode() // 获取当前时间作为登录和注册时间 currentTime := time.Now() if exists { // 如果用户已存在,更新数据库中的 RandCode 和 lastLoginAt updateRandCode := "UPDATE users SET userName = ?,randCode = ?, lastLoginAt = ? WHERE openid = ?" _, err := db.Exec(updateRandCode, person.UserName, person.RandCode, currentTime, openid) if err != nil { log.Printf("Error updating MySQL users") return "500", err // 如果更新失败,返回错误 } log.Printf("用户 %s 登录成功, 生成的新随机码: %s,生成新的userName:%s, 登录时间: %s", openid, person.RandCode, person.UserName, currentTime.Format("2006-01-02 15:04:05")) return "200", nil } else { // 如果 userName, url, parentAddress, language, tg_userid 没有传入,默认设置为空字符串 if userName == "" { userName = person.UserName } if url == "" { url = "" } if parentAddress == "" { parentAddress = "" } if language == "" { language = "" } if tg_userid == "" { tg_userid = "" } //玩家注册时用随机码生成一个邀请码插入数据库,这个不会改变 person.InviteCode = generateRandomCode() // 如果用户不存在,生成新的随机码并插入相关信息,包括注册时间和登录时间 //当玩家注册时,随机生成一个邀请码插入数据库,这个不会改变 insertOpenId := "INSERT INTO users (openid, userName, url, randCode, parentAddress, language, tg_userid, registeredAt, lastLoginAt,inviteCode) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?,?)" _, err := db.Exec(insertOpenId, openid, userName, url, person.RandCode, parentAddress, language, tg_userid, currentTime, currentTime, person.InviteCode) // 执行插入操作 if err != nil { log.Printf("Error inserting MySQL users") return "500", err // 如果插入出错,返回错误 } log.Printf("用户 %s 注册成功, 生成的新随机码: %s,生成的userName:%s, 注册时间: %s,邀请码:%s", openid, person.RandCode, person.UserName, currentTime.Format("2006-01-02 15:04:05"), person.InviteCode) return "200", nil } } // 生成随机码RandCode func generateRandomCode() string { const length = 16 // 随机码的长度 const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" randCode := make([]byte, length) for i := range randCode { randCode[i] = charset[rand.Intn(len(charset))] } return string(randCode) } func loginHandler(w http.ResponseWriter, r *http.Request) { // 定义用于接收 JSON 请求体的结构体 var req struct { OpenId string `json:"openId"` UserName string `json:"userName"` Url string `json:"url"` ParentAddress string `json:"parentAddress"` Language string `json:"language"` TgUserId string `json:"tg_userid"` } // 使用 json.NewDecoder() 将请求体中的 JSON 数据解析为结构体 err := json.NewDecoder(r.Body).Decode(&req) if err != nil { http.Error(w, "Invalid request body", http.StatusBadRequest) return } // 调用 loginOrRegister() 函数,传入解析出的参数 message, err := loginOrRegister(db, req.OpenId, req.UserName, req.Url, req.ParentAddress, req.Language, req.TgUserId) if err != nil { http.Error(w, "登录或注册过程出错", http.StatusInternalServerError) return } // 返回 JSON 格式的响应,包含登录或注册的结果 w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]string{ "message": message, }) } // ******************************************************************每日任务********************************************************************/ // 任务列表 type DailyTask struct { TaskId int `json:"task_id"` Reward string `json:"reward"` IsComplete bool `json:"is_complete"` EvenId int `json:"even_id"` } /* func showDailyTaskHandler(w http.ResponseWriter, r *http.Request) { // 定义用于接收 JSON 请求体的结构体 var req struct { AskDailyTask string `json:"AskDailyTask"` } // 使用 json.NewDecoder() 将请求体中的 JSON 数据解析为结构体 err := json.NewDecoder(r.Body).Decode(&req) if err != nil { http.Error(w, "Invalid request body", http.StatusBadRequest) return } // 调用 showDailyTask() 函数,传入解析出的参数 tasks, err := showDailyTask(db, req.AskDailyTask) if err != nil { http.Error(w, "Error retrieving daily tasks", http.StatusInternalServerError) return } // 打印任务列表到服务器日志 for _, task := range tasks { log.Printf("Task ID: %d, Reward: %s, IsComplete: %v, Event ID: %d\n", task.TaskId, task.Reward, task.IsComplete, task.EvenId) } // 返回 JSON 格式的响应,包含每日任务表 w.Header().Set("Content-Type", "application/json") err = json.NewEncoder(w).Encode(tasks) // tasks 是一个切片,因此直接编码为 JSON if err != nil { http.Error(w, "Error encoding response", http.StatusInternalServerError) return } }*/ // 测试代码,功能与上面那个方法相同,但有更多日志打印。出现问题用它 func showDailyTaskHandler(w http.ResponseWriter, r *http.Request) { // 定义用于接收 JSON 请求体的结构体 var req struct { AskDailyTask string `json:"AskDailyTask"` } // 使用 json.NewDecoder() 将请求体中的 JSON 数据解析为结构体 err := json.NewDecoder(r.Body).Decode(&req) if err != nil { log.Printf("Error decoding request body: %v\n", err) // 打印解码错误 http.Error(w, "Invalid request body", http.StatusBadRequest) return } // 调用 showDailyTask() 函数,传入解析出的参数 tasks, err := showDailyTask(db, req.AskDailyTask) if err != nil { log.Printf("Error retrieving tasks: %v\n", err) // 打印任务获取错误 http.Error(w, "Error retrieving daily tasks", http.StatusInternalServerError) return } // 打印任务列表到服务器日志 for _, task := range tasks { log.Printf("Task ID: %d, Reward: %s, IsComplete: %v, Event ID: %d\n", task.TaskId, task.Reward, task.IsComplete, task.EvenId) } // 返回 JSON 格式的响应,包含每日任务表 w.Header().Set("Content-Type", "application/json") err = json.NewEncoder(w).Encode(tasks) // tasks 是一个切片,因此直接编码为 JSON if err != nil { log.Printf("Error encoding tasks to JSON: %v\n", err) // 打印编码错误 http.Error(w, "Error encoding response", http.StatusInternalServerError) return } } // 展示任务列表 func showDailyTask(db *sql.DB, string2 string) ([]DailyTask, error) { // 查询数据库 rows, err := db.Query("SELECT taskId, reward, isComplete, evenId FROM dailytask") if err != nil { return nil, fmt.Errorf("query error: %v", err) } defer rows.Close() // 存储任务列表的切片 var tasks []DailyTask // 遍历查询结果 for rows.Next() { var task DailyTask // 扫描每行数据到 DailyTask 结构体 err := rows.Scan(&task.TaskId, &task.Reward, &task.IsComplete, &task.EvenId) if err != nil { return nil, fmt.Errorf("scan error: %v", err) } tasks = append(tasks, task) } // 检查 rows.Next() 是否有发生错误 if err = rows.Err(); err != nil { return nil, fmt.Errorf("rows iteration error: %v", err) } // 返回任务列表 return tasks, nil } /********************************************************返回邀请好友列表(和邀请奖励)*************************************************************/ type Invitee struct { InviteeId int `json:"invitee_id"` InviteeName string `json:"invitee_userName"` InviteeOpenId string `json:"invitee_open_id"` InviteeUrl string `json:"invitee_url"` InviteReward int `json:"invite_reward"` } type Inviter struct { InviterUrl string `json:"inviter_url"` InviterId int `json:"inviter_id"` InviterOpenId string `json:"openId"` InviterUserName string `json:"userName"` InviterNickName sql.NullString `json:"nickName"` // 处理可能为 NULL 的字段 Gold int `json:"gold"` InviteCode string `json:"invite_code"` } // 定义一个结构体,用于同时返回邀请者和被邀请者信息 type InviteeResponse struct { Invitees []Invitee `json:"invitees"` Inviter Inviter `json:"inviter"` } // inviteeListHandler 处理 HTTP 请求,返回某个用户邀请的好友列表和邀请者信息 func inviteeListHandler(w http.ResponseWriter, r *http.Request) { // 定义用于接收 JSON 请求体的结构体 var req struct { OpenId string `json:"openId"` } // 使用 json.NewDecoder() 将请求体中的 JSON 数据解析为结构体 err := json.NewDecoder(r.Body).Decode(&req) if err != nil { log.Printf("Error decoding request body: %v\n", err) // 打印解码错误 http.Error(w, "Invalid request body", http.StatusBadRequest) return } // 调用 inviteeList() 函数,传入解析出的参数 invitees, err := inviteeList(db, req.OpenId) if err != nil { log.Printf("Error retrieving invitee list: %v\n", err) // 打印错误日志 http.Error(w, "Error retrieving invitee list", http.StatusInternalServerError) return } // 打印邀请的好友列表信息到服务器日志 log.Println("----- 邀请的好友列表信息 -----") for _, invitee := range invitees { log.Printf("好友ID: %d, 用户名: %s, OpenId: %s, 头像URL: %s, 邀请奖励: %d", invitee.InviteeId, invitee.InviteeName, invitee.InviteeOpenId, invitee.InviteeUrl, invitee.InviteReward) } // 调用 inviter() 函数,传入解析出的参数 inviterList, err := inviter(db, req.OpenId) if err != nil { log.Printf("Error retrieving inviter: %v\n", err) // 打印错误日志 http.Error(w, "Error retrieving inviter", http.StatusInternalServerError) return } // 假设邀请者只会有一个,获取第一个邀请者 var inviter Inviter if len(inviterList) > 0 { inviter = inviterList[0] } // 打印邀请者信息到服务器日志 log.Println("----- 邀请者信息 -----") log.Printf("邀请者ID: %d, 用户名: %s, OpenId: %s, 称号: %s, 头像URL: %s, 金币: %d,邀请码:%s", inviter.InviterId, inviter.InviterUserName, inviter.InviterOpenId, inviter.InviterNickName.String, inviter.InviterUrl, inviter.Gold, inviter.InviteCode) // 创建一个结构体,包含邀请者和被邀请者信息 response := InviteeResponse{ Invitees: invitees, Inviter: inviter, } // 返回 JSON 格式的邀请好友列表和邀请者信息 w.Header().Set("Content-Type", "application/json") err = json.NewEncoder(w).Encode(response) if err != nil { log.Printf("Error encoding invitee and inviter list to JSON: %v\n", err) // 打印编码错误日志 http.Error(w, "Error encoding invitee and inviter list to JSON", http.StatusInternalServerError) } } // inviteeList 查询数据库,返回某个用户邀请的好友列表 func inviteeList(db *sql.DB, parentAddress string) ([]Invitee, error) { // 查询数据库,获取邀请者的好友列表 rows, err := db.Query("SELECT url,id, userName,openId,inviteReward FROM users WHERE parentAddress = ?", parentAddress) if err != nil { return nil, fmt.Errorf("query error: %v", err) } defer rows.Close() // 存储好友列表的切片 var invitees []Invitee // 遍历查询结果 for rows.Next() { var invitee Invitee // 扫描每行数据到 Invitee 结构体 err := rows.Scan(&invitee.InviteeUrl, &invitee.InviteeId, &invitee.InviteeName, &invitee.InviteeOpenId, &invitee.InviteReward) if err != nil { return nil, fmt.Errorf("scan error: %v", err) } invitees = append(invitees, invitee) } // 检查 rows.Next() 是否有发生错误 if err = rows.Err(); err != nil { return nil, fmt.Errorf("rows iteration error: %v", err) } // 返回好友列表 return invitees, nil } // inviter查询邀请者的信息 func inviter(db *sql.DB, openId string) ([]Inviter, error) { // 查询数据库,获取邀请者的好友列表 rows, err := db.Query("SELECT url, id, openId, userName, nickName, gold,inviteCode FROM users WHERE openId = ?", openId) if err != nil { // 如果查询过程中发生错误,返回nil和错误信息 return nil, fmt.Errorf("查询出错: %v", err) } defer rows.Close() // 确保查询结束后关闭行集 var inviters []Inviter // 定义一个Inviter切片用于存储查询结果 // 遍历查询结果 for rows.Next() { var inviter Inviter // 扫描每行数据到 Inviter 结构体,处理 nickName 为 sql.NullString 类型 err := rows.Scan(&inviter.InviterUrl, &inviter.InviterId, &inviter.InviterOpenId, &inviter.InviterUserName, &inviter.InviterNickName, &inviter.Gold, &inviter.InviteCode) if err != nil { // 如果扫描过程中出现错误,返回nil和错误信息 return nil, fmt.Errorf("扫描出错: %v", err) } inviters = append(inviters, inviter) // 将扫描到的用户信息追加到切片中 } // 检查 rows.Next() 是否有发生错误 if err = rows.Err(); err != nil { return nil, fmt.Errorf("行遍历出错: %v", err) } // 返回查询结果的邀请者列表 return inviters, nil } /**********************************************金币商品页面************************************************************/ // GoldCommodity 结构体表示 goldcommodity 表的每一行 type GoldCommodity struct { CommodityGold int `json:"commodityGold"` Commodity string `json:"commodity"` Price string `json:"price"` } // getGoldCommodities 从 goldcommodity 表中查询所有数据并返回 func getGoldCommodities(db *sql.DB) ([]GoldCommodity, error) { // 查询整个 goldcommodity 表 rows, err := db.Query("SELECT commodityGold, commodity, price FROM goldcommodity") if err != nil { return nil, fmt.Errorf("查询出错: %v", err) } defer rows.Close() var commodities []GoldCommodity // 遍历查询结果,将每行数据扫描到 GoldCommodity 结构体中 for rows.Next() { var commodity GoldCommodity err := rows.Scan(&commodity.CommodityGold, &commodity.Commodity, &commodity.Price) if err != nil { return nil, fmt.Errorf("扫描出错: %v", err) } commodities = append(commodities, commodity) } // 检查 rows.Next() 是否有其他错误 if err = rows.Err(); err != nil { return nil, fmt.Errorf("行遍历出错: %v", err) } return commodities, nil } // goldCommodityHandler 处理 HTTP 请求,返回 goldcommodity 表中的所有数据 func goldCommodityHandler(w http.ResponseWriter, r *http.Request) { // 查询 goldcommodity 表中的所有数据 commodities, err := getGoldCommodities(db) if err != nil { log.Printf("Error retrieving gold commodities: %v\n", err) http.Error(w, "Error retrieving gold commodities", http.StatusInternalServerError) return } // 设置响应头为 JSON 格式并返回数据 w.Header().Set("Content-Type", "application/json") if err := json.NewEncoder(w).Encode(commodities); err != nil { log.Printf("Error encoding commodities to JSON: %v\n", err) http.Error(w, "Error encoding commodities to JSON", http.StatusInternalServerError) } } /******************************************************排行榜数据*************************************************************/ // 定义 ranking 结构体 type ranking struct { UserId int `json:"userId"` Rank int `json:"rank"` TopScore int `json:"topScore"` NickName string `json:"nickName"` } // 查询排行榜数据 // +++++++++++++++++++++++++++++++++++返回前三+++++++++++++++++++++++++++++++++++++/ func rankingListHandler(w http.ResponseWriter, r *http.Request) { // 在查询排行榜数据之前,先更新排名 err := updatePlayerRankings(db) if err != nil { log.Printf("Error updating player rankings: %v\n", err) http.Error(w, "Error updating player rankings", http.StatusInternalServerError) return } // 查询 top N 排行榜数据 // 请根据您的需要修改 TopNumber 的值 // 例如,TopNumber = 100,则返回排行榜前 100 名 // 例如,TopNumber = 500,则返回排行榜前 500 名 // 注意:TopNumber 的值越大,返回的数据量越大,但也会占用更多的 CPU 和内存 // 请根据您的应用的需要和 CPU 和内存的情况来决定 TopNumber 的值 // 例如,TopNumber = 1000,但您的应用 CPU 和内存都有 8GB,可能需要 30-60 秒才能返回结果 // 例如,TopNumber = 50 TopNumber := 3 // 假设要获取前 3 名 rows, err := db.Query("SELECT id, ranks, topScore, nickName FROM playerranking ORDER BY topScore DESC LIMIT ?", TopNumber) if err != nil { log.Printf("Error retrieving ranking list: %v\n", err) http.Error(w, "Error retrieving ranking list", http.StatusInternalServerError) return } defer rows.Close() // 定义一个 ranking 切片来存储查询结果 var rankings []ranking // 遍历查询结果,将每行数据扫描到 ranking 结构体中 for rows.Next() { var rank ranking err := rows.Scan(&rank.UserId, &rank.Rank, &rank.TopScore, &rank.NickName) if err != nil { log.Printf("Error scanning ranking data: %v\n", err) http.Error(w, "Error processing ranking data", http.StatusInternalServerError) return } rankings = append(rankings, rank) } // 检查遍历行集时是否发生错误 if err = rows.Err(); err != nil { log.Printf("Error iterating over rows: %v\n", err) http.Error(w, "Error retrieving ranking data", http.StatusInternalServerError) return } // 设置响应头为 JSON 格式并返回数据 w.Header().Set("Content-Type", "application/json") if err := json.NewEncoder(w).Encode(rankings); err != nil { log.Printf("Error encoding ranking list to JSON: %v\n", err) http.Error(w, "Error encoding ranking list to JSON", http.StatusInternalServerError) } } // 更新玩家排名并同步到 users 表 func updatePlayerRankings(db *sql.DB) error { // 1. 初始化排名变量 _, err := db.Exec("SET @ranks := 0;") if err != nil { return fmt.Errorf("初始化排名变量时出错: %v", err) } // 2. 更新 playerranking 表中的 rank updateRankSQL := ` UPDATE playerranking SET ranks = (@ranks := @ranks + 1) ORDER BY topScore DESC; ` _, err = db.Exec(updateRankSQL) if err != nil { return fmt.Errorf("更新排名时出错: %v", err) } // 3. 将 playerranking 表中的 rank 和 topScore 同步到 users 表 syncRankTopScoreSQL := ` UPDATE users u JOIN playerranking p ON u.id = p.id SET u.ranks = p.ranks, u.topScore = p.topScore; ` _, err = db.Exec(syncRankTopScoreSQL) if err != nil { return fmt.Errorf("同步 rank 和 topScore 到 users 表时出错: %v", err) } // 4. 将 users 表中的 nickName 同步到 playerranking 表 syncNickNameSQL := ` UPDATE playerranking p JOIN users u ON p.id = u.id SET p.nickName = u.nickName; ` _, err = db.Exec(syncNickNameSQL) if err != nil { return fmt.Errorf("同步 nickName 到 playerranking 表时出错: %v", err) } return nil } /******************************************************存档****************************************************************/ /* 前端应传来的json格式 { "save_index": 1, "backup_index": 0, "major_data": {"level": 5, "progress": "50%"}, "minor_data": {"cutsceneEnded": true}, "real_time_data": {"npcSeen": true, "patchID": 123} } */ // SaveData 结构体定义 type SaveData struct { ID int `json:"id"` SaveIndex int `json:"save_index"` BackupIndex int `json:"backup_index"` MajorData json.RawMessage `json:"major_data"` MinorData json.RawMessage `json:"minor_data"` RealTimeData json.RawMessage `json:"real_time_data"` Timestamp sql.NullTime `json:"timestamp"` // 使用 sql.NullTime } // 存档数据到数据库 func saveToDatabase(db *sql.DB, data SaveData) error { query := ` INSERT INTO save_data (save_index, backup_index, major_data, minor_data, real_time_data, timestamp) VALUES (?, ?, ?, ?, ?, ?) ` _, err := db.Exec(query, data.SaveIndex, data.BackupIndex, data.MajorData, data.MinorData, data.RealTimeData, data.Timestamp, ) if err != nil { return fmt.Errorf("保存数据到数据库时出错: %v", err) } return nil } // 从数据库加载存档 func loadFromDatabase(db *sql.DB, saveIndex, backupIndex int) (SaveData, error) { var data SaveData var timestampStr string // 使用字符串接收 Timestamp 字段 query := "SELECT save_index, backup_index, major_data, minor_data, real_time_data, timestamp FROM save_data WHERE save_index = ? AND backup_index = ?" err := db.QueryRow(query, saveIndex, backupIndex).Scan( &data.SaveIndex, &data.BackupIndex, &data.MajorData, &data.MinorData, &data.RealTimeData, ×tampStr, // 将 timestamp 作为字符串接收 ) if err != nil { return data, err } // 手动解析 timestampStr 为 time.Time parsedTime, err := time.Parse("2006-01-02 15:04:05", timestampStr) if err != nil { return data, fmt.Errorf("解析时间戳时出错: %v", err) } data.Timestamp = sql.NullTime{ Time: parsedTime, Valid: true, } return data, nil } // 存档处理函数 func saveHandler(w http.ResponseWriter, r *http.Request) { log.Printf("请求方法: %s, 请求路径: %s\n", r.Method, r.URL.Path) // 打印请求方法和路径 // 检查请求方法是否为 GET if r.Method != http.MethodGet { http.Error(w, "请求方法错误,仅支持 GET", http.StatusMethodNotAllowed) return } // 从查询参数中获取存档数据 saveIndexStr := r.URL.Query().Get("save_index") backupIndexStr := r.URL.Query().Get("backup_index") majorDataStr := r.URL.Query().Get("major_data") minorDataStr := r.URL.Query().Get("minor_data") realTimeDataStr := r.URL.Query().Get("real_time_data") // 转换 save_index 和 backup_index 为整数 saveIndex, err := strconv.Atoi(saveIndexStr) backupIndex, err := strconv.Atoi(backupIndexStr) if err != nil { http.Error(w, "索引参数错误", http.StatusBadRequest) return } // 将 JSON 字符串转换为对应的结构 var majorData, minorData, realTimeData json.RawMessage err = json.Unmarshal([]byte(majorDataStr), &majorData) err = json.Unmarshal([]byte(minorDataStr), &minorData) err = json.Unmarshal([]byte(realTimeDataStr), &realTimeData) if err != nil { http.Error(w, "解析 JSON 数据时出错", http.StatusBadRequest) return } // 创建存档数据结构 data := SaveData{ SaveIndex: saveIndex, BackupIndex: backupIndex, MajorData: majorData, MinorData: minorData, RealTimeData: realTimeData, Timestamp: sql.NullTime{ Time: time.Now(), Valid: true, }, } // 存档数据到数据库 err = saveToDatabase(db, data) if err != nil { http.Error(w, fmt.Sprintf("保存存档时出错: %v", err), http.StatusInternalServerError) return } // 返回成功响应 w.WriteHeader(http.StatusCreated) w.Write([]byte("存档成功")) } // 加载处理函数 func loadHandler(w http.ResponseWriter, r *http.Request) { log.Printf("请求方法: %s, 请求路径: %s\n", r.Method, r.URL.Path) // 打印请求方法和路径 if r.Method != http.MethodGet { http.Error(w, "请求方法错误,仅支持 GET", http.StatusMethodNotAllowed) return } saveIndexStr := r.URL.Query().Get("save_index") backupIndexStr := r.URL.Query().Get("backup_index") if saveIndexStr == "" || backupIndexStr == "" { http.Error(w, "缺少 save_index 或 backup_index 参数", http.StatusBadRequest) return } saveIndex, err := strconv.Atoi(saveIndexStr) backupIndex, err := strconv.Atoi(backupIndexStr) if err != nil { http.Error(w, "索引参数错误", http.StatusBadRequest) return } data, err := loadFromDatabase(db, saveIndex, backupIndex) if err != nil { http.Error(w, fmt.Sprintf("加载存档时出错: %v", err), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") err = json.NewEncoder(w).Encode(data) if err != nil { http.Error(w, "编码 JSON 数据时出错", http.StatusInternalServerError) } } // 修改 loadHandler 中的时间格式化逻辑 func formatTimestamp(timestamp sql.NullTime) string { if timestamp.Valid { return timestamp.Time.Format("2006-01-02 15:04:05") } return "" }