Persist live status and migrate legacy live data
- Add user_live_status to store hi score, hi combo count, and clear count per chart - Expand user_live_goal to persist achieved live_goal_reward_id entries instead of only coarse goal types - Update live reward handling to refresh live status and sync newly achieved live goals on every clear - Return live status and achieved goal ids from stored user data in /api/live/status - Aggregate profile liveCnt from stored user_live_status instead of hardcoded values - Load complete-rank thresholds from live info for goal evaluation - Migrate legacy user_live_record data into user_live_status and backfill achievable live goals on startup Signed-off-by: Sean Du <do4suki@gmail.com>
This commit is contained in:
@@ -268,46 +268,49 @@ func buildRandomLiveList() []liveapischema.RandomLiveList {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildNormalLiveStatusList(ids []int) []liveapischema.NormalLiveStatusList {
|
func buildNormalLiveStatusList(ids []int, snapshotMap map[int]session.LiveStatusSnapshot) []liveapischema.NormalLiveStatusList {
|
||||||
result := make([]liveapischema.NormalLiveStatusList, 0, len(ids))
|
result := make([]liveapischema.NormalLiveStatusList, 0, len(ids))
|
||||||
for _, id := range ids {
|
for _, id := range ids {
|
||||||
|
snapshot := snapshotMap[id]
|
||||||
result = append(result, liveapischema.NormalLiveStatusList{
|
result = append(result, liveapischema.NormalLiveStatusList{
|
||||||
LiveDifficultyID: id,
|
LiveDifficultyID: id,
|
||||||
Status: 1,
|
Status: snapshot.Status,
|
||||||
HiScore: 0,
|
HiScore: snapshot.HiScore,
|
||||||
HiComboCount: 0,
|
HiComboCount: snapshot.HiComboCount,
|
||||||
ClearCnt: 0,
|
ClearCnt: snapshot.ClearCnt,
|
||||||
AchievedGoalIDList: []int{},
|
AchievedGoalIDList: snapshot.AchievedGoalIDList,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildSpecialLiveStatusList(ids []int) []liveapischema.SpecialLiveStatusList {
|
func buildSpecialLiveStatusList(ids []int, snapshotMap map[int]session.LiveStatusSnapshot) []liveapischema.SpecialLiveStatusList {
|
||||||
result := make([]liveapischema.SpecialLiveStatusList, 0, len(ids))
|
result := make([]liveapischema.SpecialLiveStatusList, 0, len(ids))
|
||||||
for _, id := range ids {
|
for _, id := range ids {
|
||||||
|
snapshot := snapshotMap[id]
|
||||||
result = append(result, liveapischema.SpecialLiveStatusList{
|
result = append(result, liveapischema.SpecialLiveStatusList{
|
||||||
LiveDifficultyID: id,
|
LiveDifficultyID: id,
|
||||||
Status: 1,
|
Status: snapshot.Status,
|
||||||
HiScore: 0,
|
HiScore: snapshot.HiScore,
|
||||||
HiComboCount: 0,
|
HiComboCount: snapshot.HiComboCount,
|
||||||
ClearCnt: 0,
|
ClearCnt: snapshot.ClearCnt,
|
||||||
AchievedGoalIDList: []int{},
|
AchievedGoalIDList: snapshot.AchievedGoalIDList,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildTrainingLiveStatusList(ids []int) []liveapischema.TrainingLiveStatusList {
|
func buildTrainingLiveStatusList(ids []int, snapshotMap map[int]session.LiveStatusSnapshot) []liveapischema.TrainingLiveStatusList {
|
||||||
result := make([]liveapischema.TrainingLiveStatusList, 0, len(ids))
|
result := make([]liveapischema.TrainingLiveStatusList, 0, len(ids))
|
||||||
for _, id := range ids {
|
for _, id := range ids {
|
||||||
|
snapshot := snapshotMap[id]
|
||||||
result = append(result, liveapischema.TrainingLiveStatusList{
|
result = append(result, liveapischema.TrainingLiveStatusList{
|
||||||
LiveDifficultyID: id,
|
LiveDifficultyID: id,
|
||||||
Status: 1,
|
Status: snapshot.Status,
|
||||||
HiScore: 0,
|
HiScore: snapshot.HiScore,
|
||||||
HiComboCount: 0,
|
HiComboCount: snapshot.HiComboCount,
|
||||||
ClearCnt: 0,
|
ClearCnt: snapshot.ClearCnt,
|
||||||
AchievedGoalIDList: []int{},
|
AchievedGoalIDList: snapshot.AchievedGoalIDList,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
|
|||||||
@@ -24,12 +24,20 @@ func liveStatus(ctx *gin.Context) (res any, err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
allIDs := make([]int, 0, len(normalIDs)+len(specialIDs)+len(trainingIDs))
|
||||||
|
allIDs = append(allIDs, normalIDs...)
|
||||||
|
allIDs = append(allIDs, specialIDs...)
|
||||||
|
allIDs = append(allIDs, trainingIDs...)
|
||||||
|
statusSnapshotMap, err := ss.BuildLiveStatusSnapshotMap(allIDs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
res = liveapischema.StatusResp{
|
res = liveapischema.StatusResp{
|
||||||
Result: liveapischema.StatusData{
|
Result: liveapischema.StatusData{
|
||||||
NormalLiveStatusList: buildNormalLiveStatusList(normalIDs),
|
NormalLiveStatusList: buildNormalLiveStatusList(normalIDs, statusSnapshotMap),
|
||||||
SpecialLiveStatusList: buildSpecialLiveStatusList(specialIDs),
|
SpecialLiveStatusList: buildSpecialLiveStatusList(specialIDs, statusSnapshotMap),
|
||||||
TrainingLiveStatusList: buildTrainingLiveStatusList(trainingIDs),
|
TrainingLiveStatusList: buildTrainingLiveStatusList(trainingIDs, statusSnapshotMap),
|
||||||
MarathonLiveStatusList: []any{},
|
MarathonLiveStatusList: []any{},
|
||||||
FreeLiveStatusList: []any{},
|
FreeLiveStatusList: []any{},
|
||||||
CanResumeLive: true,
|
CanResumeLive: true,
|
||||||
|
|||||||
@@ -1,34 +1,87 @@
|
|||||||
package profile
|
package profile
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
usermodel "honoka-chan/internal/model/user"
|
||||||
profileapischema "honoka-chan/internal/schema/api/profile"
|
profileapischema "honoka-chan/internal/schema/api/profile"
|
||||||
|
"honoka-chan/internal/session"
|
||||||
|
"sort"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
func liveCnt() (res any, err error) {
|
type profileLiveDifficultyRow struct {
|
||||||
|
LiveDifficultyID int `xorm:"live_difficulty_id"`
|
||||||
|
Difficulty int `xorm:"difficulty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func liveCnt(ctx *gin.Context, targetUserID int) (res any, err error) {
|
||||||
|
ss := session.Get(ctx)
|
||||||
|
|
||||||
|
targetPref, err := getTargetUserPref(ss, targetUserID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
difficultyByLiveID := map[int]int{}
|
||||||
|
loadDifficultyRows := func(table string) error {
|
||||||
|
rows := []profileLiveDifficultyRow{}
|
||||||
|
err := ss.MainEng.Table(table).Alias("live").
|
||||||
|
Join("LEFT", "live_setting_m setting", "live.live_setting_id = setting.live_setting_id").
|
||||||
|
Select("live.live_difficulty_id, setting.difficulty").
|
||||||
|
Find(&rows)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, row := range rows {
|
||||||
|
difficultyByLiveID[row.LiveDifficultyID] = row.Difficulty
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err := loadDifficultyRows("normal_live_m"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := loadDifficultyRows("special_live_m"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
statusRows := []usermodel.UserLiveStatus{}
|
||||||
|
err = ss.UserEng.Table(new(usermodel.UserLiveStatus)).
|
||||||
|
Where("user_id = ?", targetPref.UserID).
|
||||||
|
Where("clear_cnt > 0").
|
||||||
|
Cols("live_difficulty_id", "clear_cnt").
|
||||||
|
Find(&statusRows)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
difficultyCounts := map[int]int{}
|
||||||
|
for _, row := range statusRows {
|
||||||
|
difficulty, ok := difficultyByLiveID[row.LiveDifficultyID]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if difficulty == 5 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
difficultyCounts[difficulty]++
|
||||||
|
}
|
||||||
|
|
||||||
|
orderedDifficulties := []int{1, 2, 3, 4, 6}
|
||||||
|
result := make([]profileapischema.LiveCntData, 0, len(orderedDifficulties))
|
||||||
|
for _, difficulty := range orderedDifficulties {
|
||||||
|
result = append(result, profileapischema.LiveCntData{
|
||||||
|
Difficulty: difficulty,
|
||||||
|
ClearCnt: difficultyCounts[difficulty],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Slice(result, func(i, j int) bool {
|
||||||
|
return result[i].Difficulty < result[j].Difficulty
|
||||||
|
})
|
||||||
|
|
||||||
res = profileapischema.LiveCntResp{
|
res = profileapischema.LiveCntResp{
|
||||||
Result: []profileapischema.LiveCntData{
|
Result: result,
|
||||||
{
|
|
||||||
Difficulty: 1,
|
|
||||||
ClearCnt: 315,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Difficulty: 2,
|
|
||||||
ClearCnt: 310,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Difficulty: 3,
|
|
||||||
ClearCnt: 314,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Difficulty: 4,
|
|
||||||
ClearCnt: 455,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Difficulty: 6,
|
|
||||||
ClearCnt: 233,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Status: 200,
|
Status: 200,
|
||||||
CommandNum: false,
|
CommandNum: false,
|
||||||
TimeStamp: time.Now().Unix(),
|
TimeStamp: time.Now().Unix(),
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ func ProfileApi(ctx *gin.Context, action string, targetUserID int) (res any, err
|
|||||||
case "cardRanking":
|
case "cardRanking":
|
||||||
res, err = cardRanking()
|
res, err = cardRanking()
|
||||||
case "liveCnt":
|
case "liveCnt":
|
||||||
res, err = liveCnt()
|
res, err = liveCnt(ctx, targetUserID)
|
||||||
case "profileInfo":
|
case "profileInfo":
|
||||||
res, err = profileInfo(ctx, targetUserID)
|
res, err = profileInfo(ctx, targetUserID)
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -42,6 +42,36 @@ func reward(ctx *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
totalScore := playRewardReq.ScoreSmile + playRewardReq.ScoreCool + playRewardReq.ScoreCute
|
totalScore := playRewardReq.ScoreSmile + playRewardReq.ScoreCool + playRewardReq.ScoreCute
|
||||||
|
beforeLiveStatus, afterLiveStatus, err := ss.UpdateUserLiveStatus(difficultyID, totalScore, playRewardReq.MaxCombo)
|
||||||
|
if ss.CheckErr(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
newGoalRewardIDs, err := ss.SyncUserLiveGoals(difficultyID, afterLiveStatus.HiScore, afterLiveStatus.HiComboCount, afterLiveStatus.ClearCnt)
|
||||||
|
if ss.CheckErr(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
goalAccomplishedIDs := make([]any, 0, len(newGoalRewardIDs))
|
||||||
|
goalRewards := make([]any, 0, len(newGoalRewardIDs))
|
||||||
|
if len(newGoalRewardIDs) > 0 {
|
||||||
|
goalRewardRows, err := ss.GetLiveGoalRewardRowsByIDs(newGoalRewardIDs)
|
||||||
|
if ss.CheckErr(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, goalRewardID := range newGoalRewardIDs {
|
||||||
|
goalAccomplishedIDs = append(goalAccomplishedIDs, goalRewardID)
|
||||||
|
}
|
||||||
|
for _, row := range goalRewardRows {
|
||||||
|
goalRewards = append(goalRewards, liveschema.PlayRewardList{
|
||||||
|
ItemID: row.ItemID,
|
||||||
|
AddType: row.AddType,
|
||||||
|
Amount: row.Amount,
|
||||||
|
ItemCategoryID: row.ItemCategoryID,
|
||||||
|
RewardBoxFlag: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
playResp := liveschema.RewardResp{
|
playResp := liveschema.RewardResp{
|
||||||
ResponseData: liveschema.RewardData{
|
ResponseData: liveschema.RewardData{
|
||||||
LiveInfo: []liveschema.RewardLiveInfo{
|
LiveInfo: []liveschema.RewardLiveInfo{
|
||||||
@@ -53,8 +83,8 @@ func reward(ctx *gin.Context) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
TotalLove: 0,
|
TotalLove: 0,
|
||||||
IsHighScore: true,
|
IsHighScore: totalScore >= beforeLiveStatus.HiScore,
|
||||||
HiScore: totalScore,
|
HiScore: afterLiveStatus.HiScore,
|
||||||
BaseRewardInfo: liveschema.BaseRewardInfo{
|
BaseRewardInfo: liveschema.BaseRewardInfo{
|
||||||
PlayerExp: 0,
|
PlayerExp: 0,
|
||||||
PlayerExpUnitMax: liveschema.PlayerExpUnitMax{
|
PlayerExpUnitMax: liveschema.PlayerExpUnitMax{
|
||||||
@@ -93,8 +123,8 @@ func reward(ctx *gin.Context) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
GoalAccompInfo: liveschema.GoalAccompInfo{
|
GoalAccompInfo: liveschema.GoalAccompInfo{
|
||||||
AchievedIds: []any{},
|
AchievedIds: goalAccomplishedIDs,
|
||||||
Rewards: []any{},
|
Rewards: goalRewards,
|
||||||
},
|
},
|
||||||
SpecialRewardInfo: []any{},
|
SpecialRewardInfo: []any{},
|
||||||
EventInfo: []any{},
|
EventInfo: []any{},
|
||||||
@@ -123,25 +153,25 @@ func reward(ctx *gin.Context) {
|
|||||||
StatusCode: 200,
|
StatusCode: 200,
|
||||||
}
|
}
|
||||||
|
|
||||||
if playRewardReq.MaxCombo > liveInfo.SRankCombo {
|
if playRewardReq.MaxCombo >= liveInfo.SRankCombo {
|
||||||
playResp.ResponseData.ComboRank = 1
|
playResp.ResponseData.ComboRank = 1
|
||||||
} else if playRewardReq.MaxCombo > liveInfo.ARankCombo {
|
} else if playRewardReq.MaxCombo >= liveInfo.ARankCombo {
|
||||||
playResp.ResponseData.ComboRank = 2
|
playResp.ResponseData.ComboRank = 2
|
||||||
} else if playRewardReq.MaxCombo > liveInfo.BRankCombo {
|
} else if playRewardReq.MaxCombo >= liveInfo.BRankCombo {
|
||||||
playResp.ResponseData.ComboRank = 3
|
playResp.ResponseData.ComboRank = 3
|
||||||
} else if playRewardReq.MaxCombo > liveInfo.CRankCombo {
|
} else if playRewardReq.MaxCombo >= liveInfo.CRankCombo {
|
||||||
playResp.ResponseData.ComboRank = 4
|
playResp.ResponseData.ComboRank = 4
|
||||||
} else {
|
} else {
|
||||||
playResp.ResponseData.ComboRank = 5
|
playResp.ResponseData.ComboRank = 5
|
||||||
}
|
}
|
||||||
|
|
||||||
if totalScore > liveInfo.SRankScore {
|
if totalScore >= liveInfo.SRankScore {
|
||||||
playResp.ResponseData.Rank = 1
|
playResp.ResponseData.Rank = 1
|
||||||
} else if totalScore > liveInfo.ARankScore {
|
} else if totalScore >= liveInfo.ARankScore {
|
||||||
playResp.ResponseData.Rank = 2
|
playResp.ResponseData.Rank = 2
|
||||||
} else if totalScore > liveInfo.BRankScore {
|
} else if totalScore >= liveInfo.BRankScore {
|
||||||
playResp.ResponseData.Rank = 3
|
playResp.ResponseData.Rank = 3
|
||||||
} else if totalScore > liveInfo.CRankScore {
|
} else if totalScore >= liveInfo.CRankScore {
|
||||||
playResp.ResponseData.Rank = 4
|
playResp.ResponseData.Rank = 4
|
||||||
} else {
|
} else {
|
||||||
playResp.ResponseData.Rank = 5
|
playResp.ResponseData.Rank = 5
|
||||||
|
|||||||
@@ -4,9 +4,11 @@ import "honoka-chan/internal/constant"
|
|||||||
|
|
||||||
type UserLiveGoal struct {
|
type UserLiveGoal struct {
|
||||||
ID int `xorm:"id pk autoincr"`
|
ID int `xorm:"id pk autoincr"`
|
||||||
LiveDifficultyID int `xorm:"live_difficulty_id"`
|
LiveDifficultyID int `xorm:"live_difficulty_id index"`
|
||||||
UserID int `xorm:"user_id"`
|
UserID int `xorm:"user_id index"`
|
||||||
|
LiveGoalRewardID int `xorm:"live_goal_reward_id index"`
|
||||||
GoalType constant.LiveGoalType `xorm:"goal_type"`
|
GoalType constant.LiveGoalType `xorm:"goal_type"`
|
||||||
|
Rank int `xorm:"rank"`
|
||||||
CompletedAt int64 `xorm:"completed_at"`
|
CompletedAt int64 `xorm:"completed_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package usermodel
|
||||||
|
|
||||||
|
type UserLiveStatus struct {
|
||||||
|
ID int `xorm:"id pk autoincr"`
|
||||||
|
UserID int `xorm:"user_id unique(user_live_status_pair) index"`
|
||||||
|
LiveDifficultyID int `xorm:"live_difficulty_id unique(user_live_status_pair) index"`
|
||||||
|
HiScore int `xorm:"hi_score"`
|
||||||
|
HiComboCount int `xorm:"hi_combo_count"`
|
||||||
|
ClearCnt int `xorm:"clear_cnt"`
|
||||||
|
InsertDate int64 `xorm:"insert_date"`
|
||||||
|
UpdateDate int64 `xorm:"update_date"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UserLiveStatus) TableName() string {
|
||||||
|
return "user_live_status"
|
||||||
|
}
|
||||||
@@ -39,6 +39,10 @@ type LiveInfo struct {
|
|||||||
BRankCombo int `json:"-"`
|
BRankCombo int `json:"-"`
|
||||||
CRankCombo int `json:"-"`
|
CRankCombo int `json:"-"`
|
||||||
SRankCombo int `json:"-"`
|
SRankCombo int `json:"-"`
|
||||||
|
ARankComplete int `json:"-"`
|
||||||
|
BRankComplete int `json:"-"`
|
||||||
|
CRankComplete int `json:"-"`
|
||||||
|
SRankComplete int `json:"-"`
|
||||||
AcFlag int `json:"ac_flag"`
|
AcFlag int `json:"ac_flag"`
|
||||||
SwingFlag int `json:"swing_flag"`
|
SwingFlag int `json:"swing_flag"`
|
||||||
NotesList []NotesList `json:"notes_list"`
|
NotesList []NotesList `json:"notes_list"`
|
||||||
|
|||||||
+44
-27
@@ -1,7 +1,6 @@
|
|||||||
package session
|
package session
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"honoka-chan/internal/constant"
|
|
||||||
usermodel "honoka-chan/internal/model/user"
|
usermodel "honoka-chan/internal/model/user"
|
||||||
liveschema "honoka-chan/internal/schema/live"
|
liveschema "honoka-chan/internal/schema/live"
|
||||||
liverecordschema "honoka-chan/internal/schema/liverecord"
|
liverecordschema "honoka-chan/internal/schema/liverecord"
|
||||||
@@ -64,13 +63,34 @@ func (ss *Session) GetLiveInfo(LiveDifficultyID int) (bool, *liveschema.LiveInfo
|
|||||||
BRankCombo int `xorm:"b_rank_combo"`
|
BRankCombo int `xorm:"b_rank_combo"`
|
||||||
CRankCombo int `xorm:"c_rank_combo"`
|
CRankCombo int `xorm:"c_rank_combo"`
|
||||||
SRankCombo int `xorm:"s_rank_combo"`
|
SRankCombo int `xorm:"s_rank_combo"`
|
||||||
|
ARankComplete int `xorm:"a_rank_complete"`
|
||||||
|
BRankComplete int `xorm:"b_rank_complete"`
|
||||||
|
CRankComplete int `xorm:"c_rank_complete"`
|
||||||
|
SRankComplete int `xorm:"s_rank_complete"`
|
||||||
AcFlag int `xorm:"ac_flag"`
|
AcFlag int `xorm:"ac_flag"`
|
||||||
SwingFlag int `xorm:"swing_flag"`
|
SwingFlag int `xorm:"swing_flag"`
|
||||||
}
|
}
|
||||||
has, err := ss.MainEng.Table("special_live_m").Alias("a").
|
has, err := ss.MainEng.Table("special_live_m").Alias("a").
|
||||||
Join("LEFT", "live_setting_m", "a.live_setting_id = live_setting_m.live_setting_id").
|
Join("LEFT", "live_setting_m", "a.live_setting_id = live_setting_m.live_setting_id").
|
||||||
Where("live_difficulty_id = ?", LiveDifficultyID).
|
Where("live_difficulty_id = ?", LiveDifficultyID).
|
||||||
Cols("a.live_difficulty_id,live_setting_m.*").Get(&info)
|
Select(`
|
||||||
|
a.live_difficulty_id,
|
||||||
|
a.c_rank_complete,
|
||||||
|
a.b_rank_complete,
|
||||||
|
a.a_rank_complete,
|
||||||
|
a.s_rank_complete,
|
||||||
|
live_setting_m.notes_setting_asset,
|
||||||
|
live_setting_m.a_rank_score,
|
||||||
|
live_setting_m.b_rank_score,
|
||||||
|
live_setting_m.c_rank_score,
|
||||||
|
live_setting_m.s_rank_score,
|
||||||
|
live_setting_m.a_rank_combo,
|
||||||
|
live_setting_m.b_rank_combo,
|
||||||
|
live_setting_m.c_rank_combo,
|
||||||
|
live_setting_m.s_rank_combo,
|
||||||
|
live_setting_m.ac_flag,
|
||||||
|
live_setting_m.swing_flag
|
||||||
|
`).Get(&info)
|
||||||
if ss.CheckErr(err) {
|
if ss.CheckErr(err) {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
@@ -79,7 +99,24 @@ func (ss *Session) GetLiveInfo(LiveDifficultyID int) (bool, *liveschema.LiveInfo
|
|||||||
has, err = ss.MainEng.Table("normal_live_m").Alias("a").
|
has, err = ss.MainEng.Table("normal_live_m").Alias("a").
|
||||||
Join("LEFT", "live_setting_m", "a.live_setting_id = live_setting_m.live_setting_id").
|
Join("LEFT", "live_setting_m", "a.live_setting_id = live_setting_m.live_setting_id").
|
||||||
Where("live_difficulty_id = ?", LiveDifficultyID).
|
Where("live_difficulty_id = ?", LiveDifficultyID).
|
||||||
Cols("a.live_difficulty_id,live_setting_m.*").Get(&info)
|
Select(`
|
||||||
|
a.live_difficulty_id,
|
||||||
|
a.c_rank_complete,
|
||||||
|
a.b_rank_complete,
|
||||||
|
a.a_rank_complete,
|
||||||
|
a.s_rank_complete,
|
||||||
|
live_setting_m.notes_setting_asset,
|
||||||
|
live_setting_m.a_rank_score,
|
||||||
|
live_setting_m.b_rank_score,
|
||||||
|
live_setting_m.c_rank_score,
|
||||||
|
live_setting_m.s_rank_score,
|
||||||
|
live_setting_m.a_rank_combo,
|
||||||
|
live_setting_m.b_rank_combo,
|
||||||
|
live_setting_m.c_rank_combo,
|
||||||
|
live_setting_m.s_rank_combo,
|
||||||
|
live_setting_m.ac_flag,
|
||||||
|
live_setting_m.swing_flag
|
||||||
|
`).Get(&info)
|
||||||
if ss.CheckErr(err) {
|
if ss.CheckErr(err) {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
@@ -105,6 +142,10 @@ func (ss *Session) GetLiveInfo(LiveDifficultyID int) (bool, *liveschema.LiveInfo
|
|||||||
BRankCombo: info.BRankCombo,
|
BRankCombo: info.BRankCombo,
|
||||||
CRankCombo: info.CRankCombo,
|
CRankCombo: info.CRankCombo,
|
||||||
SRankCombo: info.SRankCombo,
|
SRankCombo: info.SRankCombo,
|
||||||
|
ARankComplete: info.ARankComplete,
|
||||||
|
BRankComplete: info.BRankComplete,
|
||||||
|
CRankComplete: info.CRankComplete,
|
||||||
|
SRankComplete: info.SRankComplete,
|
||||||
AcFlag: info.AcFlag,
|
AcFlag: info.AcFlag,
|
||||||
SwingFlag: info.SwingFlag,
|
SwingFlag: info.SwingFlag,
|
||||||
NotesList: []liveschema.NotesList{},
|
NotesList: []liveschema.NotesList{},
|
||||||
@@ -188,27 +229,3 @@ func (ss *Session) GetDeckInfo(deckID int) (bool, *liverecordschema.DeckInfo) {
|
|||||||
UnitList: unitData,
|
UnitList: unitData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ss *Session) GetLiveGoalList(LiveDifficultyID int) []constant.LiveGoalType {
|
|
||||||
var goal []constant.LiveGoalType
|
|
||||||
err := ss.MainEng.Table("live_goal_reward_m").
|
|
||||||
Where("live_difficulty_id = ?", LiveDifficultyID).Cols("live_goal_type").Find(&goal)
|
|
||||||
if ss.CheckErr(err) {
|
|
||||||
return []constant.LiveGoalType{}
|
|
||||||
}
|
|
||||||
return goal
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ss *Session) GetUserLiveGoalList(LiveDifficultyID int) []constant.LiveGoalType {
|
|
||||||
var goal []constant.LiveGoalType
|
|
||||||
var goalData []usermodel.UserLiveGoal
|
|
||||||
err := ss.UserEng.Table(new(usermodel.UserLiveGoal)).
|
|
||||||
Where("user_id = ? AND live_difficulty_id = ?", ss.UserID, LiveDifficultyID).Find(&goalData)
|
|
||||||
if ss.CheckErr(err) {
|
|
||||||
return []constant.LiveGoalType{}
|
|
||||||
}
|
|
||||||
for _, g := range goalData {
|
|
||||||
goal = append(goal, g.GoalType)
|
|
||||||
}
|
|
||||||
return goal
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,281 @@
|
|||||||
|
package session
|
||||||
|
|
||||||
|
import (
|
||||||
|
"honoka-chan/internal/constant"
|
||||||
|
usermodel "honoka-chan/internal/model/user"
|
||||||
|
"sort"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type liveGoalRewardRow struct {
|
||||||
|
LiveGoalRewardID int `xorm:"live_goal_reward_id"`
|
||||||
|
LiveDifficultyID int `xorm:"live_difficulty_id"`
|
||||||
|
LiveGoalType constant.LiveGoalType `xorm:"live_goal_type"`
|
||||||
|
Rank int `xorm:"rank"`
|
||||||
|
AddType int `xorm:"add_type"`
|
||||||
|
ItemID int `xorm:"item_id"`
|
||||||
|
ItemCategoryID int `xorm:"item_category_id"`
|
||||||
|
Amount int `xorm:"amount"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type LiveStatusSnapshot struct {
|
||||||
|
Status int
|
||||||
|
HiScore int
|
||||||
|
HiComboCount int
|
||||||
|
ClearCnt int
|
||||||
|
AchievedGoalIDList []int
|
||||||
|
}
|
||||||
|
|
||||||
|
func liveRank(value int, cRank int, bRank int, aRank int, sRank int) int {
|
||||||
|
switch {
|
||||||
|
case value >= sRank:
|
||||||
|
return 1
|
||||||
|
case value >= aRank:
|
||||||
|
return 2
|
||||||
|
case value >= bRank:
|
||||||
|
return 3
|
||||||
|
case value >= cRank:
|
||||||
|
return 4
|
||||||
|
default:
|
||||||
|
return 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss *Session) GetUserLiveStatus(liveDifficultyID int) (bool, *usermodel.UserLiveStatus, error) {
|
||||||
|
status := usermodel.UserLiveStatus{}
|
||||||
|
has, err := ss.UserEng.Table(new(usermodel.UserLiveStatus)).
|
||||||
|
Where("user_id = ? AND live_difficulty_id = ?", ss.UserID, liveDifficultyID).
|
||||||
|
Get(&status)
|
||||||
|
if err != nil {
|
||||||
|
return false, nil, err
|
||||||
|
}
|
||||||
|
return has, &status, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss *Session) GetUserLiveStatusMap(liveDifficultyIDs []int) (map[int]usermodel.UserLiveStatus, error) {
|
||||||
|
result := map[int]usermodel.UserLiveStatus{}
|
||||||
|
if len(liveDifficultyIDs) == 0 {
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
rows := []usermodel.UserLiveStatus{}
|
||||||
|
err := ss.UserEng.Table(new(usermodel.UserLiveStatus)).
|
||||||
|
In("live_difficulty_id", liveDifficultyIDs).
|
||||||
|
Where("user_id = ?", ss.UserID).
|
||||||
|
Find(&rows)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, row := range rows {
|
||||||
|
result[row.LiveDifficultyID] = row
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss *Session) GetUserLiveGoalRewardIDMap(liveDifficultyIDs []int) (map[int][]int, error) {
|
||||||
|
result := map[int][]int{}
|
||||||
|
if len(liveDifficultyIDs) == 0 {
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
rows := []usermodel.UserLiveGoal{}
|
||||||
|
err := ss.UserEng.Table(new(usermodel.UserLiveGoal)).
|
||||||
|
In("live_difficulty_id", liveDifficultyIDs).
|
||||||
|
Where("user_id = ?", ss.UserID).
|
||||||
|
Where("live_goal_reward_id > 0").
|
||||||
|
OrderBy("live_goal_reward_id ASC").
|
||||||
|
Find(&rows)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, row := range rows {
|
||||||
|
result[row.LiveDifficultyID] = append(result[row.LiveDifficultyID], row.LiveGoalRewardID)
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss *Session) BuildLiveStatusSnapshotMap(liveDifficultyIDs []int) (map[int]LiveStatusSnapshot, error) {
|
||||||
|
statusMap, err := ss.GetUserLiveStatusMap(liveDifficultyIDs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
goalMap, err := ss.GetUserLiveGoalRewardIDMap(liveDifficultyIDs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make(map[int]LiveStatusSnapshot, len(liveDifficultyIDs))
|
||||||
|
for _, liveDifficultyID := range liveDifficultyIDs {
|
||||||
|
status := LiveStatusSnapshot{
|
||||||
|
Status: 1,
|
||||||
|
HiScore: 0,
|
||||||
|
HiComboCount: 0,
|
||||||
|
ClearCnt: 0,
|
||||||
|
AchievedGoalIDList: []int{},
|
||||||
|
}
|
||||||
|
|
||||||
|
if row, ok := statusMap[liveDifficultyID]; ok {
|
||||||
|
status.HiScore = row.HiScore
|
||||||
|
status.HiComboCount = row.HiComboCount
|
||||||
|
status.ClearCnt = row.ClearCnt
|
||||||
|
if row.ClearCnt > 0 {
|
||||||
|
status.Status = 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ids := goalMap[liveDifficultyID]; len(ids) > 0 {
|
||||||
|
status.AchievedGoalIDList = ids
|
||||||
|
}
|
||||||
|
|
||||||
|
result[liveDifficultyID] = status
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss *Session) UpdateUserLiveStatus(liveDifficultyID int, totalScore int, maxCombo int) (before usermodel.UserLiveStatus, after usermodel.UserLiveStatus, err error) {
|
||||||
|
now := time.Now().Unix()
|
||||||
|
has, current, err := ss.GetUserLiveStatus(liveDifficultyID)
|
||||||
|
if err != nil {
|
||||||
|
return before, after, err
|
||||||
|
}
|
||||||
|
if current == nil {
|
||||||
|
current = &usermodel.UserLiveStatus{}
|
||||||
|
}
|
||||||
|
|
||||||
|
before = *current
|
||||||
|
if !has {
|
||||||
|
after = usermodel.UserLiveStatus{
|
||||||
|
UserID: ss.UserID,
|
||||||
|
LiveDifficultyID: liveDifficultyID,
|
||||||
|
HiScore: totalScore,
|
||||||
|
HiComboCount: maxCombo,
|
||||||
|
ClearCnt: 1,
|
||||||
|
InsertDate: now,
|
||||||
|
UpdateDate: now,
|
||||||
|
}
|
||||||
|
_, err = ss.UserEng.Insert(&after)
|
||||||
|
return before, after, err
|
||||||
|
}
|
||||||
|
|
||||||
|
after = before
|
||||||
|
if totalScore > after.HiScore {
|
||||||
|
after.HiScore = totalScore
|
||||||
|
}
|
||||||
|
if maxCombo > after.HiComboCount {
|
||||||
|
after.HiComboCount = maxCombo
|
||||||
|
}
|
||||||
|
after.ClearCnt++
|
||||||
|
after.UpdateDate = now
|
||||||
|
|
||||||
|
_, err = ss.UserEng.Table(new(usermodel.UserLiveStatus)).
|
||||||
|
Where("user_id = ? AND live_difficulty_id = ?", ss.UserID, liveDifficultyID).
|
||||||
|
Cols("hi_score", "hi_combo_count", "clear_cnt", "update_date").
|
||||||
|
Update(&after)
|
||||||
|
return before, after, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss *Session) GetAchievedLiveGoalRewardRows(liveDifficultyID int, hiScore int, hiComboCount int, clearCnt int) ([]liveGoalRewardRow, error) {
|
||||||
|
has, liveInfo := ss.GetLiveInfo(liveDifficultyID)
|
||||||
|
if !has || liveInfo == nil {
|
||||||
|
return []liveGoalRewardRow{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
rows := []liveGoalRewardRow{}
|
||||||
|
err := ss.MainEng.Table("live_goal_reward_m").
|
||||||
|
Where("live_difficulty_id = ?", liveDifficultyID).
|
||||||
|
OrderBy("live_goal_type ASC, rank ASC, live_goal_reward_id ASC").
|
||||||
|
Find(&rows)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
scoreRank := liveRank(hiScore, liveInfo.CRankScore, liveInfo.BRankScore, liveInfo.ARankScore, liveInfo.SRankScore)
|
||||||
|
comboRank := liveRank(hiComboCount, liveInfo.CRankCombo, liveInfo.BRankCombo, liveInfo.ARankCombo, liveInfo.SRankCombo)
|
||||||
|
clearRank := liveRank(clearCnt, liveInfo.CRankComplete, liveInfo.BRankComplete, liveInfo.ARankComplete, liveInfo.SRankComplete)
|
||||||
|
|
||||||
|
result := make([]liveGoalRewardRow, 0, len(rows))
|
||||||
|
for _, row := range rows {
|
||||||
|
switch row.LiveGoalType {
|
||||||
|
case constant.LiveGoalTypeScore:
|
||||||
|
if scoreRank <= row.Rank {
|
||||||
|
result = append(result, row)
|
||||||
|
}
|
||||||
|
case constant.LiveGoalTypeCombo:
|
||||||
|
if comboRank <= row.Rank {
|
||||||
|
result = append(result, row)
|
||||||
|
}
|
||||||
|
case constant.LiveGoalTypeClear:
|
||||||
|
if clearRank <= row.Rank {
|
||||||
|
result = append(result, row)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss *Session) SyncUserLiveGoals(liveDifficultyID int, hiScore int, hiComboCount int, clearCnt int) ([]int, error) {
|
||||||
|
achievedRows, err := ss.GetAchievedLiveGoalRewardRows(liveDifficultyID, hiScore, hiComboCount, clearCnt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(achievedRows) == 0 {
|
||||||
|
return []int{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
existingRows := []usermodel.UserLiveGoal{}
|
||||||
|
err = ss.UserEng.Table(new(usermodel.UserLiveGoal)).
|
||||||
|
Where("user_id = ? AND live_difficulty_id = ?", ss.UserID, liveDifficultyID).
|
||||||
|
Where("live_goal_reward_id > 0").
|
||||||
|
Find(&existingRows)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
existing := make(map[int]struct{}, len(existingRows))
|
||||||
|
for _, row := range existingRows {
|
||||||
|
existing[row.LiveGoalRewardID] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now().Unix()
|
||||||
|
newlyAchieved := make([]int, 0)
|
||||||
|
for _, row := range achievedRows {
|
||||||
|
if _, ok := existing[row.LiveGoalRewardID]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = ss.UserEng.Insert(&usermodel.UserLiveGoal{
|
||||||
|
UserID: ss.UserID,
|
||||||
|
LiveDifficultyID: liveDifficultyID,
|
||||||
|
LiveGoalRewardID: row.LiveGoalRewardID,
|
||||||
|
GoalType: row.LiveGoalType,
|
||||||
|
Rank: row.Rank,
|
||||||
|
CompletedAt: now,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
newlyAchieved = append(newlyAchieved, row.LiveGoalRewardID)
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Ints(newlyAchieved)
|
||||||
|
return newlyAchieved, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ss *Session) GetLiveGoalRewardRowsByIDs(goalRewardIDs []int) ([]liveGoalRewardRow, error) {
|
||||||
|
if len(goalRewardIDs) == 0 {
|
||||||
|
return []liveGoalRewardRow{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
rows := []liveGoalRewardRow{}
|
||||||
|
err := ss.MainEng.Table("live_goal_reward_m").
|
||||||
|
In("live_goal_reward_id", goalRewardIDs).
|
||||||
|
OrderBy("live_goal_reward_id ASC").
|
||||||
|
Find(&rows)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return rows, nil
|
||||||
|
}
|
||||||
@@ -1,12 +1,14 @@
|
|||||||
package startup
|
package startup
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"honoka-chan/internal/constant"
|
||||||
ghomemodel "honoka-chan/internal/model/ghome"
|
ghomemodel "honoka-chan/internal/model/ghome"
|
||||||
loginmodel "honoka-chan/internal/model/login"
|
loginmodel "honoka-chan/internal/model/login"
|
||||||
unitmodel "honoka-chan/internal/model/unit"
|
unitmodel "honoka-chan/internal/model/unit"
|
||||||
usermodel "honoka-chan/internal/model/user"
|
usermodel "honoka-chan/internal/model/user"
|
||||||
"honoka-chan/pkg/db"
|
"honoka-chan/pkg/db"
|
||||||
"log"
|
"log"
|
||||||
|
"sort"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
@@ -25,6 +27,7 @@ func CreateTables() {
|
|||||||
db.UserEng.Sync2(new(usermodel.UserDeckUnit))
|
db.UserEng.Sync2(new(usermodel.UserDeckUnit))
|
||||||
db.UserEng.Sync2(new(usermodel.UserKey))
|
db.UserEng.Sync2(new(usermodel.UserKey))
|
||||||
db.UserEng.Sync2(new(usermodel.UserLiveGoal))
|
db.UserEng.Sync2(new(usermodel.UserLiveGoal))
|
||||||
|
db.UserEng.Sync2(new(usermodel.UserLiveStatus))
|
||||||
db.UserEng.Sync2(new(usermodel.UserLiveInProgress))
|
db.UserEng.Sync2(new(usermodel.UserLiveInProgress))
|
||||||
db.UserEng.Sync2(new(usermodel.UserLiveRecord))
|
db.UserEng.Sync2(new(usermodel.UserLiveRecord))
|
||||||
db.UserEng.Sync2(new(usermodel.UserFriend))
|
db.UserEng.Sync2(new(usermodel.UserFriend))
|
||||||
@@ -35,6 +38,7 @@ func CreateTables() {
|
|||||||
db.UserEng.Sync2(new(usermodel.UserUnitSkillEquip))
|
db.UserEng.Sync2(new(usermodel.UserUnitSkillEquip))
|
||||||
|
|
||||||
MigrateUserPref()
|
MigrateUserPref()
|
||||||
|
MigrateUserLiveData()
|
||||||
}
|
}
|
||||||
|
|
||||||
func MigrateUserPref() {
|
func MigrateUserPref() {
|
||||||
@@ -62,6 +66,233 @@ func MigrateUserPref() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type liveGoalRewardMigrationRow struct {
|
||||||
|
LiveGoalRewardID int `xorm:"live_goal_reward_id"`
|
||||||
|
LiveDifficultyID int `xorm:"live_difficulty_id"`
|
||||||
|
LiveGoalType int `xorm:"live_goal_type"`
|
||||||
|
Rank int `xorm:"rank"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type liveGoalInfoMigrationRow struct {
|
||||||
|
LiveDifficultyID int `xorm:"live_difficulty_id"`
|
||||||
|
CRankScore int `xorm:"c_rank_score"`
|
||||||
|
BRankScore int `xorm:"b_rank_score"`
|
||||||
|
ARankScore int `xorm:"a_rank_score"`
|
||||||
|
SRankScore int `xorm:"s_rank_score"`
|
||||||
|
CRankCombo int `xorm:"c_rank_combo"`
|
||||||
|
BRankCombo int `xorm:"b_rank_combo"`
|
||||||
|
ARankCombo int `xorm:"a_rank_combo"`
|
||||||
|
SRankCombo int `xorm:"s_rank_combo"`
|
||||||
|
CRankComplete int `xorm:"c_rank_complete"`
|
||||||
|
BRankComplete int `xorm:"b_rank_complete"`
|
||||||
|
ARankComplete int `xorm:"a_rank_complete"`
|
||||||
|
SRankComplete int `xorm:"s_rank_complete"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func liveRankForMigration(value int, cRank int, bRank int, aRank int, sRank int) int {
|
||||||
|
switch {
|
||||||
|
case value >= sRank:
|
||||||
|
return 1
|
||||||
|
case value >= aRank:
|
||||||
|
return 2
|
||||||
|
case value >= bRank:
|
||||||
|
return 3
|
||||||
|
case value >= cRank:
|
||||||
|
return 4
|
||||||
|
default:
|
||||||
|
return 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func MigrateUserLiveData() {
|
||||||
|
session := db.UserEng.NewSession()
|
||||||
|
defer session.Close()
|
||||||
|
|
||||||
|
recordRows := []usermodel.UserLiveRecord{}
|
||||||
|
if err := session.Table(new(usermodel.UserLiveRecord)).Find(&recordRows); err != nil {
|
||||||
|
log.Fatalln("迁移 user_live_status 失败:", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
existingStatusRows := []usermodel.UserLiveStatus{}
|
||||||
|
if err := session.Table(new(usermodel.UserLiveStatus)).Find(&existingStatusRows); err != nil {
|
||||||
|
log.Fatalln("迁移 user_live_status 失败:", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
type liveStatusKey struct {
|
||||||
|
UserID int
|
||||||
|
LiveDifficultyID int
|
||||||
|
}
|
||||||
|
|
||||||
|
statusMap := make(map[liveStatusKey]usermodel.UserLiveStatus, len(existingStatusRows))
|
||||||
|
for _, row := range existingStatusRows {
|
||||||
|
statusMap[liveStatusKey{UserID: row.UserID, LiveDifficultyID: row.LiveDifficultyID}] = row
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now().Unix()
|
||||||
|
for _, row := range recordRows {
|
||||||
|
key := liveStatusKey{UserID: row.UserID, LiveDifficultyID: row.LiveDifficultyID}
|
||||||
|
status, ok := statusMap[key]
|
||||||
|
if !ok {
|
||||||
|
status = usermodel.UserLiveStatus{
|
||||||
|
UserID: row.UserID,
|
||||||
|
LiveDifficultyID: row.LiveDifficultyID,
|
||||||
|
HiScore: row.TotalScore,
|
||||||
|
HiComboCount: row.MaxCombo,
|
||||||
|
ClearCnt: 1,
|
||||||
|
InsertDate: now,
|
||||||
|
UpdateDate: now,
|
||||||
|
}
|
||||||
|
if _, err := session.Insert(&status); err != nil {
|
||||||
|
log.Fatalln("迁移 user_live_status 失败:", err.Error())
|
||||||
|
}
|
||||||
|
statusMap[key] = status
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
updated := false
|
||||||
|
if row.TotalScore > status.HiScore {
|
||||||
|
status.HiScore = row.TotalScore
|
||||||
|
updated = true
|
||||||
|
}
|
||||||
|
if row.MaxCombo > status.HiComboCount {
|
||||||
|
status.HiComboCount = row.MaxCombo
|
||||||
|
updated = true
|
||||||
|
}
|
||||||
|
if status.ClearCnt <= 0 {
|
||||||
|
status.ClearCnt = 1
|
||||||
|
updated = true
|
||||||
|
}
|
||||||
|
if updated {
|
||||||
|
status.UpdateDate = now
|
||||||
|
if _, err := session.Table(new(usermodel.UserLiveStatus)).
|
||||||
|
Where("user_id = ? AND live_difficulty_id = ?", status.UserID, status.LiveDifficultyID).
|
||||||
|
Cols("hi_score", "hi_combo_count", "clear_cnt", "update_date").
|
||||||
|
Update(&status); err != nil {
|
||||||
|
log.Fatalln("迁移 user_live_status 失败:", err.Error())
|
||||||
|
}
|
||||||
|
statusMap[key] = status
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
liveGoalInfoMap := map[int]liveGoalInfoMigrationRow{}
|
||||||
|
loadLiveGoalInfoRows := func(table string) {
|
||||||
|
rows := []liveGoalInfoMigrationRow{}
|
||||||
|
err := db.MainEng.Table(table).Alias("live").
|
||||||
|
Join("LEFT", "live_setting_m setting", "live.live_setting_id = setting.live_setting_id").
|
||||||
|
Select(`
|
||||||
|
live.live_difficulty_id,
|
||||||
|
setting.c_rank_score,
|
||||||
|
setting.b_rank_score,
|
||||||
|
setting.a_rank_score,
|
||||||
|
setting.s_rank_score,
|
||||||
|
setting.c_rank_combo,
|
||||||
|
setting.b_rank_combo,
|
||||||
|
setting.a_rank_combo,
|
||||||
|
setting.s_rank_combo,
|
||||||
|
live.c_rank_complete,
|
||||||
|
live.b_rank_complete,
|
||||||
|
live.a_rank_complete,
|
||||||
|
live.s_rank_complete
|
||||||
|
`).
|
||||||
|
Find(&rows)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("迁移 user_live_goal 失败:", err.Error())
|
||||||
|
}
|
||||||
|
for _, row := range rows {
|
||||||
|
liveGoalInfoMap[row.LiveDifficultyID] = row
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loadLiveGoalInfoRows("normal_live_m")
|
||||||
|
loadLiveGoalInfoRows("special_live_m")
|
||||||
|
|
||||||
|
goalRewardRows := []liveGoalRewardMigrationRow{}
|
||||||
|
if err := db.MainEng.Table("live_goal_reward_m").
|
||||||
|
OrderBy("live_difficulty_id ASC, live_goal_type ASC, rank ASC, live_goal_reward_id ASC").
|
||||||
|
Find(&goalRewardRows); err != nil {
|
||||||
|
log.Fatalln("迁移 user_live_goal 失败:", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
goalRewardMap := map[int][]liveGoalRewardMigrationRow{}
|
||||||
|
for _, row := range goalRewardRows {
|
||||||
|
goalRewardMap[row.LiveDifficultyID] = append(goalRewardMap[row.LiveDifficultyID], row)
|
||||||
|
}
|
||||||
|
|
||||||
|
existingGoalRows := []usermodel.UserLiveGoal{}
|
||||||
|
if err := session.Table(new(usermodel.UserLiveGoal)).
|
||||||
|
Where("live_goal_reward_id > 0").
|
||||||
|
Find(&existingGoalRows); err != nil {
|
||||||
|
log.Fatalln("迁移 user_live_goal 失败:", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
existingGoalMap := make(map[liveStatusKey]map[int]struct{})
|
||||||
|
for _, row := range existingGoalRows {
|
||||||
|
key := liveStatusKey{UserID: row.UserID, LiveDifficultyID: row.LiveDifficultyID}
|
||||||
|
if existingGoalMap[key] == nil {
|
||||||
|
existingGoalMap[key] = map[int]struct{}{}
|
||||||
|
}
|
||||||
|
existingGoalMap[key][row.LiveGoalRewardID] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
statusKeys := make([]liveStatusKey, 0, len(statusMap))
|
||||||
|
for key := range statusMap {
|
||||||
|
statusKeys = append(statusKeys, key)
|
||||||
|
}
|
||||||
|
sort.Slice(statusKeys, func(i, j int) bool {
|
||||||
|
if statusKeys[i].UserID == statusKeys[j].UserID {
|
||||||
|
return statusKeys[i].LiveDifficultyID < statusKeys[j].LiveDifficultyID
|
||||||
|
}
|
||||||
|
return statusKeys[i].UserID < statusKeys[j].UserID
|
||||||
|
})
|
||||||
|
|
||||||
|
for _, key := range statusKeys {
|
||||||
|
status := statusMap[key]
|
||||||
|
liveInfo, ok := liveGoalInfoMap[key.LiveDifficultyID]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
scoreRank := liveRankForMigration(status.HiScore, liveInfo.CRankScore, liveInfo.BRankScore, liveInfo.ARankScore, liveInfo.SRankScore)
|
||||||
|
comboRank := liveRankForMigration(status.HiComboCount, liveInfo.CRankCombo, liveInfo.BRankCombo, liveInfo.ARankCombo, liveInfo.SRankCombo)
|
||||||
|
clearRank := liveRankForMigration(status.ClearCnt, liveInfo.CRankComplete, liveInfo.BRankComplete, liveInfo.ARankComplete, liveInfo.SRankComplete)
|
||||||
|
|
||||||
|
for _, reward := range goalRewardMap[key.LiveDifficultyID] {
|
||||||
|
achieved := false
|
||||||
|
switch reward.LiveGoalType {
|
||||||
|
case 1:
|
||||||
|
achieved = scoreRank <= reward.Rank
|
||||||
|
case 2:
|
||||||
|
achieved = comboRank <= reward.Rank
|
||||||
|
case 3:
|
||||||
|
achieved = clearRank <= reward.Rank
|
||||||
|
}
|
||||||
|
if !achieved {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if existingGoalMap[key] != nil {
|
||||||
|
if _, ok := existingGoalMap[key][reward.LiveGoalRewardID]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := session.Insert(&usermodel.UserLiveGoal{
|
||||||
|
UserID: key.UserID,
|
||||||
|
LiveDifficultyID: key.LiveDifficultyID,
|
||||||
|
LiveGoalRewardID: reward.LiveGoalRewardID,
|
||||||
|
GoalType: constant.LiveGoalType(reward.LiveGoalType),
|
||||||
|
Rank: reward.Rank,
|
||||||
|
CompletedAt: now,
|
||||||
|
}); err != nil {
|
||||||
|
log.Fatalln("迁移 user_live_goal 失败:", err.Error())
|
||||||
|
}
|
||||||
|
if existingGoalMap[key] == nil {
|
||||||
|
existingGoalMap[key] = map[int]struct{}{}
|
||||||
|
}
|
||||||
|
existingGoalMap[key][reward.LiveGoalRewardID] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func LoadUnitData() {
|
func LoadUnitData() {
|
||||||
userEng = db.UserEng.NewSession()
|
userEng = db.UserEng.NewSession()
|
||||||
defer userEng.Close()
|
defer userEng.Close()
|
||||||
|
|||||||
Reference in New Issue
Block a user