Files
honoka-chan/internal/handler/friend/list.go
T
2026-07-11 16:18:17 +08:00

313 lines
8.9 KiB
Go

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"
"net/http"
"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: totalCount,
FriendList: friendList,
NewFriendList: []any{},
ServerTimestamp: time.Now().Unix(),
},
ReleaseInfo: []any{},
StatusCode: http.StatusOK,
})
}
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 {
hasAccessory, accessoryData := ss.GetAccessoryByAccessoryOwningUserID(accessoryWear.AccessoryOwningUserID)
if hasAccessory {
accessoryInfo = friendschema.AccessoryInfo{
AccessoryOwningUserID: accessoryWear.AccessoryOwningUserID,
AccessoryID: accessoryData.AccessoryID,
Exp: accessoryData.Exp,
NextExp: 0,
Level: accessoryData.MaxLevel,
MaxLevel: accessoryData.MaxLevel,
RankUpCount: accessoryData.MaxLevel - accessoryData.DefaultMaxLevel,
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)
}