webui: Make some user preferences configurable
This extends 09f0237560.
- Extend user_pref with configurable profile fields and automatic migration defaults
- Move user info responses to read from user_pref instead of hardcoded values
- Add admin profile page for editing UI values after login
Signed-off-by: Sean Du <do4suki@gmail.com>
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
|||||||
usermodel "honoka-chan/internal/model/user"
|
usermodel "honoka-chan/internal/model/user"
|
||||||
profileapischema "honoka-chan/internal/schema/api/profile"
|
profileapischema "honoka-chan/internal/schema/api/profile"
|
||||||
"honoka-chan/internal/session"
|
"honoka-chan/internal/session"
|
||||||
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
@@ -12,11 +13,10 @@ import (
|
|||||||
|
|
||||||
func profileInfo(ctx *gin.Context) (res any, err error) {
|
func profileInfo(ctx *gin.Context) (res any, err error) {
|
||||||
ss := session.Get(ctx)
|
ss := session.Get(ctx)
|
||||||
|
pref := ss.UserPref
|
||||||
pref := usermodel.UserPref{}
|
displayUserID := pref.UserID
|
||||||
_, err = ss.UserEng.Table("user_pref").Where("user_id = ?", ss.UserID).Get(&pref)
|
if inviteUserID, convErr := strconv.Atoi(pref.InviteCode); convErr == nil && inviteUserID > 0 {
|
||||||
if err != nil {
|
displayUserID = inviteUserID
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unitCount, err := ss.UserEng.Table(new(usermodel.UserUnitData)).
|
unitCount, err := ss.UserEng.Table(new(usermodel.UserUnitData)).
|
||||||
@@ -64,12 +64,12 @@ func profileInfo(ctx *gin.Context) (res any, err error) {
|
|||||||
res = profileapischema.InfoResp{
|
res = profileapischema.InfoResp{
|
||||||
Result: profileapischema.InfoData{
|
Result: profileapischema.InfoData{
|
||||||
UserInfo: profileapischema.UserInfo{
|
UserInfo: profileapischema.UserInfo{
|
||||||
UserID: pref.UserID,
|
UserID: displayUserID,
|
||||||
Name: pref.UserName,
|
Name: pref.UserName,
|
||||||
Level: pref.UserLevel,
|
Level: pref.UserLevel,
|
||||||
CostMax: 100,
|
CostMax: 100,
|
||||||
UnitMax: 5000,
|
UnitMax: 5000,
|
||||||
EnergyMax: 417,
|
EnergyMax: pref.EffectiveEnergyMax(),
|
||||||
FriendMax: 99,
|
FriendMax: 99,
|
||||||
UnitCnt: int(unitCount),
|
UnitCnt: int(unitCount),
|
||||||
InviteCode: pref.InviteCode,
|
InviteCode: pref.InviteCode,
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package user
|
package user
|
||||||
|
|
||||||
import (
|
import (
|
||||||
usermodel "honoka-chan/internal/model/user"
|
|
||||||
userapischema "honoka-chan/internal/schema/api/user"
|
userapischema "honoka-chan/internal/schema/api/user"
|
||||||
"honoka-chan/internal/session"
|
"honoka-chan/internal/session"
|
||||||
"time"
|
"time"
|
||||||
@@ -12,12 +11,6 @@ import (
|
|||||||
func userInfo(ctx *gin.Context) (res any, err error) {
|
func userInfo(ctx *gin.Context) (res any, err error) {
|
||||||
ss := session.Get(ctx)
|
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{
|
res = userapischema.InfoResp{
|
||||||
Result: ss.GetUserInfo(),
|
Result: ss.GetUserInfo(),
|
||||||
Status: 200,
|
Status: 200,
|
||||||
|
|||||||
@@ -269,9 +269,16 @@ func addUser(dbSession *xorm.Session, phone, password string, isDefault bool) (g
|
|||||||
BackgroundID: backgroundID,
|
BackgroundID: backgroundID,
|
||||||
UnitOwningUserID: unitOwningUserID,
|
UnitOwningUserID: unitOwningUserID,
|
||||||
UserName: userName,
|
UserName: userName,
|
||||||
UserLevel: 1028,
|
UserLevel: usermodel.DefaultUserLevel,
|
||||||
UserDesc: userDesc,
|
UserDesc: userDesc,
|
||||||
InviteCode: strconv.Itoa(userID),
|
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(),
|
UpdateTime: time.Now().Unix(),
|
||||||
}
|
}
|
||||||
_, err = dbSession.Table("user_pref").Insert(&userPref)
|
_, err = dbSession.Table("user_pref").Insert(&userPref)
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ func continuee(ctx *gin.Context) {
|
|||||||
|
|
||||||
ss.Respond(liveschema.ContinueResp{
|
ss.Respond(liveschema.ContinueResp{
|
||||||
ResponseData: liveschema.ContinueData{
|
ResponseData: liveschema.ContinueData{
|
||||||
BeforeSnsCoin: 100000,
|
BeforeSnsCoin: ss.UserPref.SnsCoin,
|
||||||
AfterSnsCoin: 100000,
|
AfterSnsCoin: ss.UserPref.SnsCoin,
|
||||||
ServerTimestamp: time.Now().Unix(),
|
ServerTimestamp: time.Now().Unix(),
|
||||||
},
|
},
|
||||||
ReleaseInfo: []any{},
|
ReleaseInfo: []any{},
|
||||||
|
|||||||
@@ -42,12 +42,6 @@ func reward(ctx *gin.Context) {
|
|||||||
return
|
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
|
totalScore := playRewardReq.ScoreSmile + playRewardReq.ScoreCool + playRewardReq.ScoreCute
|
||||||
playResp := liveschema.RewardResp{
|
playResp := liveschema.RewardResp{
|
||||||
ResponseData: liveschema.RewardData{
|
ResponseData: liveschema.RewardData{
|
||||||
@@ -73,8 +67,8 @@ func reward(ctx *gin.Context) {
|
|||||||
After: 99,
|
After: 99,
|
||||||
},
|
},
|
||||||
PlayerExpLpMax: liveschema.PlayerExpLpMax{
|
PlayerExpLpMax: liveschema.PlayerExpLpMax{
|
||||||
Before: 417,
|
Before: ss.UserPref.EffectiveEnergyMax(),
|
||||||
After: 417,
|
After: ss.UserPref.EffectiveEnergyMax(),
|
||||||
},
|
},
|
||||||
GameCoin: 0,
|
GameCoin: 0,
|
||||||
GameCoinRewardBoxFlag: false,
|
GameCoinRewardBoxFlag: false,
|
||||||
@@ -95,8 +89,8 @@ func reward(ctx *gin.Context) {
|
|||||||
AfterUserInfo: ss.GetUserInfo(),
|
AfterUserInfo: ss.GetUserInfo(),
|
||||||
NextLevelInfo: []liveschema.NextLevelInfo{
|
NextLevelInfo: []liveschema.NextLevelInfo{
|
||||||
{
|
{
|
||||||
Level: pref.UserLevel,
|
Level: ss.UserPref.UserLevel,
|
||||||
FromExp: 1089696,
|
FromExp: ss.UserPref.UserExp,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
GoalAccompInfo: liveschema.GoalAccompInfo{
|
GoalAccompInfo: liveschema.GoalAccompInfo{
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package user
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"honoka-chan/internal/middleware"
|
"honoka-chan/internal/middleware"
|
||||||
usermodel "honoka-chan/internal/model/user"
|
|
||||||
"honoka-chan/internal/router"
|
"honoka-chan/internal/router"
|
||||||
userschema "honoka-chan/internal/schema/user"
|
userschema "honoka-chan/internal/schema/user"
|
||||||
"honoka-chan/internal/session"
|
"honoka-chan/internal/session"
|
||||||
@@ -15,12 +14,6 @@ func userInfo(ctx *gin.Context) {
|
|||||||
ss := session.Get(ctx)
|
ss := session.Get(ctx)
|
||||||
defer ss.Finalize()
|
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{
|
userResp := userschema.UserInfoResp{
|
||||||
ResponseData: userschema.UserInfoData{
|
ResponseData: userschema.UserInfoData{
|
||||||
User: ss.GetUserInfo(),
|
User: ss.GetUserInfo(),
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
@@ -1,5 +1,22 @@
|
|||||||
package usermodel
|
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 {
|
type UserPref struct {
|
||||||
ID int `xorm:"id pk autoincr"`
|
ID int `xorm:"id pk autoincr"`
|
||||||
UserID int `xorm:"user_id"`
|
UserID int `xorm:"user_id"`
|
||||||
@@ -10,9 +27,76 @@ type UserPref struct {
|
|||||||
UserLevel int `xorm:"user_level"`
|
UserLevel int `xorm:"user_level"`
|
||||||
UserDesc string `xorm:"user_desc"`
|
UserDesc string `xorm:"user_desc"`
|
||||||
InviteCode string `xorm:"invite_code"`
|
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"`
|
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 {
|
func (UserPref) TableName() string {
|
||||||
return "user_pref"
|
return "user_pref"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,16 @@ func (ss *Session) GetUserPref(userID string) usermodel.UserPref {
|
|||||||
ss.Abort(errors.New("用户不存在!"))
|
ss.Abort(errors.New("用户不存在!"))
|
||||||
return usermodel.UserPref{}
|
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
|
return pref
|
||||||
}
|
}
|
||||||
@@ -27,22 +37,22 @@ func (ss *Session) GetUserInfo() userschema.UserInfo {
|
|||||||
UserID: ss.UserID,
|
UserID: ss.UserID,
|
||||||
Name: ss.UserPref.UserName,
|
Name: ss.UserPref.UserName,
|
||||||
Level: ss.UserPref.UserLevel,
|
Level: ss.UserPref.UserLevel,
|
||||||
Exp: 1089696,
|
Exp: ss.UserPref.UserExp,
|
||||||
PreviousExp: 0,
|
PreviousExp: 0,
|
||||||
NextExp: 1207185,
|
NextExp: ss.UserPref.NextExp,
|
||||||
GameCoin: 112124104,
|
GameCoin: ss.UserPref.GameCoin,
|
||||||
SnsCoin: 10000,
|
SnsCoin: ss.UserPref.SnsCoin,
|
||||||
FreeSnsCoin: 50000,
|
FreeSnsCoin: ss.UserPref.SnsCoin,
|
||||||
PaidSnsCoin: 50000,
|
PaidSnsCoin: 0,
|
||||||
SocialPoint: 1438165,
|
SocialPoint: 1438165,
|
||||||
UnitMax: 5000,
|
UnitMax: 5000,
|
||||||
WaitingUnitMax: 5000,
|
WaitingUnitMax: 5000,
|
||||||
CurrentEnergy: 417,
|
CurrentEnergy: ss.UserPref.EffectiveCurrentEnergy(),
|
||||||
EnergyMax: 417,
|
EnergyMax: ss.UserPref.EffectiveEnergyMax(),
|
||||||
EnergyFullTime: "2023-03-20 01:28:55",
|
EnergyFullTime: "2023-03-20 01:28:55",
|
||||||
LicenseLiveEnergyRecoverlyTime: 60,
|
LicenseLiveEnergyRecoverlyTime: 60,
|
||||||
EnergyFullNeedTime: 0,
|
EnergyFullNeedTime: 0,
|
||||||
OverMaxEnergy: 417,
|
OverMaxEnergy: ss.UserPref.EffectiveCurrentEnergy(),
|
||||||
TrainingEnergy: 10,
|
TrainingEnergy: 10,
|
||||||
TrainingEnergyMax: 10,
|
TrainingEnergyMax: 10,
|
||||||
FriendMax: 99,
|
FriendMax: 99,
|
||||||
|
|||||||
@@ -31,6 +31,33 @@ func CreateTables() {
|
|||||||
db.UserEng.Sync2(new(usermodel.Users))
|
db.UserEng.Sync2(new(usermodel.Users))
|
||||||
db.UserEng.Sync2(new(usermodel.UserUnit))
|
db.UserEng.Sync2(new(usermodel.UserUnit))
|
||||||
db.UserEng.Sync2(new(usermodel.UserUnitSkillEquip))
|
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() {
|
func LoadUnitData() {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{{ define "admin/nav/footer.html" }}
|
{{ define "admin/nav/footer.html" }}
|
||||||
<div class="layui-footer">
|
<div class="layui-footer">
|
||||||
Copyright © 2023 YumeMichi
|
Copyright © 2023-2026 YumeMichi
|
||||||
</div>
|
</div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
设置
|
设置
|
||||||
</a>
|
</a>
|
||||||
<dl class="layui-nav-child">
|
<dl class="layui-nav-child">
|
||||||
<dd><a href="">个人信息</a></dd>
|
<dd><a href="/admin/profile">个人信息</a></dd>
|
||||||
<dd><a href="/admin/logout">登出</a></dd>
|
<dd><a href="/admin/logout">登出</a></dd>
|
||||||
</dl>
|
</dl>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@@ -2,6 +2,12 @@
|
|||||||
<div class="layui-side">
|
<div class="layui-side">
|
||||||
<div class="layui-side-scroll">
|
<div class="layui-side-scroll">
|
||||||
<ul class="layui-nav layui-nav-tree" lay-filter="test">
|
<ul class="layui-nav layui-nav-tree" lay-filter="test">
|
||||||
|
<li class="layui-nav-item {{ if eq .menu 2 }} layui-nav-itemed {{ end }}">
|
||||||
|
<a class="" href="javascript:;">用户设置</a>
|
||||||
|
<dl class="layui-nav-child">
|
||||||
|
<dd {{ if eq .url "/admin/profile" }} class="layui-this" {{ end }}><a href="/admin/profile">个人资料</a></dd>
|
||||||
|
</dl>
|
||||||
|
</li>
|
||||||
<li class="layui-nav-item {{ if eq .menu 1 }} layui-nav-itemed {{ end }}">
|
<li class="layui-nav-item {{ if eq .menu 1 }} layui-nav-itemed {{ end }}">
|
||||||
<a class="" href="javascript:;">卡片管理</a>
|
<a class="" href="javascript:;">卡片管理</a>
|
||||||
<dl class="layui-nav-child">
|
<dl class="layui-nav-child">
|
||||||
|
|||||||
@@ -0,0 +1,118 @@
|
|||||||
|
{{ define "admin/profile.html" }}
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||||||
|
<title>SIF 小助手</title>
|
||||||
|
{{ template "admin/nav/css.html" . }}
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="layui-layout layui-layout-admin">
|
||||||
|
{{ template "admin/nav/header.html" . }}
|
||||||
|
{{ template "admin/nav/side.html" . }}
|
||||||
|
|
||||||
|
<div class="layui-body">
|
||||||
|
<form class="layui-form layui-col-md8" action="" style="padding: 15px 20px 30px;">
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label">用户名</label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<input type="text" name="user_name" required lay-verify="required" autocomplete="off"
|
||||||
|
class="layui-input" value="{{ .pref.UserName }}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label">等级</label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<input type="number" name="user_level" min="1" required lay-verify="required" autocomplete="off"
|
||||||
|
class="layui-input" value="{{ .pref.UserLevel }}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label">当前经验</label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<input type="number" name="user_exp" min="0" required lay-verify="required" autocomplete="off"
|
||||||
|
class="layui-input" value="{{ .pref.UserExp }}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label">升级经验</label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<input type="number" name="next_exp" min="0" required lay-verify="required" autocomplete="off"
|
||||||
|
class="layui-input" value="{{ .pref.NextExp }}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label">金币</label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<input type="number" name="game_coin" min="0" required lay-verify="required" autocomplete="off"
|
||||||
|
class="layui-input" value="{{ .pref.GameCoin }}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label">爱心</label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<input type="number" name="sns_coin" min="0" required lay-verify="required" autocomplete="off"
|
||||||
|
class="layui-input" value="{{ .pref.SnsCoin }}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label">体力上限</label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<input type="number" name="energy_max" min="1" required lay-verify="required" autocomplete="off"
|
||||||
|
class="layui-input" value="{{ .pref.EnergyMax }}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label">当前体力</label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<input type="number" name="over_max_energy" min="0" required lay-verify="required"
|
||||||
|
autocomplete="off" class="layui-input" value="{{ .pref.OverMaxEnergy }}">
|
||||||
|
<div class="layui-form-mid layui-word-aux">填 0 时按体力上限返回给客户端。</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label">用户 ID</label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<input type="text" name="invite_code" required lay-verify="required" autocomplete="off"
|
||||||
|
class="layui-input" value="{{ .pref.InviteCode }}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item layui-form-text">
|
||||||
|
<label class="layui-form-label">简介</label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<textarea name="user_desc" class="layui-textarea"
|
||||||
|
style="min-height: 120px;">{{ .pref.UserDesc }}</textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<button type="button" class="layui-btn" id="save-profile">保存</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{ template "admin/nav/footer.html" . }}
|
||||||
|
</div>
|
||||||
|
<script src="/static/js/layui.js"></script>
|
||||||
|
<script>
|
||||||
|
layui.use(['form', 'layer'], function () {
|
||||||
|
var $ = layui.jquery
|
||||||
|
, layer = layui.layer;
|
||||||
|
|
||||||
|
$("#save-profile").click(function () {
|
||||||
|
$.post("/admin/profile", $("form").serialize(), function (res) {
|
||||||
|
layer.msg(res.message || "请求完成");
|
||||||
|
}).fail(function () {
|
||||||
|
layer.msg("请求失败,请稍后再试");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
{{ end }}
|
||||||
Reference in New Issue
Block a user