diff --git a/brotato/.idea/.gitignore b/brotato/.idea/.gitignore
new file mode 100644
index 0000000..35410ca
--- /dev/null
+++ b/brotato/.idea/.gitignore
@@ -0,0 +1,8 @@
+# 默认忽略的文件
+/shelf/
+/workspace.xml
+# 基于编辑器的 HTTP 客户端请求
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/brotato/.idea/brotato.iml b/brotato/.idea/brotato.iml
new file mode 100644
index 0000000..5e764c4
--- /dev/null
+++ b/brotato/.idea/brotato.iml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/brotato/.idea/inspectionProfiles/Project_Default.xml b/brotato/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000..8d66637
--- /dev/null
+++ b/brotato/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/brotato/.idea/modules.xml b/brotato/.idea/modules.xml
new file mode 100644
index 0000000..67af9f5
--- /dev/null
+++ b/brotato/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/brotato/.idea/vcs.xml b/brotato/.idea/vcs.xml
new file mode 100644
index 0000000..d843f34
--- /dev/null
+++ b/brotato/.idea/vcs.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/brotato/go.mod b/brotato/go.mod
new file mode 100644
index 0000000..96f89d3
--- /dev/null
+++ b/brotato/go.mod
@@ -0,0 +1,10 @@
+module brotato
+
+go 1.23.1
+
+require (
+ github.com/go-sql-driver/mysql v1.8.1
+ golang.org/x/crypto v0.28.0
+)
+
+require filippo.io/edwards25519 v1.1.0 // indirect
diff --git a/brotato/go.sum b/brotato/go.sum
new file mode 100644
index 0000000..d9ed419
--- /dev/null
+++ b/brotato/go.sum
@@ -0,0 +1,6 @@
+filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
+filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
+github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
+github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
+golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
+golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
diff --git a/brotato/main.go b/brotato/main.go
new file mode 100644
index 0000000..5248462
--- /dev/null
+++ b/brotato/main.go
@@ -0,0 +1,17 @@
+package main
+
+import (
+ "database/sql"
+ _ "github.com/go-sql-driver/mysql"
+)
+
+var db *sql.DB
+
+func main() {
+ initweb()
+ //openFileDate()
+
+}
+
+// TIP See GoLand help at jetbrains.com/help/go/.
+// Also, you can try interactive lessons for GoLand by selecting 'Help | Learn IDE Features' from the main menu.
diff --git a/brotato/playerInfo.json b/brotato/playerInfo.json
new file mode 100644
index 0000000..c043090
--- /dev/null
+++ b/brotato/playerInfo.json
@@ -0,0 +1,13 @@
+
+
+
+{
+ "id": 123456821,
+ "userName": "player137",
+ "url": "https://example.com/pic.png",
+
+ "openId": "119",
+ "parentAddress": "66666",
+ "language": "en",
+ "tg_userid":"123456828player132"
+}
diff --git a/brotato/web.go b/brotato/web.go
new file mode 100644
index 0000000..8d21920
--- /dev/null
+++ b/brotato/web.go
@@ -0,0 +1,968 @@
+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 ""
+}