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:
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user