feat: 优化 BeatLeader 和 ScoreSaber 数据查询逻辑,新增数据变更检测方法

This commit is contained in:
lixiangwuxian 2025-03-08 19:01:47 +08:00
parent fcc99efe7e
commit b0892412ce
5 changed files with 96 additions and 53 deletions

View File

@ -2,8 +2,12 @@ package beatleader
import (
"database/sql"
"encoding/json"
"errors"
"fmt"
"io"
"log"
"net/http"
"strconv"
"time"
@ -182,7 +186,7 @@ func (bl *blQuery) GetScore(qqId string) (reply string, err error) {
}
// 如果有新的数据,则插入
if lastDataLite.TotalPlayCount != dataLite.TotalPlayCount {
if lastDataLite.IsDiffFrom(dataLite) {
_, err = tx.NamedExec(`INSERT INTO blData (id, name, country, pp, rank, country_rank, total_score,
total_ranked_score, average_ranked_accuracy, total_play_count, ranked_play_count, replays_watched, generated_time)
VALUES (:id, :name, :country, :pp, :rank, :country_rank, :total_score,
@ -295,23 +299,72 @@ func (bl *blQuery) GetRecentScores(count int, qqId string) ([]RecordDataLite, er
return nil, err
}
// 查询记录
var records []RecordDataLite
err = tx.Select(&records, "SELECT * FROM blRecordData WHERE bl_id = ? ORDER BY generated_time DESC LIMIT ?", blId, count)
if err != nil {
if err == sql.ErrNoRows {
return nil, errors.New("未查询到数据")
}
log.Println("查询数据出错:", err)
return nil, errors.New("查询记录失败")
}
// // 查询记录
// var records []RecordDataLite
// err = tx.Select(&records, "SELECT * FROM blRecordData WHERE bl_id = ? ORDER BY generated_time DESC LIMIT ?", blId, count)
// if err != nil {
// if err == sql.ErrNoRows {
// return nil, errors.New("未查询到数据")
// }
// log.Println("查询数据出错:", err)
// return nil, errors.New("查询记录失败")
// }
// 提交事务
err = tx.Commit()
if err != nil {
log.Print(err)
return nil, errors.New("提交事务失败")
}
// // 提交事务
// err = tx.Commit()
// if err != nil {
// log.Print(err)
// return nil, errors.New("提交事务失败")
// }
// return records, nil
//从线上获取
historyUrl := "https://api.beatleader.xyz/player/%s/scores"
fullUrl := fmt.Sprintf(historyUrl, blId)
resp, err := http.Get(fullUrl)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
var scores []ScoreData
err = json.Unmarshal(body, &scores)
if err != nil {
return nil, err
}
records := make([]RecordDataLite, 0)
for _, score := range scores {
records = append(records, RecordDataLite{
ScoreID: score.ID,
BlID: score.Player.ID,
Name: score.Player.Name,
Country: score.Player.Country,
SongName: score.Leaderboard.Song.Name,
SongSubName: score.Leaderboard.Song.SubName,
SongAuthorName: score.Leaderboard.Song.Author,
SongHash: score.Leaderboard.Song.Hash,
SongId: score.Leaderboard.Song.ID,
CoverImage: score.Leaderboard.Song.CoverImage,
DifficultyRaw: score.Leaderboard.Difficulty.DifficultyName,
PP: score.Pp,
Stars: *score.Leaderboard.Difficulty.Stars,
Weight: score.Weight,
Modifiers: score.Modifiers,
Multiplier: float64(score.ModifiedScore) / float64(score.BaseScore),
BadCuts: score.BadCuts,
Score: score.ModifiedScore,
Rank: score.Rank,
MaxScore: score.ModifiedScore,
FullCombo: score.FullCombo,
DeviceHmd: GetHMDStr(score.HMD),
DeviceControllerLeft: GetControllerStr(score.Controller),
DeviceControllerRight: GetControllerStr(score.Controller),
GeneratedTime: time.Now().Format("2006-01-02 15:04:05.999999999-07:00"),
})
}
return records, nil
}

View File

@ -176,6 +176,7 @@ type RecordDataLite struct {
SongSubName string `json:"songSubName" db:"song_sub_name"`
SongAuthorName string `json:"songAuthorName" db:"song_author_name"`
SongHash string `json:"songHash" db:"song_hash"`
SongId string `json:"songId" db:"song_id"`
CoverImage string `json:"coverImage" db:"cover_image"`
DifficultyRaw string `json:"difficultyRaw" db:"difficulty_raw"`
Stars float64 `json:"stars" db:"stars"`
@ -391,6 +392,15 @@ type PlayerDataLite struct {
GeneratedTime string `json:"generatedTime" db:"generated_time"`
}
func (p PlayerDataLite) IsDiffFrom(p2 PlayerDataLite) bool {
return p.TotalScore != p2.TotalScore ||
p.TotalRankedScore != p2.TotalRankedScore ||
p.AverageRankedAccuracy != p2.AverageRankedAccuracy ||
p.TotalPlayCount != p2.TotalPlayCount ||
p.RankedPlayCount != p2.RankedPlayCount ||
p.ReplaysWatched != p2.ReplaysWatched
}
func (p PlayerData) ToString() string {
formatedStr := "玩家 %s\n" +
"区域 %s\n" +

View File

@ -181,7 +181,7 @@ func (ss *ssQuery) GetScore(ssId string) (reply string, err error) {
}
// 如果有新的数据,则插入
if lastDataLite.TotalPlayCount != dataLite.TotalPlayCount {
if lastDataLite.IsDiffFrom(dataLite) {
_, err = tx.NamedExec(`INSERT INTO ssData (id, name, country, pp, rank, country_rank, total_score,
total_ranked_score, average_ranked_accuracy, total_play_count, ranked_play_count, replays_watched, generated_time)
VALUES (:id, :name, :country, :pp, :rank, :country_rank, :total_score,

View File

@ -12,26 +12,6 @@ type Command struct {
CommandData CommandData `json:"commandData"`
}
func (c Command) ToString() string {
if c.CommandName != "score" {
return ""
}
strWithRank := "%s 使用 %s 在 %s 的 %s 难度(%.1f🌟)中取得了排名第%d的成绩pp 为%.2f。"
strWithoutRank := "%s 使用 %s 在 %s 的 %s 难度中取得了排名第%d的成绩。"
strWithOutDevice := "%s 在 %s 的 %s 难度(%.1f🌟)中取得了排名第%d的成绩pp 为%.2f。"
strWithOutDeviceAndRank := "%s 在 %s 的 %s 难度(%.1f🌟)中取得了排名第%d的成绩。"
hardStr := strings.Split(c.CommandData.Leaderboard.Difficulty.DifficultyRaw, "_")[1]
if c.CommandData.Leaderboard.Ranked && c.CommandData.Score.DeviceHmd != nil {
return fmt.Sprintf(strWithRank, c.CommandData.Score.LeaderboardPlayerInfo.Name, *c.CommandData.Score.DeviceHmd, c.CommandData.Leaderboard.SongName, hardStr, c.CommandData.Leaderboard.Stars, c.CommandData.Score.Rank, c.CommandData.Score.Pp)
} else if !c.CommandData.Leaderboard.Ranked && c.CommandData.Score.DeviceHmd != nil {
return fmt.Sprintf(strWithoutRank, c.CommandData.Score.LeaderboardPlayerInfo.Name, *c.CommandData.Score.DeviceHmd, c.CommandData.Leaderboard.SongName, hardStr, c.CommandData.Score.Rank)
} else if c.CommandData.Leaderboard.Ranked && c.CommandData.Score.DeviceHmd == nil {
return fmt.Sprintf(strWithOutDevice, c.CommandData.Score.LeaderboardPlayerInfo.Name, c.CommandData.Leaderboard.SongName, hardStr, c.CommandData.Leaderboard.Stars, c.CommandData.Score.Rank, c.CommandData.Score.Pp)
} else {
return fmt.Sprintf(strWithOutDeviceAndRank, c.CommandData.Score.LeaderboardPlayerInfo.Name, c.CommandData.Leaderboard.SongName, hardStr, c.CommandData.Leaderboard.Stars, c.CommandData.Score.Rank)
}
}
// type Badge struct {
// Description string `json:"description"`
// Image string `json:"image"`
@ -228,6 +208,15 @@ type PlayerDataLite struct {
GeneratedTime string `json:"generatedTime" db:"generated_time"`
}
func (p PlayerDataLite) IsDiffFrom(p2 PlayerDataLite) bool {
return p.TotalScore != p2.TotalScore ||
p.TotalRankedScore != p2.TotalRankedScore ||
p.AverageRankedAccuracy != p2.AverageRankedAccuracy ||
p.TotalPlayCount != p2.TotalPlayCount ||
p.RankedPlayCount != p2.RankedPlayCount ||
p.ReplaysWatched != p2.ReplaysWatched
}
func (p PlayerData) ToString() string {
formatedStr := "玩家 %s\n" +
"区域 %s\n" +
@ -235,8 +224,8 @@ func (p PlayerData) ToString() string {
"全球排名 %d\n" +
"区域排名 %d\n" +
"Ranked谱面均准 %.2f%%\n" +
"总游玩数 %d\n" +
"Ranked谱面游玩数 %d\n" +
"总游玩数 %d\n" +
"Ranked谱面游玩数 %d\n" +
"回放被观看次数 %d"
return fmt.Sprintf(formatedStr,
p.Name,
@ -257,8 +246,8 @@ func (p PlayerDataLite) ToString() string {
"全球排名 %d\n" +
"区域排名 %d\n" +
"Ranked谱面均准 %.2f%%\n" +
"总游玩数 %d\n" +
"Ranked谱面游玩数 %d\n" +
"总游玩数 %d\n" +
"Ranked谱面游玩数 %d\n" +
"回放被观看次数 %d"
return fmt.Sprintf(formatedStr,
p.Name,
@ -279,8 +268,8 @@ func (p PlayerData) LastDiffToString(lastDayQueryData PlayerDataLite) string {
"全球排名 %d(%+d)\n" +
"区域排名 %d(%+d)\n" +
"Ranked谱面均准 %.2f%%(%+.2f%%)\n" +
"总游玩数 %d(%+d)\n" +
"Ranked谱面游玩数 %d(%+d)\n" +
"总游玩数 %d(%+d)\n" +
"Ranked谱面游玩数 %d(%+d)\n" +
"回放被观看次数 %d"
return fmt.Sprintf(formatedStr,
p.Name,

View File

@ -35,15 +35,6 @@ func cleanTmpFolder() {
func cleanDB() {
db := sqlite3.GetDB()
// if time.Now().Weekday() == time.Sunday && time.Now().Hour() < 1 {
// start := time.Now()
// _, err := db.Exec("VACUUM")
// if err != nil {
// log.Printf("清理数据库失败: %v", err)
// return
// }
// log.Printf("数据库清理完成,耗时: %v", time.Since(start))
// }
now := time.Now()
if now.Hour() == 1 && now.Minute() < 10 {
start := time.Now()