This commit is contained in:
HuangZiBo 2024-10-26 14:22:25 +08:00
parent 772ad415ee
commit 7200c6c99b
10 changed files with 1048 additions and 0 deletions

8
brotato/.idea/.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
# 默认忽略的文件
/shelf/
/workspace.xml
# 基于编辑器的 HTTP 客户端请求
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="Go" enabled="true" />
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@ -0,0 +1,5 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
</profile>
</component>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/brotato.iml" filepath="$PROJECT_DIR$/.idea/brotato.iml" />
</modules>
</component>
</project>

4
brotato/.idea/vcs.xml Normal file
View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings" defaultProject="true" />
</project>

10
brotato/go.mod Normal file
View File

@ -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

6
brotato/go.sum Normal file
View File

@ -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=

17
brotato/main.go Normal file
View File

@ -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 <a href="https://www.jetbrains.com/help/go/">jetbrains.com/help/go/</a>.
// Also, you can try interactive lessons for GoLand by selecting 'Help | Learn IDE Features' from the main menu.

13
brotato/playerInfo.json Normal file
View File

@ -0,0 +1,13 @@
{
"id": 123456821,
"userName": "player137",
"url": "https://example.com/pic.png",
"openId": "119",
"parentAddress": "66666",
"language": "en",
"tg_userid":"123456828player132"
}

968
brotato/web.go Normal file
View File

@ -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,
&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 ""
}