From 3afb697266adef6a5b5931c603f53a53ed9b4e25 Mon Sep 17 00:00:00 2001 From: hujiebin Date: Fri, 16 Jun 2023 15:43:26 +0800 Subject: [PATCH] =?UTF-8?q?Revert=20"db=E5=8A=A0=E4=B8=80=E4=B8=AAtype"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 6278131693201bb62468ca1d80a97c931287888d. --- _const/enum/cp_e/achievement.go | 11 + _const/enum/cp_e/anniversary.go | 9 + _const/enum/cp_e/cp_relation.go | 27 ++ _const/enum/cp_e/level.go | 96 +++++ _const/enum/gift_e/enum.go | 60 +++ _const/enum/res_e/enum.go | 43 +-- corn/cp_corn/cp.go | 93 +++++ cron/cp_cron/anniversary.go | 65 ++++ cron/cp_cron/clear.go | 24 ++ cron/cron.go | 19 + cron/gift_cron/remark.go | 47 +++ cron/gift_cron/send_gift.go | 31 ++ cv/cp_cv/anniversary.go | 24 ++ cv/cp_cv/cp.go | 20 + cv/cp_cv/cp_relation.go | 9 + cv/cp_cv/rank.go | 31 ++ cv/cp_cv/space.go | 127 +++++++ cv/headwear_cv/headwear.go | 148 ++++++++ cv/medal_cv/medal.go | 60 +++ cv/noble_cv/noble.go | 89 +++++ cv/property_cv/property.go | 302 +++++++++++++++ cv/user_cv/bag.go | 28 +- cv/user_cv/user.go | 357 ++++++++++++++++++ cv/user_cv/utils.go | 100 +++++ domain/cache/user_c/user.go | 98 ++++- domain/event/cp_ev/space.go | 30 ++ domain/event/gift_ev/send_gift.go | 52 +++ domain/model/cp_m/achievement.go | 52 +++ domain/model/cp_m/anniversary.go | 180 +++++++++ domain/model/cp_m/cp_relation.go | 283 ++++++++++++++ domain/model/cp_m/level.go | 395 +++++++++++++++++++ domain/model/cp_m/privilege.go | 55 +++ domain/model/cp_m/rank.go | 60 +++ domain/model/cp_m/repo.go | 10 + domain/model/cp_m/visit.go | 46 +++ domain/model/diamond_m/diamond.go | 190 ++++++++++ domain/model/diamond_m/repo.go | 52 +++ domain/model/event_m/repo.go | 11 + domain/model/event_m/send_gift.go | 103 +++++ domain/model/res_m/country.go | 2 +- domain/model/res_m/headwear.go | 12 + domain/model/res_m/medal.go | 38 ++ domain/model/res_m/property.go | 149 ++++++++ domain/model/res_m/resMultiText.go | 2 +- domain/model/user_m/medal.go | 12 + domain/model/user_m/property.go | 30 +- domain/model/user_m/superManager.go | 79 ++++ domain/model/user_m/user.go | 38 ++ domain/model/user_m/vip.go | 21 ++ domain/service/cp_s/cp_relation.go | 188 +++++++++ domain/service/event_s/cp_level.go | 71 ++++ domain/service/event_s/cp_visit.go | 29 ++ domain/service/event_s/event_init.go | 2 + domain/service/event_s/send_gift.go | 77 ++++ go.mod | 23 +- go.sum | 193 +++++++++- main.go | 3 +- myerr/bizerr/bizCode.go | 26 +- myerr/code.go | 36 +- mysql/3.9.0.sql | 331 ++++++++++++++++ req/request.go | 26 ++ resp/response.go | 35 +- route/cp_r/anniversary.go | 163 ++++++++ route/cp_r/cp_relation.go | 545 +++++++++++++++++++++++++++ route/cp_r/rank.go | 287 ++++++++++++++ route/cp_r/space.go | 175 +++++++++ route/errorHandle.go | 3 - route/middleHandle.go | 6 + route/router.go | 26 ++ route/user_r/bag.go | 14 +- route/user_r/inner.go | 166 ++++++++ test/.env | 1 + test/cp_test.go | 16 + test/local.ini | 171 +++++++++ 74 files changed, 6287 insertions(+), 146 deletions(-) create mode 100644 _const/enum/cp_e/achievement.go create mode 100644 _const/enum/cp_e/anniversary.go create mode 100644 _const/enum/cp_e/cp_relation.go create mode 100644 _const/enum/cp_e/level.go create mode 100644 _const/enum/gift_e/enum.go create mode 100644 corn/cp_corn/cp.go create mode 100644 cron/cp_cron/anniversary.go create mode 100644 cron/cp_cron/clear.go create mode 100644 cron/cron.go create mode 100644 cron/gift_cron/remark.go create mode 100644 cron/gift_cron/send_gift.go create mode 100644 cv/cp_cv/anniversary.go create mode 100644 cv/cp_cv/cp.go create mode 100644 cv/cp_cv/cp_relation.go create mode 100644 cv/cp_cv/rank.go create mode 100644 cv/cp_cv/space.go create mode 100644 cv/headwear_cv/headwear.go create mode 100644 cv/medal_cv/medal.go create mode 100644 cv/noble_cv/noble.go create mode 100644 cv/property_cv/property.go create mode 100644 cv/user_cv/user.go create mode 100644 cv/user_cv/utils.go create mode 100644 domain/event/cp_ev/space.go create mode 100644 domain/event/gift_ev/send_gift.go create mode 100644 domain/model/cp_m/achievement.go create mode 100644 domain/model/cp_m/anniversary.go create mode 100644 domain/model/cp_m/cp_relation.go create mode 100644 domain/model/cp_m/level.go create mode 100644 domain/model/cp_m/privilege.go create mode 100644 domain/model/cp_m/rank.go create mode 100644 domain/model/cp_m/repo.go create mode 100644 domain/model/cp_m/visit.go create mode 100644 domain/model/diamond_m/diamond.go create mode 100755 domain/model/diamond_m/repo.go create mode 100644 domain/model/event_m/repo.go create mode 100644 domain/model/event_m/send_gift.go create mode 100644 domain/model/res_m/property.go create mode 100644 domain/model/user_m/superManager.go create mode 100644 domain/service/cp_s/cp_relation.go create mode 100644 domain/service/event_s/cp_level.go create mode 100644 domain/service/event_s/cp_visit.go create mode 100644 domain/service/event_s/send_gift.go create mode 100644 mysql/3.9.0.sql create mode 100644 route/cp_r/anniversary.go create mode 100644 route/cp_r/cp_relation.go create mode 100644 route/cp_r/rank.go create mode 100644 route/cp_r/space.go create mode 100644 test/.env create mode 100644 test/cp_test.go create mode 100644 test/local.ini diff --git a/_const/enum/cp_e/achievement.go b/_const/enum/cp_e/achievement.go new file mode 100644 index 0000000..2d69655 --- /dev/null +++ b/_const/enum/cp_e/achievement.go @@ -0,0 +1,11 @@ +package cp_e + +type CpAchievement int + +const ( + CpAchievementLevel CpAchievement = 1 // 等级 + CpAchievementVisitors CpAchievement = 2 // 空间访问人数 + CpAchievementMonthRank CpAchievement = 3 // 月榜最高 + CpAchievementWeekRank CpAchievement = 4 // 周榜最高 + CpAchievementDayRank CpAchievement = 5 // 日榜最高 +) diff --git a/_const/enum/cp_e/anniversary.go b/_const/enum/cp_e/anniversary.go new file mode 100644 index 0000000..a201100 --- /dev/null +++ b/_const/enum/cp_e/anniversary.go @@ -0,0 +1,9 @@ +package cp_e + +type AnniversaryItemType int + +const ( + AnniversaryItemTypeNormal AnniversaryItemType = 0 // 普通类型 + AnniversaryItemTypeAvatar AnniversaryItemType = 1 // 头像类型 + AnniversaryItemTypeAnniversary AnniversaryItemType = 2 // 纪念日类型(一年一度,如生日/结婚纪念日) +) diff --git a/_const/enum/cp_e/cp_relation.go b/_const/enum/cp_e/cp_relation.go new file mode 100644 index 0000000..3c61dd8 --- /dev/null +++ b/_const/enum/cp_e/cp_relation.go @@ -0,0 +1,27 @@ +package cp_e + +import "git.hilo.cn/hilo-common/resource/mysql" + +type CpInviteStatus mysql.Type +type CpCancelStatus mysql.Type + +const ( + //新用户 + CpRelationInviteDiamond = 7777 + + //1.未接受2.已接受3.拒接导致退费4.过期导致退费 + CpInvite CpInviteStatus = 1 + CpInviteAccept CpInviteStatus = 2 + CpInviteRefuse CpInviteStatus = 3 + CpInviteExpired CpInviteStatus = 4 + + //1.未处理2.发起者已撤销3.对方已确认4.到期自动确认 + CpCancel CpCancelStatus = 1 + CpCancelRevoke CpCancelStatus = 2 + CpCancelAccept CpCancelStatus = 3 + CpCancelAcceptAuto CpCancelStatus = 4 + + CpConfessionGiftId = 3481 // cp礼物id + CpMaleHeadwearId = 2551 // cp绑定成功后赠送的头饰-男 + CpFemaleHeadwearId = 2561 // cp绑定成功后赠送的头饰-女 +) diff --git a/_const/enum/cp_e/level.go b/_const/enum/cp_e/level.go new file mode 100644 index 0000000..c43f14c --- /dev/null +++ b/_const/enum/cp_e/level.go @@ -0,0 +1,96 @@ +package cp_e + +import "git.hilo.cn/hilo-common/resource/mysql" + +const ( + EffectDays = 30 // 30天有效期 + CpHeadwearId = 2571 +) + +type CpLevel int + +const ( + CpLevel0 CpLevel = 0 // 无称号 + CpLevel1 CpLevel = 1 // 恋爱CP + CpLevel2 CpLevel = 2 // 甜蜜CP + CpLevel3 CpLevel = 3 // 忠诚CP + CpLevel4 CpLevel = 4 // 炽热CP + CpLevel5 CpLevel = 5 // 荣耀CP + + CpLevelMax = CpLevel5 +) + +var ( + // cp等级积分 + CpLevelPoints = map[CpLevel]mysql.Num{ + CpLevel0: 0, + CpLevel1: 200000, + CpLevel2: 800000, + CpLevel3: 1000000, + CpLevel4: 3000000, + CpLevel5: 10000000, + } + // cp等级icon // todo ui + CpLevelIcon = map[CpLevel]string{ + CpLevel0: "icon0.png", + CpLevel1: "icon1.png", + CpLevel2: "icon2.png", + CpLevel3: "icon3.png", + CpLevel4: "icon4.png", + CpLevel5: "icon5.png", + } + // cp等级称号 + CpLevelTitle = map[CpLevel]uint{ + CpLevel1: 252, + CpLevel2: 253, + CpLevel3: 254, + CpLevel4: 255, + CpLevel5: 256, + } + // cp特权名称 + CpPrivilegeNameMsgId = map[CpPrivilege]uint{ + CpPrivilegeSpace: 234, + CpPrivilegeBanner: 235, + CpPrivilegeMedal: 236, + CpPrivilegeCert: 237, + CpPrivilegeRoomEffect: 238, + CpPrivilegeHeadwear: 239, + CpPrivilegeActiveProfile: 240, + CpPrivilegeMicEffect: 241, + } + // cp特权描述 + CpPrivilegeDescMsgId = map[CpPrivilege]uint{ + CpPrivilegeSpace: 264, + CpPrivilegeBanner: 265, + CpPrivilegeMedal: 266, + CpPrivilegeCert: 267, + CpPrivilegeRoomEffect: 268, + CpPrivilegeHeadwear: 269, + CpPrivilegeActiveProfile: 270, + CpPrivilegeMicEffect: 271, + } + // cp特权icon // todo ui + CpPrivilegeIcon = map[CpPrivilege]string{ + CpPrivilegeSpace: "icon_p_1.png", + CpPrivilegeBanner: "icon_p_2.png", + CpPrivilegeMedal: "icon_p_3.png", + CpPrivilegeCert: "icon_p_4.png", + CpPrivilegeRoomEffect: "icon_p_5.png", + CpPrivilegeHeadwear: "icon_p_6.png", + CpPrivilegeActiveProfile: "icon_p_7.png", + CpPrivilegeMicEffect: "icon_p_8.png", + } +) + +type CpPrivilege int + +const ( + CpPrivilegeSpace CpPrivilege = 1 // 空间 + CpPrivilegeBanner CpPrivilege = 2 // 横幅 + CpPrivilegeMedal CpPrivilege = 3 // 等级勋章 + CpPrivilegeCert CpPrivilege = 4 // 证书 + CpPrivilegeRoomEffect CpPrivilege = 5 // 进场特效 + CpPrivilegeHeadwear CpPrivilege = 6 // 头像头饰 + CpPrivilegeActiveProfile CpPrivilege = 7 // 动态资料卡 + CpPrivilegeMicEffect CpPrivilege = 8 // 麦位特效 +) diff --git a/_const/enum/gift_e/enum.go b/_const/enum/gift_e/enum.go new file mode 100644 index 0000000..0a7aeb8 --- /dev/null +++ b/_const/enum/gift_e/enum.go @@ -0,0 +1,60 @@ +package gift_e + +import "git.hilo.cn/hilo-common/resource/mysql" + +type GiftOperateSceneType mysql.Type + +const ( + //匹配声网中场景 + MatchVedioSceneType GiftOperateSceneType = 1 + // 私聊 + PriveChatSceneType GiftOperateSceneType = 2 + //1对1视频 + VideoSceneType GiftOperateSceneType = 3 + //群组 + GroupSceneType GiftOperateSceneType = 4 +) + +type ResGiftAvatarType = mysql.Type + +const ( + SendGiftCpGiftAvatarType ResGiftAvatarType = 1 //周CP + MonthlyWealthGiftAvatarType ResGiftAvatarType = 2 //月冠财富榜 + MonthlyCharmGiftAvatarType ResGiftAvatarType = 3 //月冠魅力榜 + MonthlyPayGiftAvatarType ResGiftAvatarType = 4 //月冠充值榜 + WeekStarGiftAvatarType ResGiftAvatarType = 5 //周星榜 + CountryStarGiftAvatarType ResGiftAvatarType = 6 // 国家之星 +) + +type GiftPrivateRecordType = mysql.Type + +const ( + PrivateRecord GiftPrivateRecordType = 1 + VideoTradeUnion GiftPrivateRecordType = 2 + PrivateRecordBag GiftPrivateRecordType = 3 +) + +type GiftColumnType = uint16 + +const ( + GiftColumnGift GiftColumnType = 1 // 礼物 + GiftColumnRomance GiftColumnType = 2 // 浪漫 + GiftColumnCountry GiftColumnType = 3 // 国家 + GiftColumnCustom GiftColumnType = 4 // 定制 +) + +type GiftTagType = uint16 + +const ( + GiftTagMedal GiftTagType = 1 // 勋章礼物 + GiftTagWeeklyStar GiftTagType = 2 // 周星礼物 +) + +type GiftEntryType = uint16 + +const ( + GiftEntryWeeklyStar GiftEntryType = 1 // 周星活动入口 + GiftEntryWeeklyCp GiftEntryType = 2 // 周CP活动入口 + GiftEntryMedal GiftEntryType = 3 // 勋章激活动入口 + GiftEntryCountryStar GiftEntryType = 4 // 国家之星活动入口 +) diff --git a/_const/enum/res_e/enum.go b/_const/enum/res_e/enum.go index ef2d565..f7bc65d 100755 --- a/_const/enum/res_e/enum.go +++ b/_const/enum/res_e/enum.go @@ -5,55 +5,16 @@ import "git.hilo.cn/hilo-common/resource/mysql" type MsgIdType = uint const ( - DEFAULT_LANG = "en" + DefaultLang = "en" ) type ResMedalType = mysql.Type - -const ( - Wealth ResMedalType = 1 - Charm ResMedalType = 2 - LoveForAll ResMedalType = 3 - StartForAll ResMedalType = 4 - MoonForAll ResMedalType = 5 - MarryMe ResMedalType = 6 - RoomRocket ResMedalType = 7 - Actity ResMedalType = 8 - FruitKing ResMedalType = 9 - BoxKing ResMedalType = 10 - Helicopter ResMedalType = 11 - Roadster ResMedalType = 12 - Watermelon ResMedalType = 13 - Kiss ResMedalType = 14 - Love ResMedalType = 15 - Chick ResMedalType = 16 - SportsCar ResMedalType = 17 - Rocket ResMedalType = 18 - Tower ResMedalType = 19 - Eagle ResMedalType = 20 - Lion ResMedalType = 21 - Vacation ResMedalType = 22 - RomanticNight ResMedalType = 23 - SweetCouple ResMedalType = 24 - Castle ResMedalType = 25 - WeddingCar ResMedalType = 26 - VideoChat ResMedalType = 27 -) - type ResMedalScope = mysql.Type - -const ( - //私有,只能时管理人发放 - Private ResMedalScope = 1 - //公有,自己获取 - Public ResMedalScope = 2 -) - type ResNameplateType = mysql.Type type ResNameplateObtainType = mysql.Type type ResNameplateScope = mysql.Type - type ResUserBag = mysql.Type +type ResPropertyAvatarType = mysql.Type const ( ResUserBagGift ResUserBag = 1 // 背包道具-礼物 diff --git a/corn/cp_corn/cp.go b/corn/cp_corn/cp.go new file mode 100644 index 0000000..aed68b3 --- /dev/null +++ b/corn/cp_corn/cp.go @@ -0,0 +1,93 @@ +package cp_corn + +import ( + "git.hilo.cn/hilo-common/_const/enum/diamond_e" + "git.hilo.cn/hilo-common/domain" + "git.hilo.cn/hilo-common/mylogrus" + "git.hilo.cn/hilo-common/resource/config" + "git.hilo.cn/hilo-common/utils" + "github.com/robfig/cron" + "hilo-user/_const/enum/cp_e" + "hilo-user/domain/model/cp_m" + "hilo-user/domain/model/diamond_m" + "time" +) + +// cp邀请、解除到期结算 +func CpInviteCancelInit() { + if !config.IsMaster() { + return + } + mylogrus.MyLog.Infof("CpInviteCancelInit") + + // 每2min监测一次 + c := cron.New() + spec := "0 */2 * * * *" + if !config.AppIsRelease() { + spec = "0 */1 * * * ?" // 测服每1分钟 + } + _ = c.AddFunc(spec, func() { + defer utils.CheckGoPanic() + model := domain.CreateModelNil() + // 获取超过24小时没被处理的cp邀请 + inviteList, err := cp_m.GetCpInviteByTime(model, time.Now().AddDate(0, 0, -1)) + if err != nil { + model.Log.Errorf("CpInviteCancelInit err:%v", err) + return + } + for _, v := range inviteList { + model.Log.Infof("CpInviteCancelInit invite:%+v", v) + err = model.Transaction(func(model *domain.Model) error { + // 更新邀请记录 + err = cp_m.UpdateStatusCpInvite(model, v.Id, cp_e.CpInviteExpired) + if err != nil { + model.Log.Errorf("CpInviteCancelInit Id:%d, err:%v", v.Id, err) + return err + } + // 退费 + err = diamond_m.ChangeDiamondAccountDetail(model, diamond_e.CpInviteRefund, v.Id, v.UserId, v.DiamondNum) + if err != nil { + model.Log.Errorf("CpInviteCancelInit Id:%d, err:%v", v.Id, err) + return err + } + return nil + }) + if err != nil { + model.Log.Errorf("CpInviteCancelInit invite:%+v, err:%v", v, err) + return + } + time.Sleep(time.Millisecond * 10) + } + + // 获取超过24小时没被处理的cp解除申请 + cancelList, err := cp_m.GetCpCancelByTime(model, time.Now().AddDate(0, 0, -1)) + if err != nil { + model.Log.Errorf("CpInviteCancelInit err:%v", err) + return + } + for _, v := range cancelList { + model.Log.Infof("CpInviteCancelInit cancel:%+v", v) + err = model.Transaction(func(model *domain.Model) error { + // 更新解除记录 + err = cp_m.UpdateStatusCpCancel(model, v.Id, cp_e.CpCancelAcceptAuto) + if err != nil { + model.Log.Errorf("CpInviteCancelInit Id:%d, err:%v", v.Id, err) + return err + } + // 删除cp关系表的记录 + err = cp_m.DelCpRelation(model, v.UserId, v.RecUserId) + if err != nil { + model.Log.Errorf("CpInviteCancelInit Id:%d, err:%v", v.Id, err) + return err + } + return nil + }) + if err != nil { + model.Log.Errorf("CpInviteCancelInit cancel:%+v, err:%v", v, err) + return + } + time.Sleep(time.Millisecond * 10) + } + }) + c.Start() +} diff --git a/cron/cp_cron/anniversary.go b/cron/cp_cron/anniversary.go new file mode 100644 index 0000000..a8a1fa9 --- /dev/null +++ b/cron/cp_cron/anniversary.go @@ -0,0 +1,65 @@ +package cp_cron + +import ( + "git.hilo.cn/hilo-common/_const/enum/msg_e" + "git.hilo.cn/hilo-common/domain" + "git.hilo.cn/hilo-common/resource/config" + "github.com/robfig/cron" + "hilo-user/domain/model/cp_m" + "hilo-user/domain/model/msg_m" + "hilo-user/domain/model/user_m" +) + +// 纪念日 +type CpAnniversaryNoticeMsg struct { + Identifier string `json:"identifier"` + Content string `json:"content"` + Timestamp int64 `json:"timestamp"` +} + +func CpAnniversaryNotice() { + c := cron.New() + // 1小时操作一次 + spec := "0 0 */1 * * ?" + if !config.AppIsRelease() { + // 测服1分钟 + spec = "0 * * * * ?" + } + _ = c.AddFunc(spec, func() { + var model = domain.CreateModelNil() + anniversary := cp_m.GetNeedRemindCpAnniversary(model) + if len(anniversary) <= 0 { + return + } + var userIds []uint64 + for _, v := range anniversary { + userIds = append(userIds, v.UserId1) + userIds = append(userIds, v.UserId2) + } + users, err := user_m.GetUserMapByIds(model, userIds) + if err != nil { + model.Log.Errorf("GetUserMapByIds fail:%v", err) + } + for _, v := range anniversary { + content1 := cp_m.GetTranslate(285, users[v.UserId1].Language) + content2 := cp_m.GetTranslate(285, users[v.UserId2].Language) + record1 := msg_m.NewUserRecord(model, v.UserId1, msg_e.CpAnniversaryNotice, content1, 0, "", "", "", "", "") + record2 := msg_m.NewUserRecord(model, v.UserId2, msg_e.CpAnniversaryNotice, content2, 0, "", "", "", "", "") + err1, err2 := record1.Persistent(), record2.Persistent() + if err1 != nil || err2 != nil { + model.Log.Errorf("NewUserRecord fail:%v-%v", err1, err2) + return + } + //if err := tencentyun.BatchSendCustomMsg(model, 1, users[0].ExternalId, []string{users[1].ExternalId}, string(data), "cp纪念日"); err != nil { + // model.Log.Errorf("BatchSendCustomMsg fail:%v", err) + //} + //if err := tencentyun.BatchSendCustomMsg(model, 1, users[1].ExternalId, []string{users[0].ExternalId}, string(data), "cp纪念日"); err != nil { + // model.Log.Errorf("BatchSendCustomMsg fail:%v", err) + //} + if err := cp_m.UpdateCpAnniversaryReminded(model, v.ID); err != nil { + model.Log.Errorf("UpdateCpAnniversaryReminded fail:%v", err) + } + } + }) + c.Start() +} diff --git a/cron/cp_cron/clear.go b/cron/cp_cron/clear.go new file mode 100644 index 0000000..3500201 --- /dev/null +++ b/cron/cp_cron/clear.go @@ -0,0 +1,24 @@ +package cp_cron + +import ( + "git.hilo.cn/hilo-common/domain" + "git.hilo.cn/hilo-common/resource/config" + "github.com/robfig/cron" + "hilo-user/domain/model/cp_m" +) + +func ClearCpExpire() { + c := cron.New() + spec := "0 0 */1 * * ?" + if !config.AppIsRelease() { + spec = "0 * * * * ?" + } + // 1小时清理一次 + _ = c.AddFunc(spec, func() { + var model = domain.CreateModelNil() + if err := cp_m.ClearExpireCpPoints(model); err != nil { + model.Log.Errorf("ClearExpireCpPoints fail:%v", err) + } + }) + c.Start() +} diff --git a/cron/cron.go b/cron/cron.go new file mode 100644 index 0000000..f9256e3 --- /dev/null +++ b/cron/cron.go @@ -0,0 +1,19 @@ +package cron + +import ( + "git.hilo.cn/hilo-common/resource/config" + "hilo-user/corn/cp_corn" + "hilo-user/cron/cp_cron" + "hilo-user/cron/gift_cron" +) + +func Init() { + if !config.IsMaster() { + return + } + gift_cron.SendGiftEventInit() // 礼物消息 + gift_cron.GiftRemark() // 礼物消息补偿 + cp_cron.ClearCpExpire() // 清理过期cp + cp_cron.CpAnniversaryNotice() // cp纪念日 + cp_corn.CpInviteCancelInit() // cp邀请、解除到期结算 +} diff --git a/cron/gift_cron/remark.go b/cron/gift_cron/remark.go new file mode 100644 index 0000000..8f692b8 --- /dev/null +++ b/cron/gift_cron/remark.go @@ -0,0 +1,47 @@ +package gift_cron + +import ( + "git.hilo.cn/hilo-common/domain" + "git.hilo.cn/hilo-common/resource/config" + "github.com/robfig/cron" + "hilo-user/domain/model/event_m" +) + +// 补偿检查mark=2 +func GiftRemark() { + c := cron.New() + // 每5分钟 + spec := "0 */5 * * * ?" + if !config.AppIsRelease() { + spec = "0 * * * * ?" // 测服1分钟 + } + _ = c.AddFunc(spec, func() { + var model = domain.CreateModelNil() + unmarks, err := event_m.FetchUnMarkEvents(model, 10) + if err != nil { + model.Log.Errorf("FetchUnMarkEvents fail:%v", err) + return + } + for _, unmark := range unmarks { + model.Log.Infof("FetchUnMarkEvent start:%v", unmark.ID) + // 重新入库 + if err := event_m.AddUnMarkEvent(model, &event_m.EventGiftSend{ + Proto: unmark.Proto, + Payload: unmark.Payload, + MarkHiloGroup: unmark.MarkHiloGroup, + MarkHiloUser: unmark.MarkHiloUser, + Mark: unmark.Mark, // 不能影响别的服务的mark状态 + }); err != nil { + model.Log.Errorf("FetchUnMarkEvent add unmark fail:%v", err) + continue + } + // 旧的标记已处理 + unmark.Model = model + if err := unmark.MarkDone(); err != nil { + model.Log.Errorf("FetchUnMarkEvent mark fail:%v", err) + } + model.Log.Infof("FetchUnMarkEvent success:%v", unmark.ID) + } + }) + c.Start() +} diff --git a/cron/gift_cron/send_gift.go b/cron/gift_cron/send_gift.go new file mode 100644 index 0000000..0560851 --- /dev/null +++ b/cron/gift_cron/send_gift.go @@ -0,0 +1,31 @@ +package gift_cron + +import ( + "git.hilo.cn/hilo-common/mycontext" + "git.hilo.cn/hilo-common/mylogrus" + "hilo-user/domain/service/event_s" + "time" +) + +// 送礼事件 +func SendGiftEventInit() { + + mylogrus.MyLog.Infof("SendGiftEventInit") + go func() { + ticker := time.NewTicker(time.Millisecond * 500) + defer ticker.Stop() + for { + select { + case <-ticker.C: + //start := time.Now() + myCtx := mycontext.CreateMyContext(nil) + // 消费送礼事件 + if err := event_s.NewGiftSendEventService(myCtx).Consume(); err != nil { + myCtx.Log.Errorf("eventServcie consume fail:%v", err) + } else { + //myCtx.Log.Infof("eventServcie consume success,cost:%v", time.Now().Sub(start)) + } + } + } + }() +} diff --git a/cv/cp_cv/anniversary.go b/cv/cp_cv/anniversary.go new file mode 100644 index 0000000..d98252c --- /dev/null +++ b/cv/cp_cv/anniversary.go @@ -0,0 +1,24 @@ +package cp_cv + +import ( + "hilo-user/_const/enum/cp_e" + "hilo-user/cv/user_cv" +) + +// cp信息 +type CvCpBase struct { + UserInfo user_cv.UserTiny `json:"userInfo"` // 用户信息 + CpUserInfo user_cv.UserTiny `json:"cpUserInfo,omitempty"` // cp用户信息 +} + +type CvCpAnniversary struct { + Type cp_e.AnniversaryItemType `json:"type"` // 列表类型 0:普通 1:头像 + CpInfo *CvCpBase `json:"cpInfo,omitempty"` // cp信息,type=1(头像)时候用到 + Id uint64 `json:"id"` // 记录id + Content string `json:"content"` // 纪念日内容 + OriginTimestamp int64 `json:"originTimestamp"` // 纪念日的时间 + Timestamp int64 `json:"timestamp"` // 下次纪念日时间戳 + IsRemind bool `json:"isRemind"` // 是否提醒 + CanDel bool `json:"canDel"` // 能否删除 + IsTop bool `json:"isTop"` // 是否置顶 +} diff --git a/cv/cp_cv/cp.go b/cv/cp_cv/cp.go new file mode 100644 index 0000000..c0a2a30 --- /dev/null +++ b/cv/cp_cv/cp.go @@ -0,0 +1,20 @@ +package cp_cv + +import "hilo-user/cv/user_cv" + +// cp信息 +type CvCp struct { + CpUserInfo *user_cv.CvUserBase `json:"cpUserInfo"` // cp用户信息 + CpLevel CvCpLevel `json:"cpLevel"` // cp等级 + MyPrivilegeList []CvPrivilege `json:"myPrivilegeList"` // 等级特权 + CreatedUnix int64 `json:"createdUnix"` // cp创建时间 + CpDays int `json:"cpDays"` // cp天数 +} + +// cp关系 +type CvCpRelation struct { + CpId uint64 `json:"cpId"` + UserId uint64 `json:"userId"` + CpUserId uint64 `json:"cpUserId"` + CpUserAvatar string `json:"cpUserAvatar,omitempty"` +} diff --git a/cv/cp_cv/cp_relation.go b/cv/cp_cv/cp_relation.go new file mode 100644 index 0000000..91138b4 --- /dev/null +++ b/cv/cp_cv/cp_relation.go @@ -0,0 +1,9 @@ +package cp_cv + +type CheckCpRelationRes struct { + Diamond uint32 `json:"diamond"` + Msg string `json:"msg"` +} + +type CheckCpImRes struct { +} diff --git a/cv/cp_cv/rank.go b/cv/cp_cv/rank.go new file mode 100644 index 0000000..f03c1f3 --- /dev/null +++ b/cv/cp_cv/rank.go @@ -0,0 +1,31 @@ +package cp_cv + +import ( + "hilo-user/_const/enum/cp_e" + "hilo-user/cv/user_cv" +) + +type CvCpRank struct { + CpId uint64 `json:"cpId"` // cpId + User1 *user_cv.CvUserBase `json:"user1"` // user1 + User2 *user_cv.CvUserBase `json:"user2,omitempty"` // user2 + Score uint32 `json:"score"` // 分值 + CpLevel CvCpLevel `json:"cpLevel"` // cp等级 + Ranking string `json:"ranking"` // 排名 +} + +type CvCpAchievement struct { + CpId uint64 `json:"cpId"` // cpId + User1 *user_cv.UserTiny `json:"user1"` // user1 + User2 *user_cv.UserTiny `json:"user2"` // user2 + CpLevel cp_e.CpLevel `json:"cpLevel"` // cpLevel + Type cp_e.CpAchievement `json:"type"` // 成就类型 1:等级 2:空间访问人数 3:月榜最高 4:周榜最高 5:日榜最高 + TypeDesc string `json:"typeDesc"` // 成就类型翻译 + Score uint32 `json:"score"` // 分值 + TimeUnix int64 `json:"timeUnix"` // 达成成就时间戳 +} + +type CpTops struct { + Day []CvCpRank `json:"day"` + Week []CvCpRank `json:"week"` +} diff --git a/cv/cp_cv/space.go b/cv/cp_cv/space.go new file mode 100644 index 0000000..b07c369 --- /dev/null +++ b/cv/cp_cv/space.go @@ -0,0 +1,127 @@ +package cp_cv + +import ( + "fmt" + "git.hilo.cn/hilo-common/resource/mysql" + "github.com/bluele/gcache" + "hilo-user/_const/enum/cp_e" + "hilo-user/cv/user_cv" + "hilo-user/domain/model/res_m" + "time" +) + +// cp信息 +type CvCpInfo struct { + UserInfo *user_cv.UserTiny `json:"userInfo"` // 用户信息 + CpUserInfo *user_cv.UserTiny `json:"cpUserInfo,omitempty"` // cp用户信息 + CreatedUnix int64 `json:"createdUnix"` // cp创建时间 + CpDays int `json:"cpDays"` // cp天数 + VisitTimes int64 `json:"visitTimes"` // 空间访问量 + ApplyToUnbind bool `json:"applyToUnbind"` // 是否申请撤销cp +} + +// cp等级 +type CvCpLevel struct { + Level cp_e.CpLevel `json:"level"` // 等级 0:无称号 1:恋爱CP 2:甜蜜CP 3:忠诚CP 4:炽热CP 5:荣耀CP + Points uint32 `json:"points"` // CP值 + StartPoints uint32 `json:"startPoints,omitempty"` // 上个等级所需CP值 + EndPoints uint32 `json:"endPoints,omitempty"` // 下个等级所需CP值 + ExpireAtUnix int64 `json:"expireAtUnix,omitempty"` // 有效期,时间戳 + SettlementDate string `json:"settlementDate"` // 等级过期时间 + MaxLevel cp_e.CpLevel `json:"maxLevel"` // cp最大的等级 + Title string `json:"title,omitempty"` // 称号翻译 +} + +// 资源等级 +type CvResLevel struct { + Level cp_e.CpLevel `json:"level"` // 等级 + Icon string `json:"icon"` // 等级icon图 + StartPoints uint32 `json:"startPoints"` // 上个等级所需CP值 + EndPoints uint32 `json:"endPoints,omitempty"` // 下个等级所需CP值 +} + +// 特权信息 +type CvPrivilege struct { + Type cp_e.CpPrivilege `json:"type"` // 特权id 1:空间 2:横幅 3:等级勋章 4:证书 5:进场特效 6:头像头饰 7:动态资料卡 8:麦位特效 + NameMsgId uint `json:"-"` // 名称-翻译id + Name string `json:"name"` // 名称 + DescMsgId uint `json:"-"` // 描述-翻译id + Desc string `json:"desc"` + Icon string `json:"icon"` // 图标 + CanSwitch bool `json:"canSwitch"` // 能否开关 + UserSwitch bool `json:"userSwitch"` // 用户开关 + LevelList []cp_e.CpLevel `json:"levelList"` // 特权->level的反向索引 +} + +// cp空间页 +type CvSpace struct { + CpInfo CvCpInfo `json:"cpInfo"` // cp信息 + CpLevel CvCpLevel `json:"cpLevel"` // cp等级 + ResLevelList []CvResLevel `json:"resLevelList"` // 资源等级列表,无称号/恋爱CP/甜蜜CP/忠诚CP/炽热CP/荣耀CP + PrivilegeList [][]CvPrivilege `json:"privilegeList"` // 等级特权 + CpAnniversary []CvCpAnniversary `json:"cpAnniversary"` // 提醒的纪念日 +} + +var CvResLevelList = []CvResLevel{ + {cp_e.CpLevel0, cp_e.CpLevelIcon[cp_e.CpLevel0], cp_e.CpLevelPoints[cp_e.CpLevel0], cp_e.CpLevelPoints[cp_e.CpLevel1]}, + {cp_e.CpLevel1, cp_e.CpLevelIcon[cp_e.CpLevel1], cp_e.CpLevelPoints[cp_e.CpLevel1], cp_e.CpLevelPoints[cp_e.CpLevel2]}, + {cp_e.CpLevel2, cp_e.CpLevelIcon[cp_e.CpLevel2], cp_e.CpLevelPoints[cp_e.CpLevel2], cp_e.CpLevelPoints[cp_e.CpLevel3]}, + {cp_e.CpLevel3, cp_e.CpLevelIcon[cp_e.CpLevel3], cp_e.CpLevelPoints[cp_e.CpLevel3], cp_e.CpLevelPoints[cp_e.CpLevel4]}, + {cp_e.CpLevel4, cp_e.CpLevelIcon[cp_e.CpLevel4], cp_e.CpLevelPoints[cp_e.CpLevel4], cp_e.CpLevelPoints[cp_e.CpLevel5]}, + {cp_e.CpLevel5, cp_e.CpLevelIcon[cp_e.CpLevel5], cp_e.CpLevelPoints[cp_e.CpLevel5], cp_e.CpLevelPoints[cp_e.CpLevelMax]}, +} + +var ( + CvPrivilege1 = CvPrivilege{cp_e.CpPrivilegeSpace, cp_e.CpPrivilegeNameMsgId[cp_e.CpPrivilegeSpace], "", cp_e.CpPrivilegeDescMsgId[cp_e.CpPrivilegeSpace], "", cp_e.CpPrivilegeIcon[cp_e.CpPrivilegeSpace], false, false, CpPrivilegeLevelList[cp_e.CpPrivilegeSpace]} + CvPrivilege2 = CvPrivilege{cp_e.CpPrivilegeBanner, cp_e.CpPrivilegeNameMsgId[cp_e.CpPrivilegeBanner], "", cp_e.CpPrivilegeDescMsgId[cp_e.CpPrivilegeBanner], "", cp_e.CpPrivilegeIcon[cp_e.CpPrivilegeBanner], false, false, CpPrivilegeLevelList[cp_e.CpPrivilegeBanner]} + CvPrivilege3 = CvPrivilege{cp_e.CpPrivilegeMedal, cp_e.CpPrivilegeNameMsgId[cp_e.CpPrivilegeMedal], "", cp_e.CpPrivilegeDescMsgId[cp_e.CpPrivilegeMedal], "", cp_e.CpPrivilegeIcon[cp_e.CpPrivilegeMedal], false, false, CpPrivilegeLevelList[cp_e.CpPrivilegeMedal]} + CvPrivilege4 = CvPrivilege{cp_e.CpPrivilegeCert, cp_e.CpPrivilegeNameMsgId[cp_e.CpPrivilegeCert], "", cp_e.CpPrivilegeDescMsgId[cp_e.CpPrivilegeCert], "", cp_e.CpPrivilegeIcon[cp_e.CpPrivilegeCert], false, false, CpPrivilegeLevelList[cp_e.CpPrivilegeCert]} + CvPrivilege5 = CvPrivilege{cp_e.CpPrivilegeRoomEffect, cp_e.CpPrivilegeNameMsgId[cp_e.CpPrivilegeRoomEffect], "", cp_e.CpPrivilegeDescMsgId[cp_e.CpPrivilegeRoomEffect], "", cp_e.CpPrivilegeIcon[cp_e.CpPrivilegeRoomEffect], true, false, CpPrivilegeLevelList[cp_e.CpPrivilegeRoomEffect]} + CvPrivilege6 = CvPrivilege{cp_e.CpPrivilegeHeadwear, cp_e.CpPrivilegeNameMsgId[cp_e.CpPrivilegeHeadwear], "", cp_e.CpPrivilegeDescMsgId[cp_e.CpPrivilegeHeadwear], "", cp_e.CpPrivilegeIcon[cp_e.CpPrivilegeHeadwear], false, false, CpPrivilegeLevelList[cp_e.CpPrivilegeHeadwear]} + CvPrivilege7 = CvPrivilege{cp_e.CpPrivilegeActiveProfile, cp_e.CpPrivilegeNameMsgId[cp_e.CpPrivilegeActiveProfile], "", cp_e.CpPrivilegeDescMsgId[cp_e.CpPrivilegeActiveProfile], "", cp_e.CpPrivilegeIcon[cp_e.CpPrivilegeActiveProfile], true, false, CpPrivilegeLevelList[cp_e.CpPrivilegeActiveProfile]} + CvPrivilege8 = CvPrivilege{cp_e.CpPrivilegeMicEffect, cp_e.CpPrivilegeNameMsgId[cp_e.CpPrivilegeMicEffect], "", cp_e.CpPrivilegeDescMsgId[cp_e.CpPrivilegeMicEffect], "", cp_e.CpPrivilegeIcon[cp_e.CpPrivilegeMicEffect], false, false, CpPrivilegeLevelList[cp_e.CpPrivilegeMicEffect]} +) + +var CpLevelPrivilegeList = map[cp_e.CpLevel][]CvPrivilege{ + cp_e.CpLevel0: {CvPrivilege1}, + cp_e.CpLevel1: {CvPrivilege1, CvPrivilege2, CvPrivilege3, CvPrivilege4}, + cp_e.CpLevel2: {CvPrivilege1, CvPrivilege2, CvPrivilege3, CvPrivilege4, CvPrivilege5}, + cp_e.CpLevel3: {CvPrivilege1, CvPrivilege2, CvPrivilege3, CvPrivilege4, CvPrivilege5, CvPrivilege6}, + cp_e.CpLevel4: {CvPrivilege1, CvPrivilege2, CvPrivilege3, CvPrivilege4, CvPrivilege5, CvPrivilege6, CvPrivilege7}, + cp_e.CpLevel5: {CvPrivilege1, CvPrivilege2, CvPrivilege3, CvPrivilege4, CvPrivilege5, CvPrivilege6, CvPrivilege7, CvPrivilege8}, +} + +var CpPrivilegeLevelList = map[cp_e.CpPrivilege][]cp_e.CpLevel{ + cp_e.CpPrivilegeSpace: {cp_e.CpLevel0, cp_e.CpLevel1, cp_e.CpLevel2, cp_e.CpLevel3, cp_e.CpLevel4, cp_e.CpLevel5}, + cp_e.CpPrivilegeBanner: {cp_e.CpLevel1, cp_e.CpLevel2, cp_e.CpLevel3, cp_e.CpLevel4, cp_e.CpLevel5}, + cp_e.CpPrivilegeMedal: {cp_e.CpLevel1, cp_e.CpLevel2, cp_e.CpLevel3, cp_e.CpLevel4, cp_e.CpLevel5}, + cp_e.CpPrivilegeCert: {cp_e.CpLevel1, cp_e.CpLevel2, cp_e.CpLevel3, cp_e.CpLevel4, cp_e.CpLevel5}, + cp_e.CpPrivilegeRoomEffect: {cp_e.CpLevel2, cp_e.CpLevel3, cp_e.CpLevel4, cp_e.CpLevel5}, + cp_e.CpPrivilegeHeadwear: {cp_e.CpLevel3, cp_e.CpLevel4, cp_e.CpLevel5}, + cp_e.CpPrivilegeActiveProfile: {cp_e.CpLevel4, cp_e.CpLevel5}, + cp_e.CpPrivilegeMicEffect: {cp_e.CpLevel5}, +} + +func CopyCpLevelPrivilegeList(Level cp_e.CpLevel, lang string) []CvPrivilege { + privileges := make([]CvPrivilege, len(CpLevelPrivilegeList[Level])) + copy(privileges, CpLevelPrivilegeList[Level]) + for i, v := range privileges { + privileges[i].Name = GetTranslate(v.NameMsgId, lang) + privileges[i].Desc = GetTranslate(v.DescMsgId, lang) + } + return privileges +} + +var translateCache = gcache.New(1000).LRU().Build() + +func GetTranslate(msgId uint, lang string) string { + key := fmt.Sprintf("%v-%v", msgId, lang) + if data, err := translateCache.Get(key); err == nil { + return data.(string) + } + if resMul, _ := res_m.GetResMultiTextBy(mysql.Db, msgId, lang); resMul != nil { + _ = translateCache.SetWithExpire(key, resMul.Content, time.Hour) + return resMul.Content + } + return "default" +} diff --git a/cv/headwear_cv/headwear.go b/cv/headwear_cv/headwear.go new file mode 100644 index 0000000..c009846 --- /dev/null +++ b/cv/headwear_cv/headwear.go @@ -0,0 +1,148 @@ +package headwear_cv + +import ( + "git.hilo.cn/hilo-common/resource/mysql" + "gorm.io/gorm" + "hilo-user/_const/enum/headwear_e" + "hilo-user/domain/model/res_m" + "hilo-user/domain/model/user_m" + "hilo-user/myerr" + "strconv" + "time" +) + +type CvHeadwear struct { + Id uint64 `json:"id"` + Using bool `json:"using"` + PicUrl mysql.Str `json:"picUrl"` + EffectUrl mysql.Str `json:"effectUrl"` + TimeLeft int64 `json:"timeLeft"` // 离到期还有多少秒(过期则是负数) +} + +type CvHeadwearDiamond struct { + Id uint64 `json:"id"` + PicUrl string `json:"picUrl"` + EffectUrl string `json:"effectUrl"` + DiamondNum uint32 `json:"diamondNum"` + Second uint32 `json:"second"` + Days string `json:"days"` +} + +func GetCvHeadwearDiamond(pageSize int, pageIndex int) ([]CvHeadwearDiamond, error) { + headwearDiamonds := []CvHeadwearDiamond{} + if err := mysql.Db.Raw("SELECT d.id, r.pic_url, r.effect_url, d.`second`, d.diamond_num from res_headwear r, res_headwear_diamond d where r.id = d.res_headwear_id and d.`status` = ? ORDER BY d.diamond_num asc LIMIT ?, ?", mysql.USER, (pageIndex-1)*pageSize, pageSize).Scan(&headwearDiamonds).Error; err != nil { + return nil, myerr.WrapErr(err) + } + for i, _ := range headwearDiamonds { + headwearDiamonds[i].Days = strconv.FormatUint(uint64(headwearDiamonds[i].Second/(24*60*60)), 10) + } + return headwearDiamonds, nil +} + +func GetHeadwearList(db *gorm.DB, userId uint64) ([]CvHeadwear, error) { + rows := make([]user_m.UserHeadwear, 0) + if err := db.Where(&user_m.UserHeadwear{ + UserId: userId, + }).Where("end_time >= ?", time.Now()).Order("`using` DESC, updated_time DESC").Find(&rows).Error; err != nil { + return nil, err + } + resHwMap, err := res_m.GetResHeadwearMap(db) + if err != nil { + return nil, myerr.WrapErr(err) + } + + result := make([]CvHeadwear, 0) + now := time.Now() + hasUsing := false + for _, i := range rows { + // TODO: 没过期并且有设置using的,才算是,因为写入方不维护using状态的更新 + isUsing := i.Using == headwear_e.YesUsing && i.EndTime.After(now) + result = append(result, CvHeadwear{ + Id: i.HeadwearId, + PicUrl: resHwMap[i.HeadwearId].PicUrl, + EffectUrl: resHwMap[i.HeadwearId].EffectUrl, + Using: isUsing, + TimeLeft: i.EndTime.Unix() - now.Unix(), + }) + if isUsing { + hasUsing = true + } + } + // 如果没有一个using,则找第一个没过期的充当 + if ! hasUsing { + for i, e := range result { + if e.TimeLeft > 0 { + result[i].Using = true + break + } + } + } + + return result, nil +} + +func GetCvHeadwear(userId uint64) (*CvHeadwear, error) { + userHeadwear := user_m.UserHeadwear{} + if err := mysql.Db.Model(&user_m.UserHeadwear{}).Where(&user_m.UserHeadwear{ + UserId: userId, + }).Where("end_time >= ?", time.Now()).Order("`using` DESC, updated_time DESC").First(&userHeadwear).Error; err != nil { + if err == gorm.ErrRecordNotFound { + return nil, nil + } else { + return nil, myerr.WrapErr(err) + } + } + resHeadwear := res_m.ResHeadwear{} + if err := mysql.Db.Model(&res_m.ResHeadwear{}).First(&resHeadwear, userHeadwear.HeadwearId).Error; err != nil { + return nil, myerr.WrapErr(err) + } + return &CvHeadwear{ + Id: userHeadwear.HeadwearId, + PicUrl: resHeadwear.PicUrl, + EffectUrl: resHeadwear.EffectUrl, + Using: userHeadwear.Using == headwear_e.YesUsing, + }, nil +} + +func BatchGetCvHeadwears(userIds []uint64) (map[uint64]CvHeadwear, error) { + if len(userIds) == 0 { + return map[uint64]CvHeadwear{}, nil + } + rows := make([]user_m.UserHeadwear, 0) + //asc 进行覆盖,保证了updated_time 最大的是最后的输出 + if err := mysql.Db.Where("user_id IN ?", userIds).Where("end_time >= ?", time.Now()). + Order("`using` ASC, updated_time ASC").Find(&rows).Error; err != nil { + return nil, err + } + // + resHeadwearIds := make([]uint64, 0, len(rows)) + for i, _ := range rows { + resHeadwearIds = append(resHeadwearIds, rows[i].HeadwearId) + } + //获取头饰资源,然后转换成map结构 + resHeadwearMap := map[uint64]res_m.ResHeadwear{} + resHeadwears := []res_m.ResHeadwear{} + if err := mysql.Db.Where("id IN ?", resHeadwearIds).Find(&resHeadwears).Error; err != nil { + return nil, err + } + for i, _ := range resHeadwears { + resHeadwearMap[resHeadwears[i].ID] = resHeadwears[i] + } + + result := make(map[uint64]CvHeadwear, 0) + for _, r := range rows { + headwear, flag := resHeadwearMap[r.HeadwearId] + if flag { + result[r.UserId] = CvHeadwear{ + Id: headwear.ID, + PicUrl: headwear.PicUrl, + EffectUrl: headwear.EffectUrl, + Using: r.Using == headwear_e.YesUsing, + } + } + + } + return result, nil + +} + diff --git a/cv/medal_cv/medal.go b/cv/medal_cv/medal.go new file mode 100644 index 0000000..dca4988 --- /dev/null +++ b/cv/medal_cv/medal.go @@ -0,0 +1,60 @@ +package medal_cv + +import ( + "git.hilo.cn/hilo-common/resource/mysql" + "gorm.io/gorm" + "hilo-user/domain/model/res_m" + "sort" +) + +type CvMedal struct { + Id uint32 `json:"id"` + PicUrl mysql.Str `json:"picUrl"` + EffectUrl mysql.Str `json:"effectUrl"` +} + +type ReturnGroupMedal struct { + PicUrl string `json:"picUrl"` + SvgaUrl string `json:"svgaUrl"` +} + +type PicElement struct { + PicUrl string `json:"picUrl"` + SvgaUrl string `json:"svgaUrl"` +} + +func GetMedalInfoMap(db *gorm.DB, medals map[uint64][]uint32) (map[uint64][]uint32, map[uint64][]CvMedal, error) { + resMedals, err := res_m.MedalGetAllMap(db) + if err != nil { + return nil, nil, err + } + + medalIds := make(map[uint64][]uint32) + medalMap := make(map[uint64][]CvMedal, 0) + + // 只选择合法的勋章 + for u, i := range medals { + medalIds[u] = make([]uint32, 0) + medalMap[u] = make([]CvMedal, 0) + + for _, j := range i { + if e, ok := resMedals[j]; ok { + medalIds[u] = append(medalIds[u], j) + medalMap[u] = append(medalMap[u], CvMedal{ + Id: j, + PicUrl: e.PicUrl, + EffectUrl: e.SvgaUrl, + }) + } + } + // 按照勋章排序 + sort.Slice(medalIds[u], func(i, j int) bool { + return resMedals[medalIds[u][i]].Sort < resMedals[medalIds[u][j]].Sort + }) + + sort.Slice(medalMap[u], func(i, j int) bool { + return resMedals[medalMap[u][i].Id].Sort < resMedals[medalMap[u][j].Id].Sort + }) + } + return medalIds, medalMap, nil +} diff --git a/cv/noble_cv/noble.go b/cv/noble_cv/noble.go new file mode 100644 index 0000000..39166ce --- /dev/null +++ b/cv/noble_cv/noble.go @@ -0,0 +1,89 @@ +package noble_cv + +import ( + "git.hilo.cn/hilo-common/resource/mysql" + "gorm.io/gorm" + "hilo-user/domain/model/noble_m" + "hilo-user/myerr" + "time" +) + +type NobleConfig struct { + Level uint16 `json:"level"` // 贵族等级 + PurchasePrice uint32 `json:"purchasePrice"` // 购买价格 + RenewalPrice uint32 `json:"renewalPrice"` // 续费价格 + Duration uint16 `json:"duration"` // 有效期(天) + PicUrl string `json:"picUrl"` // 大图url + DailyGold uint `json:"dailyGold"` // 每日登录领取的金币 + RideId uint64 `json:"rideId"` // 赠送的勋章ID + HeaddressId uint64 `json:"headdressId"` // 赠送的头饰ID + Privileges []int `json:"privileges"` // 权益列表 +} + +type NobleInfo struct { + Level uint16 `json:"level"` // 等级 + EndTime int64 `json:"endTime"` // 截止时间 + RemainTime int64 `json:"remainTime"` // 还有多久(秒)过期,可以是负数 + Price uint32 `json:"price"` // 购买或续费价格 +} + +type CvNoble struct { + Level uint16 `json:"level"` + EndTime int64 `json:"endTime"` +} + +func GetCvNoble(userId uint64) (CvNoble, error) { + userNoble := noble_m.UserNoble{} + if err := mysql.Db.Model(&noble_m.UserNoble{}).Where(&noble_m.UserNoble{ + UserId: userId, + }).Where("end_time > ?", time.Now()).Order("level desc").First(&userNoble).Error; err != nil { + if err == gorm.ErrRecordNotFound { + return CvNoble{ + Level: 0, + EndTime: 0, + }, nil + } else { + return CvNoble{ + Level: 0, + EndTime: 0, + }, myerr.WrapErr(err) + } + } else { + return CvNoble{ + Level: userNoble.Level, + EndTime: userNoble.EndTime.Unix(), + }, nil + } +} + +func GetCvNobles(userIds []uint64) (map[uint64]CvNoble, error) { + if len(userIds) == 0 { + return map[uint64]CvNoble{}, nil + } + var userNobles []noble_m.UserNoble + if err := mysql.Db.Model(&noble_m.UserNoble{}).Where("user_id in (?)", userIds).Where("end_time > ?", time.Now()).Order("level asc").Find(&userNobles).Error; err != nil { + return nil, err + } + + userNobleMap := map[uint64]noble_m.UserNoble{} + for i, _ := range userNobles { + userNobleMap[userNobles[i].UserId] = userNobles[i] + } + + result := map[uint64]CvNoble{} + for _, r := range userIds { + userNoble, flag := userNobleMap[r] + if flag { + result[r] = CvNoble{ + Level: userNoble.Level, + EndTime: userNoble.EndTime.Unix(), + } + } else { + result[r] = CvNoble{ + Level: 0, + EndTime: 0, + } + } + } + return result, nil +} diff --git a/cv/property_cv/property.go b/cv/property_cv/property.go new file mode 100644 index 0000000..c6af321 --- /dev/null +++ b/cv/property_cv/property.go @@ -0,0 +1,302 @@ +package property_cv + +import ( + "git.hilo.cn/hilo-common/resource/mysql" + "gorm.io/gorm" + "hilo-user/_const/enum/headwear_e" + "hilo-user/domain/model/res_m" + "hilo-user/domain/model/user_m" + "hilo-user/myerr" + "strconv" + "time" +) + +type CvProperty struct { + Id uint64 `json:"id"` + PicUrl mysql.Str `json:"picUrl"` + EffectUrl mysql.Str `json:"effectUrl"` + Using bool `json:"using"` + TimeLeft int64 `json:"timeLeft"` // 离到期还有多少秒(过期则是负数) + SenderAvatar string `json:"senderAvatar"` + ReceiverAvatar string `json:"receiverAvatar"` +} + +type CvPropertyDiamond struct { + Id uint64 `json:"id"` + PicUrl string `json:"picUrl"` + EffectUrl string `json:"effectUrl"` + DiamondNum uint32 `json:"diamondNum"` + Second uint32 `json:"second"` + Days string `json:"days"` +} + +func GetCvPropertyDiamond(pageSize int, pageIndex int) ([]CvPropertyDiamond, error) { + propertyDiamonds := []CvPropertyDiamond{} + if err := mysql.Db.Raw("select d.id, r.pic_url, r.effect_url, d.`second`, d.diamond_num from res_property r, res_property_diamond d where r.id = d.res_property_id and d.`status` = ? ORDER BY d.diamond_num ASC LIMIT ?, ?", mysql.USER, (pageIndex-1)*pageSize, pageSize).Scan(&propertyDiamonds).Error; err != nil { + return nil, myerr.WrapErr(err) + } + for i, _ := range propertyDiamonds { + propertyDiamonds[i].Days = strconv.FormatUint(uint64(propertyDiamonds[i].Second/(24*60*60)), 10) + } + return propertyDiamonds, nil +} + +func GetPropertyById(resPropertyId mysql.ID) (CvProperty, error) { + resProperty := res_m.ResProperty{} + if err := mysql.Db.Model(&res_m.ResProperty{}).First(&resProperty, resPropertyId).Error; err != nil { + return CvProperty{}, err + } + + //获取座驾头像 + propertieAvatarMap, err := (&res_m.ResPropertyAvatar{}).GetAll(mysql.Db) + if err != nil { + return CvProperty{}, err + } + + userIds := []uint64{} + for _, value := range propertieAvatarMap { + if value.SendUserId > 0 { + userIds = append(userIds, value.SendUserId) + } + if value.ReceiverUserId > 0 { + userIds = append(userIds, value.ReceiverUserId) + } + } + //获取用户信息 + users := []user_m.User{} + if err := mysql.Db.Model(&user_m.User{}).Where("id in (?)", userIds).Find(&users).Error; err != nil { + return CvProperty{}, myerr.WrapErr(err) + } + userAvatarMap := map[mysql.ID]string{} + for _, r := range users { + userAvatarMap[r.ID] = r.Avatar + } + var senderAvatar string = "" + var receiverAvatar string = "" + + if propertieAvatar, flag := propertieAvatarMap[resProperty.ID]; flag { + if propertieAvatar.SendUserId > 0 { + if avatar, flag := userAvatarMap[propertieAvatar.SendUserId]; flag { + senderAvatar = avatar + } + } + if propertieAvatar.ReceiverUserId > 0 { + if avatar, flag := userAvatarMap[propertieAvatar.ReceiverUserId]; flag { + receiverAvatar = avatar + } + } + } + return CvProperty{ + Id: resProperty.ID, + PicUrl: resProperty.PicUrl, + EffectUrl: resProperty.EffectUrl, + SenderAvatar: senderAvatar, + ReceiverAvatar: receiverAvatar, + }, nil +} + +func GetPropertyAll(db *gorm.DB) (map[uint64]CvProperty, error) { + rp := res_m.ResProperty{} + properties, err := rp.GetAll(mysql.Db) + if err != nil { + return nil, err + } + + //获取座驾头像 + propertieAvatarMap, err := (&res_m.ResPropertyAvatar{}).GetAll(mysql.Db) + + userIds := []uint64{} + for _, value := range propertieAvatarMap { + if value.SendUserId > 0 { + userIds = append(userIds, value.SendUserId) + } + if value.ReceiverUserId > 0 { + userIds = append(userIds, value.ReceiverUserId) + } + } + //获取用户信息 + users := []user_m.User{} + if err := db.Model(&user_m.User{}).Where("id in (?)", userIds).Find(&users).Error; err != nil { + return nil, myerr.WrapErr(err) + } + userAvatarMap := map[mysql.ID]string{} + for _, r := range users { + userAvatarMap[r.ID] = r.Avatar + } + + result := map[uint64]CvProperty{} + for _, r := range properties { + + var senderAvatar string = "" + var receiverAvatar string = "" + if propertieAvatar, flag := propertieAvatarMap[r.ID]; flag { + if propertieAvatar.SendUserId > 0 { + if avatar, flag := userAvatarMap[propertieAvatar.SendUserId]; flag { + senderAvatar = avatar + } + } + if propertieAvatar.ReceiverUserId > 0 { + if avatar, flag := userAvatarMap[propertieAvatar.ReceiverUserId]; flag { + receiverAvatar = avatar + } + } + } + + result[r.ID] = CvProperty{ + Id: r.ID, + PicUrl: r.PicUrl, + EffectUrl: r.EffectUrl, + SenderAvatar: senderAvatar, + ReceiverAvatar: receiverAvatar, + } + } + return result, nil +} + +func GetPropertyList(db *gorm.DB, userId uint64) ([]CvProperty, error) { + rows := make([]user_m.UserProperty, 0) + if err := db.Where(&user_m.UserProperty{ + UserId: userId, + }).Order("`using` DESC, updated_time DESC").Find(&rows).Error; err != nil { + return nil, err + } + rp := res_m.ResProperty{} + properties, err := rp.GetAll(mysql.Db) + if err != nil { + return nil, err + } + + //获取座驾头像 + propertieAvatarMap, err := (&res_m.ResPropertyAvatar{}).GetAll(mysql.Db) + + userIds := []uint64{} + for _, value := range propertieAvatarMap { + if value.SendUserId > 0 { + userIds = append(userIds, value.SendUserId) + } + if value.ReceiverUserId > 0 { + userIds = append(userIds, value.ReceiverUserId) + } + } + //获取用户信息 + users := []user_m.User{} + if err := db.Model(&user_m.User{}).Where("id in (?)", userIds).Find(&users).Error; err != nil { + return nil, myerr.WrapErr(err) + } + userAvatarMap := map[mysql.ID]string{} + for _, r := range users { + userAvatarMap[r.ID] = r.Avatar + } + + result := make([]CvProperty, 0) + now := time.Now() + hasUsing := false + for _, i := range rows { + // TODO: 没过期并且有设置using的,才算是,因为写入方不维护using状态的更新 + isUsing := i.Using == headwear_e.YesUsing && i.EndTime.After(now) + var senderAvatar string = "" + var receiverAvatar string = "" + if propertieAvatar, flag := propertieAvatarMap[i.PropertyId]; flag { + if propertieAvatar.SendUserId > 0 { + if avatar, flag := userAvatarMap[propertieAvatar.SendUserId]; flag { + senderAvatar = avatar + } + } + if propertieAvatar.ReceiverUserId > 0 { + if avatar, flag := userAvatarMap[propertieAvatar.ReceiverUserId]; flag { + receiverAvatar = avatar + } + } + } + result = append(result, CvProperty{ + Id: i.PropertyId, + PicUrl: properties[i.PropertyId].PicUrl, + EffectUrl: properties[i.PropertyId].EffectUrl, + Using: isUsing, + TimeLeft: i.EndTime.Unix() - now.Unix(), + SenderAvatar: senderAvatar, + ReceiverAvatar: receiverAvatar, + }) + if isUsing { + hasUsing = true + } + } + // 如果没有一个using,则找第一个没过期的充当 + if !hasUsing { + for i, e := range result { + if e.TimeLeft > 0 { + result[i].Using = true + break + } + } + } + return result, nil +} + +type PropertyExt struct { + Id uint64 + PicUrl mysql.Str + EffectUrl mysql.Str + Using bool + TimeLeft int64 // 离到期还有多少秒(过期则是负数) + SenderAvatar string + ReceiverAvatar string +} + +func GetExtendedProperty(db *gorm.DB) (map[uint64]PropertyExt, error) { + rp := res_m.ResProperty{} + properties, err := rp.GetAll(mysql.Db) + if err != nil { + return nil, err + } + + //获取座驾头像 + propertieAvatarMap, err := (&res_m.ResPropertyAvatar{}).GetAll(mysql.Db) + + userIds := []uint64{} + for _, value := range propertieAvatarMap { + if value.SendUserId > 0 { + userIds = append(userIds, value.SendUserId) + } + if value.ReceiverUserId > 0 { + userIds = append(userIds, value.ReceiverUserId) + } + } + //获取用户信息 + users := []user_m.User{} + if err := db.Model(&user_m.User{}).Where("id in (?)", userIds).Find(&users).Error; err != nil { + return nil, myerr.WrapErr(err) + } + userAvatarMap := map[mysql.ID]string{} + for _, r := range users { + userAvatarMap[r.ID] = r.Avatar + } + + result := map[uint64]PropertyExt{} + for _, r := range properties { + + var senderAvatar string = "" + var receiverAvatar string = "" + if propertieAvatar, flag := propertieAvatarMap[r.ID]; flag { + if propertieAvatar.SendUserId > 0 { + if avatar, flag := userAvatarMap[propertieAvatar.SendUserId]; flag { + senderAvatar = avatar + } + } + if propertieAvatar.ReceiverUserId > 0 { + if avatar, flag := userAvatarMap[propertieAvatar.ReceiverUserId]; flag { + receiverAvatar = avatar + } + } + } + + result[r.ID] = PropertyExt{ + Id: r.ID, + PicUrl: r.PicUrl, + EffectUrl: r.EffectUrl, + SenderAvatar: senderAvatar, + ReceiverAvatar: receiverAvatar, + } + } + return result, nil +} diff --git a/cv/user_cv/bag.go b/cv/user_cv/bag.go index 37174ef..e33a638 100644 --- a/cv/user_cv/bag.go +++ b/cv/user_cv/bag.go @@ -5,14 +5,22 @@ import ( ) type UserBag struct { - BagId mysql.ID `json:"bagId"` // 背包id - ResType mysql.Type `json:"resType"` // 道具类型 1:礼物道具 - ResId mysql.ID `json:"resId"` // 道具资源id - GiftId mysql.ID `json:"giftId"` // 道具的礼物id - Name string `json:"name"` // 资源名称 - DiamondNum mysql.Num `json:"diamondNum"` // 钻石数量 - IconUrl string `json:"iconUrl"` // icon url - SvgaUrl string `json:"svgaUrl"` // svga url - Count mysql.Num `json:"count"` // 拥有数量 - RemainDays int `json:"remainDays"` // 有效天数 + BagId mysql.ID `json:"bagId"` // 背包id + ResType mysql.Type `json:"resType"` // 道具类型 1:礼物道具 + ResId mysql.ID `json:"resId"` // 道具资源id + GiftId mysql.ID `json:"giftId"` // 道具的礼物id + Name string `json:"name"` // 资源名称 + DiamondNum mysql.Num `json:"diamondNum"` // 钻石数量 + IconUrl string `json:"iconUrl"` // icon url + SvgaUrl string `json:"svgaUrl"` // svga url + Count mysql.Num `json:"count"` // 拥有数量 + RemainDays int `json:"remainDays"` // 有效天数 + TextStyleList []*TextStyle `json:"textStyleList"` // 文本样式 +} + +type TextStyle struct { + TextColor string `json:"textColor"` //文本颜色 + TextSize float32 `json:"textSize"` //文本字体大小 + IsBold bool `json:"isBold"` //是否加粗 + TextKey string `json:"textKey"` //替换svga 对象里面的key } diff --git a/cv/user_cv/user.go b/cv/user_cv/user.go new file mode 100644 index 0000000..419988e --- /dev/null +++ b/cv/user_cv/user.go @@ -0,0 +1,357 @@ +package user_cv + +import ( + "git.hilo.cn/hilo-common/domain" + "git.hilo.cn/hilo-common/mylogrus" + "git.hilo.cn/hilo-common/resource/mysql" + "git.hilo.cn/hilo-common/rpc" + "gorm.io/gorm" + "hilo-user/cv/headwear_cv" + "hilo-user/cv/medal_cv" + "hilo-user/cv/noble_cv" + "hilo-user/cv/property_cv" + "hilo-user/domain/model/noble_m" + "hilo-user/domain/model/res_m" + "hilo-user/domain/model/user_m" + "hilo-user/myerr" +) + +type UserTiny struct { + Id uint64 `json:"id,omitempty"` + ExternalId string `json:"externalId"` + Avatar string `json:"avatar"` + Nick string `json:"nick"` + Sex uint8 `json:"sex"` + Code string `json:"code"` + Country string `json:"country"` + CountryIcon string `json:"countryIcon"` + IsPrettyCode bool `json:"isPrettyCode"` // 是否靓号 +} + +func UserToTiny(user user_m.User) *UserTiny { + return &UserTiny{ + Id: user.ID, + ExternalId: user.ExternalId, + Avatar: user.Avatar, + Nick: user.Nick, + Sex: user.Sex, + Code: user.Code, + Country: user.Country, + CountryIcon: user.CountryIcon, + IsPrettyCode: user.IsPrettyCode(), + } +} + +func UserTinyToCvTiny(user *user_m.UserTiny) *UserTiny { + return &UserTiny{ + Id: user.ID, + ExternalId: user.ExternalId, + Avatar: user.Avatar, + Nick: user.Nick, + Sex: user.Sex, + Code: user.Code, + Country: user.Country, + CountryIcon: user.CountryIcon, + IsPrettyCode: user.IsPrettyCode, + } +} + +//用户基本信息 +type CvUserBase struct { + //不会有返回值 + Id *mysql.ID `json:"id,omitempty"` + //头像,不存在为nil + Avatar *string `json:"avatar"` + //是否默认头像 true:是 false:不是 + DefaultAvatar *bool `json:"defaultAvatar"` + //用户对外ID + ExternalId *string `json:"externalId"` + //昵称,不存在为nil + Nick *string `json:"nick"` + //签名,不存在为nil + Description *string `json:"description"` + //性别 1:男 2:女,不存在为nil + Sex *uint8 `json:"sex"` + //国家,不存在为nil + Country *string `json:"country"` + //国旗图标,不存在为nil + CountryIcon *string `json:"countryIcon"` + //邀请码 + Code *string `json:"code"` + IsPrettyCode bool `json:"isPrettyCode"` // 是否靓号 + IsLogout bool `json:"isLogout"` //是否注销 + //生日,如果是其它人用户信息,年龄则按照是否展示显示,如果是本人,年龄则按照是否存在展示 + Birthday *uint64 `json:"birthday"` + //是否展示年龄, 是本人才有数据,看其他用户均为nil + IsShowAge *uint8 `json:"isShowAge"` + //是否工会成员, 只有是自己查自己,这个才有值,其它全为nil, 20220329 数据开放:原因:产品1对1视频聊天中,公会用户视频需要送礼物。改为: 全部人可以知道是否是公会用户。 + IsTradeUnion *bool `json:"isTradeUnion"` + //是否代理管理员, 只有自己查自己的时候才有值,其他情况为nil + IsAgentMgr *bool `json:"isAgentMgr"` + //工会成员,是否开启了,匹配通知,只有 isTradeUnion值为true,这里才有值, + IsTradeUnionMatchNotification *bool `json:"isTradeUnionMatchNotification"` + //是否VIP用户 + IsVip bool `json:"isVip"` + //是否是官方人员 + IsOfficialStaff bool `json:"isOfficialStaff"` + //VIP用户过期时间(只有自己查询自己,才返回) + VipExpireTime *int64 `json:"vipExpireTime"` + Svip rpc.CvSvip `json:"svip"` // svip结构,等级+权限 + MedalInfo []medal_cv.CvMedal `json:"medalInfo"` // 勋章列表 + Headwear *headwear_cv.CvHeadwear `json:"headwear"` // 当前使用的头饰 + Ride property_cv.CvProperty `json:"ride"` // 当前使用的座驾 + Noble noble_cv.CvNoble `json:"noble"` // 当前的 +} + +//批量获取用户基本信息 +func GetUserBases(userIds []mysql.ID, myUserId mysql.ID) ([]*CvUserBase, error) { + if len(userIds) == 0 { + return []*CvUserBase{}, nil + } + var users []user_m.User + if err := mysql.Db.Model(&user_m.User{}).Where("id in (?)", userIds).Find(&users).Error; err != nil { + return nil, myerr.WrapErr(err) + } + vips, err := user_m.BatchGetVips(userIds) + if err != nil { + return nil, myerr.WrapErr(err) + } + svips, err := rpc.MGetUserSvip(domain.CreateModelNil(), userIds) + if err != nil { + mylogrus.MyLog.Errorf("MGetUserSvip fail:%v", err) + //return nil, myerr.WrapErr(err) + } + + headwearMap, err := headwear_cv.BatchGetCvHeadwears(userIds) + if err != nil { + return nil, err + } + + logger := mylogrus.MyLog.WithField("func", "GetUserBases") + medals, err := user_m.BatchGetUserMedalMerge(logger, mysql.Db, userIds) + if err != nil { + return nil, err + } + + medals, medalInfo, err := getMedalInfoMap(mysql.Db, medals) + if err != nil { + return nil, err + } + + up := user_m.UserProperty{} + rides, err := up.BatchGet(mysql.Db, userIds) + if err != nil { + return nil, err + } + + //rp := res_m.ResProperty{} + //properties, err := rp.GetAll(mysql.Db) + properties, err := GetPropertyAll(mysql.Db) + if err != nil { + return nil, err + } + + nobles, err := noble_m.BatchGetActiveNoble(domain.CreateModelNil(), userIds) + if err != nil { + return nil, err + } + + superManagerMap, err := GetSuperManagerMap(userIds) + if err != nil { + return nil, err + } + + cvUserBases := []*CvUserBase{} + for i := 0; i < len(users); i++ { + user := users[i] + invisible := IfLogout(user.LogoutTime) + invisibleAvatar := "" + invisibleNick := user.Code + //for _, p := range svips[user.ID].Privileges { + // if p.Type == 17 && p.UserSwitch { // 神秘人特权 + // invisible = true + // invisibleAvatar, invisibleNick = rpc.ReplaceSvipAvatarNick(invisibleAvatar, invisibleNick, svips[user.ID].Privileges) + // } + //} + cvUserBase := &CvUserBase{ + Id: &user.ID, + Avatar: StrNil(IfLogoutStr(invisible, invisibleAvatar, user.Avatar)), + DefaultAvatar: &user.DefaultAvatar, + ExternalId: StrToString(&user.ExternalId), + Nick: StrNil(IfLogoutNick(invisible, invisibleNick, user.Nick)), + Description: StrNil(IfLogoutStr(invisible, "", user.Description)), + Sex: TypeToUint8(&user.Sex), + Country: StrNil(user.Country), + CountryIcon: StrNil(user.CountryIcon), + Code: StrToString(&user.Code), + IsPrettyCode: user.IsPrettyCode(), + IsVip: vips[user.ID] != nil, + IsOfficialStaff: superManagerMap[user.ID], + MedalInfo: IfLogoutMedalInfo(invisible, []medal_cv.CvMedal{}, medalInfo[user.ID]), + Ride: IfLogoutRide(IfLogout(user.LogoutTime), property_cv.CvProperty{}, property_cv.CvProperty{ + Id: rides[user.ID], + PicUrl: properties[rides[user.ID]].PicUrl, + EffectUrl: properties[rides[user.ID]].EffectUrl, + Using: true, + SenderAvatar: properties[rides[user.ID]].SenderAvatar, + ReceiverAvatar: properties[rides[user.ID]].ReceiverAvatar, + }), + Noble: noble_cv.CvNoble{ + Level: nobles[user.ID].Level, + EndTime: nobles[user.ID].EndTime.Unix(), + }, + } + if cvUserBase.Noble.Level <= 0 { + cvUserBase.Noble.EndTime = 0 + } + // + if headwear, flag := headwearMap[user.ID]; flag { + cvUserBase.Headwear = IfLogoutHeadwear(IfLogout(user.LogoutTime), nil, &headwear) + } + + if user.ID == myUserId { + cvUserBase.VipExpireTime = vips[user.ID] + cvUserBase.IsShowAge = TypeToUint8(&user.IsShowAge) + cvUserBase.Birthday = BirthdayToUint64(&user.Birthday) + } else if user.IsShowAge == mysql.OPEN { + cvUserBase.Birthday = BirthdayToUint64(&user.Birthday) + } + cvUserBase.Svip = svips[user.ID] + + cvUserBases = append(cvUserBases, cvUserBase) + } + return cvUserBases, nil +} + +func getMedalInfoMap(db *gorm.DB, medals map[uint64][]uint32) (map[uint64][]uint32, map[uint64][]medal_cv.CvMedal, error) { + resMedals, err := res_m.MedalGetAllMap(db) + if err != nil { + return nil, nil, err + } + + medalIds := make(map[uint64][]uint32) + medalMap := make(map[uint64][]medal_cv.CvMedal, 0) + + // 只选择合法的勋章 + for u, i := range medals { + medalIds[u] = make([]uint32, 0) + medalMap[u] = make([]medal_cv.CvMedal, 0) + + for _, j := range i { + if e, ok := resMedals[j]; ok { + medalIds[u] = append(medalIds[u], j) + medalMap[u] = append(medalMap[u], medal_cv.CvMedal{ + Id: j, + PicUrl: e.PicUrl, + EffectUrl: e.SvgaUrl, + }) + } + } + } + return medalIds, medalMap, nil +} + +func GetPropertyAll(db *gorm.DB) (map[uint64]property_cv.CvProperty, error) { + rp := res_m.ResProperty{} + properties, err := rp.GetAll(mysql.Db) + if err != nil { + return nil, err + } + + //获取座驾头像 + propertyAvatarMap, err := (&res_m.ResPropertyAvatar{}).GetAll(mysql.Db) + + var userIds []uint64 + for _, value := range propertyAvatarMap { + if value.SendUserId > 0 { + userIds = append(userIds, value.SendUserId) + } + if value.ReceiverUserId > 0 { + userIds = append(userIds, value.ReceiverUserId) + } + } + //获取用户信息 + users := []user_m.User{} + if err := db.Model(&user_m.User{}).Where("id in (?)", userIds).Find(&users).Error; err != nil { + return nil, myerr.WrapErr(err) + } + userAvatarMap := map[mysql.ID]string{} + for _, r := range users { + userAvatarMap[r.ID] = r.Avatar + } + + result := map[uint64]property_cv.CvProperty{} + for _, r := range properties { + + var senderAvatar string = "" + var receiverAvatar string = "" + if propertyAvatar, flag := propertyAvatarMap[r.ID]; flag { + if propertyAvatar.SendUserId > 0 { + if avatar, flag := userAvatarMap[propertyAvatar.SendUserId]; flag { + senderAvatar = avatar + } + } + if propertyAvatar.ReceiverUserId > 0 { + if avatar, flag := userAvatarMap[propertyAvatar.ReceiverUserId]; flag { + receiverAvatar = avatar + } + } + } + + result[r.ID] = property_cv.CvProperty{ + Id: r.ID, + PicUrl: r.PicUrl, + EffectUrl: r.EffectUrl, + SenderAvatar: senderAvatar, + ReceiverAvatar: receiverAvatar, + } + } + return result, nil +} + +func GetSuperManagerMap(userIds []uint64) (map[uint64]bool, error) { + if len(userIds) == 0 { + return map[uint64]bool{}, nil + } + var superManagers []user_m.SuperManager + if err := mysql.Db.Model(&user_m.SuperManager{}).Where("user_id in (?)", userIds).Find(&superManagers).Error; err != nil { + return nil, myerr.WrapErr(err) + } + //转换成map + rs := map[uint64]bool{} + for i, _ := range userIds { + rs[userIds[i]] = false + } + for i, _ := range superManagers { + rs[superManagers[i].UserId] = true + } + return rs, nil +} + +func GetUserBaseMap(userIds []mysql.ID, myUserId mysql.ID) (map[mysql.ID]*CvUserBase, error) { + userBases, err := GetUserBases(userIds, myUserId) + if err != nil { + return nil, err + } + //转换成map + mapIdUser := map[mysql.ID]*CvUserBase{} + for i := 0; i < len(userBases); i++ { + mapIdUser[*userBases[i].Id] = userBases[i] + } + return mapIdUser, nil +} + +type CvUserTiny struct { + Id uint64 `json:"id,omitempty"` + ExternalId string `json:"externalId"` + Avatar string `json:"avatar"` + Nick string `json:"nick"` + Sex uint8 `json:"sex"` + Code string `json:"code"` + Country string `json:"country"` + CountryIcon string `json:"countryIcon"` + IsPrettyCode bool `json:"isPrettyCode"` // 是否靓号 + IsLogout bool `json:"isLogout"` //是否注销 true:已经注销, false:没有注销 + //生日,如果是其它人用户信息,年龄则按照是否展示显示,如果是本人,年龄则按照是否存在展示 + Birthday *uint64 `json:"birthday"` +} diff --git a/cv/user_cv/utils.go b/cv/user_cv/utils.go new file mode 100644 index 0000000..827e24e --- /dev/null +++ b/cv/user_cv/utils.go @@ -0,0 +1,100 @@ +package user_cv + +import ( + "git.hilo.cn/hilo-common/resource/mysql" + "hilo-user/cv/headwear_cv" + "hilo-user/cv/medal_cv" + "hilo-user/cv/property_cv" + "time" +) + +//空字符串转成nil +func StrNil(msg string) *string { + if msg == "" { + return nil + } + return &msg +} + +func TypeToUint8(t *mysql.Type) *uint8 { + if *t == 0 { + return nil + } else { + return (*uint8)(t) + } +} + +func BirthdayToUint64(birthday *mysql.Timestamp) *uint64 { + if *birthday == 0 { + return nil + } + return (*uint64)(birthday) +} + +func NumToUint32(num *mysql.Num) *uint32 { + return (*uint32)(num) +} + +func TimeToUint64(t *time.Time) *uint64 { + a := uint64(t.Unix()) + return &a +} + +func StrToString(str *mysql.Str) *string { + return (*string)(str) +} + +func IndexToUint16(i *mysql.Index) *uint16 { + return (*uint16)(i) +} + +func IdToUint64(id *mysql.ID) *uint64 { + return (*uint64)(id) +} + +func IsInStringList(str string, list []string) bool { + for _, v := range list { + if str == v { + return true + } + } + return false +} + +func IfLogoutStr(condition bool, trueVal, falseVal string) string { + if condition { + return trueVal + } + return falseVal +} + +func IfLogoutNick(condition bool, code string, nick string) string { + if condition { + return "Hilo No." + code + } + return nick +} +func IfLogout(logoutTime int64) bool { + return logoutTime > 0 && time.Now().Unix() > logoutTime +} + +func IfLogoutMedalInfo(condition bool, trueVal, falseVal []medal_cv.CvMedal) []medal_cv.CvMedal { + if condition { + return trueVal + } + return falseVal +} + +func IfLogoutRide(condition bool, trueVal, falseVal property_cv.CvProperty) property_cv.CvProperty { + if condition { + return trueVal + } + return falseVal +} + +func IfLogoutHeadwear(condition bool, trueVal, falseVal *headwear_cv.CvHeadwear) *headwear_cv.CvHeadwear { + if condition { + return trueVal + } + return falseVal +} diff --git a/domain/cache/user_c/user.go b/domain/cache/user_c/user.go index 39321ad..f84f79f 100755 --- a/domain/cache/user_c/user.go +++ b/domain/cache/user_c/user.go @@ -1,8 +1,10 @@ package user_c import ( + "git.hilo.cn/hilo-common/_const/common" "git.hilo.cn/hilo-common/domain" "git.hilo.cn/hilo-common/resource/mysql" + "git.hilo.cn/hilo-common/utils" redisV8 "github.com/go-redis/redis/v8" "github.com/jinzhu/copier" "hilo-user/_const/redis_key/user_k" @@ -10,6 +12,7 @@ import ( "hilo-user/domain/model/user_m" "hilo-user/myerr" "hilo-user/myerr/bizerr" + "time" ) // 获取用户简要信息 @@ -96,10 +99,11 @@ func ToUserIdByCode(model *domain.Model, code mysql.Str) (mysql.ID, error) { // 顺手缓存code->userId // param user: 已经在上层获取的db User结构 func cacheUserTiny(model *domain.Model, user *user_m.User) error { - userTiny := new(user_m.UserTiny) - if err := copier.Copy(userTiny, user); err != nil { - return err - } + userTiny := ToUserTinyBy(user) + //userTiny := new(user_m.UserTiny) + //if err := copier.Copy(userTiny, user); err != nil { + // return err + //} // cache externalId->userId if err := model.Redis.SetEX(model.Context, user_k.GetExternalIdToUidKey(user.ExternalId), user.ID, cache.GetDefaultTTL()).Err(); err != nil { return err @@ -113,3 +117,89 @@ func cacheUserTiny(model *domain.Model, user *user_m.User) error { } return nil } + +// 获取用户简要信息 +// param userIds 用户id列表 +func GetUserTinyMap(model *domain.Model, userIds []mysql.ID, isDefAvatar bool) (map[mysql.ID]*user_m.UserTiny, error) { + redisNoIds := make([]mysql.ID, 0) + res := make(map[mysql.ID]*user_m.UserTiny) + for _, id := range userIds { + userTiny := new(user_m.UserTiny) + key := user_k.GetUserTinyKey(id) + err := cache.GetJSON(model, key, userTiny) + if err != nil && err != redisV8.Nil { + return nil, err + } + if err == redisV8.Nil { + redisNoIds = append(redisNoIds, id) + } else if userTiny.ID > 0 { + res[id] = userTiny + } + } + // 从db中读 + users, err := user_m.GetUsers(model, redisNoIds) + if err != nil { + return nil, err + } + for _, u := range users { + userTiny := ToUserTinyBy(u) + err = cacheUserTiny(model, u) + if err != nil { + return nil, err + } + res[userTiny.ID] = userTiny + } + if isDefAvatar { + for _, v := range res { + if len(v.Avatar) <= 0 { + if v.Sex == mysql.MAN { + v.Avatar = utils.MakeFullUrl(common.DefaultAvatarMan) + } else if v.Sex == mysql.WOMAN { + v.Avatar = utils.MakeFullUrl(common.DefaultAvatarWoman) + } + } + } + } + return res, nil +} + +func ToUserTinyBy(user *user_m.User) *user_m.UserTiny { + return &user_m.UserTiny{ + ID: user.ID, + Avatar: IfLogoutStr(IfLogout(user.LogoutTime), "", user.Avatar), + ExternalId: user.ExternalId, + Nick: IfLogoutNick(IfLogout(user.LogoutTime), user.Code, user.Nick), + Sex: user.Sex, + Code: user.Code, + Country: user.Country, + CountryIcon: user.CountryIcon, + IsPrettyCode: user.IsPrettyCode(), + IsLogout: IfLogout(user.LogoutTime), + Birthday: BirthdayToUint64(&user.Birthday), + } +} + +func IfLogout(logoutTime int64) bool { + return logoutTime > 0 && time.Now().Unix() > logoutTime +} + +func BirthdayToUint64(birthday *mysql.Timestamp) *uint64 { + if *birthday == 0 { + return nil + } + return (*uint64)(birthday) +} + +func IfLogoutStr(condition bool, trueVal, falseVal string) string { + if condition { + return trueVal + } + return falseVal +} + +func IfLogoutNick(condition bool, code string, nick string) string { + if condition { + return "Hilo No." + code + } + return nick +} diff --git a/domain/event/cp_ev/space.go b/domain/event/cp_ev/space.go new file mode 100644 index 0000000..106ff73 --- /dev/null +++ b/domain/event/cp_ev/space.go @@ -0,0 +1,30 @@ +package cp_ev + +import ( + "git.hilo.cn/hilo-common/domain" + "git.hilo.cn/hilo-common/resource/mysql" +) + +//注册监听 +var spaceVisitListen = new(domain.EventBase) + +type SpaceVisitEvent struct { + UserId mysql.ID + CpId mysql.ID + UserId1, UserId2 mysql.ID +} + +//添加领域事件,在每个领域模型中init中添加,因为这是静态业务,非动态的。 +func AddCpSpaceVisitSync(callback func(model *domain.Model, event interface{}) error) { + domain.AddEventSync(spaceVisitListen, callback) +} + +//加入到异步操作中 +func AddCpSpaceVisitAsync(callback func(model *domain.Model, event interface{}) error) { + domain.AddEventAsync(spaceVisitListen, callback) +} + +//领域事件发布 +func PublishCpSpaceVisit(model *domain.Model, event interface{}) error { + return domain.PublishEvent(spaceVisitListen, model, event) +} diff --git a/domain/event/gift_ev/send_gift.go b/domain/event/gift_ev/send_gift.go new file mode 100644 index 0000000..bae70bd --- /dev/null +++ b/domain/event/gift_ev/send_gift.go @@ -0,0 +1,52 @@ +package gift_ev + +import ( + "git.hilo.cn/hilo-common/domain" + "git.hilo.cn/hilo-common/resource/mysql" + "hilo-user/_const/enum/gift_e" +) + +var sendGiftListen = new(domain.EventBase) + +// 送礼事件 +type SendGiftEvent struct { + SendUserId mysql.ID + ReceiveUserIds []mysql.ID + ResGift EventResGift + GiftOperateIds []mysql.ID + GiftN mysql.Num + SceneType gift_e.GiftOperateSceneType + SceneUid mysql.Str + NoDiamondConsume bool // 不要消费钻石 + TotalConsume uint64 // 房间的总消费额 +} + +type EventResGift struct { + ID mysql.ID + Name mysql.Str + IconUrl mysql.Str + SvgaUrl mysql.Str + MusicUrl mysql.Str + DiamondNum mysql.Num + BeanNum mysql.Num + ReceiveDiamondNum mysql.Num + Second mysql.Num + N mysql.Num + GroupBroadcast bool + Cp bool + Together bool + Status mysql.UserYesNo + GiftType mysql.Type +} + +func AddSendGiftEventSync(callback func(model *domain.Model, event interface{}) error) { + domain.AddEventSync(sendGiftListen, callback) +} + +func AddSendGiftEventAsync(callback func(model *domain.Model, event interface{}) error) { + domain.AddEventAsync(sendGiftListen, callback) +} + +func PublishSendGiftEvent(model *domain.Model, event interface{}) error { + return domain.PublishEvent(sendGiftListen, model, event) +} diff --git a/domain/model/cp_m/achievement.go b/domain/model/cp_m/achievement.go new file mode 100644 index 0000000..d192370 --- /dev/null +++ b/domain/model/cp_m/achievement.go @@ -0,0 +1,52 @@ +package cp_m + +import ( + "git.hilo.cn/hilo-common/domain" + "git.hilo.cn/hilo-common/resource/mysql" + "gorm.io/gorm" + "hilo-user/_const/enum/cp_e" + "time" +) + +type CpAchievement struct { + CpId mysql.ID + UserId1 mysql.ID + UserId2 mysql.ID + Type cp_e.CpAchievement + Score mysql.Num + CreatedTime time.Time `gorm:"->"` + UpdatedTime time.Time `gorm:"->"` +} + +// 更新cp成就 +// 单进程操作,先create,后update +func UpdateCpAchievement(model *domain.Model, cpId, userId1, userId2 mysql.ID, Type cp_e.CpAchievement, score mysql.Num) error { + var cpAchievement CpAchievement + if err := model.DB().Model(CpAchievement{}).Where("cp_id = ? AND `type` = ?", cpId, Type).First(&cpAchievement).Error; err != nil { + if err != gorm.ErrRecordNotFound { + model.Log.Errorf("UpdateCpAchievement fail:%v", err) + return err + } + // gorm.ErrRecordNotFound + cpAchievement = CpAchievement{ + CpId: cpId, + UserId1: userId1, + UserId2: userId2, + Type: Type, + Score: score, + } + return model.DB().Model(CpAchievement{}).Create(&cpAchievement).Error + } + // update if less than + return model.DB().Model(CpAchievement{}).Where("cp_id = ? AND `type` = ?", cpId, Type).Where("score < ?", score).UpdateColumn("score", score).Error +} + +// 获取cp成就 +func GetCpAchievements(model *domain.Model) []CpAchievement { + var achievements []CpAchievement + subQuery := model.DB().Table("cp_achievement a").Joins("JOIN cp_relation r ON a.cp_id = r.id").Select("type,MAX(score) AS max_score").Group("type") + if err := model.DB().Model(CpAchievement{}).Joins("INNER JOIN cp_relation r ON cp_id = r.id").Where("(type, score) IN (?)", subQuery).Order("type ASC").Find(&achievements).Error; err != nil { + model.Log.Errorf("GetAchievements fail:%v", err) + } + return achievements +} diff --git a/domain/model/cp_m/anniversary.go b/domain/model/cp_m/anniversary.go new file mode 100644 index 0000000..69613da --- /dev/null +++ b/domain/model/cp_m/anniversary.go @@ -0,0 +1,180 @@ +package cp_m + +import ( + "fmt" + "git.hilo.cn/hilo-common/domain" + "git.hilo.cn/hilo-common/resource/mysql" + "github.com/bluele/gcache" + "hilo-user/_const/enum/cp_e" + "hilo-user/domain/model/res_m" + "hilo-user/domain/model/user_m" + "time" +) + +// CpAnniversary cp纪念日 +type CpAnniversary struct { + mysql.Entity + Type cp_e.AnniversaryItemType + CpId mysql.ID + UserId1 mysql.ID + UserId2 mysql.ID + Content string + Timestamp int64 + IsRemind bool + LastRemindTime int64 + Sort int +} + +// 初始化6个cp纪念日 +// 1)我们在一起;2)XXX的生日;3)XXX的生日;4)第一次说我爱你;5)第一次亲吻;6)结婚纪念日 +func InitCpAnniversary(model *domain.Model, cp CpRelation, lang string) error { + users, err := user_m.GetUserMapByIds(model, []uint64{cp.UserId1, cp.UserId2}) + if err != nil { + return err + } + if err := AddCpAnniversary(model, cp_e.AnniversaryItemTypeNormal, cp, GetTranslate(259, lang), time.Now().Unix(), true, 100); err != nil { + return err + } + if err := AddCpAnniversary(model, cp_e.AnniversaryItemTypeAnniversary, cp, fmt.Sprintf(GetTranslate(260, lang), users[cp.UserId1].Nick), 0, true, 0); err != nil { + return err + } + if err := AddCpAnniversary(model, cp_e.AnniversaryItemTypeAnniversary, cp, fmt.Sprintf(GetTranslate(260, lang), users[cp.UserId2].Nick), 0, true, 0); err != nil { + return err + } + if err := AddCpAnniversary(model, cp_e.AnniversaryItemTypeNormal, cp, GetTranslate(261, lang), 0, true, 0); err != nil { + return err + } + if err := AddCpAnniversary(model, cp_e.AnniversaryItemTypeNormal, cp, GetTranslate(262, lang), 0, true, 0); err != nil { + return err + } + if err := AddCpAnniversary(model, cp_e.AnniversaryItemTypeAnniversary, cp, GetTranslate(263, lang), 0, true, 0); err != nil { + return err + } + return nil +} + +var translateCache = gcache.New(1000).LRU().Build() + +func GetTranslate(msgId uint, lang string) string { + key := fmt.Sprintf("%v-%v", msgId, lang) + if data, err := translateCache.Get(key); err == nil { + return data.(string) + } + if resMul, _ := res_m.GetResMultiTextBy(mysql.Db, msgId, lang); resMul != nil { + _ = translateCache.SetWithExpire(key, resMul.Content, time.Hour) + return resMul.Content + } + return "default" +} + +// 添加cp纪念日 +func AddCpAnniversary(model *domain.Model, Type cp_e.AnniversaryItemType, cp CpRelation, content string, ts int64, isRemind bool, sort int) error { + return model.DB().Model(CpAnniversary{}).Create(&CpAnniversary{ + CpId: cp.Id, + Type: Type, + UserId1: cp.UserId1, + UserId2: cp.UserId2, + Content: content, + Timestamp: ts, + IsRemind: isRemind, + LastRemindTime: 0, + Sort: sort, + }).Error +} + +// 更新cp纪念日 +func UpdateCpAnniversary(model *domain.Model, id mysql.ID, content string, ts int64, isRemind bool) error { + updates := map[string]interface{}{ + "content": content, + "timestamp": ts, + "is_remind": isRemind, + } + return model.DB().Model(CpAnniversary{}).Where("id = ?", id).Updates(updates).Error +} + +func DelCpAnniversary(model *domain.Model, id mysql.ID) error { + return model.DB().Model(CpAnniversary{}).Where("id = ? ", id).Delete(&CpAnniversary{}).Error +} + +// 根据用户id获取所有纪念日 +func GetAllCpAnniversary(model *domain.Model, userId mysql.ID) []CpAnniversary { + var res []CpAnniversary + relation, exists := GetCpRelation(model, userId) + if !exists { + return res + } + if err := model.DB().Model(CpAnniversary{}).Where("cp_id = ?", relation.Id).Order("`sort` DESC,updated_time DESC,id ASC").Find(&res).Error; err != nil { + model.Log.Errorf("GetAllCpAnniversary fail:%v", err) + } + return res +} + +// 获取所有需要提醒的纪念日 +func GetNeedRemindCpAnniversary(model *domain.Model) []CpAnniversary { + var rows, res []CpAnniversary + if err := model.DB().Model(CpAnniversary{}). + Where("`timestamp` > 0"). + Where("`timestamp` < ?", time.Now().Unix()). + Where("is_remind = 1"). + Where("last_remind_time < ?", time.Now().AddDate(-1, 0, 0).Unix()). // 一年前提醒过 + Find(&rows).Error; err != nil { + model.Log.Errorf("GetNeedRemindCpAnniversary fail:%v", err) + } + now := time.Now().Unix() + for i, v := range rows { + ts := CalcNextAnniversary(v.Timestamp) + if now > ts { + res = append(res, rows[i]) + } + } + return res +} + +// 获取cp当天需要提醒的纪念日 +func GetUserTodayCpAnniversary(model *domain.Model, cpId mysql.ID) []CpAnniversary { + var rows, res []CpAnniversary + if err := model.DB().Model(CpAnniversary{}). + Where("`timestamp` > 0"). + Where("`timestamp` < ?", time.Now().Unix()). + Where("is_remind = 1"). + Where("cp_id = ?", cpId). + Find(&rows).Error; err != nil { + model.Log.Errorf("GetUserTodayCpAnniversary fail:%v", err) + } + now := time.Now().Unix() + for i, v := range rows { + ts := CalcNextAnniversary(v.Timestamp) + if now > ts { + res = append(res, rows[i]) + } + } + return rows +} + +func UpdateCpAnniversaryReminded(model *domain.Model, id mysql.ID) error { + return model.DB().Model(CpAnniversary{}).Where("id = ?", id).Update("last_remind_time", time.Now().Unix()).Error +} + +// 计算下一个纪念日 +func CalcNextAnniversary(timestamp int64) int64 { + now := time.Now() + // 还没超过一天,不用计算明年的 + if now.Unix()-timestamp < 86400 { + return timestamp + } + birthday := time.Unix(timestamp, 0) + // 计算今年的生日日期 + thisYearBirthday := time.Date(now.Year(), birthday.Month(), birthday.Day(), birthday.Hour(), birthday.Minute(), birthday.Second(), 0, time.Local) + + // 如果今年的生日还未到,则生日日期为今年的生日日期;否则为明年的生日日期 + var next time.Time + if thisYearBirthday.After(now) || now.Sub(thisYearBirthday).Seconds() < 86400 { + next = thisYearBirthday + } else { + next = thisYearBirthday.AddDate(1, 0, 0) + } + + // 计算时间戳 + nextTimestamp := next.Unix() + return nextTimestamp +} diff --git a/domain/model/cp_m/cp_relation.go b/domain/model/cp_m/cp_relation.go new file mode 100644 index 0000000..009b7cb --- /dev/null +++ b/domain/model/cp_m/cp_relation.go @@ -0,0 +1,283 @@ +package cp_m + +import ( + "git.hilo.cn/hilo-common/domain" + "git.hilo.cn/hilo-common/utils" + "gorm.io/gorm" + "hilo-user/_const/enum/cp_e" + "hilo-user/domain/model/user_m" + "hilo-user/myerr/bizerr" + "time" +) + +type CpRelation struct { + Id uint64 `json:"id"` + UserId1 uint64 `json:"userId1"` + UserId2 uint64 `json:"userId2"` + CreatedTime time.Time `json:"createdTime"` +} + +type CpInvite struct { + Id uint64 `json:"id"` + UserId uint64 `json:"userId"` + InviteUserId uint64 `json:"inviteUserId"` + DiamondNum uint32 `json:"diamondNum"` + Status cp_e.CpInviteStatus `json:"status"` +} + +// 发送私信-发起邀请 +type CpInviteMessage struct { + Identifier string `json:"identifier"` + Msg string `json:"msg"` + Tip string `json:"tip"` + Sender *user_m.UserTiny `json:"sender"` + MsgType int `json:"msgType"` + MsgId uint64 `json:"msgId"` +} + +// 发送私信-接受邀请 +type CpAcceptInviteMessage struct { + Identifier string `json:"identifier"` + Msg string `json:"msg"` + Tip string `json:"tip"` + Sender *user_m.UserTiny `json:"sender"` + Receiver *user_m.UserTiny `json:"receiver"` +} + +// 发送私信-拒绝邀请 +type CpDenyInviteMessage struct { + Identifier string `json:"identifier"` + Msg string `json:"msg"` + Sender *user_m.UserTiny `json:"sender"` +} + +type CpCancel struct { + Id uint64 `json:"id"` + UserId uint64 `json:"userId"` + RecUserId uint64 `json:"recUserId"` + Status cp_e.CpCancelStatus `json:"status"` +} + +// 发送私信(解除) +type CpCancelMessage struct { + Identifier string `json:"identifier"` + Msg string `json:"msg"` + Tip string `json:"tip"` + Sender *user_m.UserTiny `json:"sender"` + MsgType int `json:"msgType"` + MsgId uint64 `json:"msgId"` +} + +// 发送私信-撤销解除、接受解除 +type CpDealCancelMessage struct { + Identifier string `json:"identifier"` + Msg string `json:"msg"` + Status uint8 `json:"status"` //1.撤销解除2.接受解除 +} + +type Cp struct { + Id uint64 `json:"id"` + UserId1 uint64 `json:"userId1"` + UserId2 uint64 `json:"userId2"` + DisconnectSecond int64 `json:"disconnectSecond"` + Score int32 `json:"score"` + DayScore int32 `json:"dayScore"` + PeriodDay string `json:"periodDay"` + WeekScore int32 `json:"weekScore"` + PeriodWeek string `json:"periodWeek"` + MonthScore int32 `json:"monthScore"` + PeriodMonth string `json:"periodMonth"` + Status int8 `json:"status"` + CreatedTime time.Time `json:"createdTime"` +} + +func CreateCp(model *domain.Model, userId1, userId2 uint64) (int64, error) { + userIds := []uint64{userId1, userId2} + oldCp := make([]*Cp, 0) + // 这两个人以前是否有旧的cp关系 + err := model.DB().Model(Cp{}).Where("status = 1 and user_id1 in (?) and user_id2 in (?)", userIds, userIds).Find(&oldCp).Error + if err != nil { + model.Log.Errorf("CreateCp user1:%d, user2:%d, err:%v", userId1, userId2, err) + return 0, err + } + createdTime := time.Now() + if len(oldCp) > 0 { + // 旧的cp关系,有效的时间给他加回去 + oldSecond := time.Duration(time.Now().Unix() - oldCp[0].CreatedTime.Unix() - oldCp[0].DisconnectSecond) + if oldSecond > 0 { + createdTime = createdTime.Add(-1 * time.Second * time.Duration(time.Now().Unix()-oldCp[0].CreatedTime.Unix()-oldCp[0].DisconnectSecond)) + } + } + + result, err := model.DB().Config.ConnPool.ExecContext(model, "insert into cp_relation(user_id1, user_id2, created_time) select ?,?,? where not exists (select user_id1 from cp_relation where user_id1 in (?,?) or user_id2 in (?,?));", userId1, userId2, createdTime.Format(utils.DATETIME_FORMAT), userId1, userId2, userId1, userId2) + if err != nil { + model.Log.Errorf("CreateCp user1:%d, user2:%d, err:%v", userId1, userId2, err) + return 0, err + } + rowAffected, _ := result.RowsAffected() + if rowAffected <= 0 { + model.Log.Errorf("CreateCp user1:%d, user2:%d, err:%v", userId1, userId2, bizerr.TransactionFailed) + return 0, bizerr.TransactionFailed + } + id, err := result.LastInsertId() + return id, err +} + +func GetCp(model *domain.Model, userId uint64) (*CpRelation, error) { + res := new(CpRelation) + err := model.DB().Model(CpRelation{}).Where("user_id1 = ? or user_id2 = ?", userId, userId).First(&res).Error + if err != nil { + if err == gorm.ErrRecordNotFound { + return res, nil + } + model.Log.Errorf("CreateCp userId:%d, err:%v", userId, err) + return nil, err + } + return res, nil +} + +func GetCpInvite(model *domain.Model, userId, userIdInvite uint64, status cp_e.CpInviteStatus) (*CpInvite, error) { + res := new(CpInvite) + err := model.DB().Model(CpInvite{}).Where(CpInvite{UserId: userId, InviteUserId: userIdInvite, Status: status}).First(&res).Error + if err != nil { + if err == gorm.ErrRecordNotFound { + return nil, nil + } + model.Log.Errorf("GetCpInvite user1:%d, user2:%d, err:%v", userId, userIdInvite, err) + return nil, err + } + return res, nil +} + +func GetCpInviteById(model *domain.Model, id, userId uint64) (*CpInvite, error) { + res := new(CpInvite) + err := model.DB().Model(CpInvite{}).Where(CpInvite{Id: id}).Where("user_id = ? or invite_user_id = ?", userId, userId).First(&res).Error + if err != nil { + if err == gorm.ErrRecordNotFound { + return nil, nil + } + model.Log.Errorf("GetCpInviteById id:%d, err:%v", id, err) + return nil, err + } + return res, nil +} + +func CreateCpInvite(model *domain.Model, userId, userIdInvite uint64, diamondNum uint32) (uint64, error) { + cpInvite := CpInvite{UserId: userId, InviteUserId: userIdInvite, DiamondNum: diamondNum, Status: cp_e.CpInvite} + err := model.DB().Model(CpInvite{}).Create(&cpInvite).Error + if err != nil { + model.Log.Errorf("CreateCpInvite user1:%d, user2:%d, diamondNum:%d, err:%v", userId, userIdInvite, diamondNum, err) + return 0, err + } + return cpInvite.Id, nil +} + +// userId:发起邀请者 +func UpdateStatusCpInvite(model *domain.Model, id uint64, status cp_e.CpInviteStatus) error { + result := model.DB().Exec("update cp_invite set status=? where id=? and status=? limit 1", status, id, cp_e.CpInvite) + if result.Error != nil { + model.Log.Errorf("UpdateStatusCpInvite id:%d, status:%d, err:%v", id, status, result.Error) + return result.Error + } + if result.RowsAffected <= 0 { + model.Log.Errorf("UpdateStatusCpInvite id:%d, status:%d, err:%v", id, status, bizerr.TransactionFailed) + return bizerr.TransactionFailed + } + return nil +} + +func CreateCpCancel(model *domain.Model, userId, recUserId uint64) (uint64, error) { + cpCancel := CpCancel{UserId: userId, RecUserId: recUserId, Status: cp_e.CpCancel} + err := model.DB().Model(CpCancel{}).Create(&cpCancel).Error + if err != nil { + model.Log.Errorf("CreateCpCancel user1:%d, user2:%d, err:%v", userId, recUserId, err) + return 0, err + } + return cpCancel.Id, nil +} + +func GetCpCancel(model *domain.Model, userIds []uint64, status cp_e.CpCancelStatus) (*CpCancel, error) { + res := new(CpCancel) + err := model.DB().Model(CpCancel{}).Where("status = ? and user_id in (?) and rec_user_id in (?)", status, userIds, userIds).First(&res).Error + if err != nil { + if err == gorm.ErrRecordNotFound { + return nil, nil + } + model.Log.Errorf("GetCpCancel users:%d, err:%v", userIds, err) + return nil, err + } + return res, nil +} + +func GetCpCancelById(model *domain.Model, id, userId uint64) (*CpCancel, error) { + res := new(CpCancel) + err := model.DB().Model(CpCancel{}).Where("id = ? and (user_id = ? or rec_user_id = ?)", id, userId, userId).First(&res).Error + if err != nil { + if err == gorm.ErrRecordNotFound { + return nil, nil + } + model.Log.Errorf("GetCpCancelById id:%d, err:%v", id, err) + return nil, err + } + return res, nil +} + +func GetCpCancelWithMe(model *domain.Model, userId uint64, status cp_e.CpCancelStatus) (*CpCancel, error) { + res := new(CpCancel) + err := model.DB().Model(CpCancel{}).Where("status = ? and (user_id = ? or rec_user_id = ?)", status, userId, userId).First(&res).Error + if err != nil { + if err == gorm.ErrRecordNotFound { + return nil, nil + } + model.Log.Errorf("GetCpCancel user1:%d, err:%v", userId, err) + return nil, err + } + return res, nil +} + +func UpdateStatusCpCancel(model *domain.Model, id uint64, status cp_e.CpCancelStatus) error { + result := model.DB().Exec("update cp_cancel set status=? where id=? and status=? limit 1", status, id, cp_e.CpCancel) + if result.Error != nil { + model.Log.Errorf("UpdateStatusCpCancel id:%d, status:%d, err:%v", id, status, result.Error) + return result.Error + } + if result.RowsAffected <= 0 { + model.Log.Errorf("UpdateStatusCpCancel id:%d, status:%d, err:%v", id, status, bizerr.TransactionFailed) + return bizerr.TransactionFailed + } + return nil +} + +func DelCpRelation(model *domain.Model, userId1, userId2 uint64) error { + userIds := []uint64{userId1, userId2} + result := model.DB().Exec("delete from cp_relation where user_id1 in (?) and user_id2 in (?) limit 1;", userIds, userIds) + if result.Error != nil { + model.Log.Errorf("DelCpRelation user1:%d, user2:%d, err:%v", userId1, userId2, result.Error) + return result.Error + } + if result.RowsAffected <= 0 { + model.Log.Errorf("DelCpRelation user1:%d, user2:%d, err:%v", userId1, userId2, bizerr.TransactionFailed) + return bizerr.TransactionFailed + } + return nil +} + +func GetCpInviteByTime(model *domain.Model, expTime time.Time) ([]*CpInvite, error) { + res := make([]*CpInvite, 0) + err := model.DB().Model(CpInvite{}).Where("status = ? and created_time <= ?", cp_e.CpInvite, expTime.Format(utils.DATETIME_FORMAT)).Find(&res).Error + if err != nil { + model.Log.Errorf("GetCpInviteByTime err:%v", err) + return nil, err + } + return res, nil +} + +func GetCpCancelByTime(model *domain.Model, expTime time.Time) ([]*CpCancel, error) { + res := make([]*CpCancel, 0) + err := model.DB().Model(CpCancel{}).Where("status = ? and created_time <= ?", cp_e.CpCancel, expTime.Format(utils.DATETIME_FORMAT)).Find(&res).Error + if err != nil { + model.Log.Errorf("GetCpCancelByTime err:%v", err) + return nil, err + } + return res, nil +} diff --git a/domain/model/cp_m/level.go b/domain/model/cp_m/level.go new file mode 100644 index 0000000..283e924 --- /dev/null +++ b/domain/model/cp_m/level.go @@ -0,0 +1,395 @@ +package cp_m + +import ( + "fmt" + "git.hilo.cn/hilo-common/domain" + "git.hilo.cn/hilo-common/resource/mysql" + "git.hilo.cn/hilo-common/rpc" + "git.hilo.cn/hilo-common/txop/headwear_tx" + "gorm.io/gorm" + "gorm.io/gorm/clause" + "hilo-user/_const/enum/cp_e" + "hilo-user/_const/enum/gift_e" + "hilo-user/domain/model/user_m" + "hilo-user/myerr" + "time" +) + +// cp等级 +type CpLevel struct { + mysql.Entity + CpId mysql.ID + UserId1 mysql.ID + UserId2 mysql.ID + Points mysql.Num + Level cp_e.CpLevel + ExpireAt time.Time +} + +// cp等级积分明细 +type CpLevelDetail struct { + mysql.Entity + CpId mysql.ID + UserId1 mysql.ID + UserId2 mysql.ID + AddReduce mysql.AddReduce + Num mysql.Num + BefNum mysql.Num + AftNum mysql.Num + Remark string +} + +// 获取cp等级 +func GetCpLevel(model *domain.Model, cpId mysql.ID) CpLevel { + var level CpLevel + if err := model.DB().Model(CpLevel{}).Where("cp_id = ?", cpId).First(&level).Error; err != nil { + model.Log.Errorf("GetCpLevel fail:%v", err) + } + return level +} + +// 批量获取cp等级 +func MGetCpLevel(model *domain.Model, cpIds []mysql.ID) map[mysql.ID]CpLevel { + var res = make(map[mysql.ID]CpLevel) + var level []CpLevel + if err := model.DB().Model(CpLevel{}).Where("cp_id in ?", cpIds).Find(&level).Error; err != nil { + model.Log.Errorf("MGetCpLevel fail:%v", err) + } + for i, v := range level { + res[v.CpId] = level[i] + } + return res +} + +// 添加cp等级积分增减明细 +func AddCpLevelDetail(model *domain.Model, detail CpLevelDetail) error { + return model.DB().Create(&detail).Error +} + +// 获取cpRelation +func GetCpRelation(model *domain.Model, userId mysql.ID) (cpRelation CpRelation, exits bool) { + if err := model.DB().Model(CpRelation{}).Where("user_id1 = ? or user_id2 = ?", userId, userId).First(&cpRelation).Error; err != nil { + if err != gorm.ErrRecordNotFound { + model.Log.Errorf("GetCpRelation fail:%v", err) + return + } + } else { + exits = true + } + return +} + +// 获取cpRelation pair +func GetCpRelationPair(model *domain.Model, userId1, userId2 mysql.ID) (cpRelation CpRelation, exits bool) { + if err := model.DB().Model(CpRelation{}).Where("user_id1 = ? AND user_id2 = ?", userId1, userId2).First(&cpRelation).Error; err != nil { + if err != gorm.ErrRecordNotFound { + model.Log.Errorf("GetCpRelation fail:%v", err) + return + } else { + // gorm.ErrRecordNotFound + if err := model.DB().Model(CpRelation{}).Where("user_id1 = ? AND user_id2 = ?", userId2, userId1).First(&cpRelation).Error; err != nil { + if err != gorm.ErrRecordNotFound { + model.Log.Errorf("GetCpRelation fail:%v", err) + return + } + } else { + exits = true + } + } + } else { + exits = true + } + return +} + +// 获取cpRelation pair +func MGetCpRelation(model *domain.Model, userIds []mysql.ID) (cpRelation []CpRelation) { + if err := model.DB().Model(CpRelation{}).Where("user_id1 in ? or user_id2 in ?", userIds, userIds).Find(&cpRelation).Error; err != nil { + model.Log.Errorf("GetCpRelation fail:%v", err) + } + return +} + +// 获取是否申请解绑中 +func GetApplyToUnbind(model *domain.Model, userId, cpUserId mysql.ID) bool { + var total int64 + if err := model.DB().Model(CpCancel{}).Where("user_id = ? AND rec_user_id = ? AND status = 1", userId, cpUserId).Count(&total).Error; err != nil { + model.Log.Errorf("GetApplyToUnbind fail:%v", err) + } + return total > 0 +} + +// 初始化cpLevel +func InitCpLevel(model *domain.Model, cpId, userId1, userId2 mysql.ID) error { + return model.DB().Model(CpLevel{}).Clauses(clause.OnConflict{DoNothing: true}).Create(&CpLevel{ + CpId: cpId, + UserId1: userId1, + UserId2: userId2, + ExpireAt: time.Now().AddDate(0, 1, 0), + }).Error +} + +// 增加cp等级积分 +// 此函数并发不安全,利用mysql事件串行执行保证顺序 +// 送礼1钻石=1点数 +// condition +// 1.记录不存在,首充加points计算level增加90天有效期 +// 2.记录存在 +// 2.1 在有效期内,直接加points后判断新level,升级需要更新有效期 +// 2.2 不有效期内,算首充,重置points后判断新level,升级需要更新有效期 +func AddCpLevelPoints(model *domain.Model, cpRelation CpRelation, points mysql.Num, sceneType gift_e.GiftOperateSceneType) (err error) { + start := time.Now() + defer func() { + model.Log.Infof("AddCpLevelPoints cost:%v,err:%v", time.Now().Sub(start), err) + }() + var oldLevel cp_e.CpLevel + var cpLevel CpLevel + var cpLevelDetails []CpLevelDetail + if err := model.DB().Model(CpLevel{}).Where("cp_id = ?", cpRelation.Id).First(&cpLevel).Error; err != nil { + if err != gorm.ErrRecordNotFound { + return myerr.WrapErr(err) + } + // 明细 + cpLevelDetails = append(cpLevelDetails, CpLevelDetail{ + CpId: cpRelation.Id, + UserId1: cpRelation.UserId1, + UserId2: cpRelation.UserId2, + AddReduce: mysql.ADD, + Num: points, + BefNum: 0, + AftNum: points, + Remark: fmt.Sprintf("send %d gift diamonds", points), + }) + // 1.记录不存在,首充加points计算level增加90天有效期 + var level cp_e.CpLevel + for l := cp_e.CpLevelMax; l >= cp_e.CpLevel0; l-- { + if cp_e.CpLevelPoints[l] <= points { + level = l + break + } + } + if level > 0 { + points = points - cp_e.CpLevelPoints[level] // 减去用于已用于升级的积分 + // 明细 + cpLevelDetails = append(cpLevelDetails, CpLevelDetail{ + CpId: cpRelation.Id, + UserId1: cpRelation.UserId1, + UserId2: cpRelation.UserId2, + AddReduce: mysql.REDUCE, + Num: cp_e.CpLevelPoints[level], + BefNum: cp_e.CpLevelPoints[level] + points, + AftNum: points, + Remark: fmt.Sprintf("Become LEVEL%d", level), + }) + } + cpLevel = CpLevel{ + CpId: cpRelation.Id, + UserId1: cpRelation.UserId1, + UserId2: cpRelation.UserId2, + Points: points, + Level: level, + ExpireAt: time.Now().AddDate(0, 0, cp_e.EffectDays), + } + } else { + // 2.记录存在 + // 2.1 在有效期内,直接加points后判断新level,升级需要更新有效期 + if cpLevel.ExpireAt.After(time.Now()) { + cpLevel.Points += points + // 明细 + cpLevelDetails = append(cpLevelDetails, CpLevelDetail{ + CpId: cpRelation.Id, + UserId1: cpRelation.UserId1, + UserId2: cpRelation.UserId2, + AddReduce: mysql.ADD, + Num: points, + BefNum: cpLevel.Points - points, + AftNum: cpLevel.Points, + Remark: fmt.Sprintf("send %d gift diamonds", points), + }) + oldLevel = cpLevel.Level + levelPoint := cp_e.CpLevelPoints[oldLevel] // 已经用于升级的积分 + for level := cp_e.CpLevelMax; level > oldLevel; level-- { + if cp_e.CpLevelPoints[level] <= cpLevel.Points+levelPoint { + cpLevel.Level = level + break + } + } + // 升级 + if oldLevel != cpLevel.Level { + // 减去已用于升级的积分 + cpLevel.Points = cpLevel.Points - (cp_e.CpLevelPoints[cpLevel.Level] - cp_e.CpLevelPoints[oldLevel]) + cpLevel.ExpireAt = time.Now().AddDate(0, 0, cp_e.EffectDays) + // 明细 + cpLevelDetails = append(cpLevelDetails, CpLevelDetail{ + CpId: cpRelation.Id, + UserId1: cpRelation.UserId1, + UserId2: cpRelation.UserId2, + AddReduce: mysql.REDUCE, + Num: cp_e.CpLevelPoints[cpLevel.Level] - cp_e.CpLevelPoints[oldLevel], + BefNum: cpLevel.Points + cp_e.CpLevelPoints[cpLevel.Level] - cp_e.CpLevelPoints[oldLevel], + AftNum: cpLevel.Points, + Remark: fmt.Sprintf("Become LEVEL%d", cpLevel.Level), + }) + } + } else { + // 2.2 不有效期内,算首充,重置points后判断新level,更新有效期30天 + oldPoints := cpLevel.Points + cpLevel.Points = points + // 明细 + cpLevelDetails = append(cpLevelDetails, CpLevelDetail{ + CpId: cpRelation.Id, + UserId1: cpRelation.UserId1, + UserId2: cpRelation.UserId2, + AddReduce: mysql.ADD, + Num: points, + BefNum: oldPoints, + AftNum: cpLevel.Points, + Remark: fmt.Sprintf("send %d gift diamonds", points), + }) + for level := cp_e.CpLevelMax; level >= cp_e.CpLevel0; level-- { + if cp_e.CpLevelPoints[level] <= cpLevel.Points { + cpLevel.Level = level + break + } + } + if cpLevel.Level > 0 { + cpLevel.Points -= cp_e.CpLevelPoints[cpLevel.Level] // 减去已用于升级的积分 + // 明细 + cpLevelDetails = append(cpLevelDetails, CpLevelDetail{ + CpId: cpRelation.Id, + UserId1: cpRelation.UserId1, + UserId2: cpRelation.UserId2, + AddReduce: mysql.REDUCE, + Num: cp_e.CpLevelPoints[cpLevel.Level], + BefNum: cpLevel.Points + cp_e.CpLevelPoints[cpLevel.Level], + AftNum: cpLevel.Points, + Remark: fmt.Sprintf("Become SVIP%d", cpLevel.Level), + }) + } + cpLevel.ExpireAt = time.Now().AddDate(0, 0, cp_e.EffectDays) + } + } + // 顺序增加明细 + for _, detail := range cpLevelDetails { + if err := AddCpLevelDetail(model, detail); err != nil { + return myerr.WrapErr(err) + } + } + // 赠送cp头像头饰 + if oldLevel < 3 && cpLevel.Level >= 3 { + if err := headwear_tx.SendHeadwear(model, cpRelation.UserId1, cp_e.CpHeadwearId, 30); err != nil { + return err + } + if err := headwear_tx.SendHeadwear(model, cpRelation.UserId2, cp_e.CpHeadwearId, 30); err != nil { + return err + } + } + // 群组中送礼升级 + if oldLevel != cpLevel.Level && sceneType == gift_e.GroupSceneType { + go func() { + userId1, userId2 := cpRelation.UserId1, cpRelation.UserId2 + model := domain.CreateModelContext(model.MyContext) + users, err := user_m.GetUserMapByIds(model, []mysql.ID{userId1, userId2}) + if err != nil { + model.Log.Errorf("") + return + } + levelMsgIdMap := map[cp_e.CpLevel]uint{ + cp_e.CpLevel1: 252, + cp_e.CpLevel2: 253, + cp_e.CpLevel3: 254, + cp_e.CpLevel4: 255, + cp_e.CpLevel5: 256, + } + content := fmt.Sprintf(GetTranslate(286, users[userId1].Language), GetTranslate(levelMsgIdMap[cpLevel.Level], users[userId1].Language)) + if err := rpc.SendCpUpgrade(users[userId1].Nick, users[userId2].Nick, users[userId1].Avatar, users[userId2].Avatar, uint32(cpLevel.Level), content); err != nil { + model.Log.Errorf("SendCpUpgrade fail:%v", err) + } + }() + } + return cpLevel.Persistence(model) +} + +// 清理过期svip积分 +// 降级保级: 积分清零,svip去到大于0的等级,有效期30天 +// svip0:积分清零,有效期保持过期 +func ClearExpireCpPoints(model *domain.Model) error { + var cpLevels []*CpLevel + // 过期 + (积分 or level) 大于0 + if err := model.DB().Table("cp_level").Joins("INNER JOIN cp_relation r ON cp_id = r.id").Where("expire_at < ? AND (points > 0 or level > 0) ", time.Now()).Find(&cpLevels).Error; err != nil { + return myerr.WrapErr(err) + } + for _, cpLevel := range cpLevels { + model.Log.Infof("ClearExpireCpPoints %v", *cpLevel) + var doubleCheck CpLevel + if err := model.DB().Model(CpLevel{}).Where("id = ?", cpLevel.ID).First(&doubleCheck).Error; err != nil { + model.Log.Errorf("double check fail:%v", err) + continue + } + if doubleCheck.ExpireAt.After(time.Now()) { + continue + } + oldPoints := cpLevel.Points + cpLevel.Level, cpLevel.Points = cp_e.CpLevel0, 0 // 清零 + var newLevel cp_e.CpLevel + // 0级不刷新30天有效期 + for level := cp_e.CpLevelMax; level > cp_e.CpLevel0; level-- { + if cp_e.CpLevelPoints[level] <= oldPoints { + newLevel = level + break + } + } + // 降级/保级刷新30天有效期 + if newLevel > 0 { + cpLevel.Level = newLevel + cpLevel.ExpireAt = time.Now().AddDate(0, 0, cp_e.EffectDays) + // 明细 + if err := AddCpLevelDetail(model, CpLevelDetail{ + CpId: cpLevel.CpId, + UserId1: cpLevel.UserId1, + UserId2: cpLevel.UserId2, + AddReduce: mysql.REDUCE, + Num: cp_e.CpLevelPoints[newLevel], + BefNum: oldPoints, + AftNum: 0, + Remark: fmt.Sprintf("Become LEVEL%d", newLevel), + }); err != nil { + model.Log.Errorf("AddCpLevelDetail fail:%v", err) + } + } + if err := cpLevel.Persistence(model); err != nil { + model.Log.Errorf("cpLevel persistence fail:%v", err) + } + // 明细 + if err := AddCpLevelDetail(model, CpLevelDetail{ + CpId: cpLevel.CpId, + UserId1: cpLevel.UserId1, + UserId2: cpLevel.UserId2, + AddReduce: mysql.SET, + Num: oldPoints - cp_e.CpLevelPoints[newLevel], + BefNum: oldPoints - cp_e.CpLevelPoints[newLevel], + AftNum: 0, + Remark: fmt.Sprintf("Expired clear"), + }); err != nil { + model.Log.Errorf("AddCpLevelDetail fail:%v", err) + } + // 保级续送cp头像头饰 + if newLevel >= 3 { + if err := headwear_tx.SendHeadwear(model, cpLevel.UserId1, cp_e.CpHeadwearId, 30); err != nil { + model.Log.Errorf("SendHeadwear fail:%v", err) + } + if err := headwear_tx.SendHeadwear(model, cpLevel.UserId2, cp_e.CpHeadwearId, 30); err != nil { + model.Log.Errorf("SendHeadwear fail:%v", err) + } + } else { + // 否则删除cp头像头饰 + if err := headwear_tx.DelHeadwear(model, cpLevel.UserId1, cp_e.CpHeadwearId); err != nil { + model.Log.Errorf("DelHeadwear fail:%v", err) + } + if err := headwear_tx.DelHeadwear(model, cpLevel.UserId2, cp_e.CpHeadwearId); err != nil { + model.Log.Errorf("DelHeadwear fail:%v", err) + } + } + } + return nil +} diff --git a/domain/model/cp_m/privilege.go b/domain/model/cp_m/privilege.go new file mode 100644 index 0000000..8627fd9 --- /dev/null +++ b/domain/model/cp_m/privilege.go @@ -0,0 +1,55 @@ +package cp_m + +import ( + "git.hilo.cn/hilo-common/domain" + "git.hilo.cn/hilo-common/resource/mysql" + "gorm.io/gorm" + "hilo-user/_const/enum/cp_e" +) + +// svip特权 +type CpPrivilege struct { + mysql.Entity + UserId mysql.ID + Type cp_e.CpPrivilege + OpenClose mysql.OpenClose +} + +// 开关用于svip特权 +func OpenCLoseUserSvipPrivilege(model *domain.Model, userId mysql.ID, Type cp_e.CpPrivilege, openClose mysql.OpenClose) error { + var pri CpPrivilege + if err := model.DB().Model(CpPrivilege{}).Where("user_id = ? AND `type` = ?", userId, Type). + First(&pri).Error; err != nil { + if err != gorm.ErrRecordNotFound { + return err + } + // record not found + return model.DB().Create(&CpPrivilege{ + UserId: userId, + Type: Type, + OpenClose: openClose, + }).Error + } + // update + return model.DB().Model(CpPrivilege{}).Where("user_id = ? AND `type` = ?", userId, Type). + UpdateColumn("open_close", openClose).Error +} + +// 批量获取用户svip特权开关 +// map userId->type->open +func MGetUserSvipPrivilege(model *domain.Model, userIds []mysql.ID) (map[mysql.ID]map[cp_e.CpPrivilege]bool, error) { + res := make(map[mysql.ID]map[cp_e.CpPrivilege]bool) + var privileges []CpPrivilege + if err := model.DB().Model(CpPrivilege{}).Where("user_id in ? AND open_close = ?", userIds, mysql.OPEN).Find(&privileges).Error; err != nil { + return res, err + } + for _, v := range privileges { + if data, ok := res[v.UserId]; ok { + data[v.Type] = true + } else { + res[v.UserId] = make(map[cp_e.CpPrivilege]bool) + res[v.UserId][v.Type] = true + } + } + return res, nil +} diff --git a/domain/model/cp_m/rank.go b/domain/model/cp_m/rank.go new file mode 100644 index 0000000..ab6eb2d --- /dev/null +++ b/domain/model/cp_m/rank.go @@ -0,0 +1,60 @@ +package cp_m + +import ( + "git.hilo.cn/hilo-common/domain" + "git.hilo.cn/hilo-common/resource/mysql" + "gorm.io/gorm" + "gorm.io/gorm/clause" + "time" +) + +type CpDayRank struct { + Date string + CpId mysql.ID + UserId1 mysql.ID + UserId2 mysql.ID + Score mysql.Num + CreatedTime time.Time `gorm:"->"` + UpdatedTime time.Time `gorm:"->"` +} + +// 增加cp排行榜-天 +func AddCpDayRank(model *domain.Model, cpRelation CpRelation, score mysql.Num) (err error) { + date := time.Now().Format("2006-01-02") + rank := &CpDayRank{ + Date: date, + CpId: cpRelation.Id, + UserId1: cpRelation.UserId1, + UserId2: cpRelation.UserId2, + Score: score, + } + if err = model.DB().Model(CpDayRank{}).Clauses(clause.OnConflict{Columns: []clause.Column{{Name: "date"}, {Name: "cp_id"}}, + DoUpdates: clause.Assignments(map[string]interface{}{ + "score": gorm.Expr("score + ?", rank.Score)})}).Create(rank).Error; err != nil { + model.Log.Errorf("AddCpDayRank fail:%v", err) + return err + } + return nil +} + +// 分页获取cp排行榜 +func PageCpDayRank(model *domain.Model, beginDate, endDate string, offset, limit int) []CpDayRank { + var ranks []CpDayRank + if err := model.DB().Table("cp_day_rank r").Joins("INNER JOIN cp_relation c ON c.id = r.cp_id"). + Where("r.date BETWEEN ? AND ?", beginDate, endDate).Group("cp_id").Select("cp_id,r.user_id1,r.user_id2,SUM(r.score) score"). + Order("score DESC").Offset(offset).Limit(limit).Find(&ranks).Error; err != nil { + model.Log.Errorf("PageCpDayRank fail:%v", err) + } + return ranks +} + +// 获取指定cp排行榜 +func GetCpDayRank(model *domain.Model, beginDate, endDate string, cpId mysql.ID) CpDayRank { + var rank CpDayRank + if err := model.DB().Table("cp_day_rank"). + Where("date BETWEEN ? AND ?", beginDate, endDate).Where("cp_id = ?", cpId).Select("cp_id,user_id1,user_id2,SUM(score) score"). + First(&rank).Error; err != nil { + model.Log.Errorf("GetCpDayRank fail:%v", err) + } + return rank +} diff --git a/domain/model/cp_m/repo.go b/domain/model/cp_m/repo.go new file mode 100644 index 0000000..8a0f1be --- /dev/null +++ b/domain/model/cp_m/repo.go @@ -0,0 +1,10 @@ +package cp_m + +import ( + "git.hilo.cn/hilo-common/domain" + "hilo-user/domain/model" +) + +func (p *CpLevel) Persistence(m *domain.Model) error { + return model.Persistent(m.DB(), p) +} diff --git a/domain/model/cp_m/visit.go b/domain/model/cp_m/visit.go new file mode 100644 index 0000000..ab2a02e --- /dev/null +++ b/domain/model/cp_m/visit.go @@ -0,0 +1,46 @@ +package cp_m + +import ( + "git.hilo.cn/hilo-common/domain" + "git.hilo.cn/hilo-common/resource/mysql" + "gorm.io/gorm" + "gorm.io/gorm/clause" + "time" +) + +type CpVisitor struct { + CpId mysql.ID + UserId1 mysql.ID + UserId2 mysql.ID + Visitor mysql.ID + Times mysql.Num + CreatedTime time.Time `gorm:"->"` + UpdatedTime time.Time `gorm:"->"` +} + +// 添加cp空间访问量 +func AddCpSpaceVisitor(model *domain.Model, cpId, userId1, userId2, visitor mysql.ID) error { + vis := &CpVisitor{ + CpId: cpId, + UserId1: userId1, + UserId2: userId2, + Visitor: visitor, + Times: 1, + } + if err := model.DB().Model(CpVisitor{}).Clauses(clause.OnConflict{Columns: []clause.Column{{Name: "cp_id"}, {Name: "visitor"}}, + DoUpdates: clause.Assignments(map[string]interface{}{ + "times": gorm.Expr("times + ?", 1)})}).Create(vis).Error; err != nil { + model.Log.Errorf("AddCpSpaceVisitor fail:%v", err) + return err + } + return nil +} + +// 获取cp空间访问人数 +func CountCpSpaceVisitors(model *domain.Model, cpId mysql.ID) int64 { + var cnt int64 + if err := model.DB().Model(CpVisitor{}).Where("cp_id = ?", cpId).Count(&cnt).Error; err != nil { + model.Log.Errorf("CountCpSpaceVisitors fail:%v", err) + } + return cnt +} diff --git a/domain/model/diamond_m/diamond.go b/domain/model/diamond_m/diamond.go new file mode 100644 index 0000000..22b63b0 --- /dev/null +++ b/domain/model/diamond_m/diamond.go @@ -0,0 +1,190 @@ +package diamond_m + +import ( + "git.hilo.cn/hilo-common/_const/enum/diamond_e" + "git.hilo.cn/hilo-common/domain" + "git.hilo.cn/hilo-common/resource/mysql" + "git.hilo.cn/hilo-common/utils" + "hilo-user/myerr" + "hilo-user/myerr/bizerr" + "strconv" + "time" +) + +type DiamondAccount struct { + mysql.Entity + *domain.Model `gorm:"-"` + UserId mysql.ID + DiamondNum mysql.Num + PinkDiamondNum mysql.Num + Status diamond_e.StatusAccount +} + +//账号详情 +type DiamondAccountDetail struct { + mysql.Entity + *domain.Model `gorm:"-"` + UserId mysql.ID + DiamondAccountId mysql.ID + OperateId mysql.ID + OperateType diamond_e.OperateType + OriginId mysql.ID + AddReduce mysql.AddReduce + Num mysql.Num + Remark mysql.Str + BefNum mysql.Num + AftNum mysql.Num + diamondAccount *DiamondAccount `gorm:"-"` +} + +// 粉钻详情 +type DiamondPinkAccountDetail struct { + mysql.Entity + *domain.Model `gorm:"-"` + UserId mysql.ID + DiamondAccountId mysql.ID + OperateId mysql.ID + OperateType diamond_e.OperateType + OriginId mysql.ID + AddReduce mysql.AddReduce + Num mysql.Num + Remark mysql.Str + BefNum mysql.Num + AftNum mysql.Num + diamondAccount *DiamondAccount `gorm:"-"` +} + +//账号操作配置 +type DiamondOperateSet struct { + mysql.Entity + *domain.Model `gorm:"-"` + DiamondNum mysql.NumAll + FrequencyNum mysql.NumAll + FrequencyDay mysql.NumAll + DiamondMaxNum mysql.NumAll + AddReduce mysql.AddReduce + Type diamond_e.OperateType + Name mysql.Str + Status mysql.UserYesNo + DiamondType diamond_e.OperateType +} + +//通过userId获取帐号 +func GetDiamondAccountByUserId(model *domain.Model, userId mysql.ID) (*DiamondAccount, error) { + var diamondAccount DiamondAccount + if err := model.Db.WithContext(model).Where(&DiamondAccount{ + UserId: userId, + }).First(&diamondAccount).Error; err != nil { + return nil, myerr.WrapErr(err) + } + diamondAccount.Model = model + return &diamondAccount, nil +} + +//匹配条件扣费 +func (diamondAccount *DiamondAccount) ChangeDiamondAccountDetail(operateType diamond_e.OperateType, originId mysql.ID, diamondNum mysql.Num) (*DiamondAccountDetail, error) { + return diamondAccount.addDiamondAccountDetail(operateType, originId, diamondNum) +} + +//钻石操作记录, +func (diamondAccount *DiamondAccount) addDiamondAccountDetail(operateType diamond_e.OperateType, originId mysql.ID, diamondNum mysql.Num) (*DiamondAccountDetail, error) { + var diamondOperateSet DiamondOperateSet + var err error + if err = diamondAccount.Db.Where(&DiamondOperateSet{ + Type: operateType, + Status: mysql.USER, + DiamondType: mysql.DiamondYellow, + }).First(&diamondOperateSet).Error; err != nil { + return nil, myerr.WrapErr(err) + } + + //判断是增加,账号是否被冻结 + if diamondAccount.Status == diamond_e.Frozen && diamondOperateSet.AddReduce == mysql.REDUCE { + return nil, bizerr.DiamondAccountFrozen + } + + //无限,检查次数 + var count int64 + if diamondOperateSet.FrequencyDay == -1 { + if diamondOperateSet.FrequencyNum != -1 { + diamondAccount.Db.Model(&DiamondAccountDetail{}).Where(&DiamondAccountDetail{ + UserId: diamondAccount.UserId, + OperateType: operateType, + }).Count(&count) + if count >= int64(diamondOperateSet.FrequencyNum) { + return nil, bizerr.DiamondFrequency + //return nil, myerr.NewSysError("钻石操作次数多大, userId:" + mysql.IdToStr(diamondAccount.UserId) + " diamondOperateSetId" + mysql.IdToStr(diamondOperateSet.ID)) + } + } + } else if diamondOperateSet.FrequencyDay == 1 { + beginTime, err := time.ParseInLocation("2006-01-02", time.Now().Format("2006-01-02"), time.Local) + if err != nil { + return nil, myerr.WrapErr(err) + } + //一天的次数 + diamondAccount.Db.Model(&DiamondAccountDetail{}).Where(&DiamondAccountDetail{ + UserId: diamondAccount.UserId, + OperateType: operateType, + }).Where("created_time >= ? ", beginTime).Count(&count) + if count >= int64(diamondOperateSet.FrequencyNum) { + return nil, bizerr.DiamondFrequency + } + //终极拦截,利用 + diamondAccount.SetCheckUpdateCondition(" EXISTS (SELECT * from (SELECT COUNT(1) as n from diamond_account_detail d where d.user_id = " + strconv.FormatUint(diamondAccount.UserId, 10) + " and d.operate_type = " + strconv.FormatUint(uint64(operateType), 10) + " and d.created_time >= from_unixtime(" + strconv.FormatInt(utils.GetZeroTime(time.Now()).Unix(), 10) + ")) t where t.n < " + strconv.Itoa(diamondOperateSet.FrequencyNum) + " )") + } + + //-1,代表值无效,由参数给与 + var upateDiamondNum mysql.Num + if diamondOperateSet.DiamondNum == -1 { + upateDiamondNum = diamondNum + } else { + upateDiamondNum = mysql.Num(diamondOperateSet.DiamondNum) + } + + var afterNum mysql.Num + if diamondOperateSet.AddReduce == mysql.ADD { + afterNum = diamondAccount.DiamondNum + upateDiamondNum + } else if diamondOperateSet.AddReduce == mysql.REDUCE { + if diamondAccount.DiamondNum < upateDiamondNum { + return nil, bizerr.DiamondNoEnough + } + afterNum = diamondAccount.DiamondNum - upateDiamondNum + } else { + return nil, myerr.NewSysError("AddReduce 值错误:" + mysql.TypeToString(mysql.Type(diamondOperateSet.AddReduce))) + } + + diamondAccountDetail := &DiamondAccountDetail{ + Model: diamondAccount.Model, + UserId: diamondAccount.UserId, + DiamondAccountId: diamondAccount.ID, + OperateId: diamondOperateSet.ID, + OperateType: diamondOperateSet.Type, + OriginId: originId, + AddReduce: diamondOperateSet.AddReduce, + Num: upateDiamondNum, + Remark: diamondOperateSet.Name, + BefNum: diamondAccount.DiamondNum, + AftNum: afterNum, + diamondAccount: diamondAccount, + } + return diamondAccountDetail, err +} + +// 此方法必须要在事务里面调用 +func ChangeDiamondAccountDetail(model *domain.Model, operateType diamond_e.OperateType, originId mysql.ID, userId mysql.ID, diamondNum mysql.Num) error { + diamondAccount, err := GetDiamondAccountByUserId(model, userId) + if err != nil { + model.Log.Errorf("ChangeDiamondAccountDetail operateType:%v, originId:%v, userId:%v, diamondNum:%v, err:%v", operateType, originId, userId, diamondNum, err) + return err + } + diamondAccountDetail, err := diamondAccount.ChangeDiamondAccountDetail(operateType, originId, diamondNum) + if err != nil { + model.Log.Errorf("ChangeDiamondAccountDetail operateType:%v, originId:%v, userId:%v, diamondNum:%v, err:%v", operateType, originId, userId, diamondNum, err) + return err + } + if err := diamondAccountDetail.PersistentNoInTransactional(); err != nil { + model.Log.Errorf("ChangeDiamondAccountDetail operateType:%v, originId:%v, userId:%v, diamondNum:%v, err:%v", operateType, originId, userId, diamondNum, err) + return err + } + return nil +} diff --git a/domain/model/diamond_m/repo.go b/domain/model/diamond_m/repo.go new file mode 100755 index 0000000..7b48c15 --- /dev/null +++ b/domain/model/diamond_m/repo.go @@ -0,0 +1,52 @@ +package diamond_m + +import ( + "git.hilo.cn/hilo-common/resource/mysql" + "gorm.io/gorm" + "hilo-user/domain/model" + "hilo-user/myerr" + "strconv" +) + +func (diamondAccountDetail *DiamondAccountDetail) PersistentNoInTransactional() error { + //fixme: 这里有点奇怪, diamondAccount持久化动作在diamondAccountDetail持久化之后,RowsAffected 就一定是0 + txDiamondAccount := diamondAccountDetail.Db.Model(diamondAccountDetail.diamondAccount) + if diamondAccountDetail.diamondAccount.CheckUpdateCondition() { + txDiamondAccount = txDiamondAccount.Where(diamondAccountDetail.diamondAccount.GetUpdateCondition()) + } + if diamondAccountDetail.AddReduce == mysql.ADD { + //增加 + txDiamondAccount.UpdateColumn("diamond_num", gorm.Expr("diamond_num + ?", diamondAccountDetail.Num)) + } else if diamondAccountDetail.AddReduce == mysql.REDUCE { + //减少,保证不能扣成负数 + txDiamondAccount.Where("diamond_num >= ?", diamondAccountDetail.Num).UpdateColumn("diamond_num", gorm.Expr("diamond_num - ?", diamondAccountDetail.Num)) + } else { + myerr.NewSysError("addReduce 枚举错误 value:" + mysql.TypeToString(mysql.Type(diamondAccountDetail.AddReduce))) + } + if err := txDiamondAccount.Error; err != nil { + return myerr.WrapErr(err) + } + if txDiamondAccount.RowsAffected == 0 { + diamondAccountDetail.Log.Errorf("gorm condition update.RowsAffected = 0,AddReduce:%v", diamondAccountDetail.AddReduce) + return myerr.NewWaring("gorm condition update.RowsAffected = 0") + } + + //持久化diamondAccountDetail + if err := model.Persistent(diamondAccountDetail.Db, diamondAccountDetail); err != nil { + return myerr.WrapErr(err) + } + //改变diamondAccount值 + if diamondAccountDetail.diamondAccount == nil { + return myerr.NewSysError("持久化错误, 模型:DiamondAccountDetail 中没有diamondAccount, DiamondAccountDetail.Id =" + strconv.Itoa(int(diamondAccountDetail.ID))) + } + + var newDiamondAccount DiamondAccount + if err := diamondAccountDetail.Db.First(&newDiamondAccount, diamondAccountDetail.diamondAccount.ID).Error; err != nil { + return myerr.WrapErr(err) + } + + if newDiamondAccount.DiamondNum < 0 { + return myerr.NewSysError("diamond_account表中,diamond_num 不能小于0, diamondAccount.id = " + strconv.Itoa(int(newDiamondAccount.ID))) + } + return nil +} diff --git a/domain/model/event_m/repo.go b/domain/model/event_m/repo.go new file mode 100644 index 0000000..3e2fd70 --- /dev/null +++ b/domain/model/event_m/repo.go @@ -0,0 +1,11 @@ +package event_m + +import "hilo-user/domain/model" + +func (p *EventGiftSendOffsetHiloUser) Persistence() error { + return model.Persistent(p.Db, p) +} + +func (p *EventGiftSend) Persistence() error { + return model.Persistent(p.Db, p) +} diff --git a/domain/model/event_m/send_gift.go b/domain/model/event_m/send_gift.go new file mode 100644 index 0000000..19318d1 --- /dev/null +++ b/domain/model/event_m/send_gift.go @@ -0,0 +1,103 @@ +package event_m + +import ( + "git.hilo.cn/hilo-common/domain" + "git.hilo.cn/hilo-common/resource/mysql" + "github.com/pkg/errors" + "gorm.io/gorm" + "time" +) + +// 送礼事件消息 +type EventGiftSend struct { + mysql.Entity + *domain.Model `gorm:"-"` + Proto mysql.Type + Payload []byte + Mark mysql.YesNo + MarkHiloGroup mysql.YesNo + MarkHiloUser mysql.YesNo +} + +func (EventGiftSend) TableName() string { + return "event_gift_send" +} + +// 偏移值 +type EventGiftSendOffsetHiloUser struct { + mysql.Entity + *domain.Model `gorm:"-"` + MarkOffset mysql.ID +} + +// 获取当前偏移值 +func Offset(model *domain.Model) (*EventGiftSendOffsetHiloUser, error) { + offset := new(EventGiftSendOffsetHiloUser) + if err := model.Db.WithContext(model).First(offset).Error; err != nil { + if err != gorm.ErrRecordNotFound { + model.Log.Errorf("Offset fail:%v", err) + return nil, err + } + // gorm.ErrRecordNotFound + } + offset.Model = model + return offset, nil +} + +// 批量获取送礼 +func FetchEventGiftSend(model *domain.Model, limit int) ([]*EventGiftSend, *EventGiftSendOffsetHiloUser, error) { + offset, err := Offset(model) + if err != nil { + return nil, nil, err + } + var events []*EventGiftSend + if err := model.Db.WithContext(model).Model(EventGiftSend{}). + Where("id > ?", offset.MarkOffset). + Order("id asc").Limit(limit).Find(&events).Error; err != nil { + model.Log.Errorf("FetchEventGiftSend fail:%v", err) + return nil, nil, err + } + return events, offset, nil +} + +// 标记已完成 +func (p *EventGiftSend) MarkDone() error { + p.MarkHiloUser = mysql.YES + result := p.Db.WithContext(p.Model).Model(EventGiftSend{}).Where("id = ?", p.ID).Update("mark_hilo_user", p.MarkHiloUser).Limit(1) + if result.Error != nil { + return result.Error + } + if result.RowsAffected <= 0 { + p.Model.Log.Errorf("MarkDone row affeced 0") + return errors.New("row affect 0") + } + return nil +} + +// 查询过去1小时-5分钟前未mark的事件 +// 用来补偿 +// 记得加limit +func FetchUnMarkEvents(model *domain.Model, limit int) ([]*EventGiftSend, error) { + offset, err := Offset(model) + if err != nil { + return nil, err + } + t := time.Now().Add(-time.Minute * 5) + lt := t.Add(-time.Hour) + var events []*EventGiftSend + if err := model.Db.WithContext(model).Model(EventGiftSend{}). + Where("mark_hilo_user = ?", mysql.NO). + Where("id <= ?", offset.MarkOffset). + Where("created_time < ?", t). + Where("created_time > ?", lt). + Order("id asc").Limit(limit).Find(&events).Error; err != nil { + model.Log.Errorf("FetchUnMarkEvents fail:%v", err) + return nil, err + } + return events, nil +} + +// 补偿加上unmark的event +func AddUnMarkEvent(model *domain.Model, event *EventGiftSend) error { + return model.Db.WithContext(model).Create(event).Error +} diff --git a/domain/model/res_m/country.go b/domain/model/res_m/country.go index 988b0fd..2bc12ec 100755 --- a/domain/model/res_m/country.go +++ b/domain/model/res_m/country.go @@ -191,7 +191,7 @@ func GetLangeByCountry(db *gorm.DB, country mysql.Str) (string, error) { if err == nil { return r.Lang, nil } else if err == gorm.ErrRecordNotFound { - return res_e.DEFAULT_LANG, nil + return res_e.DefaultLang, nil } else { return "", myerr.WrapErr(err) } diff --git a/domain/model/res_m/headwear.go b/domain/model/res_m/headwear.go index 3c02f09..39c662e 100644 --- a/domain/model/res_m/headwear.go +++ b/domain/model/res_m/headwear.go @@ -123,3 +123,15 @@ func CheckHeadwearValidById(model *domain.Model, id mysql.ID) (bool, error) { return true, nil } } + +func GetResHeadwearMap(db *gorm.DB) (map[uint64]ResHeadwear, error) { + rows := make([]ResHeadwear, 0) + if err := db.Model(&ResHeadwear{}).Find(&rows).Error; err != nil { + return nil, err + } + result := make(map[uint64]ResHeadwear, 0) + for _, i := range rows { + result[i.ID] = i + } + return result, nil +} \ No newline at end of file diff --git a/domain/model/res_m/medal.go b/domain/model/res_m/medal.go index fe7a16b..fd377e1 100755 --- a/domain/model/res_m/medal.go +++ b/domain/model/res_m/medal.go @@ -2,8 +2,10 @@ package res_m import ( "git.hilo.cn/hilo-common/resource/mysql" + "github.com/bluele/gcache" "gorm.io/gorm" "hilo-user/_const/enum/res_e" + "time" ) type ResMedal struct { @@ -41,3 +43,39 @@ func GetUserMedalLevelMap(db *gorm.DB) (map[uint64]uint8, map[uint8][]uint64, er } return medalTypes, result, nil } + +var medalCache = gcache.New(1000).LRU().Build() +var medalMapCache = gcache.New(1).LRU().Build() + +const medalKey = "MEDAL" + +// pprof看到内存分配很多 +// 加上15min lru +func MedalGetAll(db *gorm.DB) ([]ResMedal, error) { + if data, err := medalCache.Get(medalKey); err == nil { + return data.([]ResMedal), nil + } + rows := make([]ResMedal, 0) + err := db.Find(&rows).Error + if err != nil { + return nil, err + } + _ = medalCache.SetWithExpire(medalKey, rows, time.Minute*15) + return rows, nil +} + +func MedalGetAllMap(db *gorm.DB) (map[uint32]ResMedal, error) { + if data, err := medalMapCache.Get(medalKey); err == nil { + return data.(map[uint32]ResMedal), nil + } + rows, err := MedalGetAll(db) + if err != nil { + return nil, err + } + result := make(map[uint32]ResMedal, 0) + for _, i := range rows { + result[uint32(i.ID)] = i + } + _ = medalMapCache.SetWithExpire(medalKey, result, time.Minute*15) + return result, nil +} diff --git a/domain/model/res_m/property.go b/domain/model/res_m/property.go new file mode 100644 index 0000000..d390314 --- /dev/null +++ b/domain/model/res_m/property.go @@ -0,0 +1,149 @@ +package res_m + +import ( + "git.hilo.cn/hilo-common/domain" + "git.hilo.cn/hilo-common/resource/mysql" + "gorm.io/gorm" + "hilo-user/_const/enum/res_e" + "hilo-user/myerr" + "hilo-user/myerr/bizerr" +) + +type ResProperty struct { + mysql.Entity + *domain.Model `gorm:"-"` + Name mysql.Str + PicUrl mysql.Str + EffectUrl mysql.Str +} + +type ResPropertyDiamond struct { + mysql.Entity + *domain.Model `gorm:"-"` + ResPropertyId mysql.ID + DiamondNum mysql.Num + Second mysql.Num + Status mysql.UserYesNo +} + +type ResPropertyAvatar struct { + mysql.Entity + *domain.Model `gorm:"-"` + ResPropertyId mysql.ID + Type res_e.ResPropertyAvatarType + SendUserId mysql.ID + ReceiverUserId mysql.ID +} + +func InitResPropertyDiamond(model *domain.Model, resPropertyId mysql.ID, diamondNum mysql.Num, second mysql.Num) *ResPropertyDiamond { + return &ResPropertyDiamond{ + Model: model, + ResPropertyId: resPropertyId, + DiamondNum: diamondNum, + Second: second, + Status: mysql.NOUSER, + } +} + +//id获取头饰,不存在则抛异常 +func GetPropertyById(model *domain.Model, id mysql.ID) (*ResProperty, error) { + resProperty := ResProperty{} + if err := model.Db.Model(&ResProperty{}).First(&resProperty, id).Error; err != nil { + return nil, myerr.WrapErr(err) + } else { + resProperty.Model = model + return &resProperty, nil + } +} + +func GetResPropertyDiamond(model *domain.Model, resPropertyDiamondId mysql.ID) (*ResPropertyDiamond, error) { + resPropertyDiamond := ResPropertyDiamond{} + if err := model.Db.Model(&ResPropertyDiamond{}).First(&resPropertyDiamond, resPropertyDiamondId).Error; err != nil { + return nil, myerr.WrapErr(err) + } + if resPropertyDiamond.Status == mysql.NOUSER { + return nil, bizerr.ResPropertyDiamondNoUse + } + return &resPropertyDiamond, nil +} + +func GetResPropertyDiamondByPropertyIdOrNil(model *domain.Model, resPropertyId mysql.ID) (*ResPropertyDiamond, error) { + resPropertyDiamond := ResPropertyDiamond{} + if err := model.Db.Model(&ResPropertyDiamond{}).Where(&ResPropertyDiamond{ + ResPropertyId: resPropertyId, + }).First(&resPropertyDiamond).Error; err != nil { + if err == gorm.ErrRecordNotFound { + return nil, nil + } else { + return nil, myerr.WrapErr(err) + } + } + resPropertyDiamond.Model = model + return &resPropertyDiamond, nil +} + +//设置成未使用 +func (resPropertyDiamond *ResPropertyDiamond) SetUser() *ResPropertyDiamond { + resPropertyDiamond.Status = mysql.USER + return resPropertyDiamond +} + +//设置成未使用 +func (resPropertyDiamond *ResPropertyDiamond) SetNoUser() *ResPropertyDiamond { + resPropertyDiamond.Status = mysql.NOUSER + return resPropertyDiamond +} + +func (resPropertyDiamond *ResPropertyDiamond) SetDiamondNum(diamondNum uint32) *ResPropertyDiamond { + resPropertyDiamond.DiamondNum = diamondNum + return resPropertyDiamond +} + +func (resPropertyDiamond *ResPropertyDiamond) SetSecond(second uint32) *ResPropertyDiamond { + resPropertyDiamond.Second = second + return resPropertyDiamond +} + +func AddProperty(model *domain.Model, p *ResProperty) error { + return model.Db.Create(p).Error +} + +func EditProperty(model *domain.Model, propertyId uint64, name, picUrl, effectUrl string) error { + activityConfig := ResProperty{Entity: mysql.Entity{ID: propertyId}} + err := model.Db.First(&activityConfig).Error + if err != nil { + return err + } + activityConfig.Name = name + activityConfig.PicUrl = picUrl + activityConfig.EffectUrl = effectUrl + return model.Db.Save(&activityConfig).Error +} + +func (p *ResProperty) Get(db *gorm.DB) error { + return db.Where(p).First(p).Error +} + +func (p *ResProperty) GetAll(db *gorm.DB) (map[uint64]ResProperty, error) { + rows := make([]ResProperty, 0) + if err := db.Where(p).Find(&rows).Error; err != nil { + return nil, err + } + result := make(map[uint64]ResProperty, 0) + for _, i := range rows { + result[i.ID] = i + } + return result, nil +} + +func (p *ResPropertyAvatar) GetAll(db *gorm.DB) (map[uint64]ResPropertyAvatar, error) { + rows := make([]ResPropertyAvatar, 0) + if err := db.Where(p).Find(&rows).Error; err != nil { + return nil, myerr.WrapErr(err) + } + result := make(map[uint64]ResPropertyAvatar, 0) + for _, i := range rows { + result[i.ResPropertyId] = i + } + return result, nil +} diff --git a/domain/model/res_m/resMultiText.go b/domain/model/res_m/resMultiText.go index 59e2ded..e2f4312 100755 --- a/domain/model/res_m/resMultiText.go +++ b/domain/model/res_m/resMultiText.go @@ -27,7 +27,7 @@ func GetResMultiTextBy(db *gorm.DB, msgId uint, Language mysql.Str) (*ResMultiTe if err == gorm.ErrRecordNotFound { if err := db.Where(&ResMultiText{ MsgId: msgId, - Language: res_e.DEFAULT_LANG, + Language: res_e.DefaultLang, }).First(&r).Error; err != nil { return nil, myerr.WrapErr(err) } diff --git a/domain/model/user_m/medal.go b/domain/model/user_m/medal.go index aa0184a..9712bb4 100755 --- a/domain/model/user_m/medal.go +++ b/domain/model/user_m/medal.go @@ -89,3 +89,15 @@ func GetUserMedal(db *gorm.DB, userId mysql.ID) ([]uint32, error) { } return result, nil } + +func BatchGetUserMedalMerge(logger *logrus.Entry, db *gorm.DB, userIds []mysql.ID) (map[uint64][]uint32, error) { + result := make(map[uint64][]uint32, 0) + for _, u := range userIds { + a, err := GetUserMedalMerge(logger, db, u) + if err != nil { + return nil, err + } + result[u] = a + } + return result, nil +} diff --git a/domain/model/user_m/property.go b/domain/model/user_m/property.go index 17a0325..c5a80cf 100644 --- a/domain/model/user_m/property.go +++ b/domain/model/user_m/property.go @@ -108,4 +108,32 @@ type UserPropertyLog struct { Type property_e.UserPropertyLogType AddSecond *mysql.Num UpdateEndTime *time.Time -} \ No newline at end of file +} + +func (userProperty *UserProperty) BatchGet(db *gorm.DB, userIds []uint64) (map[uint64]uint64, error) { + rows := make([]UserProperty, 0) + if err := db.Model(userProperty). + Where("end_time > NOW() AND user_id IN ?", userIds).Order("id DESC").Find(&rows).Error; err != nil { + return nil, err + } + result := make(map[uint64]uint64, 0) + tmp := make(map[uint64]uint64, 0) + for _, i := range rows { + if _, ok := result[i.UserId]; !ok { + if i.Using == property_e.YesUsing { + // using = true且id最大,就确定是当前使用的 + result[i.UserId] = i.PropertyId + } else if _, ok := tmp[i.UserId]; !ok { + // using = false且id最大,先记下,因为不知道还有没有using=true的 + tmp[i.UserId] = i.PropertyId + } + } + } + for k, v := range tmp { + // result中没有的,就采用tmp保存的 + if _, ok := result[k]; !ok { + result[k] = v + } + } + return result, nil +} diff --git a/domain/model/user_m/superManager.go b/domain/model/user_m/superManager.go new file mode 100644 index 0000000..12e4483 --- /dev/null +++ b/domain/model/user_m/superManager.go @@ -0,0 +1,79 @@ +package user_m + +import ( + "git.hilo.cn/hilo-common/domain" + "git.hilo.cn/hilo-common/resource/mysql" + "gorm.io/gorm" + "hilo-user/myerr" + "strings" +) + +type SuperManager struct { + mysql.Entity + *domain.Model `gorm:"-"` + UserId mysql.ID + IsAll bool + Countries string +} + +func IsSuperManager(model *domain.Model, userId mysql.ID) (bool, error) { + var n int64 + if err := model.Db.Model(&SuperManager{}).Where(&SuperManager{ + UserId: userId, + }).Count(&n).Error; err != nil { + return false, myerr.WrapErr(err) + } + return n > 0, nil +} + +// 对某人是否有超管权限 +func IsSuperManagerV2(model *domain.Model, userId, targetUserId mysql.ID) (bool, error) { + var man SuperManager + if err := model.Db.Model(&SuperManager{}).Where(&SuperManager{ + UserId: userId, + }).First(&man).Error; err != nil { + if err != gorm.ErrRecordNotFound { + model.Log.Errorf("IsSuperManagerV2 fail:%v", err) + } + return false, nil + } + if man.IsAll { + return true, nil + } + targetUser, err := GetUser(model, targetUserId) + if err != nil { + return false, err + } + countries := strings.Split(man.Countries, ",") + for _, c := range countries { + if c == targetUser.Country { + return true, nil + } + } + return false, nil +} + +func GetSuperManagerAll(model *domain.Model) ([]uint64, error) { + var superManagers []SuperManager + if err := model.Db.Model(&SuperManager{}).Find(&superManagers).Error; err != nil { + return nil, myerr.WrapErr(err) + } + userIds := make([]uint64, 0, len(superManagers)) + for i, _ := range superManagers { + userIds = append(userIds, superManagers[i].UserId) + } + return userIds, nil +} + +/* +func GetSuperManagerMap(model *domain.Model) (map[uint64]struct{}, error) { + userIds, err := GetSuperManagerAll(model) + if err != nil { + return nil, err + } + userIdMap := map[uint64]struct{}{} + for i, _ := range userIds { + userIdMap[userIds[i]] = struct{}{} + } + return userIdMap, nil +}*/ diff --git a/domain/model/user_m/user.go b/domain/model/user_m/user.go index a749131..d1b9f36 100755 --- a/domain/model/user_m/user.go +++ b/domain/model/user_m/user.go @@ -6,6 +6,7 @@ import ( "hilo-user/_const/enum/user_e" "hilo-user/myerr" "hilo-user/myerr/bizerr" + "time" ) //用户信息 @@ -106,3 +107,40 @@ func GetUserByExtId(model *domain.Model, externalId string) (*User, error) { user.Model = model return &user, nil } + +func ToUserTiny(user *User) *UserTiny { + return &UserTiny{ + ID: user.ID, + ExternalId: user.ExternalId, + Avatar: user.Avatar, + Nick: user.Nick, + Sex: user.Sex, + Code: user.Code, + Description: user.Description, + Country: user.Country, + CountryIcon: user.CountryIcon, + IsPrettyCode: user.IsPrettyCode(), + IsLogout: IfLogout(user.LogoutTime), + Birthday: BirthdayToUint64(&user.Birthday), + } +} + +func IfLogout(logoutTime int64) bool { + return logoutTime > 0 && time.Now().Unix() > logoutTime +} + +func BirthdayToUint64(birthday *mysql.Timestamp) *uint64 { + if *birthday == 0 { + return nil + } + return (*uint64)(birthday) +} + +//获取用户 +func GetUsers(model *domain.Model, ids []mysql.ID) ([]*User, error) { + res := make([]*User, 0) + if err := model.Db.WithContext(model.Context).Where("id in (?)", ids).Find(&res).Error; err != nil { + return nil, myerr.WrapErr(err) + } + return res, nil +} diff --git a/domain/model/user_m/vip.go b/domain/model/user_m/vip.go index 1b469a4..2274d56 100755 --- a/domain/model/user_m/vip.go +++ b/domain/model/user_m/vip.go @@ -43,3 +43,24 @@ func GetVip(db *gorm.DB, userId uint64) (*UserVip, error) { } return nil, nil } + +func BatchGetVips(userIds []uint64) (map[uint64]*int64, error) { + rows := make([]UserVip, 0) + err := mysql.Db.Where("user_id IN ?", userIds).Find(&rows).Error + if err != nil { + return nil, err + } + + result := make(map[uint64]*int64, 0) + for _, i := range userIds { + result[i] = nil + } + now := time.Now() + for _, i := range rows { + if i.ExpireAt.After(now) { + ts := i.ExpireAt.Unix() + result[i.UserId] = &ts + } + } + return result, nil +} diff --git a/domain/service/cp_s/cp_relation.go b/domain/service/cp_s/cp_relation.go new file mode 100644 index 0000000..511e4d0 --- /dev/null +++ b/domain/service/cp_s/cp_relation.go @@ -0,0 +1,188 @@ +package cp_s + +import ( + "encoding/json" + "git.hilo.cn/hilo-common/_const/common" + "git.hilo.cn/hilo-common/_const/enum/diamond_e" + "git.hilo.cn/hilo-common/domain" + "git.hilo.cn/hilo-common/mycontext" + "git.hilo.cn/hilo-common/myerr/comerr" + "git.hilo.cn/hilo-common/rpc" + "git.hilo.cn/hilo-common/sdk/tencentyun" + "git.hilo.cn/hilo-common/txop/msg" + "hilo-user/_const/enum/cp_e" + "hilo-user/domain/model/cp_m" + "hilo-user/domain/model/diamond_m" + "hilo-user/domain/model/user_m" + "hilo-user/myerr" + "hilo-user/myerr/bizerr" +) + +func InviteCpRelation(myCtx *mycontext.MyContext, myUserId uint64, externalId, lang string) error { + model := domain.CreateModelContext(myCtx) + user, err := user_m.GetUser(model, myUserId) + if err != nil { + return err + } + userInvite, err := user_m.GetUserByExtId(model, externalId) + if err != nil { + return err + } + + if userInvite.ID == myUserId { + return bizerr.InvalidParameter + } + + // 自己是否有cp了 + myCp, err := cp_m.GetCp(model, myUserId) + if err != nil { + return err + } + if myCp.Id > 0 { + return myerr.ToLocal(msg.GetErrByLanguage(model, common.MSG_ID_ALREADY_HAS_CP, lang, comerr.AlreadyHasCp)) + } + // 对方是否已经有cp了 + inviCp, err := cp_m.GetCp(model, userInvite.ID) + if err != nil { + return err + } + if inviCp.Id > 0 { + return myerr.ToLocal(msg.GetErrByLanguage(model, common.MSG_ID_ALREADY_HAS_CP, lang, comerr.AlreadyHasCp)) + } + + // 我是否发起过cp邀请,且还未被处理 + myInvite, err := cp_m.GetCpInvite(model, user.ID, 0, cp_e.CpInvite) + if err != nil { + model.Log.Errorf("InviteCpRelation myUserId:%d, err:%v", myUserId, err) + return err + } + if myInvite != nil && myInvite.Id > 0 { + return bizerr.CpAlreadyInvite + } + + content, err := msg.GetResMultiTextBy(model, common.MSG_ID_WANT_BE_YOUR_CP, lang) + if err != nil { + return err + } + tip, err := msg.GetResMultiTextBy(model, common.MSG_ID_EXPIRES_AFTER_24_H, lang) + if err != nil { + return err + } + err = model.Transaction(func(model *domain.Model) error { + // 创建邀请记录 + cpInvId, err := cp_m.CreateCpInvite(model, myUserId, userInvite.ID, cp_e.CpRelationInviteDiamond) + if err != nil { + model.Log.Errorf("InviteCpRelation myUserId:%d, err:%v", myUserId, err) + return err + } + // 扣费 + err = diamond_m.ChangeDiamondAccountDetail(model, diamond_e.CpInvite, cpInvId, myUserId, cp_e.CpRelationInviteDiamond) + if err != nil { + model.Log.Errorf("InviteCpRelation myUserId:%d, err:%v", myUserId, err) + return err + } + // 发送私信-邀请 + data, _ := json.Marshal(cp_m.CpInviteMessage{ + Identifier: "CpInviteMessage", + Msg: content, + Tip: tip, + Sender: user_m.ToUserTiny(user), + MsgType: 1, + MsgId: cpInvId, + }) + if err := tencentyun.BatchSendCustomMsg(model, 1, user.ExternalId, []string{userInvite.ExternalId}, string(data), "cp邀请"); err != nil { + model.Log.Errorf("BatchSendCustomMsg fail:%v", err) + return err + } + return nil + }) + if err != nil { + model.Log.Errorf("InviteCpRelation myUserId:%d, err:%v", myUserId, err) + return err + } + // socket 推送弹窗 + go rpc.SendCpInviteNotice(userInvite.ID, user.Code, user.Nick, user.Avatar, content, user.ExternalId) + + return nil +} + +func CancelCpRelation(myCtx *mycontext.MyContext, myUserId uint64, externalId, lang string) error { + model := domain.CreateModelContext(myCtx) + user, err := user_m.GetUser(model, myUserId) + if err != nil { + return err + } + userRec, err := user_m.GetUserByExtId(model, externalId) + if err != nil { + return err + } + + if userRec.ID == myUserId { + return bizerr.InvalidParameter + } + + tip, err := msg.GetResMultiTextBy(model, common.MSG_ID_EXPIRES_AFTER_24_H, lang) + if err != nil { + return err + } + content, err := msg.GetResMultiTextBy(model, common.MSG_ID_WANT_UNBIND_CP, lang) + if err != nil { + return err + } + + // 自己没有cp了 + myCp, err := cp_m.GetCp(model, myUserId) + if err != nil { + return err + } + if myCp.Id == 0 { + return myerr.WrapErr(bizerr.InvalidParameter) + } + // 对方没有cp了 + inviCp, err := cp_m.GetCp(model, userRec.ID) + if err != nil { + return err + } + if inviCp.Id == 0 { + return myerr.WrapErr(bizerr.InvalidParameter) + } + + // 是否有关于我的cp解除申请,且还未被处理 + myCancel, err := cp_m.GetCpCancelWithMe(model, user.ID, cp_e.CpCancel) + if err != nil { + model.Log.Errorf("InviteCpRelation myUserId:%d, err:%v", myUserId, err) + return err + } + if myCancel != nil && myCancel.Id > 0 { + return bizerr.CpHaveCancelNoDeal + } + + err = model.Transaction(func(model *domain.Model) error { + // 创建邀请记录 + cancelId, err := cp_m.CreateCpCancel(model, myUserId, userRec.ID) + if err != nil { + model.Log.Errorf("CancelCpRelation myUserId:%d, err:%v", myUserId, err) + return err + } + // 发送私信-发起解除 + data, _ := json.Marshal(cp_m.CpCancelMessage{ + Identifier: "CpCancelMessage", + Msg: content, + Tip: tip, + Sender: user_m.ToUserTiny(user), + MsgType: 2, + MsgId: cancelId, + }) + if err := tencentyun.BatchSendCustomMsg(model, 1, user.ExternalId, []string{userRec.ExternalId}, string(data), "cp解除"); err != nil { + model.Log.Errorf("CancelCpRelation BatchSendCustomMsg fail:%v", err) + return err + } + return nil + }) + if err != nil { + model.Log.Errorf("CancelCpRelation myUserId:%d, err:%v", myUserId, err) + return err + } + + return nil +} diff --git a/domain/service/event_s/cp_level.go b/domain/service/event_s/cp_level.go new file mode 100644 index 0000000..6d0a4e1 --- /dev/null +++ b/domain/service/event_s/cp_level.go @@ -0,0 +1,71 @@ +package event_s + +import ( + "git.hilo.cn/hilo-common/domain" + "github.com/jinzhu/now" + "hilo-user/_const/enum/cp_e" + "hilo-user/domain/event/gift_ev" + "hilo-user/domain/model/cp_m" + "time" +) + +// 送礼增加cp等级 +// 送礼增加cp排行榜 +func CpGiftEvent() { + gift_ev.AddSendGiftEventSync(func(model *domain.Model, event interface{}) error { + sendGiftEvent, ok := event.(*gift_ev.SendGiftEvent) + if !ok { + model.Log.Errorf("AddSendGiftEventAsync event type err") + return nil + } + // 只处理cp礼物 + if !sendGiftEvent.ResGift.Cp { + return nil + } + for _, receiverUid := range sendGiftEvent.ReceiveUserIds { + diamonds := sendGiftEvent.GiftN * sendGiftEvent.ResGift.DiamondNum + // 有cp关系 + if cpRelation, exits := cp_m.GetCpRelationPair(model, sendGiftEvent.SendUserId, receiverUid); exits { + if err := cp_m.AddCpLevelPoints(model, cpRelation, diamonds, sendGiftEvent.SceneType); err != nil { + model.Log.Errorf("AddCpLevelPoints fail:%v", err) + } + if err := cp_m.AddCpDayRank(model, cpRelation, diamonds); err != nil { + model.Log.Errorf("AddCpDayRank fail:%v", err) + } + // 检查最新的等级 + if cpLevel := cp_m.GetCpLevel(model, cpRelation.Id); cpLevel.CpId >= 0 { + points := cpLevel.Points + cp_e.CpLevelPoints[cpLevel.Level] + if err := cp_m.UpdateCpAchievement(model, cpLevel.CpId, cpRelation.UserId1, cpRelation.UserId2, cp_e.CpAchievementLevel, points); err != nil { + model.Log.Errorf("UpdateCpAchievement fail:%v", err) + } + } + // 检查最高的分数 + for _, queryType := range []string{"day", "week", "month"} { + var beginDate, endDate string + var cpAchievementType cp_e.CpAchievement + switch queryType { + case "day": + beginDate, endDate = time.Now().Format("2006-01-02"), time.Now().Format("2006-01-02") + cpAchievementType = cp_e.CpAchievementDayRank + case "week": + beginDate = now.BeginningOfWeek().Format("2006-01-02") + endDate = now.EndOfWeek().Format("2006-01-02") + cpAchievementType = cp_e.CpAchievementWeekRank + case "month": + beginDate = now.BeginningOfMonth().Format("2006-01-02") + endDate = now.EndOfMonth().Format("2006-01-02") + cpAchievementType = cp_e.CpAchievementMonthRank + } + if data := cp_m.GetCpDayRank(model, beginDate, endDate, cpRelation.Id); data.Score > 0 { + if err := cp_m.UpdateCpAchievement(model, cpRelation.Id, cpRelation.UserId1, cpRelation.UserId2, cpAchievementType, data.Score); err != nil { + model.Log.Errorf("UpdateCpAchievement fail:%v", err) + } + } + } + // 检查最新日周月榜单 + return nil // 业务场景允许提前break(cp是唯一的) + } + } + return nil + }) +} diff --git a/domain/service/event_s/cp_visit.go b/domain/service/event_s/cp_visit.go new file mode 100644 index 0000000..b0aa8bc --- /dev/null +++ b/domain/service/event_s/cp_visit.go @@ -0,0 +1,29 @@ +package event_s + +import ( + "git.hilo.cn/hilo-common/domain" + "git.hilo.cn/hilo-common/resource/mysql" + "hilo-user/_const/enum/cp_e" + "hilo-user/domain/event/cp_ev" + "hilo-user/domain/model/cp_m" + "hilo-user/myerr/bizerr" +) + +// cp空间访问 +func CpSpaceVisitEvent() { + cp_ev.AddCpSpaceVisitAsync(func(model *domain.Model, event interface{}) error { + e, ok := event.(*cp_ev.SpaceVisitEvent) + if !ok { + return bizerr.InvalidParameter + } + if err := cp_m.AddCpSpaceVisitor(model, e.CpId, e.UserId1, e.UserId2, e.UserId); err != nil { + model.Log.Errorf("AddCpSpaceVisitor fail:%v", err) + } + if cnt := cp_m.CountCpSpaceVisitors(model, e.CpId); cnt > 0 { + if err := cp_m.UpdateCpAchievement(model, e.CpId, e.UserId1, e.UserId2, cp_e.CpAchievementVisitors, mysql.Num(cnt)); err != nil { + model.Log.Errorf("UpdateCpAchievement fail:%v", err) + } + } + return nil + }) +} diff --git a/domain/service/event_s/event_init.go b/domain/service/event_s/event_init.go index 9b0c69e..14d1079 100644 --- a/domain/service/event_s/event_init.go +++ b/domain/service/event_s/event_init.go @@ -14,6 +14,8 @@ import ( func EventInit() { UserBagSendEvent() + CpGiftEvent() + CpSpaceVisitEvent() } func UserBagSendEvent() { diff --git a/domain/service/event_s/send_gift.go b/domain/service/event_s/send_gift.go new file mode 100644 index 0000000..d740c97 --- /dev/null +++ b/domain/service/event_s/send_gift.go @@ -0,0 +1,77 @@ +package event_s + +import ( + "encoding/json" + "git.hilo.cn/hilo-common/domain" + "git.hilo.cn/hilo-common/mycontext" + "git.hilo.cn/hilo-common/resource/mysql" + "hilo-user/domain/event/gift_ev" + "hilo-user/domain/model/event_m" + "runtime/debug" +) + +// 每次处理500条 +const BatchCount = 500 + +type GiftSendEventService struct { + svc *domain.Service +} + +func NewGiftSendEventService(myContext *mycontext.MyContext) *GiftSendEventService { + svc := domain.CreateService(myContext) + return &GiftSendEventService{svc} +} + +// +func (s *GiftSendEventService) Consume() error { + defer func() { + if err := recover(); err != nil { + s.svc.Log.Errorf("ExceptionHandle GiftSendEventService Consume SYSTEM ACTION PANIC: %v, stack: %v", err, string(debug.Stack())) + } + }() + var model = domain.CreateModel(s.svc.CtxAndDb) + return model.Transaction(func(model *domain.Model) error { + events, offset, err := event_m.FetchEventGiftSend(model, BatchCount) + if err != nil { + return err + } + for k := range events { + cpEvent := &event_m.EventGiftSend{ + Entity: mysql.Entity{ + ID: events[k].ID, + CreatedTime: events[k].CreatedTime, + UpdatedTime: events[k].UpdatedTime, + }, + Proto: events[k].Proto, + Payload: events[k].Payload, + MarkHiloUser: events[k].MarkHiloUser, + } + if cpEvent.MarkHiloUser == mysql.YES { + model.Log.Warnf("already consume msg :%v", cpEvent) + continue + } + sendGiftEvent := new(gift_ev.SendGiftEvent) + if err := json.Unmarshal(cpEvent.Payload, sendGiftEvent); err != nil { + model.Log.Errorf("json msg fail,event:%v,err:%v", cpEvent, err) + continue + } + // 标记已经处理,mark比publish事件提前,尽量避免异步事件重复执行 + cpEvent.Model = model + err = cpEvent.MarkDone() + if err != nil { + model.Log.Errorf("consume msg fail,event:%v,err:%v", cpEvent, err) + return err + } + if err := gift_ev.PublishSendGiftEvent(model, sendGiftEvent); err != nil { + model.Log.Errorf("PublishSendGiftEvent fail,event:%v,err:%v", string(cpEvent.Payload), err) + return err + } + } + // 最后一次提交offset + if len(events) > 0 { + offset.MarkOffset = events[len(events)-1].ID + return offset.Persistence() + } + return nil + }) +} diff --git a/go.mod b/go.mod index 52c832b..d2948d4 100644 --- a/go.mod +++ b/go.mod @@ -19,6 +19,16 @@ require ( gorm.io/gorm v1.23.8 ) +require ( + github.com/jonboulle/clockwork v0.3.0 // indirect + github.com/robfig/cron v1.2.0 +) + +require ( + github.com/bluele/gcache v0.0.2 + github.com/jinzhu/now v1.1.5 +) + require ( github.com/KyleBanks/depth v1.2.1 // indirect github.com/PuerkitoBio/purell v1.1.1 // indirect @@ -37,7 +47,8 @@ require ( github.com/go-playground/universal-translator v0.17.0 // indirect github.com/go-playground/validator/v10 v10.2.0 // indirect github.com/go-sql-driver/mysql v1.7.0 // indirect - github.com/golang/protobuf v1.5.0 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/google/go-cmp v0.5.8 // indirect github.com/hashicorp/consul/api v1.7.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.1 // indirect github.com/hashicorp/go-hclog v0.12.0 // indirect @@ -47,11 +58,10 @@ require ( github.com/hashicorp/golang-lru v0.5.0 // indirect github.com/hashicorp/serf v0.9.3 // indirect github.com/jinzhu/inflection v1.0.0 // indirect - github.com/jinzhu/now v1.1.5 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/joho/godotenv v1.3.0 // indirect - github.com/jonboulle/clockwork v0.3.0 // indirect github.com/json-iterator/go v1.1.9 // indirect + github.com/kr/pretty v0.3.0 // indirect github.com/leodido/go-urn v1.2.0 // indirect github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible // indirect github.com/lestrrat-go/strftime v1.0.6 // indirect @@ -63,11 +73,16 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.1 // indirect github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 // indirect + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.479 // indirect + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ims v1.0.479 // indirect + github.com/tencentyun/tls-sig-api-v2-golang v1.0.0 // indirect github.com/ugorji/go/codec v1.1.7 // indirect golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 // indirect golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect golang.org/x/text v0.3.6 // indirect - golang.org/x/tools v0.0.0-20190907020128-2ca718005c18 // indirect + golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e // indirect + google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect + google.golang.org/grpc v1.42.0 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/ini.v1 v1.63.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index 298c630..a4bdf02 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,6 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/AgoraIO/Tools/DynamicKey/AgoraDynamicKey/go/src v0.0.0-20200910100525-12b7f1b63a6a/go.mod h1:4bXIK0ntDk9CqAXobmomWd7dedbfNv/aaIpmpqqzt+A= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= @@ -10,15 +13,53 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafo github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/aliyun/alibaba-cloud-sdk-go v1.61.1274 h1:u48e7I7h/BY5uDP8xiIFNaUkdTVk7hjj/Sucg8FrxNU= github.com/aliyun/alibaba-cloud-sdk-go v1.61.1274/go.mod h1:9CMdKNL3ynIGPpfTcdwTvIm8SGuAZYYC4jFVSSvE1YQ= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/aws/aws-sdk-go-v2 v1.16.2/go.mod h1:ytwTPBG6fXTZLxxeeCCWj2/EMYp/xDUgX+OET6TLNNU= +github.com/aws/aws-sdk-go-v2 v1.16.12/go.mod h1:C+Ym0ag2LIghJbXhfXZ0YEEp49rBWowxKzJLUoob0ts= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.1/go.mod h1:n8Bs1ElDD2wJ9kCRTczA83gYbBmjSwZp3umc6zF4EeM= +github.com/aws/aws-sdk-go-v2/config v1.15.3/go.mod h1:9YL3v07Xc/ohTsxFXzan9ZpFpdTOFl4X65BAKYaz8jg= +github.com/aws/aws-sdk-go-v2/credentials v1.11.2/go.mod h1:j8YsY9TXTm31k4eFhspiQicfXPLZ0gYXA50i4gxPE8g= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.3/go.mod h1:uk1vhHHERfSVCUnqSqz8O48LBYDSC+k6brng09jcMOk= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.9/go.mod h1:AnVH5pvai0pAF4lXRq0bmhbes1u9R8wTE+g+183bZNM= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.19/go.mod h1:llxE6bwUZhuCas0K7qGiu5OgMis3N7kdWtFSxoHmJ7E= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.3/go.mod h1:ssOhaLpRlh88H3UmEcsBoVKq309quMvm3Ds8e9d4eJM= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.13/go.mod h1:lB12mkZqCSo5PsdBFLNqc2M/OOYgNAy8UtaktyuWvE8= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.10/go.mod h1:8DcYQcz0+ZJaSxANlHIsbbi6S+zMwjwdDqwW3r9AzaE= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.0/go.mod h1:Nf3QiqrNy2sj3Rku+9z4nN/bThI97gQmR7YxG3s+ez8= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.1/go.mod h1:GeUru+8VzrTXV/83XyMJ80KpH8xO89VPoUileyNQ+tc= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.3/go.mod h1:Seb8KNmD6kVTjwRjVEgOT5hPin6sq+v4C2ycJQDwuH8= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.3/go.mod h1:wlY6SVjuwvh3TVRpTqdy4I1JpBFLX4UGeKZdWntaocw= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.3/go.mod h1:Bm/v2IaN6rZ+Op7zX+bOUMdL4fsrYZiD0dsjLhNKwZc= +github.com/aws/aws-sdk-go-v2/service/rekognition v1.20.1/go.mod h1:JziF+gVo2UhFc4A52j/0Zcjpba2oxoMp/TyJ+0tZkIg= +github.com/aws/aws-sdk-go-v2/service/s3 v1.26.5/go.mod h1:qFKU5d+PAv+23bi9ZhtWeA+TmLUz7B/R59ZGXQ1Mmu4= +github.com/aws/aws-sdk-go-v2/service/sso v1.11.3/go.mod h1:7UQ/e69kU7LDPtY40OyoHYgRmgfGM4mgsLYtcObdveU= +github.com/aws/aws-sdk-go-v2/service/sts v1.16.3/go.mod h1:bfBj0iVmsUyUg4weDB4NxktD9rDGeKSVWnjTnwbx9b8= +github.com/aws/smithy-go v1.11.2/go.mod h1:3xHYmszWVx2c0kIwQeEVf9uSm4fYZt67FBJnwub1bgM= +github.com/aws/smithy-go v1.13.0/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bluele/gcache v0.0.2 h1:WcbfdXICg7G/DGBh1PFfcirkWOQV+v077yF1pSy3DGw= +github.com/bluele/gcache v0.0.2/go.mod h1:m15KV+ECjptwSPxKhOhQoAFQVtUFjTVkc3H8o0t/fp0= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -26,10 +67,19 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumC github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/gzip v0.0.1 h1:ezvKOL6jH+jlzdHNE4h9h8q8uMpDQjyl0NN0Jd7jozc= github.com/gin-contrib/gzip v0.0.1/go.mod h1:fGBJBCdt6qCZuCAOwWuFhBB4OOq9EFqlo5dEaFhhu5w= @@ -70,18 +120,41 @@ github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c h1:964Od4U6p2jUkFxvCydnIczKteheJEzHRToSGK3Bnlw= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/consul/api v1.7.0 h1:tGs8Oep67r8CcA2Ycmb/8BLBcJ70St44mF2X10a/qPg= github.com/hashicorp/consul/api v1.7.0/go.mod h1:1NSuaUUkFaJzMasbfq/11wKYWSR67Xn6r2DXKhuDNFg= github.com/hashicorp/consul/sdk v0.6.0 h1:FfhMEkwvQl57CildXJyGHnwGGM4HMODGyfjGwNM1Vdw= @@ -117,6 +190,8 @@ github.com/hashicorp/memberlist v0.2.2 h1:5+RffWKwqJ71YPu9mWsF7ZOscZmwfasdA8kbdC github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= github.com/hashicorp/serf v0.9.3 h1:AVF6JDQQens6nMHT9OGERBvK0f8rPrAGILnsKLr6lzM= github.com/hashicorp/serf v0.9.3/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/jinzhu/copier v0.3.5 h1:GlvfUwHk62RokgqVNvYsku0TATCF7bAHVwEXoBh3iJg= github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= @@ -139,12 +214,14 @@ github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGn github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2ttpEmkA4aQ3j4u9dStX2t4M8UM6qqNsG8= @@ -186,9 +263,20 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= +github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c h1:Lgl0gzECD8GnQ5QCWA8o6BtfL6mDH5rQgM4/fX3avOs= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -198,8 +286,14 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 h1:mZHayPoR0lNmnHyvtYjDeq0zlVHn9K/ZXoy17ylucdo= github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5/go.mod h1:GEXHk5HgEKCvEIIrSpFI3ozzG5xOKA2DVlEX/gGnewM= +github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ= +github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= @@ -211,11 +305,13 @@ github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14/go.mod h1:gxQT6pBGRuIGunNf/+tSOB5OHvguWi8Tbt82WOkf35E= @@ -224,8 +320,16 @@ github.com/swaggo/gin-swagger v1.2.0/go.mod h1:qlH2+W7zXGZkczuL+r2nEBR2JTT+/lX05 github.com/swaggo/swag v1.5.1/go.mod h1:1Bl9F/ZBpVWh22nY0zmYyASPO1lI/zIwRDrpZU+tv8Y= github.com/swaggo/swag v1.6.7 h1:e8GC2xDllJZr3omJkm9YfmK0Y56+rMO3cg0JBKNz09s= github.com/swaggo/swag v1.6.7/go.mod h1:xDhTyuFIujYiN3DKWC/H/83xcfHp+UE/IzWWampG7Zc= +github.com/tealeg/xlsx v1.0.5/go.mod h1:btRS8dz54TDnvKNosuAqxrM1QgN1udgk9O34bDCnORM= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.479 h1:3kwDb6p1J3LxmwnNgSSEheemPffo+vMewoDzKysYdig= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.479/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ims v1.0.479 h1:xDmo1rBmSJ7Hw3iOa6DQ/++z1d7pgsEVKrZno35zR7w= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ims v1.0.479/go.mod h1:3YlJ1g/Ko/iFgyBJdok+dRPTH6zRB6BBopAcFkbujPc= +github.com/tencentyun/tls-sig-api-v2-golang v1.0.0 h1:NavMw9XO2iCLv8hTKaJW2kTaGR2SdNljMABbe39yu6Q= +github.com/tencentyun/tls-sig-api-v2-golang v1.0.0/go.mod h1:u7WiArmCTXTaQAHJwAOaLgpJ5e2xdY5/cgMEy3ubL60= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go v1.1.5-pre/go.mod h1:FwP/aQVg39TXzItUBMwnWp9T9gPQnXw4Poh4/oBQZ/0= +github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go/codec v0.0.0-20181022190402-e5e69e061d4f/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ugorji/go/codec v1.1.5-pre/go.mod h1:tULtS6Gy1AE1yCENaw4Vb//HLH5njI2tfCQDUqRd8fI= @@ -233,14 +337,29 @@ github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392 h1:ACG4HJsFiNMf47Y4PeRoebLNy/2lXT9EtprMuTFWt1M= golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -249,11 +368,21 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 h1:DzZ89McO9/gWPsQXS/FVKAlG02ZjaQ6AlZRBimEYOd0= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -261,48 +390,96 @@ golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606050223-4d9ae51c2468/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190611222205-d73e1c7e250b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190907020128-2ca718005c18 h1:xFbv3LvlvQAmbNJFCBKRv1Ccvnh9FVsW0FX2kTWWowE= golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e h1:4nW4NLDYnU28ojHaHO8OVxFHk/aQ33U01a9cjED+pzE= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.42.0 h1:XT2/MFpuPFsEX2fWh3YQtHkZ+WYZFQRfaUgLZYj/p6A= +google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.63.2 h1:tGK/CyBg7SMzb60vP1M03vNZ3VDu3wGQJwn7Sxi9r3c= gopkg.in/ini.v1 v1.63.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= @@ -312,3 +489,5 @@ gorm.io/driver/mysql v1.4.3 h1:/JhWJhO2v17d8hjApTltKNADm7K7YI2ogkR7avJUL3k= gorm.io/driver/mysql v1.4.3/go.mod h1:sSIebwZAVPiT+27jK9HIwvsqOGKx3YMPmrA3mBJR10c= gorm.io/gorm v1.23.8 h1:h8sGJ+biDgBA1AD1Ha9gFCx7h8npU7AsLdlkX0n2TpE= gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/main.go b/main.go index 073ab09..2c2a898 100755 --- a/main.go +++ b/main.go @@ -3,6 +3,7 @@ package main import ( "fmt" "git.hilo.cn/hilo-common/resource/consul" + "hilo-user/cron" "hilo-user/domain/service/event_s" "hilo-user/route" ) @@ -14,7 +15,7 @@ const ( ) func main() { - //cron.Init() // 开启定时任务 + cron.Init() // 开启定时任务 event_s.EventInit() // 注册事件(内部事件+mysql拟kafka) r := route.InitRouter() // 注册路由 consul.RegisterToConsul(PORT, RegisterName, RegisterTag) // 服务注册 diff --git a/myerr/bizerr/bizCode.go b/myerr/bizerr/bizCode.go index f704715..e8f79c5 100755 --- a/myerr/bizerr/bizCode.go +++ b/myerr/bizerr/bizCode.go @@ -20,30 +20,12 @@ var ( DiamondFrequency = myerr.NewBusinessCode(4001, "Diamond operation frequency too high", myerr.BusinessData{}) DiamondAccountFrozen = myerr.NewBusinessCode(4004, "Diamond Account Frozen", myerr.BusinessData{}) - // 游戏 - GameInvalidParameter = myerr.NewGameError(10002, "invalid parameter") - GameTokenInvalid = myerr.NewGameError(1004, "user token invalid") - GameTokenExpire = myerr.NewGameError(1005, "user token expire") + ResPropertyDiamondNoUse = myerr.NewBusinessCode(5004, "Property can not buy", myerr.BusinessData{}) //头饰不能买 + CpNotRelation = myerr.NewBusinessCode(6000, "cp not relation", myerr.BusinessData{}) // 群组 GroupNotFound = myerr.NewBusinessCode(14001, "Group not found", myerr.BusinessData{}) // 找不到该群 - // 游戏服务的错误码,6位,50XXX - GameAddNoPermissions = myerr.NewBusinessCode(50100, "Only room administrators can create users", myerr.BusinessData{}) // 权限不足 - GameAddNotOnMic = myerr.NewBusinessCode(50101, "Need on mic", myerr.BusinessData{}) // 需要在麦上才能创建、加入游戏 - GameHaveNoEnd = myerr.NewBusinessCode(50102, "Group user have no end", myerr.BusinessData{}) // 房间还有未结束的游戏 - GameNotFound = myerr.NewBusinessCode(50103, "Game not found", myerr.BusinessData{}) // - GameStart = myerr.NewBusinessCode(50104, "Gaming", myerr.BusinessData{}) // - GameAlreadyJoin = myerr.NewBusinessCode(50105, "Already Joined", myerr.BusinessData{}) // 已经加入了游戏 - GameNotJoin = myerr.NewBusinessCode(50106, "Not Join user", myerr.BusinessData{}) // 还未加入游戏 - GameCannotClose = myerr.NewBusinessCode(50107, "Have no power to close user", myerr.BusinessData{}) // 没有权限关闭游戏 - GameCloseGaming = myerr.NewBusinessCode(50108, "Can't close a user in progress", myerr.BusinessData{}) // 不能关闭进行中的游戏 - GamePlayerNumWrong = myerr.NewBusinessCode(50109, "Game player num wrong", myerr.BusinessData{}) // 玩家数量错误 - GameHaveNoMyRoom = myerr.NewBusinessCode(50110, "Have no my room", myerr.BusinessData{}) // 自己没有房间 - GameHaveNoEndGame = myerr.NewBusinessCode(50111, "The last user is not over yet, cannot create/join a user", myerr.BusinessData{}) // 已经加入了其他房间的游戏 - GameExitWrong = myerr.NewBusinessCode(50112, "Can not exit user", myerr.BusinessData{}) // 离开游戏失败 - GameDiamondCannotEdit = myerr.NewBusinessCode(50113, "Game diamond can not edit", myerr.BusinessData{}) // - GameSettleWrong = myerr.NewBusinessCode(50114, "Game settle wrong", myerr.BusinessData{}) // 结算修改错误 - GameCloseWrong = myerr.NewBusinessCode(50115, "Game close wrong", myerr.BusinessData{}) // 关闭错误 - GameJoinFailed = myerr.NewBusinessCode(50116, "Join failed", myerr.BusinessData{}) // 加入失败 + CpAlreadyInvite = myerr.NewBusinessCode(50120, "Already invited", myerr.BusinessData{}) // 已经发送过邀请了 + CpHaveCancelNoDeal = myerr.NewBusinessCode(50121, "You have a cancel apply", myerr.BusinessData{}) // 有接触申请需要处理 ) diff --git a/myerr/code.go b/myerr/code.go index 4e739c7..39dd656 100755 --- a/myerr/code.go +++ b/myerr/code.go @@ -2,6 +2,7 @@ package myerr import ( "fmt" + "git.hilo.cn/hilo-common/myerr" "git.hilo.cn/hilo-common/mylogrus" "github.com/pkg/errors" "strconv" @@ -226,35 +227,10 @@ func WrapErr(err error) error { } } -func WrapGameErr(err error) error { - if err == nil { - return err - } - if h, ok := err.(*GameError); ok { - return h - } - return NewGameError(500, err.Error()) -} - -// 系统错误 -type GameError struct { - code uint16 - message string - err error -} - -func (err *GameError) Error() string { - return err.err.Error() -} - -func (err *GameError) Code() uint16 { - return err.code -} - -func NewGameError(code uint16, msg string) *GameError { - return &GameError{ - code: code, - message: msg, - err: errors.New("{code:" + strconv.Itoa(int(code)) + ",message:" + msg + "}"), +func ToLocal(err *myerr.BusinessError) error { + return &BusinessError{ + code: err.GetCode(), + message: err.GetMsg(), + err: err.GetErr(), } } diff --git a/mysql/3.9.0.sql b/mysql/3.9.0.sql new file mode 100644 index 0000000..6664dfc --- /dev/null +++ b/mysql/3.9.0.sql @@ -0,0 +1,331 @@ +CREATE TABLE `cp_relation` ( + `id` bigint unsigned AUTO_INCREMENT NOT NULL, + `user_id1` bigint NOT NULL COMMENT 'user_id1', + `user_id2` bigint NOT NULL COMMENT 'user_id2', + `created_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `uid1_idx` (`user_id1`) USING BTREE, + UNIQUE KEY `uid2_idx` (`user_id2`) USING BTREE, + KEY `ctime_idx` (`created_time`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='cp关系表'; + +INSERT INTO hilo.diamond_operate_set (diamond_num, frequency_num, frequency_day, diamond_max_num, add_reduce, `type`, name, status, diamond_type) +VALUES (-1, -1, -1, -1, 2, 94, 'cp邀请扣费', 1, 1), + (-1, -1, -1, -1, 1, 95, 'cp邀请退费', 1, 1); + +CREATE TABLE `cp_invite` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id', + `user_id` bigint NOT NULL COMMENT '发起邀请者', + `invite_user_id` bigint NOT NULL COMMENT '被邀请的人', + `diamond_num` int unsigned NOT NULL COMMENT '邀请者花费的钻石', + `status` tinyint unsigned NOT NULL COMMENT '状态1.未接受2.已接受3.拒接导致退费4.过期导致退费', + `created_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `uid_idx` (`user_id`) USING BTREE, + KEY `uid2_idx` (`invite_user_id`) USING BTREE, + KEY `status_idx` (`status`) USING BTREE, + KEY `created_time` (`created_time`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='cp邀请发起记录'; + +CREATE TABLE `cp_cancel` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id', + `user_id` bigint NOT NULL COMMENT '发起者', + `rec_user_id` bigint NOT NULL COMMENT '接收者', + `status` tinyint unsigned NOT NULL COMMENT '状态1.未处理2.发起者已撤销3.对方已确认4.到期自动确认', + `created_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `uid_idx` (`user_id`) USING BTREE, + KEY `uid2_idx` (`rec_user_id`) USING BTREE, + KEY `status_idx` (`status`) USING BTREE, + KEY `created_time` (`created_time`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='cp解除发起记录'; + + +INSERT INTO hilo.res_multi_text (msg_id, `language`, content) +VALUES (242, 'zh', '仅限送给CP'), + (242, 'en', 'Only for CP'), + (242, 'ar', 'فقط من أجل CP'), + (242, 'tr', 'Sadece CP için'), + (242, 'id', 'Hanya untuk CP'), + (242, 'ru', 'Только для КП'), + (242, 'ko', 'CP 전용'), + (242, 'pt', 'Somente para CP'), + (242, 'th', 'สำหรับซีพีเท่านั้น'), + (242, 'ca', 'Solo para CP'), + (242, 'hi', 'केवल सीपी के लिए'), + (242, 'vi', 'Chỉ dành cho CP'), + (242, 'ur', 'صرف CP کے لیے'); + +INSERT INTO hilo.res_multi_text (msg_id, `language`, content) +VALUES (243, 'zh', '对方已有CP了'), + (243, 'en', 'The opponent already has CP'), + (243, 'ar', 'الطرف الآخر لديه بالفعل CP'), + (243, 'tr', "Rakibin zaten CP'si var"), + (243, 'id', 'Lawan sudah memiliki CP'), + (243, 'ru', 'У противника уже есть CP'), + (243, 'ko', '상대는 이미 CP를 가지고 있습니다.'), + (243, 'pt', 'O oponente já tem CP'), + (243, 'th', 'ฝ่ายตรงข้ามมี CP อยู่แล้ว'), + (243, 'ca', 'El oponente ya tiene CP'), + (243, 'hi', 'विरोधी के पास पहले से ही CP है'), + (243, 'vi', 'Đối thủ đã có CP'), + (243, 'ur', 'مخالف کے پاس پہلے ہی سی پی ہے۔'); + +INSERT INTO hilo.res_multi_text (msg_id, `language`, content) +VALUES (244, 'zh', "你想要和我成为CP吗?"), + (244, 'en', "Do you want to be CP with me?"), + (244, 'ar', "هل ترغب في ان تكون CP الخاص معي؟"), + (244, 'tr', "Benimle CP olmak ister misin?"), + (244, 'id', "Apakah Anda ingin menjadi CP dengan saya?"), + (244, 'ru', "Хочешь быть со мной?"), + (244, 'ko', "나랑 CP할래?"), + (244, 'pt', "Você quer ser CP comigo?"), + (244, 'th', "อยากเป็น CP กับฉันไหม?"), + (244, 'ca', "¿Quieres ser CP conmigo?"), + (244, 'hi', "क्या आप मेरे साथ सीपी बनना चाहते हैं?"), + (244, 'vi', "Bạn có muốn trở thành CP với tôi không?"), + (244, 'ur', "کیا آپ میرے ساتھ CP بننا چاہتے ہیں؟"); + + +INSERT INTO hilo.res_multi_text (msg_id, `language`, content) +VALUES (245, 'zh', "我想成为你的CP"), + (245, 'en', "I want to be your CP"), + (245, 'ar', "أريد أن أكون CP الخاص بك"), + (245, 'tr', "senin CP'n olmak istiyorum"), + (245, 'id', "Saya ingin menjadi CP Anda"), + (245, 'ru', "Я хочу быть твоим CP"), + (245, 'ko', "당신의 CP가 되고 싶습니다"), + (245, 'pt', "quero ser seu cp"), + (245, 'th', "ฉันอยากเป็นซีพีของคุณ"), + (245, 'ca', "quiero ser tu CP"), + (245, 'hi', "मैं आपका सीपी बनना चाहता हूं"), + (245, 'vi', "Tôi muốn trở thành CP của bạn"), + (245, 'ur', "میں آپ کا CP بننا چاہتا ہوں۔"); + +INSERT INTO hilo.res_multi_text (msg_id, `language`, content) +VALUES (246, 'zh', "24小时后自动失效"), + (246, 'en', "Automatically expires after 24 hours"), + (246, 'ar', "تنتهي الصلاحية تلقائيًا بعد 24 ساعة"), + (246, 'tr', "24 saat sonra otomatik olarak sona erer"), + (246, 'id', "Secara otomatis kedaluwarsa setelah 24 jam"), + (246, 'ru', "Автоматически истекает через 24 часа"), + (246, 'ko', "24시간 후 자동 만료"), + (246, 'pt', "Expira automaticamente após 24 horas"), + (246, 'th', "หมดอายุโดยอัตโนมัติหลังจาก 24 ชั่วโมง"), + (246, 'ca', "Caduca automáticamente después de 24 horas"), + (246, 'hi', "24 घंटे के बाद स्वतः समाप्त हो जाता है"), + (246, 'vi', "Tự động hết hạn sau 24 giờ"), + (246, 'ur', "24 گھنٹے کے بعد خود بخود ختم ہو جاتا ہے۔"); + +INSERT INTO hilo.res_multi_text (msg_id, `language`, content) +VALUES (247, 'zh', "我想解绑CP"), + (247, 'en', "I want to unbind CP"), + (247, 'ar', "أريد فك ارتباط CP"), + (247, 'tr', "CP bağlantısını kaldırmak istiyorum"), + (247, 'id', "Saya ingin melepaskan CP"), + (247, 'ru', "Я хочу отвязать CP"), + (247, 'ko', "CP 바인딩을 해제하고 싶습니다."), + (247, 'pt', "quero desvincular o CP"), + (247, 'th', "ฉันต้องการยกเลิกการเชื่อมโยง CP"), + (247, 'ca', "Quiero desvincular CP"), + (247, 'hi', "मैं सीपी को खोलना चाहता हूं"), + (247, 'vi', "Tôi muốn hủy liên kết CP"), + (247, 'ur', "میں CP کو بند کرنا چاہتا ہوں۔"); + +INSERT INTO hilo.res_multi_text (msg_id, `language`, content) +VALUES (248, 'zh', "我们已经是CP了!"), + (248, 'en', "We are already CP!"), + (248, 'ar', "أصبحنا CP!"), + (248, 'tr', "Biz zaten CP'yiz!"), + (248, 'id', "Kami sudah CP!"), + (248, 'ru', "Мы уже КП!"), + (248, 'ko', "우리는 이미 CP입니다!"), + (248, 'pt', "Já somos CP!"), + (248, 'th', "เราเป็นซีพีแล้ว!"), + (248, 'ca', "¡Ya somos CP!"), + (248, 'hi', "हम पहले से ही सीपी हैं!"), + (248, 'vi', "Chúng ta đã là CP rồi!"), + (248, 'ur', "ہم پہلے ہی CP ہیں!"); + +INSERT INTO hilo.res_multi_text (msg_id, `language`, content) +VALUES (249, 'zh', "庆祝礼物已发送到%s的背包"), + (249, 'en', "Celebration gifts have been sent to %s 's backpack"), + (249, 'ar', "تم إرسال هدايا الاحتفال إلى حقيبة %s"), + (249, 'tr', "%s'nin sırt çantasına kutlama hediyeleri gönderildi"), + (249, 'id', "Hadiah perayaan telah dikirim ke ransel %s"), + (249, 'ru', "Праздничные подарки отправлены в рюкзак %s."), + (249, 'ko', "축하 선물이 %s의 배낭으로 보내졌습니다."), + (249, 'pt', "Presentes de comemoração foram enviados para a mochila de %s"), + (249, 'th', "ของขวัญฉลองถูกส่งไปที่กระเป๋าเป้สะพายหลังของ %s แล้ว"), + (249, 'ca', "Se han enviado regalos de celebración a la mochila de %s"), + (249, 'hi', "उत्सव उपहार %s के बैकपैक में भेज दिए गए हैं"), + (249, 'vi', "Quà kỷ niệm đã được gửi tới ba lô của %s'"), + (249, 'ur', "جشن کے تحائف %s کے بیگ میں بھیجے گئے ہیں۔"); + +INSERT INTO hilo.res_multi_text (msg_id, `language`, content) +VALUES (250, 'zh', "%s已拒绝CP邀请"), + (250, 'en', "%s has rejected the CP invitation"), + (250, 'ar', "%s رفض دعوة CP"), + (250, 'tr', "%s CP davetini reddetti"), + (250, 'id', "%s telah menolak undangan CP"), + (250, 'ru', "%s отклонил приглашение CP"), + (250, 'ko', "%s님이 CP 초대를 거부했습니다."), + (250, 'pt', "%s rejeitou o convite CP"), + (250, 'th', "%s ได้ปฏิเสธคำเชิญ CP"), + (250, 'ca', "%s ha rechazado la invitación de CP"), + (250, 'hi', "%s ने CP आमंत्रण को अस्वीकार कर दिया है"), + (250, 'vi', "%s đã từ chối lời mời CP"), + (250, 'ur', "%s نے CP دعوت نامے کو مسترد کر دیا ہے۔"); + +INSERT INTO hilo.res_multi_text (msg_id, `language`, content) +VALUES (251, 'zh', "已解绑CP,CP值被清空,CP特权已消失"), + (251, 'en', "The CP has been unbound, the CP points has been cleared, and the CP privilege has disappeared"), + (251, 'ar', "تم فك ارتباط CP ، وتم مسح نقاط CP ، واختفيت امتيازات CP"), + (251, 'tr', "CP bağlantısı kaldırıldı, CP puanları silindi ve CP ayrıcalığı kayboldu"), + (251, 'id', "CP telah dilepas, poin CP telah dihapus, dan hak istimewa CP telah hilang"), + (251, 'ru', "CP был развязан, CP-очки были очищены, а привилегия CP исчезла."), + (251, 'ko', "CP가 해제되고 CP 포인트가 해제되며 CP 특권이 사라졌습니다."), + (251, 'pt', "O CP foi desvinculado, os pontos do CP foram apagados e o privilégio do CP desapareceu"), + (251, 'th', "CP ไม่ถูกผูกมัด คะแนน CP ถูกล้าง และสิทธิ์ CP หายไป"), + (251, 'ca', "El CP se ha desvinculado, los puntos de CP se han borrado y el privilegio de CP ha desaparecido"), + (251, 'hi', "सीपी अनबाउंड हो गया है, सीपी पॉइंट क्लियर हो गए हैं, और सीपी विशेषाधिकार गायब हो गया है"), + (251, 'vi', "CP đã bị hủy liên kết, điểm CP đã bị xóa và đặc quyền CP đã biến mất"), + (251, 'ur', "CP کو غیر پابند کر دیا گیا ہے، CP پوائنٹس کو صاف کر دیا گیا ہے، اور CP کا استحقاق غائب ہو گیا ہے"); + +INSERT INTO hilo.res_multi_text (msg_id, `language`, content) +VALUES (258, 'zh', "已经过期"), + (258, 'en', "Already expired"), + (258, 'ar', "تم انتهاء صلاحيتها"), + (258, 'tr', "Zaten süresi dolmuş"), + (258, 'id', "Sudah kedaluwarsa"), + (258, 'ru', "Срок действия уже истек"), + (258, 'ko', "이미 만료됨"), + (258, 'pt', "Já expirou"), + (258, 'th', "หมดอายุแล้ว"), + (258, 'ca', "ya vencido"), + (258, 'hi', "पहले ही समाप्त हो चुका है"), + (258, 'vi', "Đã hết hạn"), + (258, 'ur', "پہلے ہی ختم ہو چکا ہے۔"); + +INSERT INTO hilo.res_multi_text (msg_id, `language`, content) +VALUES (272, 'zh', "绑定CP花费"), + (272, 'en', "Binding CP cost"), + (272, 'ar', "تكلفة CP الارتباط"), + (272, 'tr', "Bağlayıcı CP maliyeti"), + (272, 'id', "Mengikat biaya CP"), + (272, 'ru', "Привязка стоимости CP"), + (272, 'ko', "구속력 있는 CP 비용"), + (272, 'pt', "Custo de CP vinculativo"), + (272, 'th', "ค่าซีพีเข้าเล่ม"), + (272, 'ca', "Costo de CP vinculante"), + (272, 'hi', "बाध्यकारी सीपी लागत"), + (272, 'vi', "Chi phí CP ràng buộc"), + (272, 'ur', "بائنڈنگ CP لاگت"); + +INSERT INTO hilo.res_multi_text (msg_id, `language`, content) +VALUES (273, 'zh', "绑定CP返还"), + (273, 'en', "Binding CP return"), + (273, 'ar', "استرداد CP الارتباط"), + (273, 'tr', "Bağlayıcı CP dönüşü"), + (273, 'id', "Pengembalian CP yang mengikat"), + (273, 'ru', "Обязательный возврат CP"), + (273, 'ko', "바인딩 CP 반환"), + (273, 'pt', "Retorno de CP vinculativo"), + (273, 'th', "การคืนค่า CP ที่มีผลผูกพัน"), + (273, 'ca', "Retorno de CP vinculante"), + (273, 'hi', "बाध्यकारी सीपी वापसी"), + (273, 'vi', "Lợi nhuận CP ràng buộc"), + (273, 'ur', "بائنڈنگ CP واپسی۔"); + +INSERT INTO hilo.res_multi_text (msg_id, `language`, content) +VALUES (274, 'zh', "%s撤销了解绑CP的申请"), + (274, 'en', "%s canceled the application to unbind CP"), + (274, 'ar', "%s قام بسحب طلبه لفك ارتباط CP"), + (274, 'tr', "%s, CP'nin bağlantısını kaldırma başvurusunu iptal etti"), + (274, 'id', "%s membatalkan aplikasi untuk melepaskan CP"), + (274, 'ru', "%s отменил заявку на отвязку CP"), + (274, 'ko', "%s이(가) CP 바인딩 해제 신청을 취소했습니다."), + (274, 'pt', "%s cancelou o aplicativo para desvincular CP"), + (274, 'th', "%s ยกเลิกแอปพลิเคชันเพื่อยกเลิกการเชื่อมโยง CP"), + (274, 'ca', "%s canceló la solicitud para desvincular CP"), + (274, 'hi', "%s ने सीपी को अनबाइंड करने के लिए आवेदन को रद्द कर दिया"), + (274, 'vi', "%s đã hủy đơn đăng ký hủy liên kết CP"), + (274, 'ur', "%s نے CP کی پابندی ختم کرنے کے لیے درخواست منسوخ کر دی۔"); + +INSERT INTO hilo.res_multi_text (msg_id, `language`, content) +VALUES (275, 'zh', "等待对方接受"), + (275, 'en', "Waiting to be accepted"), + (275, 'ar', "في انتظار قبول الطرف الآخرى"), + (275, 'tr', "Kabul edilmeyi bekliyorum"), + (275, 'id', "Menunggu untuk diterima"), + (275, 'ru', "Ожидание принятия"), + (275, 'ko', "수락 대기 중"), + (275, 'pt', "Esperando ser aceito"), + (275, 'th', "รอรับได้เลยครับ"), + (275, 'ca', "Esperando ser aceptado"), + (275, 'hi', "स्वीकार किए जाने की प्रतीक्षा कर रहा है"), + (275, 'vi', "Chờ đợi để được chấp nhận"), + (275, 'ur', "قبول ہونے کا انتظار ہے۔"); + +INSERT INTO hilo.res_multi_text (msg_id, `language`, content) +VALUES (276, 'zh', "邀请被接受"), + (276, 'en', "Invitation accepted"), + (276, 'ar', "تم قبول الدعوة"), + (276, 'tr', "davet kabul edildi"), + (276, 'id', "undangan diterima"), + (276, 'ru', "приглашение принято"), + (276, 'ko', "초대 수락됨"), + (276, 'pt', "convite aceito"), + (276, 'th', "ยอมรับคำเชิญแล้ว"), + (276, 'ca', "invitación aceptada"), + (276, 'hi', "निमंत्रण स्वीकार किया"), + (276, 'vi', "lời mời được chấp nhận"), + (276, 'ur', "دعوت قبول کی"); + +INSERT INTO hilo.res_multi_text (msg_id, `language`, content) +VALUES (277, 'zh', "邀请被拒绝"), + (277, 'en', "Invitation refused"), + (277, 'ar', "تم رفض الدعوة"), + (277, 'tr', "davet reddedildi"), + (277, 'id', "undangan ditolak"), + (277, 'ru', "приглашение отклонено"), + (277, 'ko', "초대가 거부됨"), + (277, 'pt', "convite recusado"), + (277, 'th', "คำเชิญถูกปฏิเสธ"), + (277, 'ca', "invitación rechazada"), + (277, 'hi', "निमंत्रण अस्वीकार कर दिया"), + (277, 'vi', "lời mời từ chối"), + (277, 'ur', "دعوت سے انکار کر دیا"); + +INSERT INTO hilo.res_multi_text (msg_id, `language`, content) +VALUES (278, 'zh', "已解绑CP"), + (278, 'en', "CP has been unbound"), + (278, 'ar', "تم فك علاقة CP"), + (278, 'tr', "CP bağlantısı kaldırıldı"), + (278, 'id', "CP telah dilepaskan"), + (278, 'ru', "CP не привязан"), + (278, 'ko', "CP가 언바운드되었습니다."), + (278, 'pt', "CP foi desvinculado"), + (278, 'th', "CP หลุดแล้ว"), + (278, 'ca', "CP ha sido desvinculado"), + (278, 'hi', "सीपी अनबाउंड किया गया है"), + (278, 'vi', "CP không bị ràng buộc"), + (278, 'ur', "سی پی کو غیر پابند کیا گیا ہے۔"); + +INSERT INTO hilo.res_multi_text (msg_id, `language`, content) +VALUES (279, 'zh', "已被取消"), + (279, 'en', "Has been canceled"), + (279, 'ar', "تم الإلغاء"), + (279, 'tr', "İptal edildi"), + (279, 'id', "Telah dibatalkan"), + (279, 'ru', "Была отменена"), + (279, 'ko', "취소되었습니다"), + (279, 'pt', "Foi cancelado"), + (279, 'th', "ถูกยกเลิก"), + (279, 'ca', "Ha sido cancelado"), + (279, 'hi', "रद्द किया गया"), + (279, 'vi', "Đã hủy bỏ"), + (279, 'ur', "منسوخ کر دیا گیا ہے۔"); diff --git a/req/request.go b/req/request.go index 498f8a1..84af386 100755 --- a/req/request.go +++ b/req/request.go @@ -10,6 +10,18 @@ import ( "hilo-user/myerr/bizerr" ) +// 分页base +type PageReqBase struct { + PageIndex int `form:"pageIndex,default=0"` + PageSize int `form:"pageSize,default=10"` +} + +type PageRespBase struct { + NextPageIndex int `json:"nextPageIndex"` + HasNextPage bool `json:"hasNextPage"` + Data interface{} `json:"data"` // 需要具体自定义 +} + func GetUserId(c *gin.Context) (mysql.ID, error) { if userIdStr, ok := c.Keys[mycontext.USERID]; ok { userId := userIdStr.(uint64) @@ -18,6 +30,20 @@ func GetUserId(c *gin.Context) (mysql.ID, error) { return 0, bizerr.ParaMissing } +func ToUserId(myContext *mycontext.MyContext, externalId mysql.Str) (mysql.ID, error) { + return user_c.ToUserId(domain.CreateModelContext(myContext), externalId) + //if externalId == "" { + // return 0, myerr.NewSysError("externalId 不能为空") + //} + //var user user_m.User + //if err := mysql.Db.Where(&user_m.User{ + // ExternalId: externalId, + //}).First(&user).Error; err != nil { + // return 0, bizerr.ExternalIdNoExist + //} + //return user.ID, nil +} + // 获取userId和externalId func GetUserIdAndExtId(c *gin.Context, myContext *mycontext.MyContext) (mysql.ID, string, error) { if userIdStr, ok := c.Keys[mycontext.USERID]; ok { diff --git a/resp/response.go b/resp/response.go index 7b5ada5..2530a0d 100755 --- a/resp/response.go +++ b/resp/response.go @@ -5,6 +5,7 @@ import ( "git.hilo.cn/hilo-common/mycontext" "github.com/gin-gonic/gin" "hilo-user/myerr" + "hilo-user/req" "net/http" ) @@ -42,27 +43,23 @@ func ResponseOk(c *gin.Context, data interface{}) { c.JSON(http.StatusOK, response) } -func GameResponseOk(c *gin.Context, data interface{}) { - // always return http.StatusOK - response := GameResponse{ - RetCode: 0, - RetMsg: myerr.GetSuccessMsg(), - SdkErrorCode: 0, - Data: data, +// 分页返回 +// 客户端入参 req.PageReqBase +// 服务端返回 req.PageRespBase +func ResponsePageBaseOk(c *gin.Context, data interface{}, nextPageIndex int, hasNextPage bool) { + if data == nil { + data = make([]interface{}, 0) } - printResponseBody(c, &response) - c.JSON(http.StatusOK, response) -} - -func GameResponseFail(c *gin.Context, err *myerr.GameError) { - // always return http.StatusOK - response := GameResponse{ - RetCode: err.Code(), - RetMsg: err.Error(), - SdkErrorCode: err.Code(), - Data: nil, + response := Response{ + Code: myerr.GetSuccessCode(), + Message: myerr.GetSuccessMsg(), + OperationMessage: myerr.GetSuccessMsg(), + Data: req.PageRespBase{ + NextPageIndex: nextPageIndex, + HasNextPage: hasNextPage, + Data: data, + }, } - printResponseBody(c, &response) c.JSON(http.StatusOK, response) } diff --git a/route/cp_r/anniversary.go b/route/cp_r/anniversary.go new file mode 100644 index 0000000..28aeb1d --- /dev/null +++ b/route/cp_r/anniversary.go @@ -0,0 +1,163 @@ +package cp_r + +import ( + "git.hilo.cn/hilo-common/domain" + "git.hilo.cn/hilo-common/mycontext" + "github.com/gin-gonic/gin" + "hilo-user/_const/enum/cp_e" + "hilo-user/cv/cp_cv" + "hilo-user/cv/user_cv" + "hilo-user/domain/model/cp_m" + "hilo-user/domain/model/user_m" + "hilo-user/myerr/bizerr" + "hilo-user/req" + "hilo-user/resp" + "strconv" +) + +type PostPutAnniversaryReq struct { + Content string `form:"content" binding:"required"` + Timestamp int64 `form:"timestamp" binding:"required"` + IsRemind bool `form:"isRemind"` +} + +// @Tags CP v2 +// @Summary 发布纪念日 +// @Param token header string true "token" +// @Param nonce header string true "随机数字" +// @Param content formData string true "纪念日名称" +// @Param timestamp formData int true "时间戳" +// @Param isRemind formData bool false "是否提醒" +// @Success 200 +// @Router /v2/cp/anniversary [post] +func PostAnniversary(c *gin.Context) (*mycontext.MyContext, error) { + myCtx := mycontext.CreateMyContext(c.Keys) + userId, err := req.GetUserId(c) + if err != nil { + return myCtx, err + } + var param PostPutAnniversaryReq + if err := c.ShouldBind(¶m); err != nil { + return myCtx, err + } + model := domain.CreateModelContext(myCtx) + relation, exits := cp_m.GetCpRelation(model, userId) + if !exits { + return myCtx, bizerr.CpNotRelation + } else { + if err := cp_m.AddCpAnniversary(model, cp_e.AnniversaryItemTypeNormal, relation, param.Content, param.Timestamp, param.IsRemind, 0); err != nil { + return myCtx, err + } + } + resp.ResponseOk(c, "") + return myCtx, nil +} + +// @Tags CP v2 +// @Summary 修改纪念日 +// @Param token header string true "token" +// @Param nonce header string true "随机数字" +// @Param content formData string true "纪念日名称" +// @Param timestamp formData int true "时间戳" +// @Param isRemind formData bool false "是否提醒" +// @Param id path int true "更新的记录id" +// @Success 200 +// @Router /v2/cp/anniversary/{id} [put] +func PutAnniversary(c *gin.Context) (*mycontext.MyContext, error) { + myCtx := mycontext.CreateMyContext(c.Keys) + var param PostPutAnniversaryReq + if err := c.ShouldBind(¶m); err != nil { + return myCtx, err + } + id, _ := strconv.ParseUint(c.Param("id"), 10, 64) + model := domain.CreateModelContext(myCtx) + if err := cp_m.UpdateCpAnniversary(model, id, param.Content, param.Timestamp, param.IsRemind); err != nil { + return myCtx, err + } + resp.ResponseOk(c, "") + return myCtx, nil +} + +// @Tags CP v2 +// @Summary 获取纪念日 +// @Param token header string true "token" +// @Param nonce header string true "随机数字" +// @Param pageIndex query int true "偏移值 默认:1" default(1) +// @Param pageSize query int true "请求数量 默认:10" default(10) +// @Success 200 {object} []cp_cv.CvCpAnniversary +// @Router /v2/cp/anniversary [get] +func PageAnniversary(c *gin.Context) (*mycontext.MyContext, error) { + myCtx := mycontext.CreateMyContext(c.Keys) + userId, err := req.GetUserId(c) + if err != nil { + return myCtx, err + } + var response = make([]cp_cv.CvCpAnniversary, 0) + model := domain.CreateModelContext(myCtx) + cpRelation, exits := cp_m.GetCpRelation(model, userId) + if exits { + userIds := []uint64{cpRelation.UserId1, cpRelation.UserId2} + users, err := user_m.GetUserMapByIds(model, userIds) + if err != nil { + return myCtx, err + } + cpUserId := cpRelation.UserId2 + if cpUserId == userId { + cpUserId = cpRelation.UserId1 + } + userInfo := user_cv.UserToTiny(users[userId]) + cpUserInfo := user_cv.UserToTiny(users[cpUserId]) + response = append(response, cp_cv.CvCpAnniversary{ + Type: cp_e.AnniversaryItemTypeAvatar, + CpInfo: &cp_cv.CvCpBase{ + UserInfo: *userInfo, + CpUserInfo: *cpUserInfo, + }, + OriginTimestamp: cpRelation.CreatedTime.Unix(), + Timestamp: cpRelation.CreatedTime.Unix(), + CanDel: false, + }) + } + anniversary := cp_m.GetAllCpAnniversary(model, userId) + for _, v := range anniversary { + timestamp := v.Timestamp + if v.Type == cp_e.AnniversaryItemTypeAnniversary && timestamp > 0 { + timestamp = cp_m.CalcNextAnniversary(timestamp) + } + // 客户端只认识0 1 + Type := v.Type + if v.Type != cp_e.AnniversaryItemTypeAvatar { + Type = cp_e.AnniversaryItemTypeNormal + } + response = append(response, cp_cv.CvCpAnniversary{ + Type: Type, + Id: v.ID, + Content: v.Content, + Timestamp: timestamp, + OriginTimestamp: v.Timestamp, + IsRemind: v.IsRemind, + CanDel: true, + IsTop: v.Sort > 0, + }) + } + resp.ResponsePageBaseOk(c, response, 0, false) + return myCtx, nil +} + +// @Tags CP v2 +// @Summary 获取纪念日 +// @Param token header string true "token" +// @Param nonce header string true "随机数字" +// @Param id path int true "记录id" +// @Success 200 +// @Router /v2/cp/anniversary/:id [delete] +func DelAnniversary(c *gin.Context) (*mycontext.MyContext, error) { + myCtx := mycontext.CreateMyContext(c.Keys) + id, _ := strconv.ParseUint(c.Param("id"), 10, 64) + model := domain.CreateModelContext(myCtx) + if err := cp_m.DelCpAnniversary(model, id); err != nil { + return myCtx, err + } + resp.ResponseOk(c, "") + return myCtx, nil +} diff --git a/route/cp_r/cp_relation.go b/route/cp_r/cp_relation.go new file mode 100644 index 0000000..4c70879 --- /dev/null +++ b/route/cp_r/cp_relation.go @@ -0,0 +1,545 @@ +package cp_r + +import ( + "encoding/json" + "fmt" + "git.hilo.cn/hilo-common/_const/common" + "git.hilo.cn/hilo-common/_const/enum/diamond_e" + "git.hilo.cn/hilo-common/_const/enum/msg_e" + "git.hilo.cn/hilo-common/domain" + "git.hilo.cn/hilo-common/mycontext" + "git.hilo.cn/hilo-common/myerr/comerr" + "git.hilo.cn/hilo-common/resource/mysql" + "git.hilo.cn/hilo-common/sdk/tencentyun" + "git.hilo.cn/hilo-common/txop/bag_tx" + "git.hilo.cn/hilo-common/txop/diamond_tx" + "git.hilo.cn/hilo-common/txop/headwear_tx" + "git.hilo.cn/hilo-common/txop/msg" + "git.hilo.cn/hilo-common/utils" + "github.com/gin-gonic/gin" + "hilo-user/_const/enum/cp_e" + "hilo-user/cv/cp_cv" + "hilo-user/cv/user_cv" + "hilo-user/domain/cache/user_c" + "hilo-user/domain/model/cp_m" + "hilo-user/domain/model/user_m" + "hilo-user/domain/service/cp_s" + "hilo-user/myerr" + "hilo-user/myerr/bizerr" + "hilo-user/req" + "hilo-user/resp" + "strconv" + "time" +) + +// @Tags cp关系 +// @Summary 检查用户是否绑定了cp +// @Param externalId query string true "用户的externalId" +// @Success 200 {object} cp_cv.CheckCpRelationRes +// @Router /v2/cp/relation/check [get] +func CheckUserCpRelation(c *gin.Context) (*mycontext.MyContext, error) { + myCtx := mycontext.CreateMyContext(c.Keys) + externalId := c.Query("externalId") + if externalId == "" { + return myCtx, bizerr.InvalidParameter + } + + _, lang, err := req.GetUserIdLang(c, myCtx) + if err != nil { + return myCtx, err + } + + if externalId == "" { + return myCtx, bizerr.InvalidParameter + } + + model := domain.CreateModelContext(myCtx) + user, err := user_m.GetUserByExtId(model, externalId) + if err != nil { + return myCtx, err + } + + cp, err := cp_m.GetCp(model, user.ID) + if err != nil { + return myCtx, err + } + if cp.Id > 0 { + return myCtx, myerr.ToLocal(msg.GetErrByLanguage(model, common.MSG_ID_ALREADY_HAS_CP, lang, comerr.AlreadyHasCp)) + } + msg, err := msg.GetResMultiTextBy(model, common.MSG_ID_INVITE_CP, lang) + if err != nil { + return myCtx, err + } + + resp.ResponseOk(c, cp_cv.CheckCpRelationRes{Diamond: cp_e.CpRelationInviteDiamond, Msg: msg}) + return myCtx, nil +} + +// @Tags cp关系 +// @Summary 发送cp邀请/发起解除cp +// @Param externalId formData string true "对方的externalId" +// @Param type formData int true "类型1.发起邀请2.发起解除" +// @Success 200 +// @Router /v2/cp/relation [post] +func CpRelation(c *gin.Context) (*mycontext.MyContext, error) { + myCtx := mycontext.CreateMyContext(c.Keys) + externalId := c.PostForm("externalId") + if externalId == "" { + return myCtx, bizerr.InvalidParameter + } + + myUserId, lang, err := req.GetUserIdLang(c, myCtx) + if err != nil { + return myCtx, err + } + + optType, err := strconv.Atoi(c.PostForm("type")) + if err != nil || optType > 2 || optType < 1 { + return myCtx, bizerr.InvalidParameter + } + + if optType == 1 { // 邀请 + err = cp_s.InviteCpRelation(myCtx, myUserId, externalId, lang) + if err != nil { + myCtx.Log.Errorf("InviteCpRelation myUserId:%d, err:%v", myUserId, err) + return myCtx, err + } + } else { + // 发起解除 + err = cp_s.CancelCpRelation(myCtx, myUserId, externalId, lang) + if err != nil { + myCtx.Log.Errorf("CancelCpRelation myUserId:%d, err:%v", myUserId, err) + return myCtx, err + } + } + + resp.ResponseOk(c, nil) + return myCtx, nil +} + +// @Tags cp关系 +// @Summary 回应cp邀请 +// @Param externalId formData string true "对方用户的externalId" +// @Param type formData int true "类型1.接受2.拒绝" +// @Success 200 +// @Router /v2/cp/relation/invite/reply [post] +func ReplyCpInvite(c *gin.Context) (*mycontext.MyContext, error) { + myCtx := mycontext.CreateMyContext(c.Keys) + externalId := c.PostForm("externalId") + if externalId == "" { + return myCtx, myerr.WrapErr(bizerr.InvalidParameter) + } + optType, err := strconv.Atoi(c.PostForm("type")) + if err != nil || optType > 2 || optType < 1 { + return myCtx, myerr.WrapErr(bizerr.InvalidParameter) + } + + myUserId, lang, err := req.GetUserIdLang(c, myCtx) + if err != nil { + return myCtx, err + } + + model := domain.CreateModelContext(myCtx) + user, err := user_m.GetUser(model, myUserId) + if err != nil { + return myCtx, err + } + userSender, err := user_m.GetUserByExtId(model, externalId) + if err != nil { + return myCtx, err + } + if userSender.ID == myUserId { + return myCtx, myerr.WrapErr(bizerr.InvalidParameter) + } + + cpRecord, err := cp_m.GetCpInvite(model, userSender.ID, user.ID, cp_e.CpInvite) + if err != nil { + model.Log.Errorf("ReplyCpInvite userSender:%d, user:%d, err:%v", userSender.ID, user.ID, err) + return myCtx, err + } + if cpRecord == nil || cpRecord.Id == 0 { + return myCtx, myerr.ToLocal(msg.GetErrByLanguage(model, common.MSG_ID_ALREADY_EXPIRED, lang, comerr.AlreadyExpired)) + } + + if optType == 1 { // 接受的时候 + // 自己是否有cp了 + myCp, err := cp_m.GetCp(model, myUserId) + if err != nil { + return myCtx, err + } + if myCp.Id > 0 { + return myCtx, myerr.ToLocal(msg.GetErrByLanguage(model, common.MSG_ID_ALREADY_HAS_CP, lang, comerr.AlreadyHasCp)) + } + // 对方是否已经有cp了 + senderCp, err := cp_m.GetCp(model, userSender.ID) + if err != nil { + return myCtx, err + } + if senderCp.Id > 0 { + return myCtx, myerr.ToLocal(msg.GetErrByLanguage(model, common.MSG_ID_ALREADY_HAS_CP, lang, comerr.AlreadyHasCp)) + } + } + err = model.Transaction(func(model *domain.Model) error { + // 更新邀请状态 + updateStatus := cp_e.CpInviteAccept + if optType == 2 { // 拒接 + updateStatus = cp_e.CpInviteRefuse + } + err = cp_m.UpdateStatusCpInvite(model, cpRecord.Id, updateStatus) + if err != nil { + model.Log.Errorf("ReplyCpInvite userSender:%d, user:%d, status:%d, err:%v", userSender.ID, user.ID, updateStatus, err) + return err + } + + var msgData []byte + if optType == 1 { // 接受 + // 写入cp关系表 + cpId, err := cp_m.CreateCp(model, userSender.ID, user.ID) + if err != nil { + model.Log.Errorf("ReplyCpInvite userSender:%d, user:%d, status:%d, err:%v", userSender.ID, user.ID, updateStatus, err) + return err + } + // 初始化cp等级 + if err := cp_m.InitCpLevel(model, mysql.ID(cpId), userSender.ID, user.ID); err != nil { + model.Log.Errorf("ReplyCpInvite InitCpLevel fail:%v-%v-%v", userSender.ID, user.ID, err) + return err + } + // 初始化cp纪念日 + if err := cp_m.InitCpAnniversary(model, cp_m.CpRelation{ + Id: mysql.ID(cpId), + UserId1: userSender.ID, + UserId2: user.ID, + }, lang); err != nil { + model.Log.Errorf("ReplyCpInvite InitCpAnniversary fail:%v-%v-%v", userSender.ID, user.ID, err) + return err + } + // 发放告白礼物 + if _, err = bag_tx.SendUserBag(model, userSender.ID, 1, cp_e.CpConfessionGiftId, 1, 3, "告白礼物"); err != nil { + model.Log.Errorf("ReplyCpInvite userSender:%d, user:%d, status:%d, err:%v", userSender.ID, user.ID, updateStatus, err) + return err + } + // 发放头饰 + for _, u := range []*user_m.User{userSender, user} { + headwearId := cp_e.CpMaleHeadwearId + if u.Sex == mysql.WOMAN { + headwearId = cp_e.CpFemaleHeadwearId + } + if err = headwear_tx.SendHeadwear(model, u.ID, mysql.ID(headwearId), 3000); err != nil { + model.Log.Errorf("ReplyCpInvite user:%d, headwearId:%d, err:%v", u.ID, headwearId, err) + return err + } + } + + // 私信-接受 + content, err := msg.GetResMultiTextBy(model, common.MSG_ID_BIND_CP_SUCCEED, lang) + if err != nil { + return err + } + tip, err := msg.GetResMultiTextBy(model, common.MSG_ID_SEND_CP_GIFT, lang) + if err != nil { + return err + } + msgData, _ = json.Marshal(cp_m.CpAcceptInviteMessage{ + Identifier: "CpAcceptInviteMessage", + Msg: content, + Tip: fmt.Sprintf(tip, userSender.Code), + Sender: user_m.ToUserTiny(userSender), + Receiver: user_m.ToUserTiny(user), + }) + } else { // 拒绝 + // 退费 + err = diamond_tx.SendDiamond(model, cpRecord.UserId, diamond_e.CpInviteRefund, cpRecord.Id, cpRecord.DiamondNum, + msg_e.MgrSendDiamondProperty) + if err != nil { + model.Log.Errorf("ReplyCpInvite UserId:%d, err:%v", cpRecord.UserId, err) + return err + } + // 私信-拒绝 + content, err := msg.GetResMultiTextBy(model, common.MSG_ID_REJECTED_CP_INVITE, lang) + if err != nil { + return err + } + msgData, _ = json.Marshal(cp_m.CpDenyInviteMessage{ + Identifier: "CpDenyInviteMessage", + Msg: fmt.Sprintf(content, user.Nick), + Sender: user_m.ToUserTiny(user), + }) + } + if err := tencentyun.BatchSendCustomMsg(model, 1, userSender.ExternalId, []string{user.ExternalId}, string(msgData), "cp邀请"); err != nil { + model.Log.Errorf("BatchSendCustomMsg fail:%v", err) + return err + } + return nil + }) + if err != nil { + model.Log.Errorf("ReplyCpInvite myUserId:%d, err:%v", myUserId, err) + return myCtx, err + } + + resp.ResponseOk(c, nil) + return myCtx, nil +} + +// @Tags cp关系 +// @Summary 回应cp解除 +// @Param externalId formData string true "对方的externalId" +// @Param type formData int true "类型1.撤销2.接受" +// @Success 200 +// @Router /v2/cp/relation/cancel/reply [post] +func ReplyCpCancel(c *gin.Context) (*mycontext.MyContext, error) { + myCtx := mycontext.CreateMyContext(c.Keys) + externalId := c.PostForm("externalId") + if externalId == "" { + return myCtx, bizerr.InvalidParameter + } + optType, err := strconv.Atoi(c.PostForm("type")) + if err != nil || optType > 2 || optType < 1 { + return myCtx, bizerr.InvalidParameter + } + + myUserId, lang, err := req.GetUserIdLang(c, myCtx) + if err != nil { + return myCtx, err + } + + model := domain.CreateModelContext(myCtx) + myUser, err := user_m.GetUser(model, myUserId) + if err != nil { + return myCtx, err + } + user2, err := user_m.GetUserByExtId(model, externalId) + if err != nil { + return myCtx, err + } + + if user2.ID == myUserId { + return myCtx, bizerr.InvalidParameter + } + + cpCancel, err := cp_m.GetCpCancel(model, []uint64{myUser.ID, user2.ID}, cp_e.CpCancel) + if err != nil { + model.Log.Errorf("ReplyCpCancel myUser:%d, user2:%d, err:%v", myUser.ID, user2.ID, err) + return myCtx, err + } + if cpCancel == nil || cpCancel.Id == 0 { + return myCtx, myerr.ToLocal(msg.GetErrByLanguage(model, common.MSG_ID_ALREADY_EXPIRED, lang, comerr.AlreadyExpired)) + } + + if optType == 1 { // 撤销,只有自己能撤销自己的申请 + if cpCancel.UserId != myUserId { + return myCtx, bizerr.InvalidParameter + } + } else { // 接受,只有对方能接受 + if cpCancel.RecUserId != myUserId { + return myCtx, bizerr.InvalidParameter + } + } + + err = model.Transaction(func(model *domain.Model) error { + // 更新邀请状态 + updateStatus := cp_e.CpCancelRevoke + if optType == 2 { // 接受 + updateStatus = cp_e.CpCancelAccept + } + err = cp_m.UpdateStatusCpCancel(model, cpCancel.Id, updateStatus) + if err != nil { + model.Log.Errorf("ReplyCpCancel myUser:%d, user2:%d, status:%d, err:%v", myUser.ID, user2.ID, updateStatus, err) + return err + } + + var msgData []byte + if optType == 1 { // 撤销解除 + // 私信 + content, err := msg.GetResMultiTextBy(model, common.MSG_ID_CANCEL_UNBIND_CP, lang) + if err != nil { + return err + } + msgData, _ = json.Marshal(cp_m.CpDealCancelMessage{ + Identifier: "CpDealCancelMessage", + Msg: fmt.Sprintf(content, myUser.Nick), + Status: 1, + }) + } else { // 接受解除 + // 删除cp关系表的记录 + err = cp_m.DelCpRelation(model, myUser.ID, user2.ID) + if err != nil { + model.Log.Errorf("ReplyCpCancel myUser:%d, user2:%d, status:%d, err:%v", myUser.ID, user2.ID, updateStatus, err) + return err + } + // 私信-接受解除 + content, err := msg.GetResMultiTextBy(model, common.MSG_ID_UNBIND_CP_SUCCEED, lang) + if err != nil { + return err + } + msgData, _ = json.Marshal(cp_m.CpDealCancelMessage{ + Identifier: "CpDealCancelMessage", + Msg: content, + Status: 2, + }) + // 删除cp头饰 + _ = headwear_tx.DelHeadwear(model, myUser.ID, cp_e.CpHeadwearId) + _ = headwear_tx.DelHeadwear(model, user2.ID, cp_e.CpHeadwearId) + } + if err := tencentyun.BatchSendCustomMsg(model, 1, myUser.ExternalId, []string{user2.ExternalId}, string(msgData), "cp解除"); err != nil { + model.Log.Errorf("ReplyCpCancel BatchSendCustomMsg fail:%v", err) + return err + } + return nil + }) + if err != nil { + model.Log.Errorf("ReplyCpCancel myUserId:%d, err:%v", myUserId, err) + return myCtx, err + } + + resp.ResponseOk(c, cp_cv.CheckCpRelationRes{}) + return myCtx, nil +} + +type CpDetail struct { + CpInfo cp_cv.CvCpInfo `json:"cpInfo"` // cp信息 + CpLevel cp_cv.CvCpLevel `json:"cpLevel"` // cp等级 +} + +// @Tags cp关系 +// @Summary 详情页cp数据 +// @Param externalId query string true "用户的externalId" +// @Success 200 {object} CpDetail +// @Router /v2/cp/relation/detail [get] +func CpDetailPage(c *gin.Context) (*mycontext.MyContext, error) { + myCtx := mycontext.CreateMyContext(c.Keys) + externalId := c.Query("externalId") + if externalId == "" { + return myCtx, bizerr.InvalidParameter + } + + model := domain.CreateModelContext(myCtx) + user, err := user_m.GetUserByExtId(model, externalId) + if err != nil { + return myCtx, err + } + if user == nil || user.ID == 0 { + return myCtx, bizerr.InvalidParameter + } + + cp, err := cp_m.GetCp(model, user.ID) + if err != nil { + return myCtx, err + } + var res *CpDetail + + if cp.Id > 0 { + userIds := []uint64{cp.UserId1, cp.UserId2} + userMap, err := user_c.GetUserTinyMap(model, userIds, false) + if err != nil { + return myCtx, err + } + + res = new(CpDetail) + // 返回值 + level := cp_m.GetCpLevel(model, cp.Id) + if level.ID > 0 { + res.CpLevel = cp_cv.CvCpLevel{ + Level: level.Level, + Points: cp_e.CpLevelPoints[level.Level] + level.Points, + StartPoints: cp_e.CpLevelPoints[level.Level], + ExpireAtUnix: level.ExpireAt.Unix(), + SettlementDate: level.ExpireAt.Format(utils.DATE_FORMAT), + } + } + if res.CpLevel.Level != cp_e.CpLevelMax { + res.CpLevel.EndPoints = cp_e.CpLevelPoints[res.CpLevel.Level+1] + } + res.CpInfo = cp_cv.CvCpInfo{ + UserInfo: user_cv.UserTinyToCvTiny(userMap[cp.UserId1]), + CpUserInfo: user_cv.UserTinyToCvTiny(userMap[cp.UserId2]), + CpDays: int(time.Now().Sub(cp.CreatedTime).Hours()/24) + 1, + CreatedUnix: cp.CreatedTime.Unix(), + } + } + + resp.ResponseOk(c, res) + return myCtx, nil +} + +// @Tags cp关系 +// @Summary 检查cp的im是否失效 +// @Param msgType query int true "类型" +// @Param msgId query int true "消息id" +// @Success 200 {object} cp_cv.CheckCpImRes +// @Router /v2/cp/im/check [get] +func CheckCpImExpire(c *gin.Context) (*mycontext.MyContext, error) { + myCtx := mycontext.CreateMyContext(c.Keys) + + userId, lang, err := req.GetUserIdLang(c, myCtx) + if err != nil { + return myCtx, err + } + + msgType, err := strconv.Atoi(c.Query("msgType")) + if err != nil { + return myCtx, err + } + if msgType < 1 || msgType > 2 { + return myCtx, bizerr.InvalidParameter + } + msgId, err := strconv.ParseUint(c.Query("msgId"), 10, 64) + if err != nil { + return myCtx, err + } + if msgId <= 0 { + return myCtx, bizerr.InvalidParameter + } + + model := domain.CreateModelContext(myCtx) + var resId common.MsgIdType + switch msgType { + case 1: // 邀请的消息im检查是否过期 + cpRecord, err := cp_m.GetCpInviteById(model, msgId, userId) + if err != nil { + model.Log.Errorf("CheckCpImExpire userId:%d, msgType:%d, msgId:%d, err:%v", userId, msgType, msgId, err) + return myCtx, err + } + if cpRecord == nil || cpRecord.Id == 0 { + model.Log.Errorf("CheckCpImExpire userId:%d, msgType:%d, msgId:%d, err:%v", userId, msgType, msgId, bizerr.InvalidParameter) + return myCtx, bizerr.InvalidParameter + } + switch cpRecord.Status { + case cp_e.CpInvite: + if userId == cpRecord.UserId { // 发起人 + resId = common.MSG_ID_WAITING_ACCEPTED // 等待对方接受 + } + case cp_e.CpInviteAccept: + resId = common.MSG_ID_INVITATION_ACCEPTED // 已接受 + case cp_e.CpInviteRefuse: + resId = common.MSG_ID_INVITATION_REFUSED // 已拒绝 + case cp_e.CpInviteExpired: + resId = common.MSG_ID_ALREADY_EXPIRED + } + case 2: // 解除的消息im检查是否过期 + cpCancel, err := cp_m.GetCpCancelById(model, msgId, userId) + if err != nil { + model.Log.Errorf("CheckCpImExpire userId:%d, msgType:%d, msgId:%d, err:%v", userId, msgType, msgId, err) + return myCtx, err + } + if cpCancel == nil || cpCancel.Id == 0 { + model.Log.Errorf("CheckCpImExpire userId:%d, msgType:%d, msgId:%d, err:%v", userId, msgType, msgId, bizerr.InvalidParameter) + return myCtx, bizerr.InvalidParameter + } + switch cpCancel.Status { + case cp_e.CpCancel: + if userId == cpCancel.UserId { // 发起人 + resId = common.MSG_ID_WAITING_ACCEPTED // 等待对方接受 + } + case cp_e.CpCancelRevoke: + resId = common.MSG_ID_HAS_BEEN_CANCELED // 已被取消 + case cp_e.CpCancelAccept, cp_e.CpCancelAcceptAuto: + resId = common.MSG_ID_CP_UNBOUND // 已解绑 + } + } + if resId > 0 { + return myCtx, myerr.ToLocal(msg.GetErrByLanguage(model, resId, lang, comerr.AlreadyExpired)) + } + + resp.ResponseOk(c, cp_cv.CheckCpImRes{}) + return myCtx, nil +} diff --git a/route/cp_r/rank.go b/route/cp_r/rank.go new file mode 100644 index 0000000..850b380 --- /dev/null +++ b/route/cp_r/rank.go @@ -0,0 +1,287 @@ +package cp_r + +import ( + "fmt" + "git.hilo.cn/hilo-common/domain" + "git.hilo.cn/hilo-common/mycontext" + "git.hilo.cn/hilo-common/resource/mysql" + "github.com/gin-gonic/gin" + "github.com/jinzhu/now" + "hilo-user/_const/enum/cp_e" + "hilo-user/cv/cp_cv" + "hilo-user/cv/user_cv" + "hilo-user/domain/model/cp_m" + "hilo-user/domain/model/user_m" + "hilo-user/myerr/bizerr" + "hilo-user/req" + "hilo-user/resp" + "time" +) + +// @Tags CP v2 +// @Summary 排行榜 +// @Param token header string true "token" +// @Param nonce header string true "随机数字" +// @Param pageIndex query int true "偏移值 默认:1" default(1) +// @Param pageSize query int true "请求数量 默认:10" default(10) +// @Param queryType path string true "类型:day/week/month" +// @Success 200 {object} []cp_cv.CvCpRank +// @Router /v2/cp/rank/{queryType} [get] +func CpRank(c *gin.Context) (*mycontext.MyContext, error) { + myCtx := mycontext.CreateMyContext(c.Keys) + userId, err := req.GetUserId(c) + if err != nil { + return myCtx, err + } + pageReq := new(req.PageReqBase) + if err := c.ShouldBindQuery(pageReq); err != nil { + return myCtx, err + } + if pageReq.PageIndex == 0 { + pageReq.PageIndex = 1 + } + queryType := c.Param("queryType") + if queryType != "day" && queryType != "week" && queryType != "month" { + return myCtx, bizerr.InvalidParameter + } + var beginDate, endDate string + switch queryType { + case "day": + beginDate, endDate = time.Now().Format("2006-01-02"), time.Now().Format("2006-01-02") + case "week": + beginDate = now.BeginningOfWeek().Format("2006-01-02") + endDate = now.EndOfWeek().Format("2006-01-02") + case "month": + beginDate = now.BeginningOfMonth().Format("2006-01-02") + endDate = now.EndOfMonth().Format("2006-01-02") + } + offset, limit := (pageReq.PageIndex-1)*pageReq.PageSize, pageReq.PageSize + model := domain.CreateModelContext(myCtx) + ranks := cp_m.PageCpDayRank(model, beginDate, endDate, offset, limit) + var response []cp_cv.CvCpRank + var userIds []mysql.ID + var cpIds []mysql.ID + for _, rank := range ranks { + userIds = append(userIds, rank.UserId1) + userIds = append(userIds, rank.UserId2) + cpIds = append(cpIds, rank.CpId) + } + userBase, err := user_cv.GetUserBaseMap(userIds, userId) + if err != nil { + return myCtx, err + } + cpLevels := cp_m.MGetCpLevel(model, cpIds) + for i, rank := range ranks { + response = append(response, cp_cv.CvCpRank{ + CpId: rank.CpId, + User1: userBase[rank.UserId1], + User2: userBase[rank.UserId2], + Score: rank.Score, + Ranking: fmt.Sprintf("%d", i+1+offset), + CpLevel: cp_cv.CvCpLevel{ + Level: cpLevels[rank.CpId].Level, + //Points: cp_e.CpLevelPoints[cpLevel] + curPoints, + //EndPoints: nextPoints, + //RemainPoints: remainPoints, + //ExpireAtUnix: expireAtUnix, + }, + }) + } + resp.ResponsePageBaseOk(c, response, pageReq.PageIndex+1, len(ranks) >= pageReq.PageSize) + return myCtx, nil +} + +// @Tags CP v2 +// @Summary CP,Top3 +// @Param token header string true "token" +// @Param nonce header string true "随机数字" +// @Success 200 {object} cp_cv.CpTops +// @Router /v2/cp/top3 [get] +func CpTop3(c *gin.Context) (*mycontext.MyContext, error) { + myCtx := mycontext.CreateMyContext(c.Keys) + userId, err := req.GetUserId(c) + if err != nil { + return myCtx, err + } + var response = cp_cv.CpTops{ + Day: make([]cp_cv.CvCpRank, 0), + Week: make([]cp_cv.CvCpRank, 0), + } + queryTypes := []string{"day", "week"} + for _, queryType := range queryTypes { + var beginDate, endDate string + switch queryType { + case "day": + beginDate, endDate = time.Now().Format("2006-01-02"), time.Now().Format("2006-01-02") + case "week": + beginDate = now.BeginningOfWeek().Format("2006-01-02") + endDate = now.EndOfWeek().Format("2006-01-02") + } + offset, limit := 0, 3 + model := domain.CreateModelContext(myCtx) + ranks := cp_m.PageCpDayRank(model, beginDate, endDate, offset, limit) + var userIds []mysql.ID + var cpIds []mysql.ID + for _, rank := range ranks { + userIds = append(userIds, rank.UserId1) + userIds = append(userIds, rank.UserId2) + cpIds = append(cpIds, rank.CpId) + } + userBase, err := user_cv.GetUserBaseMap(userIds, userId) + if err != nil { + return myCtx, err + } + cpLevels := cp_m.MGetCpLevel(model, cpIds) + for i, rank := range ranks { + if queryType == "day" { + response.Day = append(response.Day, cp_cv.CvCpRank{ + CpId: rank.CpId, + User1: userBase[rank.UserId1], + User2: userBase[rank.UserId2], + Score: rank.Score, + Ranking: fmt.Sprintf("%d", i+1+offset), + CpLevel: cp_cv.CvCpLevel{ + Level: cpLevels[rank.CpId].Level, + }, + }) + } else { + response.Week = append(response.Week, cp_cv.CvCpRank{ + CpId: rank.CpId, + User1: userBase[rank.UserId1], + User2: userBase[rank.UserId2], + Score: rank.Score, + Ranking: fmt.Sprintf("%d", i+1+offset), + CpLevel: cp_cv.CvCpLevel{ + Level: cpLevels[rank.CpId].Level, + }, + }) + } + } + } + resp.ResponseOk(c, response) + return myCtx, nil +} + +// @Tags CP v2 +// @Summary 我的cp +// @Param token header string true "token" +// @Param nonce header string true "随机数字" +// @Param queryType path string true "类型:day/week/month" +// @Success 200 {object} cp_cv.CvCpRank +// @Router /v2/cp/my/{queryType} [get] +func CpMy(c *gin.Context) (*mycontext.MyContext, error) { + myCtx := mycontext.CreateMyContext(c.Keys) + userId, err := req.GetUserId(c) + if err != nil { + return myCtx, err + } + queryType := c.Param("queryType") + if queryType != "day" && queryType != "week" && queryType != "month" { + return myCtx, bizerr.InvalidParameter + } + var beginDate, endDate string + switch queryType { + case "day": + beginDate, endDate = time.Now().Format("2006-01-02"), time.Now().Format("2006-01-02") + case "week": + beginDate = now.BeginningOfWeek().Format("2006-01-02") + endDate = now.EndOfWeek().Format("2006-01-02") + case "month": + beginDate = now.BeginningOfMonth().Format("2006-01-02") + endDate = now.EndOfMonth().Format("2006-01-02") + } + model := domain.CreateModelContext(myCtx) + relation, exits := cp_m.GetCpRelation(model, userId) + var userIds []mysql.ID + var scores uint32 + if exits { + // 保证一下自己是userId1 + userIds = append(userIds, userId) + if relation.UserId1 == userId { + userIds = append(userIds, relation.UserId2) + } else { + userIds = append(userIds, relation.UserId1) + } + rank := cp_m.GetCpDayRank(model, beginDate, endDate, relation.Id) + scores = rank.Score + } else { + userIds = append(userIds, userId) + relation.UserId1 = userId + } + userBases, err := user_cv.GetUserBaseMap(userIds, userId) + if err != nil { + return myCtx, err + } + response := cp_cv.CvCpRank{ + CpId: relation.Id, + Score: scores, + } + if relation.UserId1 > 0 { + response.User1 = userBases[relation.UserId1] + } + if relation.UserId2 > 0 { + response.User2 = userBases[relation.UserId2] + response.Ranking = "30+" + ranks := cp_m.PageCpDayRank(model, beginDate, endDate, 0, 30) + for i, rank := range ranks { + if relation.Id == rank.CpId { + response.Ranking = fmt.Sprintf("%d", i+1) + break + } + } + } + resp.ResponseOk(c, response) + return myCtx, nil +} + +// @Tags CP v2 +// @Summary 成就页 +// @Param token header string true "token" +// @Param nonce header string true "随机数字" +// @Param pageIndex query int true "偏移值 默认:1" default(1) +// @Param pageSize query int true "请求数量 默认:10" default(10) +// @Success 200 {object} []cp_cv.CvCpAchievement +// @Router /v2/cp/achievement [get] +func CpAchievement(c *gin.Context) (*mycontext.MyContext, error) { + myCtx := mycontext.CreateMyContext(c.Keys) + model := domain.CreateModelContext(myCtx) + _, lang, err := req.GetUserIdLang(c, myCtx) + if err != nil { + return myCtx, err + } + var response = make([]cp_cv.CvCpAchievement, 0) + TypeDescMap := map[cp_e.CpAchievement]uint{ + cp_e.CpAchievementLevel: 280, //"等级分值最高", + cp_e.CpAchievementVisitors: 281, //"空间访问量最高", + cp_e.CpAchievementMonthRank: 282, //"月榜最高", + cp_e.CpAchievementWeekRank: 283, //"周榜最高", + cp_e.CpAchievementDayRank: 284, //"天榜最高", + } + achievements := cp_m.GetCpAchievements(model) + var userIds []uint64 + var cpIds []uint64 + for _, v := range achievements { + userIds = append(userIds, v.UserId1) + userIds = append(userIds, v.UserId2) + cpIds = append(cpIds, v.CpId) + } + users, err := user_m.GetUserMapByIds(model, userIds) + if err != nil { + return myCtx, err + } + cpLevels := cp_m.MGetCpLevel(model, cpIds) + for _, a := range achievements { + response = append(response, cp_cv.CvCpAchievement{ + CpId: a.CpId, + User1: user_cv.UserToTiny(users[a.UserId1]), + User2: user_cv.UserToTiny(users[a.UserId2]), + CpLevel: cpLevels[a.CpId].Level, + Type: a.Type, + TypeDesc: cp_m.GetTranslate(TypeDescMap[a.Type], lang), + Score: a.Score, + TimeUnix: a.UpdatedTime.Unix(), + }) + } + resp.ResponsePageBaseOk(c, response, 0, false) + return myCtx, nil +} diff --git a/route/cp_r/space.go b/route/cp_r/space.go new file mode 100644 index 0000000..ac0ed4a --- /dev/null +++ b/route/cp_r/space.go @@ -0,0 +1,175 @@ +package cp_r + +import ( + "git.hilo.cn/hilo-common/domain" + "git.hilo.cn/hilo-common/mycontext" + "git.hilo.cn/hilo-common/resource/mysql" + "github.com/gin-gonic/gin" + "hilo-user/_const/enum/cp_e" + "hilo-user/cv/cp_cv" + "hilo-user/cv/user_cv" + "hilo-user/domain/event/cp_ev" + "hilo-user/domain/model/cp_m" + "hilo-user/domain/model/user_m" + "hilo-user/myerr/bizerr" + "hilo-user/req" + "hilo-user/resp" + "time" +) + +// @Tags CP v2 +// @Summary cp空间 +// @Param token header string true "token" +// @Param nonce header string true "随机数字" +// @Param externalId query string true "查看用户的externalId" +// @Success 200 {object} cp_cv.CvSpace +// @Router /v2/cp/space [get] +func CpSpace(c *gin.Context) (*mycontext.MyContext, error) { + myContext := mycontext.CreateMyContext(c.Keys) + userId, lang, err := req.GetUserIdLang(c, myContext) + if err != nil { + return myContext, err + } + externalId := c.Query("externalId") + if len(externalId) <= 0 { + return myContext, bizerr.InvalidParameter + } + var model = domain.CreateModelContext(myContext) + userInfo, err := user_m.GetUser(model, userId) + if err != nil { + return myContext, err + } + targetUserInfo, err := user_m.GetUserByExtId(model, externalId) + if err != nil { + return myContext, err + } + var cpUserInfo *user_m.User + expireAtUnix, cpLevel := int64(0), cp_e.CpLevel0 + visitTimes := int64(0) + settlementDate, cpDays, applyToUnbind := "", 0, false + nextPoints, curPoints := cp_e.CpLevelPoints[cp_e.CpLevel1], mysql.Num(0) + cpRelation, exists := cp_m.GetCpRelation(model, targetUserInfo.ID) + if exists { + level := cp_m.GetCpLevel(model, cpRelation.Id) + if level.ExpireAt.Before(time.Now()) { + level.ExpireAt = time.Now().AddDate(0, 1, 0) + } + cpLevel, curPoints = level.Level, level.Points + expireAtUnix, settlementDate = level.ExpireAt.Unix(), level.ExpireAt.Format("2006-01-02") + cpDays = int(time.Now().Sub(cpRelation.CreatedTime).Hours()/24) + 1 + // cp 存在的时候,发布访问cp空间事件 + if err := cp_ev.PublishCpSpaceVisit(model, &cp_ev.SpaceVisitEvent{ + UserId: userId, + CpId: cpRelation.Id, + UserId1: cpRelation.UserId1, + UserId2: cpRelation.UserId2, + }); err != nil { + return myContext, err + } + visitTimes = cp_m.CountCpSpaceVisitors(model, cpRelation.Id) + cpUserId := cpRelation.UserId2 + if cpUserId == userId { + cpUserId = cpRelation.UserId1 + } + cpUserInfo, err = user_m.GetUser(model, cpUserId) + if err != nil { + return myContext, err + } + // 查看别人的cp空间 + if userId != cpRelation.UserId1 && userId != cpRelation.UserId2 { + userInfo, err = user_m.GetUser(model, cpRelation.UserId1) + if err != nil { + return myContext, err + } + } else { + // 自己的空间 + applyToUnbind = cp_m.GetApplyToUnbind(model, userId, cpUserId) + } + } + if cpLevel != cp_e.CpLevelMax { + nextPoints = cp_e.CpLevelPoints[cpLevel+1] + } else { + nextPoints = cp_e.CpLevelPoints[cp_e.CpLevelMax] + } + userPrivileges, err := cp_m.MGetUserSvipPrivilege(model, []uint64{userId}) + if err != nil { + return myContext, err + } + allPrivilegeList := make([][]cp_cv.CvPrivilege, cp_e.CpLevelMax+1) + for level := cp_e.CpLevel0; level <= cp_e.CpLevelMax; level++ { + privilegeList := cp_cv.CopyCpLevelPrivilegeList(level, lang) + for i, v := range privilegeList { + if v.CanSwitch { + privilegeList[i].UserSwitch = userPrivileges[userId][v.Type] + } + } + allPrivilegeList[level] = privilegeList + } + // 返回值 + response := cp_cv.CvSpace{ + CpLevel: cp_cv.CvCpLevel{ + Level: cpLevel, + Points: cp_e.CpLevelPoints[cpLevel] + curPoints, + StartPoints: cp_e.CpLevelPoints[cpLevel], + EndPoints: nextPoints, + ExpireAtUnix: expireAtUnix, + SettlementDate: settlementDate, + MaxLevel: cp_e.CpLevelMax, + }, + ResLevelList: cp_cv.CvResLevelList, + PrivilegeList: allPrivilegeList, + } + response.CpInfo = cp_cv.CvCpInfo{ + UserInfo: user_cv.UserToTiny(*userInfo), + CpDays: cpDays, + VisitTimes: visitTimes, + ApplyToUnbind: applyToUnbind, + } + if cpUserInfo != nil { + response.CpInfo.CpUserInfo = user_cv.UserToTiny(*cpUserInfo) + } + // 需要提醒的纪念日 + if exists && (userId == cpRelation.UserId1 || userId == cpRelation.UserId2) { + anniversary := cp_m.GetUserTodayCpAnniversary(model, cpRelation.Id) + for _, a := range anniversary { + response.CpAnniversary = append(response.CpAnniversary, cp_cv.CvCpAnniversary{ + Id: a.ID, + Content: a.Content, + Timestamp: a.Timestamp, + }) + } + } + resp.ResponseOk(c, response) + return myContext, nil +} + +type CpPrivilegeOpenCloseReq struct { + Type cp_e.CpPrivilege `form:"type" binding:"required"` + OpenClose mysql.OpenClose `form:"openClose" binding:"required"` +} + +// @Tags CP v2 +// @Summary cp特权开关 +// @Param token header string true "token" +// @Param nonce header string true "随机数字" +// @Param type formData int true "1.空间 2.横幅 3.等级勋章 4.证书 5.进场特效 6.头像头饰 7.动态资料卡 8.麦位特效" +// @Param openClose formData int true "1:open 2:close" +// @Success 200 +// @Router /v2/cp/privilege/openClose [put] +func CpPrivilegeOpenClose(c *gin.Context) (*mycontext.MyContext, error) { + myCtx := mycontext.CreateMyContext(c.Keys) + var request CpPrivilegeOpenCloseReq + if err := c.ShouldBind(&request); err != nil { + return myCtx, err + } + userId, err := req.GetUserId(c) + if err != nil { + return myCtx, err + } + var model = domain.CreateModelContext(myCtx) + if err := cp_m.OpenCLoseUserSvipPrivilege(model, userId, request.Type, request.OpenClose); err != nil { + return myCtx, err + } + resp.ResponseOk(c, "") + return myCtx, nil +} diff --git a/route/errorHandle.go b/route/errorHandle.go index c95b89e..4f6c181 100755 --- a/route/errorHandle.go +++ b/route/errorHandle.go @@ -37,9 +37,6 @@ func wrapper(handler HandlerFunc) func(c *gin.Context) { userId, _ := req.GetUserId(c) switch h := err.(type) { - case *myerr.GameError: - myContext.Log.Warnf("request user err -> url:%v, method:%v, userId:%v, err :%+v\n", reqUri, method, userId, h.Error()) - resp.GameResponseFail(c, h) case *myerr.BusinessError: myContext.Log.Warnf("request err -> url:%v, method:%v, userId:%v, err :%+v\n", reqUri, method, userId, h.GetErr()) resp.ResponseBusiness(c, h) diff --git a/route/middleHandle.go b/route/middleHandle.go index 7f4ec55..1442553 100755 --- a/route/middleHandle.go +++ b/route/middleHandle.go @@ -102,6 +102,12 @@ func LoggerHandle(c *gin.Context) { c.Set(mycontext.APP_VERSION, appVersion) c.Set(mycontext.URL, reqUri) c.Set(mycontext.METHOD, method) + lang := header.Get(mycontext.LANGUAGE) + c.Set(mycontext.LANGUAGE, lang) + carrier := header.Get(mycontext.CARRIER) + c.Set(mycontext.CARRIER, carrier) + timezone := header.Get(mycontext.TIMEZONE) + c.Set(mycontext.TIMEZONE, timezone) userId, _ := req.GetUserId(c) diff --git a/route/router.go b/route/router.go index 2e75ca8..dfda755 100755 --- a/route/router.go +++ b/route/router.go @@ -11,6 +11,7 @@ import ( _ "hilo-user/docs" "hilo-user/domain/model/msg_m" "hilo-user/resp" + "hilo-user/route/cp_r" "hilo-user/route/user_r" ) @@ -22,17 +23,42 @@ func InitRouter() *gin.Engine { needLogin := r.Group("") needLogin.Use(ExceptionHandle, LoggerHandle, JWTApiHandle) v1 := needLogin.Group("/v1") + v2 := needLogin.Group("/v2") user := v1.Group("/user") { user.GET("/nameplate", wrapper(user_r.UserNameplate)) user.GET("/bag/:resType", wrapper(user_r.UserBag)) } + cp := v2.Group("/cp") + { + cp.GET("/space", wrapper(cp_r.CpSpace)) + cp.PUT("/privilege/openClose", wrapper(cp_r.CpPrivilegeOpenClose)) + cp.GET("/rank/:queryType", wrapper(cp_r.CpRank)) + cp.GET("/my/:queryType", wrapper(cp_r.CpMy)) + cp.GET("/achievement", wrapper(cp_r.CpAchievement)) + cp.GET("/top3", wrapper(cp_r.CpTop3)) + + cp.POST("/anniversary", wrapper(cp_r.PostAnniversary)) + cp.PUT("/anniversary/:id", wrapper(cp_r.PutAnniversary)) + cp.GET("/anniversary", wrapper(cp_r.PageAnniversary)) + cp.DELETE("/anniversary/:id", wrapper(cp_r.DelAnniversary)) + + cp.GET("/relation/check", wrapper(cp_r.CheckUserCpRelation)) + cp.POST("/relation", wrapper(cp_r.CpRelation)) + cp.POST("/relation/invite/reply", wrapper(cp_r.ReplyCpInvite)) + cp.POST("/relation/cancel/reply", wrapper(cp_r.ReplyCpCancel)) + //cp.GET("/relation/detail", wrapper(cp_r.CpDetailPage)) + cp.GET("/im/check", wrapper(cp_r.CheckCpImExpire)) + } inner := r.Group("/inner") inner.Use(ExceptionHandle, LoggerHandle) innerUser := inner.Group("/user") { innerUser.GET("/levels", wrapper(user_r.MGetUserLevels)) innerUser.GET("/bag/id", wrapper(user_r.GetUserBagId)) + innerUser.GET("/cp", wrapper(user_r.GetUserCp)) + innerUser.GET("/cpRelations", wrapper(user_r.MGetUserCpRelation)) + innerUser.GET("/cp/pair", wrapper(user_r.GetUserCpPair)) } // 道具相关 innerProp := inner.Group("/prop") diff --git a/route/user_r/bag.go b/route/user_r/bag.go index c425f55..3d83b33 100644 --- a/route/user_r/bag.go +++ b/route/user_r/bag.go @@ -4,6 +4,7 @@ import ( "git.hilo.cn/hilo-common/domain" "git.hilo.cn/hilo-common/mycontext" "github.com/gin-gonic/gin" + "hilo-user/_const/enum/cp_e" "hilo-user/_const/enum/res_e" "hilo-user/cv/user_cv" "hilo-user/domain/model/bag_m" @@ -44,7 +45,7 @@ func UserBag(c *gin.Context) (*mycontext.MyContext, error) { } for _, bagGift := range userBagGifts { if gift, ok := allGifts[bagGift.ResId]; ok { - results = append(results, user_cv.UserBag{ + info := user_cv.UserBag{ BagId: bagGift.ID, ResType: res_e.ResUserBagGift, ResId: gift.ID, @@ -55,7 +56,16 @@ func UserBag(c *gin.Context) (*mycontext.MyContext, error) { SvgaUrl: gift.SvagUrl, Count: bagGift.Count, RemainDays: int(bagGift.EndTime.Sub(time.Now()).Hours() / 24), - }) + } + if gift.ID == cp_e.CpConfessionGiftId { // 如果是cp告白礼物 + info.TextStyleList = make([]*user_cv.TextStyle, 0, 2) + info.TextStyleList = append(info.TextStyleList, + &user_cv.TextStyle{TextColor: "#ce0083", TextSize: 20, TextKey: "sender_name"}, + &user_cv.TextStyle{TextColor: "#ce0083", TextSize: 20, TextKey: "receiver_name"}, + ) + } + + results = append(results, info) } } default: diff --git a/route/user_r/inner.go b/route/user_r/inner.go index cb1d627..ad8e7e4 100644 --- a/route/user_r/inner.go +++ b/route/user_r/inner.go @@ -5,8 +5,11 @@ import ( "git.hilo.cn/hilo-common/mycontext" "git.hilo.cn/hilo-common/resource/mysql" "github.com/gin-gonic/gin" + "hilo-user/_const/enum/cp_e" + "hilo-user/cv/cp_cv" "hilo-user/cv/user_cv" "hilo-user/domain/model/bag_m" + "hilo-user/domain/model/cp_m" "hilo-user/domain/model/noble_m" "hilo-user/domain/model/res_m" "hilo-user/domain/model/user_m" @@ -90,3 +93,166 @@ func GetUserBagId(c *gin.Context) (*mycontext.MyContext, error) { }) return myCtx, nil } + +type GetUserCpReq struct { + Id mysql.ID `form:"id" binding:"required"` + Language string `form:"language"` +} + +// @Tags 用户-内部 +// @Summary 获取用户cp +// @Param id query int true "用户id" +// @Param language query string true "语言" +// @Success 200 {object} cp_cv.CvCp +// @Router /inner/user/cp [get] +func GetUserCp(c *gin.Context) (*mycontext.MyContext, error) { + myContext := mycontext.CreateMyContext(c.Keys) + var model = domain.CreateModelContext(myContext) + var req GetUserCpReq + if err := c.ShouldBindQuery(&req); err != nil { + return myContext, err + } + userId := req.Id + var response cp_cv.CvCp + cpLevel := cp_e.CpLevel0 + cpRelation, exists := cp_m.GetCpRelation(model, userId) + if !exists { + resp.ResponseOk(c, response) + return myContext, nil + } + var myPrivilegeList []cp_cv.CvPrivilege + level := cp_m.GetCpLevel(model, cpRelation.Id) + if level.ExpireAt.Before(time.Now()) { + level.ExpireAt = time.Now().AddDate(0, 1, 0) + } + cpLevel = level.Level + cpUserId := cpRelation.UserId2 + if cpUserId == userId { + cpUserId = cpRelation.UserId1 + } + userPrivileges, err := cp_m.MGetUserSvipPrivilege(model, []uint64{userId}) + if err != nil { + return myContext, err + } + privilegeList := cp_cv.CopyCpLevelPrivilegeList(level.Level, "en") + for i, v := range privilegeList { + if v.CanSwitch { + privilegeList[i].UserSwitch = userPrivileges[userId][v.Type] + } + } + // 我的特权,有开关并且打开才返回 + for i, v := range privilegeList { + if !v.CanSwitch { + myPrivilegeList = append(myPrivilegeList, privilegeList[i]) + } else if v.UserSwitch { + myPrivilegeList = append(myPrivilegeList, privilegeList[i]) + } + } + userBases, err := user_cv.GetUserBaseMap([]uint64{cpUserId}, userId) + if err != nil { + return myContext, err + } + // 返回值 + title := "" + if msgId, ok := cp_e.CpLevelTitle[level.Level]; ok { + title = cp_cv.GetTranslate(msgId, req.Language) + } + response = cp_cv.CvCp{ + CpUserInfo: userBases[cpUserId], + CpLevel: cp_cv.CvCpLevel{ + Level: cpLevel, + Points: cp_e.CpLevelPoints[cpLevel] + level.Points, + Title: title, + }, + MyPrivilegeList: myPrivilegeList, + CpDays: int(time.Now().Sub(cpRelation.CreatedTime).Hours()/24) + 1, + CreatedUnix: cpRelation.CreatedTime.Unix(), + } + resp.ResponseOk(c, response) + return myContext, nil +} + +type MGetUserCpRelationReq struct { + Ids []mysql.ID `form:"ids" binding:"required"` +} + +// @Tags 用户-内部 +// @Summary 批量获取用户cp关系 +// @Param ids query int true "用户ids" +// @Success 200 {object} map[uint64]cp_cv.CvCpRelation +// @Router /inner/user/cpRelations [get] +func MGetUserCpRelation(c *gin.Context) (*mycontext.MyContext, error) { + myContext := mycontext.CreateMyContext(c.Keys) + var model = domain.CreateModelContext(myContext) + var req MGetUserCpRelationReq + if err := c.ShouldBindQuery(&req); err != nil { + return myContext, err + } + cpRelations := cp_m.MGetCpRelation(model, req.Ids) + var m = make(map[uint64]cp_m.CpRelation) + var userIds []uint64 + for i, v := range cpRelations { + m[v.UserId1] = cpRelations[i] + m[v.UserId2] = cpRelations[i] + userIds = append(userIds, v.UserId1) + userIds = append(userIds, v.UserId2) + } + users, err := user_m.GetUserMapByIds(model, userIds) + if err != nil { + return myContext, err + } + var response = make(map[uint64]cp_cv.CvCpRelation) + for _, uid := range req.Ids { + if cpRelation, ok := m[uid]; ok { + cpUserId := cpRelation.UserId2 + if cpUserId == uid { + cpUserId = cpRelation.UserId1 + } + response[uid] = cp_cv.CvCpRelation{ + CpId: cpRelation.Id, + UserId: uid, + CpUserId: cpUserId, + CpUserAvatar: users[cpUserId].Avatar, + } + } + } + resp.ResponseOk(c, response) + return myContext, nil +} + +type GetUserCpPairReq struct { + Ids []mysql.ID `form:"ids" binding:"required"` +} + +// @Tags 用户-内部 +// @Summary 给出指定uids下的cp对 +// @Param ids query string true "用户id,如:ids=1&ids=2&ids=3" +// @Success 200 {object} [][]uint64 +// @Router /inner/user/cp/pair [get] +func GetUserCpPair(c *gin.Context) (*mycontext.MyContext, error) { + myContext := mycontext.CreateMyContext(c.Keys) + var model = domain.CreateModelContext(myContext) + var req GetUserCpPairReq + if err := c.ShouldBindQuery(&req); err != nil { + return myContext, err + } + userIds := req.Ids + m := make(map[mysql.ID]bool) + for _, uid := range userIds { + m[uid] = true + } + pairs := cp_m.MGetCpRelation(model, userIds) + var cpIds []mysql.ID + for _, pair := range pairs { + cpIds = append(cpIds, pair.Id) + } + levels := cp_m.MGetCpLevel(model, cpIds) + var response [][3]uint64 + for _, pair := range pairs { + if m[pair.UserId1] && m[pair.UserId2] { + response = append(response, [3]mysql.ID{pair.UserId1, pair.UserId2, uint64(levels[pair.Id].Level)}) + } + } + resp.ResponseOk(c, response) + return myContext, nil +} diff --git a/test/.env b/test/.env new file mode 100644 index 0000000..aaa4b80 --- /dev/null +++ b/test/.env @@ -0,0 +1 @@ +MODE=local diff --git a/test/cp_test.go b/test/cp_test.go new file mode 100644 index 0000000..0e9e17d --- /dev/null +++ b/test/cp_test.go @@ -0,0 +1,16 @@ +package test + +import ( + "git.hilo.cn/hilo-common/domain" + "hilo-user/domain/model/cp_m" + "testing" +) + +func TestInitCpAnniversary(t *testing.T) { + err := cp_m.InitCpAnniversary(domain.CreateModelNil(), cp_m.CpRelation{ + Id: 1, + UserId1: 7642, + UserId2: 4549, + }, "ar") + println(err) +} diff --git a/test/local.ini b/test/local.ini new file mode 100644 index 0000000..81bbf6b --- /dev/null +++ b/test/local.ini @@ -0,0 +1,171 @@ +[DATABASE] +MYSQL_HOST=47.244.34.27:3306 +MYSQL_USERNAME=root +MYSQL_PASSWORD=yX0jPAhO0I4s2zlA +MYSQL_DB=hilo +[DATABASECODE] +MYSQL_HOST=47.244.34.27:3306 +MYSQL_USERNAME=root +MYSQL_PASSWORD=yX0jPAhO0I4s2zlA +MYSQL_DB=hilo_code +[REDIS] +REDIS_HOST=47.244.34.27:6379 +REDIS_PASSWORD=8QZ9JD1zLvPR3yHf +[JWT] +SECRET=hilo1632 +ISSUER_API=hiloApi +ISSUER_Mgr=hiloMgr +EXPIRE=240h +[GAMEJWT] +SECRET=hilo1632 +ISSUER_CLIENT=hiloClient +ISSUER_SERVER=hiloServer +EXPIRE=240h +[APP] +MASTER=false +BIZ_SECRET=biz +WEB_SECRET=webHilo1258 +OPERATION_SECRET=operation1258236 +SUPERUSER=2701,2831 +OFFICIAL_STAFF=2701,2831 +OFFICIAL_GROUP=@TGS#3NC2ATRHS,@TGS#33W3KNLHK +MINIMAL_VERSION_ANDROID=212 +MINIMAL_VERSION_IOS=100 +MODERATE=AWS +[OSS] +OSS_ACCESS_KEY_ID=LTAIxdazV2pCuV3T +OSS_ACCESS_KEY_SECRET=zuAnreAXQ6vlAKnvvmolFLfb1N5w5S +OSS_ROLE_ARN=acs:ram::1509841556585969:role/aliyunosstokengeneratorrole +OSS_END_POINT=http://oss-accelerate.aliyuncs.com +OSS_BUCKET=starvoice +OSS_CDN=https://oss.chathot.me/ +OSS_EXPIRED_TIME=3600 +OSS_STS_POINT=me-east-1 +OSS_STS=sts-faceline-demo +OSS_STS_AES=484194d4d0f968a7 +[AWS] +AWS_BUCKET=starchat +AWS_CDN=https://image.whoisamy.shop/ +AWS_DIR=hilo/ +CONFIDENCE=80 +[RONGYUN] +RONG_CLOUD_APP_KEY=pvxdm17jpe9tr +RONG_CLOUD_APP_SECRET=rI4giiKWaBS4 +RONG_CLOUD_URL=https://api-sg01.ronghub.com +[TENCENTYUN] +TENCENTYUN_APP_ID=1400548270 +TENCENTYUN_KEY=321bd60f73096b059c7350f1cd97d51028850b34fa58c5c0d26bb4a19e783de8 +TX_OVERSEA_APP_ID=40000066 +TX_OVERSEA_KEY=3ab68ea5bddc8774d90b8c764ae71188914bd5fd06f30b28790c51e44ca7885c +[EMAS] +REGION_ID=cn-hangzhou +ACCESS_KEY_ID=LTAI4FhNPzxdzD4w6bHirL9Z +ACCESS_KEY_SECRET=OQvUJpXDrjGi3g1F2aHiAIFWIvLdbP +ANDROID_APP_KEY=30250713 +ANDROID_APP_SECRET=cae7b9a9d3e54577d2c3b60bf6d23047 +IOS_APP_KEY=30240346 +IOS_APP_SECRET=57f33ab9ca6a957a8c659f2b0b6d1205 +APNS=DEV +[AGORA] +APP_ID=fc3e087f701b4f788099e1924c3cc7b0 +APP_CERTIFICATE=ff29c100a613433db82324e8411eabc8 +CUSTOMER_KEY=6b132c0ff7164560a2bc53fda06ea85a +CUSTOMER_SECRET=eedad2cd16d24834990d5450ace9f1ce +CALLBACK_SECRET=n_ZizS_N8 +[CHECKOUT] +AUTHORIZATION=sk_test_9b5e771c-5a3f-4a8d-a4da-31b19bd43d83 +URL=https://api.sandbox.checkout.com/hosted-payments +H5=http://test.chathot.me/action/hiloHtml/22_05_30_recharge/topup.html +HILO_SECRET_KEY=sk_test_dfbaa3b6-135d-4376-9996-2089b7d8a086 +[MATCH] +MATCH_FREE_TIME=60 +MATCH_FREE_TIME_VIP=300 +MATCH_ADD_TIME_FREE=90 +MATCH_AGORA_TIME=30 +MATCH_CYCLE=8 +MATCH_USER_EXPIRES=480 +MATCH_SUCCESS_WAIT_DURATION=10 +MATCH_SUCCESS_SINGLE_WAIT_TIME_IN_SEC=12 +MATCH_SUCCESS_DUAL_WAIT_TIME_IN_SEC=15 +[ONLINE] +ONLINE_CYCLE=600 +ONLINE_USER_EXPIRES=259200 +[VIDEO] +VIDEO_DAILY_FREE_NUM=20 +VIDEO_FREE_TIME=60 +VIDEO_FREE_TIME_VIP=300 +VIDEO_ADD_TIME_FREE=60 +VIDEO_AGORA_TIME=30 +VIDEO_MINUTE_NORMAL=1000 +VIDEO_MINUTE_UNION=2000 +[SESSION] +SESSION_DAILY_FREE_NUM=50 +[BEAN] +DIAMOND_BEAN_RATE=90 +[H5] +USER_LEVEL=http://test.chathot.me/action/activityhtml/hiloUserLevel/index.html +GROUP_SUPPORT=http://test.chathot.me/action/activityhtml/21_12_06/page.html +LUCKY_WHEEL=https://h5.whoisamy.shop/action/activityhtml/21_12_30/page.html +NOBLE_BUY_IOS=https://h5.whoisamy.shop/action/hiloHtml/lxt_h5/page.html +[GROUPIM] +MSG_SORT_EXPIRE=43200 +MSG_SORT_SNAP=300 +[GRADE] +CHARM_SPEED_VIP=15 +ACTITY_SPEED_VIP=15 +WEALTH_SPEED_VIP=15 +[LIKE] +I_LIKE_NUM=30 +I_LIKE_NUM_VIP=100 +I_LIKE_NUM_NOBLE=1000 +[APPLEPAY] +PASSWORD=38702750a05c4cb09c9d6ca646835634 +[REGISTER] +IMEI_TOTAL=3 +IMEI_OAUTH=2 +ACCOUNT_IP=100 +ACCOUNT_IP_DURATION=21600 +[BANNER] +GIFT_BANNER_LEVEL1=100 +GIFT_BANNER_LEVEL2=2000 +GIFT_BANNER_LEVEL3=5000 +[DIAMOND] +DAILY_LOGIN_IMEI_LIMIT=200 +DAILY_LOGIN_IP_LIMIT=5 +PRIVATE_GIFT_RETURN=1440 +NEW_USER_INVITE_AWARD=5000 +[LUCKY_WHEEL] +MINIMAL_PARTICIPANT=2 +WAIT_TIMELONG=10 +WINNER_DIAMOND_BANNER=100 +[GROUP_CUSTOM_THEME] +PIC_LIMIT=5 +DAY=10 +[GIFT] +WALL_DIAMOND=10 +[DAILY] +LOGIN_COMMON=5 +LOGIN_VIP=300 +[DAILY] +LOGIN_COMMON=5 +LOGIN_VIP=300 +[FRUIT_TYCOON] +POOL_RATIO=20 +WATERMELON_RATIO=70 +[ACTIVITY] +COUNTRY_STAR_POOL_RATIO=20 +COUNTRY_STAR_ORDINARY_RATIO=20 +[PAYER_MAX] +URL=https://pay-gate-uat.payermax.com/aggregate-pay-gate/api/gateway +KEY=d50d149a883b8bb6 +MERCHANT_ID=SP11018326 +BIZ_TYPE=CUSTOMIZE +VERSION=2.3 +FRONT_CALLBACK_URL=https://www.hiloconn.com +SHOW_RESULT=1 +EXPIRE_TIME=1800 +LANGUAGE=en +[SUD] +API_LIST=https://sim-asc.sudden.ltd +[URL] +BIZ_HTTP=https://test.apiv1.faceline.live \ No newline at end of file -- 2.22.0