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":
|
||||
result, err = livese.LiveSeApi(v.Action)
|
||||
case "login":
|
||||
result, err = login.LoginApi(v.Action)
|
||||
result, err = login.LoginApi(ctx, v.Action)
|
||||
case "marathon":
|
||||
result, err = marathon.MarathonApi(v.Action)
|
||||
case "multiunit":
|
||||
@@ -90,7 +90,7 @@ func api(ctx *gin.Context) {
|
||||
case "payment":
|
||||
result, err = payment.PaymentApi(v.Action)
|
||||
case "profile":
|
||||
result, err = profile.ProfileApi(ctx, v.Action)
|
||||
result, err = profile.ProfileApi(ctx, v.Action, v.UserID)
|
||||
case "scenario":
|
||||
result, err = scenario.ScenarioApi(ctx, v.Action)
|
||||
case "stamp":
|
||||
|
||||
@@ -2,12 +2,14 @@ package login
|
||||
|
||||
import (
|
||||
"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 {
|
||||
case "topInfo":
|
||||
res, err = loginTopInfo()
|
||||
res, err = loginTopInfo(ctx)
|
||||
case "topInfoOnce":
|
||||
res, err = loginTopInfoOnce()
|
||||
default:
|
||||
|
||||
@@ -1,25 +1,52 @@
|
||||
package login
|
||||
|
||||
import (
|
||||
usermodel "honoka-chan/internal/model/user"
|
||||
loginapischema "honoka-chan/internal/schema/api/login"
|
||||
"honoka-chan/internal/session"
|
||||
"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{
|
||||
Result: loginapischema.TopInfoData{
|
||||
FriendActionCnt: 0,
|
||||
FriendGreetCnt: 0,
|
||||
FriendVarietyCnt: 0,
|
||||
FriendNewCnt: 0,
|
||||
FriendNewCnt: friendsRequestCnt + friendsApprovalWaitCnt,
|
||||
PresentCnt: 0,
|
||||
SecretBoxBadgeFlag: false,
|
||||
ServerDatetime: time.Now().Format("2006-01-02 15:04:05"),
|
||||
ServerTimestamp: time.Now().Unix(),
|
||||
NoticeFriendDatetime: time.Now().Format("2006-01-02 15:04:05"),
|
||||
ServerDatetime: now.Format("2006-01-02 15:04:05"),
|
||||
ServerTimestamp: now.Unix(),
|
||||
NoticeFriendDatetime: now.Format("2006-01-02 15:04:05"),
|
||||
NoticeMailDatetime: "2000-01-01 12:00:00",
|
||||
FriendsApprovalWaitCnt: 0,
|
||||
FriendsRequestCnt: 0,
|
||||
FriendsApprovalWaitCnt: friendsApprovalWaitCnt,
|
||||
FriendsRequestCnt: friendsRequestCnt,
|
||||
IsTodayBirthday: false,
|
||||
LicenseInfo: loginapischema.LicenseInfo{
|
||||
LicenseList: []any{},
|
||||
@@ -37,7 +64,7 @@ func loginTopInfo() (res any, err error) {
|
||||
},
|
||||
Status: 200,
|
||||
CommandNum: false,
|
||||
TimeStamp: time.Now().Unix(),
|
||||
TimeStamp: now.Unix(),
|
||||
}
|
||||
|
||||
return res, err
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package profile
|
||||
|
||||
import (
|
||||
"errors"
|
||||
unitmodel "honoka-chan/internal/model/unit"
|
||||
usermodel "honoka-chan/internal/model/user"
|
||||
profileapischema "honoka-chan/internal/schema/api/profile"
|
||||
@@ -11,73 +12,182 @@ import (
|
||||
"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)
|
||||
pref := ss.UserPref
|
||||
displayUserID := pref.UserID
|
||||
if inviteUserID, convErr := strconv.Atoi(pref.InviteCode); convErr == nil && inviteUserID > 0 {
|
||||
displayUserID = inviteUserID
|
||||
|
||||
targetPref, err := getTargetUserPref(ss, targetUserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
targetUserID = targetPref.UserID
|
||||
|
||||
unitCount, err := ss.UserEng.Table(new(usermodel.UserUnitData)).
|
||||
Where("user_id = ?", ss.UserID).Count()
|
||||
Where("user_id = ?", targetUserID).
|
||||
Count()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
unitData := unitmodel.UnitDataMap{}
|
||||
_, err = ss.GetBasicUnitInfo().
|
||||
Where("a.unit_owning_user_id = ?", pref.UnitOwningUserID).Get(&unitData)
|
||||
lastLoginTime, err := getTargetLastLoginTime(ss, targetUserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var accessoryOwningId, accessoryId, exp int
|
||||
_, 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)
|
||||
centerUnitOwningUserID, err := getMainDeckCenterUnitOwningUserID(ss, targetUserID, targetPref.UnitOwningUserID)
|
||||
if err != nil {
|
||||
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{}
|
||||
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)
|
||||
centerUnitInfo, centerRaw, err := getProfileCenterUnitInfo(ss, targetUserID, centerUnitOwningUserID, targetPref.AwardID)
|
||||
if err != nil {
|
||||
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{
|
||||
Result: profileapischema.InfoData{
|
||||
UserInfo: profileapischema.UserInfo{
|
||||
UserID: displayUserID,
|
||||
Name: pref.UserName,
|
||||
Level: pref.UserLevel,
|
||||
UserID: targetPref.UserID,
|
||||
Name: targetPref.UserName,
|
||||
Level: targetPref.UserLevel,
|
||||
CostMax: 100,
|
||||
UnitMax: 5000,
|
||||
EnergyMax: pref.EffectiveEnergyMax(),
|
||||
EnergyMax: targetPref.EffectiveEnergyMax(),
|
||||
FriendMax: 99,
|
||||
UnitCnt: int(unitCount),
|
||||
InviteCode: pref.InviteCode,
|
||||
ElapsedTimeFromLogin: "14\u5c0f\u65f6\u524d",
|
||||
Introduction: pref.UserDesc,
|
||||
InviteCode: targetPref.InviteCode,
|
||||
ElapsedTimeFromLogin: formatProfileElapsedTime(lastLoginTime),
|
||||
Introduction: targetPref.UserDesc,
|
||||
},
|
||||
// TODO: 区分队伍中心卡片和看板卡片
|
||||
CenterUnitInfo: profileapischema.CenterUnitInfo{
|
||||
CenterUnitInfo: centerUnitInfo,
|
||||
NaviUnitInfo: naviUnitInfo,
|
||||
IsAlliance: false,
|
||||
FriendStatus: 0,
|
||||
SettingAwardID: targetPref.AwardID,
|
||||
SettingBackgroundID: targetPref.BackgroundID,
|
||||
},
|
||||
Status: 200,
|
||||
CommandNum: false,
|
||||
TimeStamp: time.Now().Unix(),
|
||||
}
|
||||
|
||||
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,
|
||||
@@ -104,16 +214,33 @@ func profileInfo(ctx *gin.Context) (res any, err error) {
|
||||
IsRankMax: unitData.IsRankMax,
|
||||
IsSigned: unitData.IsSigned,
|
||||
IsSkillLevelMax: unitData.IsSkillLevelMax,
|
||||
SettingAwardID: pref.AwardID,
|
||||
RemovableSkillIds: removeSkillIds,
|
||||
SettingAwardID: awardID,
|
||||
RemovableSkillIds: removeSkillIDs,
|
||||
AccessoryInfo: accessoryInfo,
|
||||
Costume: profileapischema.Costume{},
|
||||
TotalSmile: unitData.Smile, // TODO: 加成计算
|
||||
TotalCute: unitData.Cute, // 同上
|
||||
TotalCool: unitData.Cool, // 同上
|
||||
TotalSmile: unitData.Smile,
|
||||
TotalCute: unitData.Cute,
|
||||
TotalCool: unitData.Cool,
|
||||
TotalHp: unitData.MaxHp,
|
||||
},
|
||||
NaviUnitInfo: profileapischema.NaviUnitInfo{
|
||||
}
|
||||
|
||||
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,
|
||||
@@ -131,28 +258,122 @@ func profileInfo(ctx *gin.Context) (res any, err error) {
|
||||
UnitRemovableSkillCapacity: unitData.UnitRemovableSkillCapacity,
|
||||
FavoriteFlag: unitData.FavoriteFlag,
|
||||
DisplayRank: unitData.DisplayRank,
|
||||
IsRankMax: unitData.IsRankMax,
|
||||
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, // 同上
|
||||
TotalSmile: unitData.Smile,
|
||||
TotalCute: unitData.Cute,
|
||||
TotalCool: unitData.Cool,
|
||||
TotalHp: unitData.MaxHp,
|
||||
RemovableSkillIds: removeSkillIds,
|
||||
},
|
||||
IsAlliance: false,
|
||||
FriendStatus: 0,
|
||||
SettingAwardID: pref.AwardID,
|
||||
SettingBackgroundID: pref.BackgroundID,
|
||||
},
|
||||
Status: 200,
|
||||
CommandNum: false,
|
||||
TimeStamp: time.Now().Unix(),
|
||||
RemovableSkillIds: removeSkillIDs,
|
||||
}
|
||||
|
||||
return res, err
|
||||
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"
|
||||
)
|
||||
|
||||
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 {
|
||||
case "cardRanking":
|
||||
res, err = cardRanking()
|
||||
case "liveCnt":
|
||||
res, err = liveCnt()
|
||||
case "profileInfo":
|
||||
res, err = profileInfo(ctx)
|
||||
res, err = profileInfo(ctx, targetUserID)
|
||||
default:
|
||||
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.HasRealInfo = 1
|
||||
loginData.Message = "ok"
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
_ "honoka-chan/internal/handler/background"
|
||||
_ "honoka-chan/internal/handler/download"
|
||||
_ "honoka-chan/internal/handler/event"
|
||||
_ "honoka-chan/internal/handler/friend"
|
||||
_ "honoka-chan/internal/handler/gdpr"
|
||||
_ "honoka-chan/internal/handler/ghome"
|
||||
_ "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 {
|
||||
Module string `json:"module"`
|
||||
UserID int `json:"user_id"`
|
||||
Action string `json:"action"`
|
||||
Timestamp int64 `json:"timeStamp"`
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ type CenterUnitInfo struct {
|
||||
IsSkillLevelMax bool `json:"is_skill_level_max"`
|
||||
SettingAwardID int `json:"setting_award_id"`
|
||||
RemovableSkillIds []int `json:"removable_skill_ids"`
|
||||
AccessoryInfo AccessoryInfo `json:"accessory_info"`
|
||||
AccessoryInfo *AccessoryInfo `json:"accessory_info,omitempty"`
|
||||
Costume Costume `json:"costume"`
|
||||
TotalSmile int `json:"total_smile"`
|
||||
TotalCute int `json:"total_cute"`
|
||||
|
||||
@@ -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.UserLiveInProgress))
|
||||
db.UserEng.Sync2(new(usermodel.UserLiveRecord))
|
||||
db.UserEng.Sync2(new(usermodel.UserFriend))
|
||||
db.UserEng.Sync2(new(usermodel.UserPref))
|
||||
db.UserEng.Sync2(new(usermodel.Users))
|
||||
db.UserEng.Sync2(new(usermodel.UserUnit))
|
||||
|
||||
@@ -2,16 +2,16 @@ package startup
|
||||
|
||||
import (
|
||||
"honoka-chan/internal/handler/ghome/account"
|
||||
usermodel "honoka-chan/internal/model/user"
|
||||
"log"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultPhone = "1"
|
||||
defaultPassword = "klsbgames"
|
||||
)
|
||||
|
||||
func CreateDefaultUser() {
|
||||
_, code, msg, created, err := account.AddUser(defaultPhone, defaultPassword, true)
|
||||
_, code, msg, created, err := account.AddUser(usermodel.DefaultSystemPhone, defaultPassword, true)
|
||||
if err != nil {
|
||||
log.Fatalln("默认用户创建失败:", err.Error())
|
||||
}
|
||||
@@ -19,7 +19,7 @@ func CreateDefaultUser() {
|
||||
log.Fatalf("默认用户创建失败: code=%d msg=%s", code, msg)
|
||||
}
|
||||
if created {
|
||||
log.Printf("默认用户创建成功, 账号: %s, 密码: %s\n", defaultPhone, defaultPassword)
|
||||
log.Printf("默认用户创建成功, 账号: %s, 密码: %s\n", usermodel.DefaultSystemPhone, defaultPassword)
|
||||
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()
|
||||
LoadUnitData()
|
||||
CreateDefaultUser()
|
||||
EnsureDefaultFriends()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user