mengchongxiaoxiaole/brotato/web.go
2024-10-26 14:22:25 +08:00

969 lines
32 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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,
&timestampStr, // 将 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 ""
}