diff --git a/internal/handler/api/profile/info.go b/internal/handler/api/profile/info.go index 065abe4..709a6b4 100644 --- a/internal/handler/api/profile/info.go +++ b/internal/handler/api/profile/info.go @@ -5,6 +5,7 @@ import ( usermodel "honoka-chan/internal/model/user" profileapischema "honoka-chan/internal/schema/api/profile" "honoka-chan/internal/session" + "strconv" "time" "github.com/gin-gonic/gin" @@ -12,11 +13,10 @@ import ( func profileInfo(ctx *gin.Context) (res any, err error) { ss := session.Get(ctx) - - pref := usermodel.UserPref{} - _, err = ss.UserEng.Table("user_pref").Where("user_id = ?", ss.UserID).Get(&pref) - if err != nil { - return nil, err + pref := ss.UserPref + displayUserID := pref.UserID + if inviteUserID, convErr := strconv.Atoi(pref.InviteCode); convErr == nil && inviteUserID > 0 { + displayUserID = inviteUserID } unitCount, err := ss.UserEng.Table(new(usermodel.UserUnitData)). @@ -64,12 +64,12 @@ func profileInfo(ctx *gin.Context) (res any, err error) { res = profileapischema.InfoResp{ Result: profileapischema.InfoData{ UserInfo: profileapischema.UserInfo{ - UserID: pref.UserID, + UserID: displayUserID, Name: pref.UserName, Level: pref.UserLevel, CostMax: 100, UnitMax: 5000, - EnergyMax: 417, + EnergyMax: pref.EffectiveEnergyMax(), FriendMax: 99, UnitCnt: int(unitCount), InviteCode: pref.InviteCode, diff --git a/internal/handler/api/user/info.go b/internal/handler/api/user/info.go index fd61b8f..975348e 100644 --- a/internal/handler/api/user/info.go +++ b/internal/handler/api/user/info.go @@ -1,7 +1,6 @@ package user import ( - usermodel "honoka-chan/internal/model/user" userapischema "honoka-chan/internal/schema/api/user" "honoka-chan/internal/session" "time" @@ -12,12 +11,6 @@ import ( func userInfo(ctx *gin.Context) (res any, err error) { ss := session.Get(ctx) - pref := usermodel.UserPref{} - _, err = ss.UserEng.Table("user_pref").Where("user_id = ?", ss.UserID).Get(&pref) - if err != nil { - return nil, err - } - res = userapischema.InfoResp{ Result: ss.GetUserInfo(), Status: 200, diff --git a/internal/handler/ghome/account/login.go b/internal/handler/ghome/account/login.go index 6fbeccb..9f2f553 100644 --- a/internal/handler/ghome/account/login.go +++ b/internal/handler/ghome/account/login.go @@ -269,9 +269,16 @@ func addUser(dbSession *xorm.Session, phone, password string, isDefault bool) (g BackgroundID: backgroundID, UnitOwningUserID: unitOwningUserID, UserName: userName, - UserLevel: 1028, + UserLevel: usermodel.DefaultUserLevel, UserDesc: userDesc, InviteCode: strconv.Itoa(userID), + UserExp: usermodel.DefaultUserExp, + NextExp: usermodel.DefaultUserNextExp, + GameCoin: usermodel.DefaultUserGameCoin, + SnsCoin: usermodel.DefaultUserSnsCoin, + EnergyMax: usermodel.DefaultUserEnergyMax, + OverMaxEnergy: usermodel.DefaultUserOverMaxEnergy, + ProfileVersion: usermodel.CurrentUserPrefProfileVersion, UpdateTime: time.Now().Unix(), } _, err = dbSession.Table("user_pref").Insert(&userPref) diff --git a/internal/handler/live/continue.go b/internal/handler/live/continue.go index 92ada89..2d6ae9e 100644 --- a/internal/handler/live/continue.go +++ b/internal/handler/live/continue.go @@ -16,8 +16,8 @@ func continuee(ctx *gin.Context) { ss.Respond(liveschema.ContinueResp{ ResponseData: liveschema.ContinueData{ - BeforeSnsCoin: 100000, - AfterSnsCoin: 100000, + BeforeSnsCoin: ss.UserPref.SnsCoin, + AfterSnsCoin: ss.UserPref.SnsCoin, ServerTimestamp: time.Now().Unix(), }, ReleaseInfo: []any{}, diff --git a/internal/handler/live/reward.go b/internal/handler/live/reward.go index 9bd6046..86edec8 100644 --- a/internal/handler/live/reward.go +++ b/internal/handler/live/reward.go @@ -42,12 +42,6 @@ func reward(ctx *gin.Context) { return } - pref := usermodel.UserPref{} - _, err = ss.UserEng.Table("user_pref").Where("user_id = ?", ss.UserID).Get(&pref) - if ss.CheckErr(err) { - return - } - totalScore := playRewardReq.ScoreSmile + playRewardReq.ScoreCool + playRewardReq.ScoreCute playResp := liveschema.RewardResp{ ResponseData: liveschema.RewardData{ @@ -73,8 +67,8 @@ func reward(ctx *gin.Context) { After: 99, }, PlayerExpLpMax: liveschema.PlayerExpLpMax{ - Before: 417, - After: 417, + Before: ss.UserPref.EffectiveEnergyMax(), + After: ss.UserPref.EffectiveEnergyMax(), }, GameCoin: 0, GameCoinRewardBoxFlag: false, @@ -95,8 +89,8 @@ func reward(ctx *gin.Context) { AfterUserInfo: ss.GetUserInfo(), NextLevelInfo: []liveschema.NextLevelInfo{ { - Level: pref.UserLevel, - FromExp: 1089696, + Level: ss.UserPref.UserLevel, + FromExp: ss.UserPref.UserExp, }, }, GoalAccompInfo: liveschema.GoalAccompInfo{ diff --git a/internal/handler/user/userinfo.go b/internal/handler/user/userinfo.go index 9b79881..fdd5f5a 100644 --- a/internal/handler/user/userinfo.go +++ b/internal/handler/user/userinfo.go @@ -2,7 +2,6 @@ package user import ( "honoka-chan/internal/middleware" - usermodel "honoka-chan/internal/model/user" "honoka-chan/internal/router" userschema "honoka-chan/internal/schema/user" "honoka-chan/internal/session" @@ -15,12 +14,6 @@ func userInfo(ctx *gin.Context) { ss := session.Get(ctx) defer ss.Finalize() - pref := usermodel.UserPref{} - _, err := ss.UserEng.Table("user_pref").Where("user_id = ?", ss.UserID).Get(&pref) - if ss.CheckErr(err) { - return - } - userResp := userschema.UserInfoResp{ ResponseData: userschema.UserInfoData{ User: ss.GetUserInfo(), diff --git a/internal/handler/webui/profile.go b/internal/handler/webui/profile.go new file mode 100644 index 0000000..489bace --- /dev/null +++ b/internal/handler/webui/profile.go @@ -0,0 +1,141 @@ +package webui + +import ( + "honoka-chan/internal/middleware" + usermodel "honoka-chan/internal/model/user" + "honoka-chan/internal/router" + webuischema "honoka-chan/internal/schema/webui" + "honoka-chan/pkg/db" + "net/http" + "strconv" + "strings" + "time" + + "github.com/gin-gonic/gin" +) + +func profilePage(ctx *gin.Context) { + pref := usermodel.UserPref{} + exists, err := db.UserEng.Table(new(usermodel.UserPref)). + Where("user_id = ?", ctx.GetInt("userid")).Get(&pref) + if err != nil || !exists { + ctx.String(http.StatusInternalServerError, "用户资料读取失败") + return + } + + ctx.HTML(http.StatusOK, "admin/profile.html", gin.H{ + "menu": 2, + "url": strings.Split(ctx.Request.URL.String(), "?")[0], + "pref": pref, + }) +} + +func updateProfile(ctx *gin.Context) { + parseInt := func(key string, min int) (int, error) { + value := strings.TrimSpace(ctx.PostForm(key)) + number, err := strconv.Atoi(value) + if err != nil { + return 0, err + } + if number < min { + return 0, strconv.ErrSyntax + } + return number, nil + } + + userName := strings.TrimSpace(ctx.PostForm("user_name")) + inviteCode := strings.TrimSpace(ctx.PostForm("invite_code")) + userDesc := strings.TrimSpace(ctx.PostForm("user_desc")) + if userName == "" || inviteCode == "" { + ctx.JSON(http.StatusOK, webuischema.Msg{ + Code: 1, + Message: "用户名和邀请码不能为空!", + }) + return + } + + userLevel, err := parseInt("user_level", 1) + if err != nil { + ctx.JSON(http.StatusOK, webuischema.Msg{Code: 1, Message: "等级格式有误!"}) + return + } + userExp, err := parseInt("user_exp", 0) + if err != nil { + ctx.JSON(http.StatusOK, webuischema.Msg{Code: 1, Message: "当前经验格式有误!"}) + return + } + nextExp, err := parseInt("next_exp", 0) + if err != nil { + ctx.JSON(http.StatusOK, webuischema.Msg{Code: 1, Message: "升级经验格式有误!"}) + return + } + gameCoin, err := parseInt("game_coin", 0) + if err != nil { + ctx.JSON(http.StatusOK, webuischema.Msg{Code: 1, Message: "金币格式有误!"}) + return + } + snsCoin, err := parseInt("sns_coin", 0) + if err != nil { + ctx.JSON(http.StatusOK, webuischema.Msg{Code: 1, Message: "爱心格式有误!"}) + return + } + energyMax, err := parseInt("energy_max", 1) + if err != nil { + ctx.JSON(http.StatusOK, webuischema.Msg{Code: 1, Message: "体力上限格式有误!"}) + return + } + overMaxEnergy, err := parseInt("over_max_energy", 0) + if err != nil { + ctx.JSON(http.StatusOK, webuischema.Msg{Code: 1, Message: "当前体力格式有误!"}) + return + } + + pref := usermodel.UserPref{ + UserName: userName, + UserLevel: userLevel, + UserDesc: userDesc, + InviteCode: inviteCode, + UserExp: userExp, + NextExp: nextExp, + GameCoin: gameCoin, + SnsCoin: snsCoin, + EnergyMax: energyMax, + OverMaxEnergy: overMaxEnergy, + ProfileVersion: usermodel.CurrentUserPrefProfileVersion, + UpdateTime: time.Now().Unix(), + } + _, err = db.UserEng.Table(new(usermodel.UserPref)). + Where("user_id = ?", ctx.GetInt("userid")). + Cols( + "user_name", + "user_level", + "user_desc", + "invite_code", + "user_exp", + "next_exp", + "game_coin", + "sns_coin", + "energy_max", + "over_max_energy", + "profile_version", + "update_time", + ). + Update(&pref) + if err != nil { + ctx.JSON(http.StatusOK, webuischema.Msg{ + Code: 1, + Message: "保存失败!", + }) + return + } + + ctx.JSON(http.StatusOK, webuischema.Msg{ + Code: 0, + Message: "保存成功!", + }) +} + +func init() { + router.AddHandler("admin", "GET", "/profile", middleware.WebAuth, profilePage) + router.AddHandler("admin", "POST", "/profile", middleware.WebAuth, updateProfile) +} diff --git a/internal/model/user/userpref.go b/internal/model/user/userpref.go index 260a187..030a973 100644 --- a/internal/model/user/userpref.go +++ b/internal/model/user/userpref.go @@ -1,5 +1,22 @@ package usermodel +import ( + "strconv" + "strings" +) + +const CurrentUserPrefProfileVersion = 1 + +const ( + DefaultUserLevel = 1028 + DefaultUserExp = 1089696 + DefaultUserNextExp = 1207185 + DefaultUserGameCoin = 112124104 + DefaultUserSnsCoin = 10000 + DefaultUserEnergyMax = 417 + DefaultUserOverMaxEnergy = 0 +) + type UserPref struct { ID int `xorm:"id pk autoincr"` UserID int `xorm:"user_id"` @@ -10,9 +27,76 @@ type UserPref struct { UserLevel int `xorm:"user_level"` UserDesc string `xorm:"user_desc"` InviteCode string `xorm:"invite_code"` + UserExp int `xorm:"user_exp"` + NextExp int `xorm:"next_exp"` + GameCoin int `xorm:"game_coin"` + SnsCoin int `xorm:"sns_coin"` + EnergyMax int `xorm:"energy_max"` + OverMaxEnergy int `xorm:"over_max_energy"` + ProfileVersion int `xorm:"profile_version"` UpdateTime int64 `xorm:"update_time"` } +func (u *UserPref) ApplyProfileDefaults() { + if u.UserLevel <= 0 { + u.UserLevel = DefaultUserLevel + } + if strings.TrimSpace(u.InviteCode) == "" && u.UserID > 0 { + u.InviteCode = strconv.Itoa(u.UserID) + } + if u.UserExp < 0 || u.UserExp == 0 { + u.UserExp = DefaultUserExp + } + if u.NextExp < 0 || u.NextExp == 0 { + u.NextExp = DefaultUserNextExp + } + if u.GameCoin < 0 || u.GameCoin == 0 { + u.GameCoin = DefaultUserGameCoin + } + if u.SnsCoin < 0 || u.SnsCoin == 0 { + u.SnsCoin = DefaultUserSnsCoin + } + if u.EnergyMax <= 0 { + u.EnergyMax = DefaultUserEnergyMax + } + if u.OverMaxEnergy < 0 { + u.OverMaxEnergy = DefaultUserOverMaxEnergy + } + u.ProfileVersion = CurrentUserPrefProfileVersion +} + +func (u UserPref) NeedsProfileMigration() bool { + return u.ProfileVersion < CurrentUserPrefProfileVersion +} + +func (u UserPref) EffectiveEnergyMax() int { + if u.EnergyMax > 0 { + return u.EnergyMax + } + return DefaultUserEnergyMax +} + +func (u UserPref) EffectiveCurrentEnergy() int { + if u.OverMaxEnergy > 0 { + return u.OverMaxEnergy + } + return u.EffectiveEnergyMax() +} + +func UserPrefProfileColumns() []string { + return []string{ + "user_level", + "invite_code", + "user_exp", + "next_exp", + "game_coin", + "sns_coin", + "energy_max", + "over_max_energy", + "profile_version", + } +} + func (UserPref) TableName() string { return "user_pref" } diff --git a/internal/session/userdata.go b/internal/session/userdata.go index 3e36f2a..00dcafb 100644 --- a/internal/session/userdata.go +++ b/internal/session/userdata.go @@ -18,6 +18,16 @@ func (ss *Session) GetUserPref(userID string) usermodel.UserPref { ss.Abort(errors.New("用户不存在!")) return usermodel.UserPref{} } + if pref.NeedsProfileMigration() { + pref.ApplyProfileDefaults() + _, err = ss.UserEng.Table(new(usermodel.UserPref)). + Where("user_id = ?", userID). + Cols(usermodel.UserPrefProfileColumns()...). + Update(&pref) + if ss.CheckErr(err) { + return usermodel.UserPref{} + } + } return pref } @@ -27,22 +37,22 @@ func (ss *Session) GetUserInfo() userschema.UserInfo { UserID: ss.UserID, Name: ss.UserPref.UserName, Level: ss.UserPref.UserLevel, - Exp: 1089696, + Exp: ss.UserPref.UserExp, PreviousExp: 0, - NextExp: 1207185, - GameCoin: 112124104, - SnsCoin: 10000, - FreeSnsCoin: 50000, - PaidSnsCoin: 50000, + NextExp: ss.UserPref.NextExp, + GameCoin: ss.UserPref.GameCoin, + SnsCoin: ss.UserPref.SnsCoin, + FreeSnsCoin: ss.UserPref.SnsCoin, + PaidSnsCoin: 0, SocialPoint: 1438165, UnitMax: 5000, WaitingUnitMax: 5000, - CurrentEnergy: 417, - EnergyMax: 417, + CurrentEnergy: ss.UserPref.EffectiveCurrentEnergy(), + EnergyMax: ss.UserPref.EffectiveEnergyMax(), EnergyFullTime: "2023-03-20 01:28:55", LicenseLiveEnergyRecoverlyTime: 60, EnergyFullNeedTime: 0, - OverMaxEnergy: 417, + OverMaxEnergy: ss.UserPref.EffectiveCurrentEnergy(), TrainingEnergy: 10, TrainingEnergyMax: 10, FriendMax: 99, diff --git a/internal/startup/database.go b/internal/startup/database.go index 0544ab9..090b59f 100644 --- a/internal/startup/database.go +++ b/internal/startup/database.go @@ -31,6 +31,33 @@ func CreateTables() { db.UserEng.Sync2(new(usermodel.Users)) db.UserEng.Sync2(new(usermodel.UserUnit)) db.UserEng.Sync2(new(usermodel.UserUnitSkillEquip)) + + MigrateUserPref() +} + +func MigrateUserPref() { + session := db.UserEng.NewSession() + defer session.Close() + + prefList := []usermodel.UserPref{} + err := session.Table(new(usermodel.UserPref)). + Where("profile_version < ?", usermodel.CurrentUserPrefProfileVersion). + Or("profile_version IS NULL"). + Find(&prefList) + if err != nil { + log.Fatalln("迁移 user_pref 失败:", err.Error()) + } + + for _, pref := range prefList { + pref.ApplyProfileDefaults() + _, err = session.Table(new(usermodel.UserPref)). + ID(pref.ID). + Cols(usermodel.UserPrefProfileColumns()...). + Update(&pref) + if err != nil { + log.Fatalln("迁移 user_pref 失败:", err.Error()) + } + } } func LoadUnitData() { diff --git a/static/templates/admin/nav/footer.html b/static/templates/admin/nav/footer.html index e6eb0c1..e187e62 100644 --- a/static/templates/admin/nav/footer.html +++ b/static/templates/admin/nav/footer.html @@ -1,5 +1,5 @@ {{ define "admin/nav/footer.html" }}
{{ end }} diff --git a/static/templates/admin/nav/header.html b/static/templates/admin/nav/header.html index 4157f3d..d0beb46 100644 --- a/static/templates/admin/nav/header.html +++ b/static/templates/admin/nav/header.html @@ -14,7 +14,7 @@ 设置