Implement friend APIs and target profile support
- Add friend search/list/request/requestCancel/response/expel handlers and schemas - Store friend relationships in user_friend and backfill default friend links at startup - Auto-add the default user as a friend for newly created accounts - Support target user_id in /api profile requests and return target profile data - Make profile accessory_info optional to avoid zero-value accessory payloads - Update login topInfo friend counters from friend relationship state Signed-off-by: Sean Du <do4suki@gmail.com>
This commit is contained in:
@@ -76,7 +76,7 @@ func api(ctx *gin.Context) {
|
|||||||
case "livese":
|
case "livese":
|
||||||
result, err = livese.LiveSeApi(v.Action)
|
result, err = livese.LiveSeApi(v.Action)
|
||||||
case "login":
|
case "login":
|
||||||
result, err = login.LoginApi(v.Action)
|
result, err = login.LoginApi(ctx, v.Action)
|
||||||
case "marathon":
|
case "marathon":
|
||||||
result, err = marathon.MarathonApi(v.Action)
|
result, err = marathon.MarathonApi(v.Action)
|
||||||
case "multiunit":
|
case "multiunit":
|
||||||
@@ -90,7 +90,7 @@ func api(ctx *gin.Context) {
|
|||||||
case "payment":
|
case "payment":
|
||||||
result, err = payment.PaymentApi(v.Action)
|
result, err = payment.PaymentApi(v.Action)
|
||||||
case "profile":
|
case "profile":
|
||||||
result, err = profile.ProfileApi(ctx, v.Action)
|
result, err = profile.ProfileApi(ctx, v.Action, v.UserID)
|
||||||
case "scenario":
|
case "scenario":
|
||||||
result, err = scenario.ScenarioApi(ctx, v.Action)
|
result, err = scenario.ScenarioApi(ctx, v.Action)
|
||||||
case "stamp":
|
case "stamp":
|
||||||
|
|||||||
@@ -2,12 +2,14 @@ package login
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
func LoginApi(action string) (res any, err error) {
|
func LoginApi(ctx *gin.Context, action string) (res any, err error) {
|
||||||
switch action {
|
switch action {
|
||||||
case "topInfo":
|
case "topInfo":
|
||||||
res, err = loginTopInfo()
|
res, err = loginTopInfo(ctx)
|
||||||
case "topInfoOnce":
|
case "topInfoOnce":
|
||||||
res, err = loginTopInfoOnce()
|
res, err = loginTopInfoOnce()
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -1,25 +1,52 @@
|
|||||||
package login
|
package login
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
usermodel "honoka-chan/internal/model/user"
|
||||||
loginapischema "honoka-chan/internal/schema/api/login"
|
loginapischema "honoka-chan/internal/schema/api/login"
|
||||||
|
"honoka-chan/internal/session"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
func loginTopInfo() (res any, err error) {
|
func loginTopInfo(ctx *gin.Context) (res any, err error) {
|
||||||
|
ss := session.Get(ctx)
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
|
friendsRequestCnt64, err := ss.UserEng.Table(new(usermodel.UserFriend)).
|
||||||
|
Where("user_id = ?", ss.UserID).
|
||||||
|
Where("status = ?", usermodel.FriendStatusAwaitingApproval).
|
||||||
|
Where("is_new = ?", true).
|
||||||
|
Count()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
friendsApprovalWaitCnt64, err := ss.UserEng.Table(new(usermodel.UserFriend)).
|
||||||
|
Where("user_id = ?", ss.UserID).
|
||||||
|
Where("status = ?", usermodel.FriendStatusPending).
|
||||||
|
Count()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
friendsRequestCnt := int(friendsRequestCnt64)
|
||||||
|
friendsApprovalWaitCnt := int(friendsApprovalWaitCnt64)
|
||||||
|
|
||||||
res = loginapischema.TopInfoResp{
|
res = loginapischema.TopInfoResp{
|
||||||
Result: loginapischema.TopInfoData{
|
Result: loginapischema.TopInfoData{
|
||||||
FriendActionCnt: 0,
|
FriendActionCnt: 0,
|
||||||
FriendGreetCnt: 0,
|
FriendGreetCnt: 0,
|
||||||
FriendVarietyCnt: 0,
|
FriendVarietyCnt: 0,
|
||||||
FriendNewCnt: 0,
|
FriendNewCnt: friendsRequestCnt + friendsApprovalWaitCnt,
|
||||||
PresentCnt: 0,
|
PresentCnt: 0,
|
||||||
SecretBoxBadgeFlag: false,
|
SecretBoxBadgeFlag: false,
|
||||||
ServerDatetime: time.Now().Format("2006-01-02 15:04:05"),
|
ServerDatetime: now.Format("2006-01-02 15:04:05"),
|
||||||
ServerTimestamp: time.Now().Unix(),
|
ServerTimestamp: now.Unix(),
|
||||||
NoticeFriendDatetime: time.Now().Format("2006-01-02 15:04:05"),
|
NoticeFriendDatetime: now.Format("2006-01-02 15:04:05"),
|
||||||
NoticeMailDatetime: "2000-01-01 12:00:00",
|
NoticeMailDatetime: "2000-01-01 12:00:00",
|
||||||
FriendsApprovalWaitCnt: 0,
|
FriendsApprovalWaitCnt: friendsApprovalWaitCnt,
|
||||||
FriendsRequestCnt: 0,
|
FriendsRequestCnt: friendsRequestCnt,
|
||||||
IsTodayBirthday: false,
|
IsTodayBirthday: false,
|
||||||
LicenseInfo: loginapischema.LicenseInfo{
|
LicenseInfo: loginapischema.LicenseInfo{
|
||||||
LicenseList: []any{},
|
LicenseList: []any{},
|
||||||
@@ -37,7 +64,7 @@ func loginTopInfo() (res any, err error) {
|
|||||||
},
|
},
|
||||||
Status: 200,
|
Status: 200,
|
||||||
CommandNum: false,
|
CommandNum: false,
|
||||||
TimeStamp: time.Now().Unix(),
|
TimeStamp: now.Unix(),
|
||||||
}
|
}
|
||||||
|
|
||||||
return res, err
|
return res, err
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package profile
|
package profile
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
unitmodel "honoka-chan/internal/model/unit"
|
unitmodel "honoka-chan/internal/model/unit"
|
||||||
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"
|
||||||
@@ -11,148 +12,368 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
func profileInfo(ctx *gin.Context) (res any, err error) {
|
func profileInfo(ctx *gin.Context, targetUserID int) (res any, err error) {
|
||||||
ss := session.Get(ctx)
|
ss := session.Get(ctx)
|
||||||
pref := ss.UserPref
|
|
||||||
displayUserID := pref.UserID
|
targetPref, err := getTargetUserPref(ss, targetUserID)
|
||||||
if inviteUserID, convErr := strconv.Atoi(pref.InviteCode); convErr == nil && inviteUserID > 0 {
|
if err != nil {
|
||||||
displayUserID = inviteUserID
|
return nil, err
|
||||||
}
|
}
|
||||||
|
targetUserID = targetPref.UserID
|
||||||
|
|
||||||
unitCount, err := ss.UserEng.Table(new(usermodel.UserUnitData)).
|
unitCount, err := ss.UserEng.Table(new(usermodel.UserUnitData)).
|
||||||
Where("user_id = ?", ss.UserID).Count()
|
Where("user_id = ?", targetUserID).
|
||||||
|
Count()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
unitData := unitmodel.UnitDataMap{}
|
lastLoginTime, err := getTargetLastLoginTime(ss, targetUserID)
|
||||||
_, err = ss.GetBasicUnitInfo().
|
|
||||||
Where("a.unit_owning_user_id = ?", pref.UnitOwningUserID).Get(&unitData)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var accessoryOwningId, accessoryId, exp int
|
centerUnitOwningUserID, err := getMainDeckCenterUnitOwningUserID(ss, targetUserID, targetPref.UnitOwningUserID)
|
||||||
_, err = ss.UserEng.Table("user_accessory_wear").Where("unit_owning_user_id = ? AND user_id = ?", pref.UnitOwningUserID, ss.UserID).
|
|
||||||
Cols("accessory_owning_user_id").Get(&accessoryOwningId)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
_, err = ss.MainEng.Table("common_accessory_m").Where("accessory_owning_user_id = ?", accessoryOwningId).
|
|
||||||
Cols("accessory_id,exp").Get(&accessoryId, &exp)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
accessoryInfo := profileapischema.AccessoryInfo{
|
|
||||||
AccessoryOwningUserID: accessoryOwningId,
|
|
||||||
AccessoryID: accessoryId,
|
|
||||||
Exp: exp,
|
|
||||||
NextExp: 0,
|
|
||||||
Level: 8,
|
|
||||||
MaxLevel: 8,
|
|
||||||
RankUpCount: 4,
|
|
||||||
FavoriteFlag: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
removeSkillIds := []int{}
|
centerUnitInfo, centerRaw, err := getProfileCenterUnitInfo(ss, targetUserID, centerUnitOwningUserID, targetPref.AwardID)
|
||||||
err = ss.UserEng.Table("user_unit_skill_equip").Where("unit_owning_user_id = ? AND user_id = ?", pref.UnitOwningUserID, ss.UserID).
|
|
||||||
Cols("unit_removable_skill_id").Find(&removeSkillIds)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
naviUnitInfo, naviRaw, err := getProfileNaviUnitInfo(ss, targetUserID, targetPref.UnitOwningUserID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if naviRaw != nil {
|
||||||
|
centerUnitInfo.Costume = profileapischema.Costume{
|
||||||
|
UnitID: naviRaw.UnitID,
|
||||||
|
IsRankMax: naviRaw.IsRankMax,
|
||||||
|
IsSigned: naviRaw.IsSigned,
|
||||||
|
}
|
||||||
|
} else if centerRaw != nil {
|
||||||
|
centerUnitInfo.Costume = profileapischema.Costume{
|
||||||
|
UnitID: centerRaw.UnitID,
|
||||||
|
IsRankMax: centerRaw.IsRankMax,
|
||||||
|
IsSigned: centerRaw.IsSigned,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
res = profileapischema.InfoResp{
|
res = profileapischema.InfoResp{
|
||||||
Result: profileapischema.InfoData{
|
Result: profileapischema.InfoData{
|
||||||
UserInfo: profileapischema.UserInfo{
|
UserInfo: profileapischema.UserInfo{
|
||||||
UserID: displayUserID,
|
UserID: targetPref.UserID,
|
||||||
Name: pref.UserName,
|
Name: targetPref.UserName,
|
||||||
Level: pref.UserLevel,
|
Level: targetPref.UserLevel,
|
||||||
CostMax: 100,
|
CostMax: 100,
|
||||||
UnitMax: 5000,
|
UnitMax: 5000,
|
||||||
EnergyMax: pref.EffectiveEnergyMax(),
|
EnergyMax: targetPref.EffectiveEnergyMax(),
|
||||||
FriendMax: 99,
|
FriendMax: 99,
|
||||||
UnitCnt: int(unitCount),
|
UnitCnt: int(unitCount),
|
||||||
InviteCode: pref.InviteCode,
|
InviteCode: targetPref.InviteCode,
|
||||||
ElapsedTimeFromLogin: "14\u5c0f\u65f6\u524d",
|
ElapsedTimeFromLogin: formatProfileElapsedTime(lastLoginTime),
|
||||||
Introduction: pref.UserDesc,
|
Introduction: targetPref.UserDesc,
|
||||||
},
|
|
||||||
// TODO: 区分队伍中心卡片和看板卡片
|
|
||||||
CenterUnitInfo: profileapischema.CenterUnitInfo{
|
|
||||||
UnitOwningUserID: unitData.UnitOwningUserID,
|
|
||||||
UnitID: unitData.UnitID,
|
|
||||||
Exp: unitData.Exp,
|
|
||||||
NextExp: 0,
|
|
||||||
Level: unitData.Level,
|
|
||||||
LevelLimitID: unitData.LevelLimitID,
|
|
||||||
MaxLevel: unitData.MaxLevel,
|
|
||||||
Rank: unitData.Rank,
|
|
||||||
MaxRank: unitData.MaxRank,
|
|
||||||
Love: unitData.Love,
|
|
||||||
MaxLove: unitData.MaxLove,
|
|
||||||
UnitSkillLevel: unitData.UnitSkillLevel,
|
|
||||||
MaxHp: unitData.MaxHp,
|
|
||||||
FavoriteFlag: unitData.FavoriteFlag,
|
|
||||||
DisplayRank: unitData.DisplayRank,
|
|
||||||
UnitSkillExp: unitData.UnitSkillExp,
|
|
||||||
UnitRemovableSkillCapacity: unitData.UnitRemovableSkillCapacity,
|
|
||||||
Attribute: unitData.Attribute,
|
|
||||||
Smile: unitData.Smile,
|
|
||||||
Cute: unitData.Cute,
|
|
||||||
Cool: unitData.Cool,
|
|
||||||
IsLoveMax: unitData.IsLoveMax,
|
|
||||||
IsLevelMax: unitData.IsLevelMax,
|
|
||||||
IsRankMax: unitData.IsRankMax,
|
|
||||||
IsSigned: unitData.IsSigned,
|
|
||||||
IsSkillLevelMax: unitData.IsSkillLevelMax,
|
|
||||||
SettingAwardID: pref.AwardID,
|
|
||||||
RemovableSkillIds: removeSkillIds,
|
|
||||||
AccessoryInfo: accessoryInfo,
|
|
||||||
Costume: profileapischema.Costume{},
|
|
||||||
TotalSmile: unitData.Smile, // TODO: 加成计算
|
|
||||||
TotalCute: unitData.Cute, // 同上
|
|
||||||
TotalCool: unitData.Cool, // 同上
|
|
||||||
TotalHp: unitData.MaxHp,
|
|
||||||
},
|
|
||||||
NaviUnitInfo: profileapischema.NaviUnitInfo{
|
|
||||||
UnitOwningUserID: unitData.UnitOwningUserID,
|
|
||||||
UnitID: unitData.UnitID,
|
|
||||||
Exp: unitData.Exp,
|
|
||||||
NextExp: 0,
|
|
||||||
Level: unitData.Level,
|
|
||||||
MaxLevel: unitData.MaxLevel,
|
|
||||||
LevelLimitID: unitData.LevelLimitID,
|
|
||||||
Rank: unitData.Rank,
|
|
||||||
MaxRank: unitData.MaxRank,
|
|
||||||
Love: unitData.Love,
|
|
||||||
MaxLove: unitData.MaxLove,
|
|
||||||
UnitSkillExp: unitData.UnitSkillExp,
|
|
||||||
UnitSkillLevel: unitData.UnitSkillLevel,
|
|
||||||
MaxHp: unitData.MaxHp,
|
|
||||||
UnitRemovableSkillCapacity: unitData.UnitRemovableSkillCapacity,
|
|
||||||
FavoriteFlag: unitData.FavoriteFlag,
|
|
||||||
DisplayRank: unitData.DisplayRank,
|
|
||||||
IsLoveMax: unitData.IsLoveMax,
|
|
||||||
IsLevelMax: unitData.IsLevelMax,
|
|
||||||
IsRankMax: unitData.IsRankMax,
|
|
||||||
IsSigned: unitData.IsSigned,
|
|
||||||
IsSkillLevelMax: unitData.IsSkillLevelMax,
|
|
||||||
IsRemovableSkillCapacityMax: unitData.IsRemovableSkillCapacityMax,
|
|
||||||
InsertDate: "2016-10-11 10:33:03",
|
|
||||||
TotalSmile: unitData.Smile, // TODO: 加成计算
|
|
||||||
TotalCute: unitData.Cute, // 同上
|
|
||||||
TotalCool: unitData.Cool, // 同上
|
|
||||||
TotalHp: unitData.MaxHp,
|
|
||||||
RemovableSkillIds: removeSkillIds,
|
|
||||||
},
|
},
|
||||||
|
CenterUnitInfo: centerUnitInfo,
|
||||||
|
NaviUnitInfo: naviUnitInfo,
|
||||||
IsAlliance: false,
|
IsAlliance: false,
|
||||||
FriendStatus: 0,
|
FriendStatus: 0,
|
||||||
SettingAwardID: pref.AwardID,
|
SettingAwardID: targetPref.AwardID,
|
||||||
SettingBackgroundID: pref.BackgroundID,
|
SettingBackgroundID: targetPref.BackgroundID,
|
||||||
},
|
},
|
||||||
Status: 200,
|
Status: 200,
|
||||||
CommandNum: false,
|
CommandNum: false,
|
||||||
TimeStamp: time.Now().Unix(),
|
TimeStamp: time.Now().Unix(),
|
||||||
}
|
}
|
||||||
|
|
||||||
return res, err
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTargetUserPref(ss *session.Session, targetUserID int) (usermodel.UserPref, error) {
|
||||||
|
if targetUserID <= 0 || targetUserID == ss.UserID {
|
||||||
|
return ss.UserPref, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
pref := usermodel.UserPref{}
|
||||||
|
has, err := ss.UserEng.Table(new(usermodel.UserPref)).
|
||||||
|
Where("user_id = ?", targetUserID).
|
||||||
|
Get(&pref)
|
||||||
|
if err != nil {
|
||||||
|
return usermodel.UserPref{}, err
|
||||||
|
}
|
||||||
|
if !has {
|
||||||
|
return usermodel.UserPref{}, errors.New("user not found")
|
||||||
|
}
|
||||||
|
if pref.NeedsProfileMigration() {
|
||||||
|
pref.ApplyProfileDefaults()
|
||||||
|
_, err = ss.UserEng.Table(new(usermodel.UserPref)).
|
||||||
|
Where("user_id = ?", targetUserID).
|
||||||
|
Cols(usermodel.UserPrefProfileColumns()...).
|
||||||
|
Update(&pref)
|
||||||
|
if err != nil {
|
||||||
|
return usermodel.UserPref{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pref, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTargetLastLoginTime(ss *session.Session, targetUserID int) (int64, error) {
|
||||||
|
if targetUserID == ss.UserID {
|
||||||
|
user := usermodel.Users{}
|
||||||
|
has, err := ss.UserEng.Table(new(usermodel.Users)).
|
||||||
|
Where("user_id = ?", targetUserID).
|
||||||
|
Get(&user)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if !has {
|
||||||
|
return 0, errors.New("user not found")
|
||||||
|
}
|
||||||
|
return user.LastLoginTime, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
user := usermodel.Users{}
|
||||||
|
has, err := ss.UserEng.Table(new(usermodel.Users)).
|
||||||
|
Where("user_id = ?", targetUserID).
|
||||||
|
Get(&user)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if !has {
|
||||||
|
return 0, errors.New("user not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return user.LastLoginTime, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getMainDeckCenterUnitOwningUserID(ss *session.Session, targetUserID, fallback int) (int, error) {
|
||||||
|
var unitOwningUserID int
|
||||||
|
has, err := ss.UserEng.Table(new(usermodel.UserDeckUnit)).Alias("udu").
|
||||||
|
Join("LEFT", "user_deck ud", "ud.id = udu.user_deck_id").
|
||||||
|
Where("udu.user_id = ?", targetUserID).
|
||||||
|
Where("ud.main_flag = ?", 1).
|
||||||
|
Where("udu.position = ?", 5).
|
||||||
|
Cols("udu.unit_owning_user_id").
|
||||||
|
Get(&unitOwningUserID)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if has && unitOwningUserID > 0 {
|
||||||
|
return unitOwningUserID, nil
|
||||||
|
}
|
||||||
|
return fallback, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getProfileCenterUnitInfo(ss *session.Session, targetUserID, unitOwningUserID, awardID int) (profileapischema.CenterUnitInfo, *unitmodel.UnitDataMap, error) {
|
||||||
|
unitData, err := getTargetUnitData(ss, targetUserID, unitOwningUserID)
|
||||||
|
if err != nil {
|
||||||
|
return profileapischema.CenterUnitInfo{}, nil, err
|
||||||
|
}
|
||||||
|
if unitData == nil {
|
||||||
|
return profileapischema.CenterUnitInfo{}, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
accessoryInfo, err := getAccessoryInfo(ss, targetUserID, unitOwningUserID)
|
||||||
|
if err != nil {
|
||||||
|
return profileapischema.CenterUnitInfo{}, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
removeSkillIDs, err := getRemovableSkillIDs(ss, targetUserID, unitOwningUserID)
|
||||||
|
if err != nil {
|
||||||
|
return profileapischema.CenterUnitInfo{}, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
info := profileapischema.CenterUnitInfo{
|
||||||
|
UnitOwningUserID: unitData.UnitOwningUserID,
|
||||||
|
UnitID: unitData.UnitID,
|
||||||
|
Exp: unitData.Exp,
|
||||||
|
NextExp: 0,
|
||||||
|
Level: unitData.Level,
|
||||||
|
LevelLimitID: unitData.LevelLimitID,
|
||||||
|
MaxLevel: unitData.MaxLevel,
|
||||||
|
Rank: unitData.Rank,
|
||||||
|
MaxRank: unitData.MaxRank,
|
||||||
|
Love: unitData.Love,
|
||||||
|
MaxLove: unitData.MaxLove,
|
||||||
|
UnitSkillLevel: unitData.UnitSkillLevel,
|
||||||
|
MaxHp: unitData.MaxHp,
|
||||||
|
FavoriteFlag: unitData.FavoriteFlag,
|
||||||
|
DisplayRank: unitData.DisplayRank,
|
||||||
|
UnitSkillExp: unitData.UnitSkillExp,
|
||||||
|
UnitRemovableSkillCapacity: unitData.UnitRemovableSkillCapacity,
|
||||||
|
Attribute: unitData.Attribute,
|
||||||
|
Smile: unitData.Smile,
|
||||||
|
Cute: unitData.Cute,
|
||||||
|
Cool: unitData.Cool,
|
||||||
|
IsLoveMax: unitData.IsLoveMax,
|
||||||
|
IsLevelMax: unitData.IsLevelMax,
|
||||||
|
IsRankMax: unitData.IsRankMax,
|
||||||
|
IsSigned: unitData.IsSigned,
|
||||||
|
IsSkillLevelMax: unitData.IsSkillLevelMax,
|
||||||
|
SettingAwardID: awardID,
|
||||||
|
RemovableSkillIds: removeSkillIDs,
|
||||||
|
AccessoryInfo: accessoryInfo,
|
||||||
|
TotalSmile: unitData.Smile,
|
||||||
|
TotalCute: unitData.Cute,
|
||||||
|
TotalCool: unitData.Cool,
|
||||||
|
TotalHp: unitData.MaxHp,
|
||||||
|
}
|
||||||
|
|
||||||
|
return info, unitData, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getProfileNaviUnitInfo(ss *session.Session, targetUserID, unitOwningUserID int) (profileapischema.NaviUnitInfo, *unitmodel.UnitDataMap, error) {
|
||||||
|
unitData, err := getTargetUnitData(ss, targetUserID, unitOwningUserID)
|
||||||
|
if err != nil {
|
||||||
|
return profileapischema.NaviUnitInfo{}, nil, err
|
||||||
|
}
|
||||||
|
if unitData == nil {
|
||||||
|
return profileapischema.NaviUnitInfo{}, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
removeSkillIDs, err := getRemovableSkillIDs(ss, targetUserID, unitOwningUserID)
|
||||||
|
if err != nil {
|
||||||
|
return profileapischema.NaviUnitInfo{}, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
info := profileapischema.NaviUnitInfo{
|
||||||
|
UnitOwningUserID: unitData.UnitOwningUserID,
|
||||||
|
UnitID: unitData.UnitID,
|
||||||
|
Exp: unitData.Exp,
|
||||||
|
NextExp: 0,
|
||||||
|
Level: unitData.Level,
|
||||||
|
MaxLevel: unitData.MaxLevel,
|
||||||
|
LevelLimitID: unitData.LevelLimitID,
|
||||||
|
Rank: unitData.Rank,
|
||||||
|
MaxRank: unitData.MaxRank,
|
||||||
|
Love: unitData.Love,
|
||||||
|
MaxLove: unitData.MaxLove,
|
||||||
|
UnitSkillExp: unitData.UnitSkillExp,
|
||||||
|
UnitSkillLevel: unitData.UnitSkillLevel,
|
||||||
|
MaxHp: unitData.MaxHp,
|
||||||
|
UnitRemovableSkillCapacity: unitData.UnitRemovableSkillCapacity,
|
||||||
|
FavoriteFlag: unitData.FavoriteFlag,
|
||||||
|
DisplayRank: unitData.DisplayRank,
|
||||||
|
IsRankMax: unitData.IsRankMax,
|
||||||
|
IsLoveMax: unitData.IsLoveMax,
|
||||||
|
IsLevelMax: unitData.IsLevelMax,
|
||||||
|
IsSigned: unitData.IsSigned,
|
||||||
|
IsSkillLevelMax: unitData.IsSkillLevelMax,
|
||||||
|
IsRemovableSkillCapacityMax: unitData.IsRemovableSkillCapacityMax,
|
||||||
|
InsertDate: "2016-10-11 10:33:03",
|
||||||
|
TotalSmile: unitData.Smile,
|
||||||
|
TotalCute: unitData.Cute,
|
||||||
|
TotalCool: unitData.Cool,
|
||||||
|
TotalHp: unitData.MaxHp,
|
||||||
|
RemovableSkillIds: removeSkillIDs,
|
||||||
|
}
|
||||||
|
|
||||||
|
return info, unitData, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTargetUnitData(ss *session.Session, targetUserID, unitOwningUserID int) (*unitmodel.UnitDataMap, error) {
|
||||||
|
if unitOwningUserID <= 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
unitData := unitmodel.UnitDataMap{}
|
||||||
|
has, err := ss.GetBasicUnitInfo().
|
||||||
|
Where("a.user_id = ?", targetUserID).
|
||||||
|
Where("a.unit_owning_user_id = ?", unitOwningUserID).
|
||||||
|
Get(&unitData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !has {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &unitData, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAccessoryInfo(ss *session.Session, targetUserID, unitOwningUserID int) (*profileapischema.AccessoryInfo, error) {
|
||||||
|
if unitOwningUserID <= 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var accessoryOwningID int
|
||||||
|
has, err := ss.UserEng.Table("user_accessory_wear").
|
||||||
|
Where("unit_owning_user_id = ? AND user_id = ?", unitOwningUserID, targetUserID).
|
||||||
|
Cols("accessory_owning_user_id").
|
||||||
|
Get(&accessoryOwningID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !has || accessoryOwningID <= 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
accessoryData := struct {
|
||||||
|
AccessoryID int `xorm:"accessory_id"`
|
||||||
|
Exp int `xorm:"exp"`
|
||||||
|
}{}
|
||||||
|
has, err = ss.MainEng.Table("common_accessory_m").
|
||||||
|
Where("accessory_owning_user_id = ?", accessoryOwningID).
|
||||||
|
Cols("accessory_id,exp").
|
||||||
|
Get(&accessoryData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !has {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &profileapischema.AccessoryInfo{
|
||||||
|
AccessoryOwningUserID: accessoryOwningID,
|
||||||
|
AccessoryID: accessoryData.AccessoryID,
|
||||||
|
Exp: accessoryData.Exp,
|
||||||
|
NextExp: 0,
|
||||||
|
Level: 8,
|
||||||
|
MaxLevel: 8,
|
||||||
|
RankUpCount: 4,
|
||||||
|
FavoriteFlag: true,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getRemovableSkillIDs(ss *session.Session, targetUserID, unitOwningUserID int) ([]int, error) {
|
||||||
|
removeSkillIDs := []int{}
|
||||||
|
if unitOwningUserID <= 0 {
|
||||||
|
return removeSkillIDs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err := ss.UserEng.Table("user_unit_skill_equip").
|
||||||
|
Where("unit_owning_user_id = ? AND user_id = ?", unitOwningUserID, targetUserID).
|
||||||
|
Cols("unit_removable_skill_id").
|
||||||
|
Find(&removeSkillIDs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return removeSkillIDs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatProfileElapsedTime(ts int64) string {
|
||||||
|
if ts <= 0 {
|
||||||
|
return "刚刚"
|
||||||
|
}
|
||||||
|
|
||||||
|
delta := time.Since(time.Unix(ts, 0))
|
||||||
|
if delta < time.Minute {
|
||||||
|
return "刚刚"
|
||||||
|
}
|
||||||
|
if delta < time.Hour {
|
||||||
|
return timeDurationInt(delta.Minutes()) + "分钟前"
|
||||||
|
}
|
||||||
|
if delta < 24*time.Hour {
|
||||||
|
return timeDurationInt(delta.Hours()) + "小时前"
|
||||||
|
}
|
||||||
|
return timeDurationInt(delta.Hours()/24) + "天前"
|
||||||
|
}
|
||||||
|
|
||||||
|
func timeDurationInt(value float64) string {
|
||||||
|
return strconv.Itoa(int(value))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,14 +6,14 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ProfileApi(ctx *gin.Context, action string) (res any, err error) {
|
func ProfileApi(ctx *gin.Context, action string, targetUserID int) (res any, err error) {
|
||||||
switch action {
|
switch action {
|
||||||
case "cardRanking":
|
case "cardRanking":
|
||||||
res, err = cardRanking()
|
res, err = cardRanking()
|
||||||
case "liveCnt":
|
case "liveCnt":
|
||||||
res, err = liveCnt()
|
res, err = liveCnt()
|
||||||
case "profileInfo":
|
case "profileInfo":
|
||||||
res, err = profileInfo(ctx)
|
res, err = profileInfo(ctx, targetUserID)
|
||||||
default:
|
default:
|
||||||
err = fmt.Errorf("unimplemented action: profile: %s", action)
|
err = fmt.Errorf("unimplemented action: profile: %s", action)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,87 @@
|
|||||||
|
package friend
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
usermodel "honoka-chan/internal/model/user"
|
||||||
|
"honoka-chan/internal/session"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resolveActualFriendUserID(ss *session.Session, requestedUserID int) (int, error) {
|
||||||
|
if requestedUserID <= 0 {
|
||||||
|
return 0, errors.New("invalid user_id")
|
||||||
|
}
|
||||||
|
|
||||||
|
pref := usermodel.UserPref{}
|
||||||
|
has, err := ss.UserEng.Table(new(usermodel.UserPref)).
|
||||||
|
Where("invite_code = ?", strconv.Itoa(requestedUserID)).
|
||||||
|
Cols("user_id").
|
||||||
|
Get(&pref)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if has && pref.UserID > 0 {
|
||||||
|
return pref.UserID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
user := usermodel.Users{}
|
||||||
|
has, err = ss.UserEng.Table(new(usermodel.Users)).
|
||||||
|
Where("user_id = ?", requestedUserID).
|
||||||
|
Cols("user_id").
|
||||||
|
Get(&user)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if !has || user.UserID <= 0 {
|
||||||
|
return 0, errors.New("user not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return user.UserID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func areUsersFriends(ss *session.Session, userID, friendUserID int) (bool, error) {
|
||||||
|
forward, err := ss.UserEng.Table(new(usermodel.UserFriend)).
|
||||||
|
Where("user_id = ?", userID).
|
||||||
|
Where("friend_user_id = ?", friendUserID).
|
||||||
|
Where("status = ?", usermodel.FriendStatusApproved).
|
||||||
|
Exist()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if !forward {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return ss.UserEng.Table(new(usermodel.UserFriend)).
|
||||||
|
Where("user_id = ?", friendUserID).
|
||||||
|
Where("friend_user_id = ?", userID).
|
||||||
|
Where("status = ?", usermodel.FriendStatusApproved).
|
||||||
|
Exist()
|
||||||
|
}
|
||||||
|
|
||||||
|
func resolveUserIDByInviteCode(ss *session.Session, inviteCode string) (int, error) {
|
||||||
|
digits := strings.Map(func(r rune) rune {
|
||||||
|
if r >= '0' && r <= '9' {
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}, inviteCode)
|
||||||
|
if digits == "" {
|
||||||
|
return 0, errors.New("invalid invite_code")
|
||||||
|
}
|
||||||
|
|
||||||
|
pref := usermodel.UserPref{}
|
||||||
|
has, err := ss.UserEng.Table(new(usermodel.UserPref)).
|
||||||
|
Where("invite_code = ?", digits).
|
||||||
|
Cols("user_id").
|
||||||
|
Get(&pref)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if !has || pref.UserID <= 0 {
|
||||||
|
return 0, errors.New("user not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return pref.UserID, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
package friend
|
||||||
|
|
||||||
|
import (
|
||||||
|
"honoka-chan/internal/middleware"
|
||||||
|
usermodel "honoka-chan/internal/model/user"
|
||||||
|
"honoka-chan/internal/router"
|
||||||
|
friendschema "honoka-chan/internal/schema/friend"
|
||||||
|
"honoka-chan/internal/session"
|
||||||
|
honokautils "honoka-chan/internal/utils"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func expel(ctx *gin.Context) {
|
||||||
|
ss := session.Get(ctx)
|
||||||
|
defer ss.Finalize()
|
||||||
|
|
||||||
|
req := friendschema.UserIDReq{}
|
||||||
|
err := honokautils.ParseRequestData(ctx, &req)
|
||||||
|
if ss.CheckErr(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
targetUserID, err := resolveActualFriendUserID(ss, req.UserID)
|
||||||
|
if ss.CheckErr(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = ss.UserEng.Table(new(usermodel.UserFriend)).
|
||||||
|
Where("user_id = ?", ss.UserID).
|
||||||
|
Where("friend_user_id = ?", targetUserID).
|
||||||
|
Delete()
|
||||||
|
if ss.CheckErr(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = ss.UserEng.Table(new(usermodel.UserFriend)).
|
||||||
|
Where("user_id = ?", targetUserID).
|
||||||
|
Where("friend_user_id = ?", ss.UserID).
|
||||||
|
Delete()
|
||||||
|
if ss.CheckErr(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ss.Respond(friendschema.ExpelResp{
|
||||||
|
ResponseData: []any{},
|
||||||
|
ReleaseInfo: []any{},
|
||||||
|
StatusCode: 200,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
router.AddHandler("main.php", "POST", "/friend/expel", middleware.Common, expel)
|
||||||
|
}
|
||||||
@@ -0,0 +1,316 @@
|
|||||||
|
package friend
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"honoka-chan/internal/middleware"
|
||||||
|
unitmodel "honoka-chan/internal/model/unit"
|
||||||
|
usermodel "honoka-chan/internal/model/user"
|
||||||
|
"honoka-chan/internal/router"
|
||||||
|
friendschema "honoka-chan/internal/schema/friend"
|
||||||
|
"honoka-chan/internal/session"
|
||||||
|
honokautils "honoka-chan/internal/utils"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type friendListRow struct {
|
||||||
|
FriendUserID int `xorm:"friend_user_id"`
|
||||||
|
Status int `xorm:"status"`
|
||||||
|
IsNew bool `xorm:"is_new"`
|
||||||
|
InsertDate int64 `xorm:"insert_date"`
|
||||||
|
UpdateDate int64 `xorm:"update_date"`
|
||||||
|
InviteCode string `xorm:"invite_code"`
|
||||||
|
UserName string `xorm:"user_name"`
|
||||||
|
UserLevel int `xorm:"user_level"`
|
||||||
|
UserDesc string `xorm:"user_desc"`
|
||||||
|
AwardID int `xorm:"award_id"`
|
||||||
|
LastLoginTime int64 `xorm:"last_login_time"`
|
||||||
|
CenterUnitOwningUserID int `xorm:"center_unit_owning_user_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func list(ctx *gin.Context) {
|
||||||
|
ss := session.Get(ctx)
|
||||||
|
defer ss.Finalize()
|
||||||
|
|
||||||
|
listReq := friendschema.ListReq{}
|
||||||
|
err := honokautils.ParseRequestData(ctx, &listReq)
|
||||||
|
if ss.CheckErr(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
friendStatus, err := mapFriendListTypeToStatus(listReq.Type)
|
||||||
|
if ss.CheckErr(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
totalCount, err := ss.UserEng.Table(new(usermodel.UserFriend)).
|
||||||
|
Where("user_id = ?", ss.UserID).
|
||||||
|
Where("status = ?", friendStatus).
|
||||||
|
Count()
|
||||||
|
if ss.CheckErr(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
page := max(listReq.Page, 0)
|
||||||
|
offset := page * usermodel.FriendListPageSize
|
||||||
|
|
||||||
|
rows := []friendListRow{}
|
||||||
|
err = ss.UserEng.Table(new(usermodel.UserFriend)).Alias("uf").
|
||||||
|
Join("LEFT", "user_pref up", "up.user_id = uf.friend_user_id").
|
||||||
|
Join("LEFT", "users u", "u.user_id = uf.friend_user_id").
|
||||||
|
Join("LEFT", "user_deck ud", "ud.user_id = uf.friend_user_id AND ud.main_flag = 1").
|
||||||
|
Join("LEFT", "user_deck_unit udu", "udu.user_deck_id = ud.id AND udu.position = 5").
|
||||||
|
Join("LEFT", "common_unit_data cud", "cud.unit_id = udu.unit_id").
|
||||||
|
Where("uf.user_id = ?", ss.UserID).
|
||||||
|
Where("uf.status = ?", friendStatus).
|
||||||
|
Select(`
|
||||||
|
uf.friend_user_id,
|
||||||
|
uf.status,
|
||||||
|
uf.is_new,
|
||||||
|
uf.insert_date,
|
||||||
|
uf.update_date,
|
||||||
|
up.invite_code,
|
||||||
|
up.user_name,
|
||||||
|
up.user_level,
|
||||||
|
up.user_desc,
|
||||||
|
up.award_id,
|
||||||
|
u.last_login_time,
|
||||||
|
COALESCE(udu.unit_owning_user_id, up.unit_owning_user_id) AS center_unit_owning_user_id
|
||||||
|
`).
|
||||||
|
OrderBy(friendListOrderBy(listReq.Sort)).
|
||||||
|
Limit(usermodel.FriendListPageSize, offset).
|
||||||
|
Find(&rows)
|
||||||
|
if ss.CheckErr(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
friendList := make([]friendschema.FriendList, 0, len(rows))
|
||||||
|
for _, row := range rows {
|
||||||
|
item, err := buildFriendListItem(ss, row)
|
||||||
|
if ss.CheckErr(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
friendList = append(friendList, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(rows) > 0 {
|
||||||
|
switch friendStatus {
|
||||||
|
case usermodel.FriendStatusApproved, usermodel.FriendStatusAwaitingApproval:
|
||||||
|
_, err = ss.UserEng.Table(new(usermodel.UserFriend)).
|
||||||
|
Where("user_id = ?", ss.UserID).
|
||||||
|
Where("status = ?", friendStatus).
|
||||||
|
Where("is_new = ?", true).
|
||||||
|
Cols("is_new", "update_date").
|
||||||
|
Update(&usermodel.UserFriend{
|
||||||
|
IsNew: false,
|
||||||
|
UpdateDate: time.Now().Unix(),
|
||||||
|
})
|
||||||
|
if ss.CheckErr(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ss.Respond(friendschema.ListResp{
|
||||||
|
ResponseData: friendschema.ListData{
|
||||||
|
ItemCount: int(totalCount),
|
||||||
|
FriendList: friendList,
|
||||||
|
NewFriendList: []any{},
|
||||||
|
ServerTimestamp: time.Now().Unix(),
|
||||||
|
},
|
||||||
|
ReleaseInfo: []any{},
|
||||||
|
StatusCode: 200,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildFriendListItem(ss *session.Session, row friendListRow) (friendschema.FriendList, error) {
|
||||||
|
appliedAt := max(row.UpdateDate, row.InsertDate)
|
||||||
|
|
||||||
|
centerUnitInfo, err := buildCenterUnitInfo(ss, row.FriendUserID, row.CenterUnitOwningUserID, row.AwardID)
|
||||||
|
if err != nil {
|
||||||
|
return friendschema.FriendList{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return friendschema.FriendList{
|
||||||
|
UserData: friendschema.UserData{
|
||||||
|
UserID: displayFriendUserID(row.InviteCode, row.FriendUserID),
|
||||||
|
Name: row.UserName,
|
||||||
|
Level: row.UserLevel,
|
||||||
|
ElapsedTimeFromLogin: formatElapsedTime(row.LastLoginTime),
|
||||||
|
ElapsedTimeFromApplied: formatElapsedTime(appliedAt),
|
||||||
|
Comment: row.UserDesc,
|
||||||
|
},
|
||||||
|
CenterUnitInfo: centerUnitInfo,
|
||||||
|
SettingAwardID: row.AwardID,
|
||||||
|
IsNew: row.IsNew,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildCenterUnitInfo(ss *session.Session, userID, unitOwningUserID, awardID int) (friendschema.CenterUnitInfo, error) {
|
||||||
|
info := friendschema.CenterUnitInfo{
|
||||||
|
SettingAwardID: awardID,
|
||||||
|
RemovableSkillIds: []int{},
|
||||||
|
}
|
||||||
|
if unitOwningUserID <= 0 {
|
||||||
|
return info, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
unitData := unitmodel.UnitDataMap{}
|
||||||
|
has, err := ss.GetBasicUnitInfo().
|
||||||
|
Where("a.user_id = ?", userID).
|
||||||
|
Where("a.unit_owning_user_id = ?", unitOwningUserID).
|
||||||
|
Get(&unitData)
|
||||||
|
if err != nil {
|
||||||
|
return info, err
|
||||||
|
}
|
||||||
|
if !has {
|
||||||
|
return info, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var accessoryInfo friendschema.AccessoryInfo
|
||||||
|
accessoryWear := usermodel.UserAccessoryWear{}
|
||||||
|
has, err = ss.UserEng.Table(new(usermodel.UserAccessoryWear)).
|
||||||
|
Where("user_id = ? AND unit_owning_user_id = ?", userID, unitOwningUserID).
|
||||||
|
Get(&accessoryWear)
|
||||||
|
if err != nil {
|
||||||
|
return info, err
|
||||||
|
}
|
||||||
|
if has && accessoryWear.AccessoryOwningUserID > 0 {
|
||||||
|
var accessoryID, exp int
|
||||||
|
_, err = ss.MainEng.Table("common_accessory_m").
|
||||||
|
Where("accessory_owning_user_id = ?", accessoryWear.AccessoryOwningUserID).
|
||||||
|
Cols("accessory_id,exp").
|
||||||
|
Get(&accessoryID, &exp)
|
||||||
|
if err != nil {
|
||||||
|
return info, err
|
||||||
|
}
|
||||||
|
accessoryInfo = friendschema.AccessoryInfo{
|
||||||
|
AccessoryOwningUserID: accessoryWear.AccessoryOwningUserID,
|
||||||
|
AccessoryID: accessoryID,
|
||||||
|
Exp: exp,
|
||||||
|
NextExp: 0,
|
||||||
|
Level: 8,
|
||||||
|
MaxLevel: 8,
|
||||||
|
RankUpCount: 4,
|
||||||
|
FavoriteFlag: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
removableSkillIDs := []int{}
|
||||||
|
err = ss.UserEng.Table(new(usermodel.UserUnitSkillEquip)).
|
||||||
|
Where("user_id = ? AND unit_owning_user_id = ?", userID, unitOwningUserID).
|
||||||
|
Cols("unit_removable_skill_id").
|
||||||
|
Find(&removableSkillIDs)
|
||||||
|
if err != nil {
|
||||||
|
return info, err
|
||||||
|
}
|
||||||
|
|
||||||
|
info = friendschema.CenterUnitInfo{
|
||||||
|
UnitOwningUserID: int64(unitData.UnitOwningUserID),
|
||||||
|
UnitID: unitData.UnitID,
|
||||||
|
Exp: unitData.Exp,
|
||||||
|
NextExp: 0,
|
||||||
|
Level: unitData.Level,
|
||||||
|
LevelLimitID: unitData.LevelLimitID,
|
||||||
|
MaxLevel: unitData.MaxLevel,
|
||||||
|
Rank: unitData.Rank,
|
||||||
|
MaxRank: unitData.MaxRank,
|
||||||
|
Love: unitData.Love,
|
||||||
|
MaxLove: unitData.MaxLove,
|
||||||
|
UnitSkillLevel: unitData.UnitSkillLevel,
|
||||||
|
MaxHp: unitData.MaxHp,
|
||||||
|
FavoriteFlag: unitData.FavoriteFlag,
|
||||||
|
DisplayRank: unitData.DisplayRank,
|
||||||
|
UnitSkillExp: unitData.UnitSkillExp,
|
||||||
|
UnitRemovableSkillCapacity: unitData.UnitRemovableSkillCapacity,
|
||||||
|
Attribute: unitData.Attribute,
|
||||||
|
Smile: unitData.Smile,
|
||||||
|
Cute: unitData.Cute,
|
||||||
|
Cool: unitData.Cool,
|
||||||
|
IsLoveMax: unitData.IsLoveMax,
|
||||||
|
IsLevelMax: unitData.IsLevelMax,
|
||||||
|
IsRankMax: unitData.IsRankMax,
|
||||||
|
IsSigned: unitData.IsSigned,
|
||||||
|
IsSkillLevelMax: unitData.IsSkillLevelMax,
|
||||||
|
SettingAwardID: awardID,
|
||||||
|
RemovableSkillIds: removableSkillIDs,
|
||||||
|
AccessoryInfo: accessoryInfo,
|
||||||
|
}
|
||||||
|
|
||||||
|
return info, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func mapFriendListTypeToStatus(listType int) (int, error) {
|
||||||
|
switch listType {
|
||||||
|
case 0:
|
||||||
|
return usermodel.FriendStatusApproved, nil
|
||||||
|
case 1:
|
||||||
|
return usermodel.FriendStatusPending, nil
|
||||||
|
case 2:
|
||||||
|
return usermodel.FriendStatusAwaitingApproval, nil
|
||||||
|
default:
|
||||||
|
return 0, errors.New("invalid friend list type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func friendListOrderBy(sort int) string {
|
||||||
|
switch sort {
|
||||||
|
case 1:
|
||||||
|
return "up.user_level ASC"
|
||||||
|
case 2:
|
||||||
|
return "up.user_level DESC"
|
||||||
|
case 3:
|
||||||
|
return "cud.smile ASC"
|
||||||
|
case 4:
|
||||||
|
return "cud.smile DESC"
|
||||||
|
case 5:
|
||||||
|
return "cud.cool ASC"
|
||||||
|
case 6:
|
||||||
|
return "cud.cool DESC"
|
||||||
|
case 7:
|
||||||
|
return "cud.cute ASC"
|
||||||
|
case 8:
|
||||||
|
return "cud.cute DESC"
|
||||||
|
case 9:
|
||||||
|
return "u.last_login_time ASC"
|
||||||
|
case 10:
|
||||||
|
return "u.last_login_time DESC"
|
||||||
|
case 11:
|
||||||
|
return "uf.insert_date ASC"
|
||||||
|
case 12:
|
||||||
|
return "uf.insert_date DESC"
|
||||||
|
default:
|
||||||
|
return "uf.insert_date DESC"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func displayFriendUserID(inviteCode string, fallback int) int {
|
||||||
|
if id, err := strconv.Atoi(inviteCode); err == nil && id > 0 {
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
return fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatElapsedTime(ts int64) string {
|
||||||
|
if ts <= 0 {
|
||||||
|
return "刚刚"
|
||||||
|
}
|
||||||
|
|
||||||
|
delta := time.Since(time.Unix(ts, 0))
|
||||||
|
if delta < time.Minute {
|
||||||
|
return "刚刚"
|
||||||
|
}
|
||||||
|
if delta < time.Hour {
|
||||||
|
return strconv.Itoa(int(delta.Minutes())) + "分钟前"
|
||||||
|
}
|
||||||
|
if delta < 24*time.Hour {
|
||||||
|
return strconv.Itoa(int(delta.Hours())) + "小时前"
|
||||||
|
}
|
||||||
|
return strconv.Itoa(int(delta.Hours())/24) + "天前"
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
router.AddHandler("main.php", "POST", "/friend/list", middleware.Common, list)
|
||||||
|
}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
package friend
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"honoka-chan/internal/middleware"
|
||||||
|
usermodel "honoka-chan/internal/model/user"
|
||||||
|
"honoka-chan/internal/router"
|
||||||
|
friendschema "honoka-chan/internal/schema/friend"
|
||||||
|
"honoka-chan/internal/session"
|
||||||
|
honokautils "honoka-chan/internal/utils"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func request(ctx *gin.Context) {
|
||||||
|
ss := session.Get(ctx)
|
||||||
|
defer ss.Finalize()
|
||||||
|
|
||||||
|
req := friendschema.UserIDReq{}
|
||||||
|
err := honokautils.ParseRequestData(ctx, &req)
|
||||||
|
if ss.CheckErr(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
targetUserID, err := resolveActualFriendUserID(ss, req.UserID)
|
||||||
|
if ss.CheckErr(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if targetUserID == ss.UserID {
|
||||||
|
ss.CheckErr(errors.New("cannot add self as friend"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
isFriend, err := areUsersFriends(ss, ss.UserID, targetUserID)
|
||||||
|
if ss.CheckErr(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if isFriend {
|
||||||
|
ss.Respond(friendschema.RequestResp{
|
||||||
|
ResponseData: friendschema.RequestData{
|
||||||
|
IsFriend: true,
|
||||||
|
},
|
||||||
|
ReleaseInfo: []any{},
|
||||||
|
StatusCode: 200,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
reversePending, err := ss.UserEng.Table(new(usermodel.UserFriend)).
|
||||||
|
Where("user_id = ?", targetUserID).
|
||||||
|
Where("friend_user_id = ?", ss.UserID).
|
||||||
|
Where("status = ?", usermodel.FriendStatusPending).
|
||||||
|
Exist()
|
||||||
|
if ss.CheckErr(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
selfAwaitingApproval, err := ss.UserEng.Table(new(usermodel.UserFriend)).
|
||||||
|
Where("user_id = ?", ss.UserID).
|
||||||
|
Where("friend_user_id = ?", targetUserID).
|
||||||
|
Where("status = ?", usermodel.FriendStatusAwaitingApproval).
|
||||||
|
Exist()
|
||||||
|
if ss.CheckErr(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if reversePending && selfAwaitingApproval {
|
||||||
|
err = usermodel.EnsureMutualFriendWithIsNew(ss.UserEng, ss.UserID, targetUserID, usermodel.FriendStatusApproved, true, true)
|
||||||
|
if ss.CheckErr(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ss.Respond(friendschema.RequestResp{
|
||||||
|
ResponseData: friendschema.RequestData{
|
||||||
|
IsFriend: true,
|
||||||
|
},
|
||||||
|
ReleaseInfo: []any{},
|
||||||
|
StatusCode: 200,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = usermodel.EnsureFriendLink(ss.UserEng, ss.UserID, targetUserID, usermodel.FriendStatusPending, false)
|
||||||
|
if ss.CheckErr(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = usermodel.EnsureFriendLink(ss.UserEng, targetUserID, ss.UserID, usermodel.FriendStatusAwaitingApproval, true)
|
||||||
|
if ss.CheckErr(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ss.Respond(friendschema.RequestResp{
|
||||||
|
ResponseData: friendschema.RequestData{
|
||||||
|
IsFriend: false,
|
||||||
|
},
|
||||||
|
ReleaseInfo: []any{},
|
||||||
|
StatusCode: 200,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
router.AddHandler("main.php", "POST", "/friend/request", middleware.Common, request)
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
package friend
|
||||||
|
|
||||||
|
import (
|
||||||
|
"honoka-chan/internal/middleware"
|
||||||
|
usermodel "honoka-chan/internal/model/user"
|
||||||
|
"honoka-chan/internal/router"
|
||||||
|
friendschema "honoka-chan/internal/schema/friend"
|
||||||
|
"honoka-chan/internal/session"
|
||||||
|
honokautils "honoka-chan/internal/utils"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func requestCancel(ctx *gin.Context) {
|
||||||
|
ss := session.Get(ctx)
|
||||||
|
defer ss.Finalize()
|
||||||
|
|
||||||
|
req := friendschema.UserIDReq{}
|
||||||
|
err := honokautils.ParseRequestData(ctx, &req)
|
||||||
|
if ss.CheckErr(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
targetUserID, err := resolveActualFriendUserID(ss, req.UserID)
|
||||||
|
if ss.CheckErr(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = ss.UserEng.Table(new(usermodel.UserFriend)).
|
||||||
|
Where("user_id = ?", ss.UserID).
|
||||||
|
Where("friend_user_id = ?", targetUserID).
|
||||||
|
Where("status = ?", usermodel.FriendStatusPending).
|
||||||
|
Delete()
|
||||||
|
if ss.CheckErr(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = ss.UserEng.Table(new(usermodel.UserFriend)).
|
||||||
|
Where("user_id = ?", targetUserID).
|
||||||
|
Where("friend_user_id = ?", ss.UserID).
|
||||||
|
Where("status = ?", usermodel.FriendStatusAwaitingApproval).
|
||||||
|
Delete()
|
||||||
|
if ss.CheckErr(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
isFriend, err := areUsersFriends(ss, ss.UserID, targetUserID)
|
||||||
|
if ss.CheckErr(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ss.Respond(friendschema.RequestCancelResp{
|
||||||
|
ResponseData: friendschema.RequestCancelData{
|
||||||
|
IsFriend: isFriend,
|
||||||
|
},
|
||||||
|
ReleaseInfo: []any{},
|
||||||
|
StatusCode: 200,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
router.AddHandler("main.php", "POST", "/friend/requestCancel", middleware.Common, requestCancel)
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package friend
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"honoka-chan/internal/middleware"
|
||||||
|
usermodel "honoka-chan/internal/model/user"
|
||||||
|
"honoka-chan/internal/router"
|
||||||
|
friendschema "honoka-chan/internal/schema/friend"
|
||||||
|
"honoka-chan/internal/session"
|
||||||
|
honokautils "honoka-chan/internal/utils"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func response(ctx *gin.Context) {
|
||||||
|
ss := session.Get(ctx)
|
||||||
|
defer ss.Finalize()
|
||||||
|
|
||||||
|
req := friendschema.ResponseReq{}
|
||||||
|
err := honokautils.ParseRequestData(ctx, &req)
|
||||||
|
if ss.CheckErr(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
targetUserID, err := resolveActualFriendUserID(ss, req.UserID)
|
||||||
|
if ss.CheckErr(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch req.Status {
|
||||||
|
case 0:
|
||||||
|
_, err = ss.UserEng.Table(new(usermodel.UserFriend)).
|
||||||
|
Where("user_id = ?", ss.UserID).
|
||||||
|
Where("friend_user_id = ?", targetUserID).
|
||||||
|
Where("status = ?", usermodel.FriendStatusAwaitingApproval).
|
||||||
|
Delete()
|
||||||
|
if ss.CheckErr(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = ss.UserEng.Table(new(usermodel.UserFriend)).
|
||||||
|
Where("user_id = ?", targetUserID).
|
||||||
|
Where("friend_user_id = ?", ss.UserID).
|
||||||
|
Where("status = ?", usermodel.FriendStatusPending).
|
||||||
|
Delete()
|
||||||
|
if ss.CheckErr(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case 2:
|
||||||
|
err = usermodel.EnsureMutualFriendWithIsNew(ss.UserEng, ss.UserID, targetUserID, usermodel.FriendStatusApproved, true, true)
|
||||||
|
if ss.CheckErr(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
ss.CheckErr(errors.New("invalid friend response status"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ss.Respond(friendschema.ResponseResp{
|
||||||
|
ResponseData: []any{},
|
||||||
|
ReleaseInfo: []any{},
|
||||||
|
StatusCode: 200,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
router.AddHandler("main.php", "POST", "/friend/response", middleware.Common, response)
|
||||||
|
}
|
||||||
@@ -0,0 +1,187 @@
|
|||||||
|
package friend
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"honoka-chan/internal/middleware"
|
||||||
|
unitmodel "honoka-chan/internal/model/unit"
|
||||||
|
usermodel "honoka-chan/internal/model/user"
|
||||||
|
"honoka-chan/internal/router"
|
||||||
|
friendschema "honoka-chan/internal/schema/friend"
|
||||||
|
"honoka-chan/internal/session"
|
||||||
|
honokautils "honoka-chan/internal/utils"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type friendSearchRow struct {
|
||||||
|
UserID int `xorm:"user_id"`
|
||||||
|
InviteCode string `xorm:"invite_code"`
|
||||||
|
UserName string `xorm:"user_name"`
|
||||||
|
UserLevel int `xorm:"user_level"`
|
||||||
|
UserDesc string `xorm:"user_desc"`
|
||||||
|
EnergyMax int `xorm:"energy_max"`
|
||||||
|
AwardID int `xorm:"award_id"`
|
||||||
|
LastLoginTime int64 `xorm:"last_login_time"`
|
||||||
|
CenterUnitOwningUserID int `xorm:"center_unit_owning_user_id"`
|
||||||
|
NaviUnitOwningUserID int `xorm:"navi_unit_owning_user_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func search(ctx *gin.Context) {
|
||||||
|
ss := session.Get(ctx)
|
||||||
|
defer ss.Finalize()
|
||||||
|
|
||||||
|
req := friendschema.SearchReq{}
|
||||||
|
err := honokautils.ParseRequestData(ctx, &req)
|
||||||
|
if ss.CheckErr(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
targetUserID, err := resolveUserIDByInviteCode(ss, req.InviteCode)
|
||||||
|
if ss.CheckErr(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
row := friendSearchRow{}
|
||||||
|
has, err := ss.UserEng.Table(new(usermodel.UserPref)).Alias("up").
|
||||||
|
Join("LEFT", "users u", "u.user_id = up.user_id").
|
||||||
|
Join("LEFT", "user_deck ud", "ud.user_id = up.user_id AND ud.main_flag = 1").
|
||||||
|
Join("LEFT", "user_deck_unit udu", "udu.user_deck_id = ud.id AND udu.position = 5").
|
||||||
|
Select(`
|
||||||
|
up.user_id,
|
||||||
|
up.invite_code,
|
||||||
|
up.user_name,
|
||||||
|
up.user_level,
|
||||||
|
up.user_desc,
|
||||||
|
up.energy_max,
|
||||||
|
up.award_id,
|
||||||
|
u.last_login_time,
|
||||||
|
COALESCE(udu.unit_owning_user_id, up.unit_owning_user_id) AS center_unit_owning_user_id,
|
||||||
|
up.unit_owning_user_id AS navi_unit_owning_user_id
|
||||||
|
`).
|
||||||
|
Where("up.user_id = ?", targetUserID).
|
||||||
|
Get(&row)
|
||||||
|
if ss.CheckErr(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !has {
|
||||||
|
ss.CheckErr(errors.New("user not found"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
unitCount, err := ss.UserEng.Table(new(usermodel.UserUnitData)).
|
||||||
|
Where("user_id = ?", targetUserID).
|
||||||
|
Count()
|
||||||
|
if ss.CheckErr(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
centerUnitInfo, err := buildCenterUnitInfo(ss, targetUserID, row.CenterUnitOwningUserID, row.AwardID)
|
||||||
|
if ss.CheckErr(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
costume, err := getSearchCostume(ss, targetUserID, row.NaviUnitOwningUserID, centerUnitInfo)
|
||||||
|
if ss.CheckErr(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ss.Respond(friendschema.SearchResp{
|
||||||
|
ResponseData: friendschema.SearchData{
|
||||||
|
UserInfo: friendschema.SearchUserInfo{
|
||||||
|
UserID: row.UserID,
|
||||||
|
Name: row.UserName,
|
||||||
|
Level: row.UserLevel,
|
||||||
|
CostMax: 100,
|
||||||
|
UnitMax: 5000,
|
||||||
|
EnergyMax: positiveIntOrDefault(row.EnergyMax, usermodel.DefaultUserEnergyMax),
|
||||||
|
FriendMax: 99,
|
||||||
|
UnitCnt: int(unitCount),
|
||||||
|
ElapsedTimeFromLogin: formatElapsedTime(row.LastLoginTime),
|
||||||
|
Comment: row.UserDesc,
|
||||||
|
},
|
||||||
|
CenterUnitInfo: toSearchCenterUnitInfo(centerUnitInfo, costume),
|
||||||
|
SettingAwardID: row.AwardID,
|
||||||
|
IsAlliance: false,
|
||||||
|
FriendStatus: 0,
|
||||||
|
ServerTimestamp: time.Now().Unix(),
|
||||||
|
},
|
||||||
|
ReleaseInfo: []any{},
|
||||||
|
StatusCode: 200,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
router.AddHandler("main.php", "POST", "/friend/search", middleware.Common, search)
|
||||||
|
}
|
||||||
|
|
||||||
|
func positiveIntOrDefault(value, fallback int) int {
|
||||||
|
if value > 0 {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
return fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
func toSearchCenterUnitInfo(info friendschema.CenterUnitInfo, costume friendschema.SearchCostume) friendschema.SearchCenterUnitInfo {
|
||||||
|
return friendschema.SearchCenterUnitInfo{
|
||||||
|
UnitOwningUserID: info.UnitOwningUserID,
|
||||||
|
UnitID: info.UnitID,
|
||||||
|
Exp: info.Exp,
|
||||||
|
NextExp: info.NextExp,
|
||||||
|
Level: info.Level,
|
||||||
|
LevelLimitID: info.LevelLimitID,
|
||||||
|
MaxLevel: info.MaxLevel,
|
||||||
|
Rank: info.Rank,
|
||||||
|
MaxRank: info.MaxRank,
|
||||||
|
Love: info.Love,
|
||||||
|
MaxLove: info.MaxLove,
|
||||||
|
UnitSkillLevel: info.UnitSkillLevel,
|
||||||
|
MaxHp: info.MaxHp,
|
||||||
|
FavoriteFlag: info.FavoriteFlag,
|
||||||
|
DisplayRank: info.DisplayRank,
|
||||||
|
UnitSkillExp: info.UnitSkillExp,
|
||||||
|
UnitRemovableSkillCapacity: info.UnitRemovableSkillCapacity,
|
||||||
|
Attribute: info.Attribute,
|
||||||
|
Smile: info.Smile,
|
||||||
|
Cute: info.Cute,
|
||||||
|
Cool: info.Cool,
|
||||||
|
IsLoveMax: info.IsLoveMax,
|
||||||
|
IsLevelMax: info.IsLevelMax,
|
||||||
|
IsRankMax: info.IsRankMax,
|
||||||
|
IsSigned: info.IsSigned,
|
||||||
|
IsSkillLevelMax: info.IsSkillLevelMax,
|
||||||
|
SettingAwardID: info.SettingAwardID,
|
||||||
|
RemovableSkillIds: info.RemovableSkillIds,
|
||||||
|
AccessoryInfo: info.AccessoryInfo,
|
||||||
|
Costume: costume,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSearchCostume(ss *session.Session, targetUserID, unitOwningUserID int, fallback friendschema.CenterUnitInfo) (friendschema.SearchCostume, error) {
|
||||||
|
costume := friendschema.SearchCostume{
|
||||||
|
UnitID: fallback.UnitID,
|
||||||
|
IsRankMax: fallback.IsRankMax,
|
||||||
|
IsSigned: fallback.IsSigned,
|
||||||
|
}
|
||||||
|
if unitOwningUserID <= 0 {
|
||||||
|
return costume, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
unitData := unitmodel.UnitDataMap{}
|
||||||
|
has, err := ss.GetBasicUnitInfo().
|
||||||
|
Where("a.user_id = ?", targetUserID).
|
||||||
|
Where("a.unit_owning_user_id = ?", unitOwningUserID).
|
||||||
|
Get(&unitData)
|
||||||
|
if err != nil {
|
||||||
|
return costume, err
|
||||||
|
}
|
||||||
|
if !has {
|
||||||
|
return costume, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return friendschema.SearchCostume{
|
||||||
|
UnitID: unitData.UnitID,
|
||||||
|
IsRankMax: unitData.IsRankMax,
|
||||||
|
IsSigned: unitData.IsSigned,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
@@ -362,6 +362,16 @@ func addUser(dbSession *xorm.Session, phone, password string, isDefault bool) (g
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !isDefault {
|
||||||
|
err = usermodel.EnsureDefaultFriendship(dbSession, userID)
|
||||||
|
if err != nil {
|
||||||
|
if localSession {
|
||||||
|
dbSession.Rollback()
|
||||||
|
}
|
||||||
|
return loginData, loginCode, loginMsg, created, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
loginData.Autokey = autoKey
|
loginData.Autokey = autoKey
|
||||||
loginData.HasRealInfo = 1
|
loginData.HasRealInfo = 1
|
||||||
loginData.Message = "ok"
|
loginData.Message = "ok"
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
_ "honoka-chan/internal/handler/background"
|
_ "honoka-chan/internal/handler/background"
|
||||||
_ "honoka-chan/internal/handler/download"
|
_ "honoka-chan/internal/handler/download"
|
||||||
_ "honoka-chan/internal/handler/event"
|
_ "honoka-chan/internal/handler/event"
|
||||||
|
_ "honoka-chan/internal/handler/friend"
|
||||||
_ "honoka-chan/internal/handler/gdpr"
|
_ "honoka-chan/internal/handler/gdpr"
|
||||||
_ "honoka-chan/internal/handler/ghome"
|
_ "honoka-chan/internal/handler/ghome"
|
||||||
_ "honoka-chan/internal/handler/lbonus"
|
_ "honoka-chan/internal/handler/lbonus"
|
||||||
|
|||||||
@@ -0,0 +1,107 @@
|
|||||||
|
package usermodel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"xorm.io/xorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
DefaultSystemPhone = "1"
|
||||||
|
|
||||||
|
FriendStatusApproved = iota
|
||||||
|
FriendStatusPending
|
||||||
|
FriendStatusAwaitingApproval
|
||||||
|
|
||||||
|
FriendListPageSize = 40
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserFriend struct {
|
||||||
|
ID int `xorm:"id pk autoincr"`
|
||||||
|
UserID int `xorm:"user_id unique(friend_pair) index"`
|
||||||
|
FriendUserID int `xorm:"friend_user_id unique(friend_pair) index"`
|
||||||
|
Status int `xorm:"status index"`
|
||||||
|
IsNew bool `xorm:"is_new"`
|
||||||
|
InsertDate int64 `xorm:"insert_date index"`
|
||||||
|
UpdateDate int64 `xorm:"update_date"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UserFriend) TableName() string {
|
||||||
|
return "user_friend"
|
||||||
|
}
|
||||||
|
|
||||||
|
func EnsureFriendLink(dbSession *xorm.Session, userID, friendUserID, status int, isNew bool) error {
|
||||||
|
if dbSession == nil || userID <= 0 || friendUserID <= 0 || userID == friendUserID {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now().Unix()
|
||||||
|
link := UserFriend{}
|
||||||
|
has, err := dbSession.Table(new(UserFriend)).
|
||||||
|
Where("user_id = ? AND friend_user_id = ?", userID, friendUserID).
|
||||||
|
Get(&link)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if has {
|
||||||
|
update := UserFriend{
|
||||||
|
Status: status,
|
||||||
|
IsNew: isNew,
|
||||||
|
UpdateDate: now,
|
||||||
|
}
|
||||||
|
if link.InsertDate <= 0 {
|
||||||
|
update.InsertDate = now
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = dbSession.Table(new(UserFriend)).
|
||||||
|
Where("user_id = ? AND friend_user_id = ?", userID, friendUserID).
|
||||||
|
Cols("status", "is_new", "update_date", "insert_date").
|
||||||
|
Update(&update)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = dbSession.Table(new(UserFriend)).Insert(&UserFriend{
|
||||||
|
UserID: userID,
|
||||||
|
FriendUserID: friendUserID,
|
||||||
|
Status: status,
|
||||||
|
IsNew: isNew,
|
||||||
|
InsertDate: now,
|
||||||
|
UpdateDate: now,
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func EnsureMutualFriend(dbSession *xorm.Session, userID, friendUserID, status int) error {
|
||||||
|
if err := EnsureFriendLink(dbSession, userID, friendUserID, status, false); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return EnsureFriendLink(dbSession, friendUserID, userID, status, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func EnsureMutualFriendWithIsNew(dbSession *xorm.Session, userID, friendUserID, status int, userIsNew, friendIsNew bool) error {
|
||||||
|
if err := EnsureFriendLink(dbSession, userID, friendUserID, status, userIsNew); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return EnsureFriendLink(dbSession, friendUserID, userID, status, friendIsNew)
|
||||||
|
}
|
||||||
|
|
||||||
|
func EnsureDefaultFriendship(dbSession *xorm.Session, userID int) error {
|
||||||
|
if dbSession == nil || userID <= 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultUser := Users{}
|
||||||
|
has, err := dbSession.Table(new(Users)).
|
||||||
|
Where("phone = ?", DefaultSystemPhone).
|
||||||
|
Cols("user_id").
|
||||||
|
Get(&defaultUser)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !has || defaultUser.UserID <= 0 || defaultUser.UserID == userID {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return EnsureMutualFriend(dbSession, userID, defaultUser.UserID, FriendStatusApproved)
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ package apischema
|
|||||||
|
|
||||||
type ApiReq struct {
|
type ApiReq struct {
|
||||||
Module string `json:"module"`
|
Module string `json:"module"`
|
||||||
|
UserID int `json:"user_id"`
|
||||||
Action string `json:"action"`
|
Action string `json:"action"`
|
||||||
Timestamp int64 `json:"timeStamp"`
|
Timestamp int64 `json:"timeStamp"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,40 +60,40 @@ type UserInfo struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type CenterUnitInfo struct {
|
type CenterUnitInfo struct {
|
||||||
UnitOwningUserID int `json:"unit_owning_user_id"`
|
UnitOwningUserID int `json:"unit_owning_user_id"`
|
||||||
UnitID int `json:"unit_id"`
|
UnitID int `json:"unit_id"`
|
||||||
Exp int `json:"exp"`
|
Exp int `json:"exp"`
|
||||||
NextExp int `json:"next_exp"`
|
NextExp int `json:"next_exp"`
|
||||||
Level int `json:"level"`
|
Level int `json:"level"`
|
||||||
LevelLimitID int `json:"level_limit_id"`
|
LevelLimitID int `json:"level_limit_id"`
|
||||||
MaxLevel int `json:"max_level"`
|
MaxLevel int `json:"max_level"`
|
||||||
Rank int `json:"rank"`
|
Rank int `json:"rank"`
|
||||||
MaxRank int `json:"max_rank"`
|
MaxRank int `json:"max_rank"`
|
||||||
Love int `json:"love"`
|
Love int `json:"love"`
|
||||||
MaxLove int `json:"max_love"`
|
MaxLove int `json:"max_love"`
|
||||||
UnitSkillLevel int `json:"unit_skill_level"`
|
UnitSkillLevel int `json:"unit_skill_level"`
|
||||||
MaxHp int `json:"max_hp"`
|
MaxHp int `json:"max_hp"`
|
||||||
FavoriteFlag bool `json:"favorite_flag"`
|
FavoriteFlag bool `json:"favorite_flag"`
|
||||||
DisplayRank int `json:"display_rank"`
|
DisplayRank int `json:"display_rank"`
|
||||||
UnitSkillExp int `json:"unit_skill_exp"`
|
UnitSkillExp int `json:"unit_skill_exp"`
|
||||||
UnitRemovableSkillCapacity int `json:"unit_removable_skill_capacity"`
|
UnitRemovableSkillCapacity int `json:"unit_removable_skill_capacity"`
|
||||||
Attribute int `json:"attribute"`
|
Attribute int `json:"attribute"`
|
||||||
Smile int `json:"smile"`
|
Smile int `json:"smile"`
|
||||||
Cute int `json:"cute"`
|
Cute int `json:"cute"`
|
||||||
Cool int `json:"cool"`
|
Cool int `json:"cool"`
|
||||||
IsLoveMax bool `json:"is_love_max"`
|
IsLoveMax bool `json:"is_love_max"`
|
||||||
IsLevelMax bool `json:"is_level_max"`
|
IsLevelMax bool `json:"is_level_max"`
|
||||||
IsRankMax bool `json:"is_rank_max"`
|
IsRankMax bool `json:"is_rank_max"`
|
||||||
IsSigned bool `json:"is_signed"`
|
IsSigned bool `json:"is_signed"`
|
||||||
IsSkillLevelMax bool `json:"is_skill_level_max"`
|
IsSkillLevelMax bool `json:"is_skill_level_max"`
|
||||||
SettingAwardID int `json:"setting_award_id"`
|
SettingAwardID int `json:"setting_award_id"`
|
||||||
RemovableSkillIds []int `json:"removable_skill_ids"`
|
RemovableSkillIds []int `json:"removable_skill_ids"`
|
||||||
AccessoryInfo AccessoryInfo `json:"accessory_info"`
|
AccessoryInfo *AccessoryInfo `json:"accessory_info,omitempty"`
|
||||||
Costume Costume `json:"costume"`
|
Costume Costume `json:"costume"`
|
||||||
TotalSmile int `json:"total_smile"`
|
TotalSmile int `json:"total_smile"`
|
||||||
TotalCute int `json:"total_cute"`
|
TotalCute int `json:"total_cute"`
|
||||||
TotalCool int `json:"total_cool"`
|
TotalCool int `json:"total_cool"`
|
||||||
TotalHp int `json:"total_hp"`
|
TotalHp int `json:"total_hp"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type NaviUnitInfo struct {
|
type NaviUnitInfo struct {
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package friendschema
|
||||||
|
|
||||||
|
type UserIDReq struct {
|
||||||
|
UserID int `json:"user_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ResponseReq struct {
|
||||||
|
UserID int `json:"user_id"`
|
||||||
|
Status int `json:"status"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package friendschema
|
||||||
|
|
||||||
|
type ExpelResp struct {
|
||||||
|
ResponseData []any `json:"response_data"`
|
||||||
|
ReleaseInfo []any `json:"release_info"`
|
||||||
|
StatusCode int `json:"status_code"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
package friendschema
|
||||||
|
|
||||||
|
type ListReq struct {
|
||||||
|
Module string `json:"module"`
|
||||||
|
Type int `json:"type"`
|
||||||
|
Action string `json:"action"`
|
||||||
|
TimeStamp int64 `json:"timeStamp"`
|
||||||
|
Mgd int `json:"mgd"`
|
||||||
|
Sort int `json:"sort"`
|
||||||
|
Page int `json:"page"`
|
||||||
|
CommandNum string `json:"commandNum"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserData struct {
|
||||||
|
UserID int `json:"user_id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Level int `json:"level"`
|
||||||
|
ElapsedTimeFromLogin string `json:"elapsed_time_from_login"`
|
||||||
|
ElapsedTimeFromApplied string `json:"elapsed_time_from_applied"`
|
||||||
|
Comment string `json:"comment"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AccessoryInfo struct {
|
||||||
|
AccessoryOwningUserID int `json:"accessory_owning_user_id"`
|
||||||
|
AccessoryID int `json:"accessory_id"`
|
||||||
|
Exp int `json:"exp"`
|
||||||
|
NextExp int `json:"next_exp"`
|
||||||
|
Level int `json:"level"`
|
||||||
|
MaxLevel int `json:"max_level"`
|
||||||
|
RankUpCount int `json:"rank_up_count"`
|
||||||
|
FavoriteFlag bool `json:"favorite_flag"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CenterUnitInfo struct {
|
||||||
|
UnitOwningUserID int64 `json:"unit_owning_user_id"`
|
||||||
|
UnitID int `json:"unit_id"`
|
||||||
|
Exp int `json:"exp"`
|
||||||
|
NextExp int `json:"next_exp"`
|
||||||
|
Level int `json:"level"`
|
||||||
|
LevelLimitID int `json:"level_limit_id"`
|
||||||
|
MaxLevel int `json:"max_level"`
|
||||||
|
Rank int `json:"rank"`
|
||||||
|
MaxRank int `json:"max_rank"`
|
||||||
|
Love int `json:"love"`
|
||||||
|
MaxLove int `json:"max_love"`
|
||||||
|
UnitSkillLevel int `json:"unit_skill_level"`
|
||||||
|
MaxHp int `json:"max_hp"`
|
||||||
|
FavoriteFlag bool `json:"favorite_flag"`
|
||||||
|
DisplayRank int `json:"display_rank"`
|
||||||
|
UnitSkillExp int `json:"unit_skill_exp"`
|
||||||
|
UnitRemovableSkillCapacity int `json:"unit_removable_skill_capacity"`
|
||||||
|
Attribute int `json:"attribute"`
|
||||||
|
Smile int `json:"smile"`
|
||||||
|
Cute int `json:"cute"`
|
||||||
|
Cool int `json:"cool"`
|
||||||
|
IsLoveMax bool `json:"is_love_max"`
|
||||||
|
IsLevelMax bool `json:"is_level_max"`
|
||||||
|
IsRankMax bool `json:"is_rank_max"`
|
||||||
|
IsSigned bool `json:"is_signed"`
|
||||||
|
IsSkillLevelMax bool `json:"is_skill_level_max"`
|
||||||
|
SettingAwardID int `json:"setting_award_id"`
|
||||||
|
RemovableSkillIds []int `json:"removable_skill_ids"`
|
||||||
|
AccessoryInfo AccessoryInfo `json:"accessory_info"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FriendList struct {
|
||||||
|
UserData UserData `json:"user_data"`
|
||||||
|
CenterUnitInfo CenterUnitInfo `json:"center_unit_info"`
|
||||||
|
SettingAwardID int `json:"setting_award_id"`
|
||||||
|
IsNew bool `json:"is_new"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListData struct {
|
||||||
|
ItemCount int `json:"item_count"`
|
||||||
|
FriendList []FriendList `json:"friend_list"`
|
||||||
|
NewFriendList []any `json:"new_friend_list"`
|
||||||
|
ServerTimestamp int64 `json:"server_timestamp"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListResp struct {
|
||||||
|
ResponseData ListData `json:"response_data"`
|
||||||
|
ReleaseInfo []any `json:"release_info"`
|
||||||
|
StatusCode int `json:"status_code"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package friendschema
|
||||||
|
|
||||||
|
type RequestData struct {
|
||||||
|
IsFriend bool `json:"is_friend"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RequestResp struct {
|
||||||
|
ResponseData RequestData `json:"response_data"`
|
||||||
|
ReleaseInfo []any `json:"release_info"`
|
||||||
|
StatusCode int `json:"status_code"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package friendschema
|
||||||
|
|
||||||
|
type RequestCancelData struct {
|
||||||
|
IsFriend bool `json:"is_friend"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RequestCancelResp struct {
|
||||||
|
ResponseData RequestCancelData `json:"response_data"`
|
||||||
|
ReleaseInfo []any `json:"release_info"`
|
||||||
|
StatusCode int `json:"status_code"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package friendschema
|
||||||
|
|
||||||
|
type ResponseResp struct {
|
||||||
|
ResponseData []any `json:"response_data"`
|
||||||
|
ReleaseInfo []any `json:"release_info"`
|
||||||
|
StatusCode int `json:"status_code"`
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
package friendschema
|
||||||
|
|
||||||
|
type SearchReq struct {
|
||||||
|
Module string `json:"module"`
|
||||||
|
Action string `json:"action"`
|
||||||
|
TimeStamp int64 `json:"timeStamp"`
|
||||||
|
Mgd int `json:"mgd"`
|
||||||
|
InviteCode string `json:"invite_code"`
|
||||||
|
CommandNum string `json:"commandNum"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SearchUserInfo struct {
|
||||||
|
UserID int `json:"user_id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Level int `json:"level"`
|
||||||
|
CostMax int `json:"cost_max"`
|
||||||
|
UnitMax int `json:"unit_max"`
|
||||||
|
EnergyMax int `json:"energy_max"`
|
||||||
|
FriendMax int `json:"friend_max"`
|
||||||
|
UnitCnt int `json:"unit_cnt"`
|
||||||
|
ElapsedTimeFromLogin string `json:"elapsed_time_from_login"`
|
||||||
|
Comment string `json:"comment"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SearchCostume struct {
|
||||||
|
UnitID int `json:"unit_id"`
|
||||||
|
IsRankMax bool `json:"is_rank_max"`
|
||||||
|
IsSigned bool `json:"is_signed"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SearchCenterUnitInfo struct {
|
||||||
|
UnitOwningUserID int64 `json:"unit_owning_user_id"`
|
||||||
|
UnitID int `json:"unit_id"`
|
||||||
|
Exp int `json:"exp"`
|
||||||
|
NextExp int `json:"next_exp"`
|
||||||
|
Level int `json:"level"`
|
||||||
|
LevelLimitID int `json:"level_limit_id"`
|
||||||
|
MaxLevel int `json:"max_level"`
|
||||||
|
Rank int `json:"rank"`
|
||||||
|
MaxRank int `json:"max_rank"`
|
||||||
|
Love int `json:"love"`
|
||||||
|
MaxLove int `json:"max_love"`
|
||||||
|
UnitSkillLevel int `json:"unit_skill_level"`
|
||||||
|
MaxHp int `json:"max_hp"`
|
||||||
|
FavoriteFlag bool `json:"favorite_flag"`
|
||||||
|
DisplayRank int `json:"display_rank"`
|
||||||
|
UnitSkillExp int `json:"unit_skill_exp"`
|
||||||
|
UnitRemovableSkillCapacity int `json:"unit_removable_skill_capacity"`
|
||||||
|
Attribute int `json:"attribute"`
|
||||||
|
Smile int `json:"smile"`
|
||||||
|
Cute int `json:"cute"`
|
||||||
|
Cool int `json:"cool"`
|
||||||
|
IsLoveMax bool `json:"is_love_max"`
|
||||||
|
IsLevelMax bool `json:"is_level_max"`
|
||||||
|
IsRankMax bool `json:"is_rank_max"`
|
||||||
|
IsSigned bool `json:"is_signed"`
|
||||||
|
IsSkillLevelMax bool `json:"is_skill_level_max"`
|
||||||
|
SettingAwardID int `json:"setting_award_id"`
|
||||||
|
RemovableSkillIds []int `json:"removable_skill_ids"`
|
||||||
|
AccessoryInfo AccessoryInfo `json:"accessory_info"`
|
||||||
|
Costume SearchCostume `json:"costume"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SearchData struct {
|
||||||
|
UserInfo SearchUserInfo `json:"user_info"`
|
||||||
|
CenterUnitInfo SearchCenterUnitInfo `json:"center_unit_info"`
|
||||||
|
SettingAwardID int `json:"setting_award_id"`
|
||||||
|
IsAlliance bool `json:"is_alliance"`
|
||||||
|
FriendStatus int `json:"friend_status"`
|
||||||
|
ServerTimestamp int64 `json:"server_timestamp"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SearchResp struct {
|
||||||
|
ResponseData SearchData `json:"response_data"`
|
||||||
|
ReleaseInfo []any `json:"release_info"`
|
||||||
|
StatusCode int `json:"status_code"`
|
||||||
|
}
|
||||||
@@ -27,6 +27,7 @@ func CreateTables() {
|
|||||||
db.UserEng.Sync2(new(usermodel.UserLiveGoal))
|
db.UserEng.Sync2(new(usermodel.UserLiveGoal))
|
||||||
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.UserPref))
|
db.UserEng.Sync2(new(usermodel.UserPref))
|
||||||
db.UserEng.Sync2(new(usermodel.Users))
|
db.UserEng.Sync2(new(usermodel.Users))
|
||||||
db.UserEng.Sync2(new(usermodel.UserUnit))
|
db.UserEng.Sync2(new(usermodel.UserUnit))
|
||||||
|
|||||||
@@ -2,16 +2,16 @@ package startup
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"honoka-chan/internal/handler/ghome/account"
|
"honoka-chan/internal/handler/ghome/account"
|
||||||
|
usermodel "honoka-chan/internal/model/user"
|
||||||
"log"
|
"log"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
defaultPhone = "1"
|
|
||||||
defaultPassword = "klsbgames"
|
defaultPassword = "klsbgames"
|
||||||
)
|
)
|
||||||
|
|
||||||
func CreateDefaultUser() {
|
func CreateDefaultUser() {
|
||||||
_, code, msg, created, err := account.AddUser(defaultPhone, defaultPassword, true)
|
_, code, msg, created, err := account.AddUser(usermodel.DefaultSystemPhone, defaultPassword, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalln("默认用户创建失败:", err.Error())
|
log.Fatalln("默认用户创建失败:", err.Error())
|
||||||
}
|
}
|
||||||
@@ -19,7 +19,7 @@ func CreateDefaultUser() {
|
|||||||
log.Fatalf("默认用户创建失败: code=%d msg=%s", code, msg)
|
log.Fatalf("默认用户创建失败: code=%d msg=%s", code, msg)
|
||||||
}
|
}
|
||||||
if created {
|
if created {
|
||||||
log.Printf("默认用户创建成功, 账号: %s, 密码: %s\n", defaultPhone, defaultPassword)
|
log.Printf("默认用户创建成功, 账号: %s, 密码: %s\n", usermodel.DefaultSystemPhone, defaultPassword)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package startup
|
||||||
|
|
||||||
|
import (
|
||||||
|
usermodel "honoka-chan/internal/model/user"
|
||||||
|
"honoka-chan/pkg/db"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func EnsureDefaultFriends() {
|
||||||
|
session := db.UserEng.NewSession()
|
||||||
|
defer session.Close()
|
||||||
|
|
||||||
|
if err := session.Begin(); err != nil {
|
||||||
|
log.Fatalln("初始化默认好友失败:", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
userIDs := []int{}
|
||||||
|
err := session.Table(new(usermodel.Users)).
|
||||||
|
Cols("user_id").
|
||||||
|
Find(&userIDs)
|
||||||
|
if err != nil {
|
||||||
|
session.Rollback()
|
||||||
|
log.Fatalln("初始化默认好友失败:", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, userID := range userIDs {
|
||||||
|
err = usermodel.EnsureDefaultFriendship(session, userID)
|
||||||
|
if err != nil {
|
||||||
|
session.Rollback()
|
||||||
|
log.Fatalln("初始化默认好友失败:", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := session.Commit(); err != nil {
|
||||||
|
session.Rollback()
|
||||||
|
log.Fatalln("初始化默认好友失败:", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,4 +4,5 @@ func StartUp() {
|
|||||||
CreateTables()
|
CreateTables()
|
||||||
LoadUnitData()
|
LoadUnitData()
|
||||||
CreateDefaultUser()
|
CreateDefaultUser()
|
||||||
|
EnsureDefaultFriends()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user