From bac2335f14639a49b47961927eb4de7e8384e6e0 Mon Sep 17 00:00:00 2001 From: Sean Du Date: Fri, 5 Jun 2026 22:38:27 +0800 Subject: [PATCH] Reintroduce friend support for live - Return approved friends in live/partyList - Use the selected support center unit during live play - Fall back to self support when no friend is available - Align friend_status with client display expectations Signed-off-by: Sean Du --- internal/handler/live/partylist.go | 65 +++-------- internal/handler/live/play.go | 16 +-- internal/handler/live/support.go | 173 +++++++++++++++++++++++++++++ 3 files changed, 193 insertions(+), 61 deletions(-) create mode 100644 internal/handler/live/support.go diff --git a/internal/handler/live/partylist.go b/internal/handler/live/partylist.go index 89bbadc..831518e 100644 --- a/internal/handler/live/partylist.go +++ b/internal/handler/live/partylist.go @@ -1,9 +1,7 @@ package live import ( - "errors" "honoka-chan/internal/middleware" - unitmodel "honoka-chan/internal/model/unit" usermodel "honoka-chan/internal/model/user" "honoka-chan/internal/router" liveschema "honoka-chan/internal/schema/live" @@ -23,63 +21,32 @@ func partyList(ctx *gin.Context) { return } - // TODO: 好友功能实装前先使用自己的卡组助战 - var unitList []usermodel.UserDeckUnit - err = ss.UserEng.Table("user_deck_unit").Where("user_id = ? AND position = 5", ss.UserID).Find(&unitList) + supportRows, err := listLiveSupportRows(ss) if ss.CheckErr(err) { return } - var partyList []liveschema.PartyList - for _, u := range unitList { - var unitInfo unitmodel.UnitDataMap - has, err := ss.GetBasicUnitInfo(). - Where("unit_owning_user_id = ?", u.UnitOwningUserID).Get(&unitInfo) + partyList := make([]liveschema.PartyList, 0, len(supportRows)) + for _, row := range supportRows { + party, err := buildLiveSupportParty(ss, row) if ss.CheckErr(err) { return } + partyList = append(partyList, party) + } - if !has { - ss.Abort(errors.New("卡片不存在!")) + if len(partyList) == 0 { + selfParty := liveschema.PartyList{ + UserInfo: ss.GetUserInfo(), + SettingAwardID: pref.AwardID, + FriendStatus: 0, + } + selfParty.AvailableSocialPoint = 10 + selfParty.CenterUnitInfo, err = mustFindCenterUnitInfo(ss, ss.UserID, pref.UnitOwningUserID) + if ss.CheckErr(err) { return } - - partyList = append(partyList, liveschema.PartyList{ - UserInfo: ss.GetUserInfo(), - CenterUnitInfo: liveschema.CenterUnitInfo{ - UnitOwningUserID: u.UnitOwningUserID, - UnitID: u.UnitID, - Exp: unitInfo.Exp, - NextExp: 0, - Level: u.Level, - LevelLimitID: u.LevelLimitID, - MaxLevel: unitInfo.MaxLevel, - Rank: unitInfo.Rank, - MaxRank: unitInfo.MaxRank, - Love: u.Love, - MaxLove: u.MaxLove, - UnitSkillLevel: u.UnitSkillLevel, - MaxHp: unitInfo.MaxHp, - FavoriteFlag: unitInfo.FavoriteFlag, - DisplayRank: u.DisplayRank, - UnitSkillExp: unitInfo.UnitSkillExp, - UnitRemovableSkillCapacity: unitInfo.UnitRemovableSkillCapacity, - Attribute: unitInfo.Attribute, - Smile: unitInfo.Smile, - Cute: unitInfo.Cute, - Cool: unitInfo.Cool, - IsLoveMax: u.IsLoveMax, - IsLevelMax: u.IsLevelMax, - IsRankMax: u.IsRankMax, - IsSigned: u.IsSigned, - IsSkillLevelMax: unitInfo.IsSkillLevelMax, - SettingAwardID: pref.AwardID, - RemovableSkillIds: []int{}, - }, - SettingAwardID: pref.AwardID, - AvailableSocialPoint: 10, - FriendStatus: 1, - }) + partyList = append(partyList, selfParty) } ss.Respond(liveschema.PartyListResp{ diff --git a/internal/handler/live/play.go b/internal/handler/live/play.go index e549ff0..de9cc8d 100644 --- a/internal/handler/live/play.go +++ b/internal/handler/live/play.go @@ -312,18 +312,10 @@ func play(ctx *gin.Context) { } // 好友主唱技能加成 - // TODO 好友支援存入数据库 - // var tomoUnitId int64 - // partyList := gjson.Parse(utils.ReadAllText("assets/serverdata/partylist.json")).Get("response_data.party_list") - // partyList.ForEach(func(key, value gjson.Result) bool { - // if value.Get("user_info.user_id").Int() == playReq.PartyUserID { - // tomoUnitId = value.Get("center_unit_info.unit_id").Int() - // return false - // } - // return true - // }) - // TODO: 好友功能实装前先使用自己的卡组助战 - tomoUnitId := myCenterUnitId + tomoUnitId, err := getSupportCenterUnitID(ss, int(playReq.PartyUserID), myCenterUnitId) + if ss.CheckErr(err) { + return + } // fmt.Println("好友UnitID:", tomoUnitId) // 好友主唱技能加成:主C技能(这里不使用新C技能,即以某属性的百分比提升另一属性) diff --git a/internal/handler/live/support.go b/internal/handler/live/support.go new file mode 100644 index 0000000..5710b78 --- /dev/null +++ b/internal/handler/live/support.go @@ -0,0 +1,173 @@ +package live + +import ( + "fmt" + unitmodel "honoka-chan/internal/model/unit" + usermodel "honoka-chan/internal/model/user" + liveschema "honoka-chan/internal/schema/live" + userschema "honoka-chan/internal/schema/user" + "honoka-chan/internal/session" +) + +type liveSupportRow struct { + FriendUserID int `xorm:"friend_user_id"` + UserName string `xorm:"user_name"` + UserLevel int `xorm:"user_level"` + InviteCode string `xorm:"invite_code"` + AwardID int `xorm:"award_id"` + CenterUnitOwningUserID int `xorm:"center_unit_owning_user_id"` +} + +func listLiveSupportRows(ss *session.Session) ([]liveSupportRow, error) { + rows := []liveSupportRow{} + err := ss.UserEng.Table(new(usermodel.UserFriend)).Alias("uf"). + Join("LEFT", "user_pref up", "up.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"). + Where("uf.user_id = ?", ss.UserID). + Where("uf.status = ?", usermodel.FriendStatusApproved). + Select(` + uf.friend_user_id, + up.user_name, + up.user_level, + up.invite_code, + up.award_id, + COALESCE(udu.unit_owning_user_id, up.unit_owning_user_id) AS center_unit_owning_user_id + `). + OrderBy("uf.update_date DESC, uf.id DESC"). + Find(&rows) + return rows, err +} + +func buildLiveSupportParty(ss *session.Session, row liveSupportRow) (liveschema.PartyList, error) { + centerUnitInfo, err := buildLiveCenterUnitInfo(ss, row.FriendUserID, row.CenterUnitOwningUserID, row.AwardID) + if err != nil { + return liveschema.PartyList{}, err + } + + userInfo := buildLiveSupportUserInfo(ss, row) + return liveschema.PartyList{ + UserInfo: userInfo, + CenterUnitInfo: centerUnitInfo, + SettingAwardID: row.AwardID, + AvailableSocialPoint: 10, + FriendStatus: 0, + }, nil +} + +func buildLiveSupportUserInfo(ss *session.Session, row liveSupportRow) userschema.UserInfo { + userInfo := ss.GetUserInfo() + userInfo.UserID = row.FriendUserID + userInfo.Name = row.UserName + userInfo.Level = row.UserLevel + userInfo.InviteCode = row.InviteCode + return userInfo +} + +func buildLiveCenterUnitInfo(ss *session.Session, targetUserID, unitOwningUserID, awardID int) (liveschema.CenterUnitInfo, error) { + info := liveschema.CenterUnitInfo{ + SettingAwardID: awardID, + RemovableSkillIds: []int{}, + } + if unitOwningUserID <= 0 { + return info, 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 info, err + } + if !has { + return info, nil + } + + removableSkillIDs := []int{} + err = ss.UserEng.Table(new(usermodel.UserUnitSkillEquip)). + Where("user_id = ? AND unit_owning_user_id = ?", targetUserID, unitOwningUserID). + Cols("unit_removable_skill_id"). + Find(&removableSkillIDs) + if err != nil { + return info, err + } + + return liveschema.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: removableSkillIDs, + }, nil +} + +func getSupportCenterUnitID(ss *session.Session, partyUserID int, fallbackUnitID int) (int, error) { + if partyUserID <= 0 || partyUserID == ss.UserID { + return fallbackUnitID, nil + } + + exists, err := ss.UserEng.Table(new(usermodel.UserFriend)). + Where("user_id = ?", ss.UserID). + Where("friend_user_id = ?", partyUserID). + Where("status = ?", usermodel.FriendStatusApproved). + Exist() + if err != nil { + return 0, err + } + if !exists { + return fallbackUnitID, nil + } + + var centerUnitID 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 = ?", partyUserID). + Where("ud.main_flag = ?", 1). + Where("udu.position = ?", 5). + Cols("udu.unit_id"). + Get(¢erUnitID) + if err != nil { + return 0, err + } + if !has || centerUnitID <= 0 { + return fallbackUnitID, nil + } + + return centerUnitID, nil +} + +func mustFindCenterUnitInfo(ss *session.Session, userID, unitOwningUserID int) (liveschema.CenterUnitInfo, error) { + info, err := buildLiveCenterUnitInfo(ss, userID, unitOwningUserID, 0) + if err != nil { + return liveschema.CenterUnitInfo{}, err + } + if info.UnitOwningUserID <= 0 || info.UnitID <= 0 { + return liveschema.CenterUnitInfo{}, fmt.Errorf("support center unit not found for user %d", userID) + } + return info, nil +}