Commit 6295f1dc authored by hujiebin's avatar hujiebin

Merge branch 'feature/3.9.0' into 'master'

Feature/3.9.0

See merge request !4
parents 3c6346df 3afb6972
package cp_e
type CpAchievement int
const (
CpAchievementLevel CpAchievement = 1 // 等级
CpAchievementVisitors CpAchievement = 2 // 空间访问人数
CpAchievementMonthRank CpAchievement = 3 // 月榜最高
CpAchievementWeekRank CpAchievement = 4 // 周榜最高
CpAchievementDayRank CpAchievement = 5 // 日榜最高
)
package cp_e
type AnniversaryItemType int
const (
AnniversaryItemTypeNormal AnniversaryItemType = 0 // 普通类型
AnniversaryItemTypeAvatar AnniversaryItemType = 1 // 头像类型
AnniversaryItemTypeAnniversary AnniversaryItemType = 2 // 纪念日类型(一年一度,如生日/结婚纪念日)
)
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绑定成功后赠送的头饰-女
)
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 // 麦位特效
)
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 // 国家之星活动入口
)
...@@ -5,55 +5,16 @@ import "git.hilo.cn/hilo-common/resource/mysql" ...@@ -5,55 +5,16 @@ import "git.hilo.cn/hilo-common/resource/mysql"
type MsgIdType = uint type MsgIdType = uint
const ( const (
DEFAULT_LANG = "en" DefaultLang = "en"
) )
type ResMedalType = mysql.Type 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 type ResMedalScope = mysql.Type
const (
//私有,只能时管理人发放
Private ResMedalScope = 1
//公有,自己获取
Public ResMedalScope = 2
)
type ResNameplateType = mysql.Type type ResNameplateType = mysql.Type
type ResNameplateObtainType = mysql.Type type ResNameplateObtainType = mysql.Type
type ResNameplateScope = mysql.Type type ResNameplateScope = mysql.Type
type ResUserBag = mysql.Type type ResUserBag = mysql.Type
type ResPropertyAvatarType = mysql.Type
const ( const (
ResUserBagGift ResUserBag = 1 // 背包道具-礼物 ResUserBagGift ResUserBag = 1 // 背包道具-礼物
......
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()
}
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()
}
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()
}
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邀请、解除到期结算
}
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()
}
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))
}
}
}
}()
}
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"` // 是否置顶
}
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"`
}
package cp_cv
type CheckCpRelationRes struct {
Diamond uint32 `json:"diamond"`
Msg string `json:"msg"`
}
type CheckCpImRes struct {
}
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"`
}
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"
}
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
}
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
}
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
}
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
}
...@@ -5,14 +5,22 @@ import ( ...@@ -5,14 +5,22 @@ import (
) )
type UserBag struct { type UserBag struct {
BagId mysql.ID `json:"bagId"` // 背包id BagId mysql.ID `json:"bagId"` // 背包id
ResType mysql.Type `json:"resType"` // 道具类型 1:礼物道具 ResType mysql.Type `json:"resType"` // 道具类型 1:礼物道具
ResId mysql.ID `json:"resId"` // 道具资源id ResId mysql.ID `json:"resId"` // 道具资源id
GiftId mysql.ID `json:"giftId"` // 道具的礼物id GiftId mysql.ID `json:"giftId"` // 道具的礼物id
Name string `json:"name"` // 资源名称 Name string `json:"name"` // 资源名称
DiamondNum mysql.Num `json:"diamondNum"` // 钻石数量 DiamondNum mysql.Num `json:"diamondNum"` // 钻石数量
IconUrl string `json:"iconUrl"` // icon url IconUrl string `json:"iconUrl"` // icon url
SvgaUrl string `json:"svgaUrl"` // svga url SvgaUrl string `json:"svgaUrl"` // svga url
Count mysql.Num `json:"count"` // 拥有数量 Count mysql.Num `json:"count"` // 拥有数量
RemainDays int `json:"remainDays"` // 有效天数 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
} }
This diff is collapsed.
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
}
package user_c package user_c
import ( import (
"git.hilo.cn/hilo-common/_const/common"
"git.hilo.cn/hilo-common/domain" "git.hilo.cn/hilo-common/domain"
"git.hilo.cn/hilo-common/resource/mysql" "git.hilo.cn/hilo-common/resource/mysql"
"git.hilo.cn/hilo-common/utils"
redisV8 "github.com/go-redis/redis/v8" redisV8 "github.com/go-redis/redis/v8"
"github.com/jinzhu/copier" "github.com/jinzhu/copier"
"hilo-user/_const/redis_key/user_k" "hilo-user/_const/redis_key/user_k"
...@@ -10,6 +12,7 @@ import ( ...@@ -10,6 +12,7 @@ import (
"hilo-user/domain/model/user_m" "hilo-user/domain/model/user_m"
"hilo-user/myerr" "hilo-user/myerr"
"hilo-user/myerr/bizerr" "hilo-user/myerr/bizerr"
"time"
) )
// 获取用户简要信息 // 获取用户简要信息
...@@ -96,10 +99,11 @@ func ToUserIdByCode(model *domain.Model, code mysql.Str) (mysql.ID, error) { ...@@ -96,10 +99,11 @@ func ToUserIdByCode(model *domain.Model, code mysql.Str) (mysql.ID, error) {
// 顺手缓存code->userId // 顺手缓存code->userId
// param user: 已经在上层获取的db User结构 // param user: 已经在上层获取的db User结构
func cacheUserTiny(model *domain.Model, user *user_m.User) error { func cacheUserTiny(model *domain.Model, user *user_m.User) error {
userTiny := new(user_m.UserTiny) userTiny := ToUserTinyBy(user)
if err := copier.Copy(userTiny, user); err != nil { //userTiny := new(user_m.UserTiny)
return err //if err := copier.Copy(userTiny, user); err != nil {
} // return err
//}
// cache externalId->userId // cache externalId->userId
if err := model.Redis.SetEX(model.Context, user_k.GetExternalIdToUidKey(user.ExternalId), user.ID, cache.GetDefaultTTL()).Err(); err != nil { if err := model.Redis.SetEX(model.Context, user_k.GetExternalIdToUidKey(user.ExternalId), user.ID, cache.GetDefaultTTL()).Err(); err != nil {
return err return err
...@@ -113,3 +117,89 @@ func cacheUserTiny(model *domain.Model, user *user_m.User) error { ...@@ -113,3 +117,89 @@ func cacheUserTiny(model *domain.Model, user *user_m.User) error {
} }
return nil 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
}
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)
}
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)
}
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
}
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
}
This diff is collapsed.
This diff is collapsed.
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
}
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
}
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)
}
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
}
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
}
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
}
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)
}
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
}
...@@ -191,7 +191,7 @@ func GetLangeByCountry(db *gorm.DB, country mysql.Str) (string, error) { ...@@ -191,7 +191,7 @@ func GetLangeByCountry(db *gorm.DB, country mysql.Str) (string, error) {
if err == nil { if err == nil {
return r.Lang, nil return r.Lang, nil
} else if err == gorm.ErrRecordNotFound { } else if err == gorm.ErrRecordNotFound {
return res_e.DEFAULT_LANG, nil return res_e.DefaultLang, nil
} else { } else {
return "", myerr.WrapErr(err) return "", myerr.WrapErr(err)
} }
......
...@@ -123,3 +123,15 @@ func CheckHeadwearValidById(model *domain.Model, id mysql.ID) (bool, error) { ...@@ -123,3 +123,15 @@ func CheckHeadwearValidById(model *domain.Model, id mysql.ID) (bool, error) {
return true, nil 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
...@@ -2,8 +2,10 @@ package res_m ...@@ -2,8 +2,10 @@ package res_m
import ( import (
"git.hilo.cn/hilo-common/resource/mysql" "git.hilo.cn/hilo-common/resource/mysql"
"github.com/bluele/gcache"
"gorm.io/gorm" "gorm.io/gorm"
"hilo-user/_const/enum/res_e" "hilo-user/_const/enum/res_e"
"time"
) )
type ResMedal struct { type ResMedal struct {
...@@ -41,3 +43,39 @@ func GetUserMedalLevelMap(db *gorm.DB) (map[uint64]uint8, map[uint8][]uint64, er ...@@ -41,3 +43,39 @@ func GetUserMedalLevelMap(db *gorm.DB) (map[uint64]uint8, map[uint8][]uint64, er
} }
return medalTypes, result, nil 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
}
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
}
...@@ -27,7 +27,7 @@ func GetResMultiTextBy(db *gorm.DB, msgId uint, Language mysql.Str) (*ResMultiTe ...@@ -27,7 +27,7 @@ func GetResMultiTextBy(db *gorm.DB, msgId uint, Language mysql.Str) (*ResMultiTe
if err == gorm.ErrRecordNotFound { if err == gorm.ErrRecordNotFound {
if err := db.Where(&ResMultiText{ if err := db.Where(&ResMultiText{
MsgId: msgId, MsgId: msgId,
Language: res_e.DEFAULT_LANG, Language: res_e.DefaultLang,
}).First(&r).Error; err != nil { }).First(&r).Error; err != nil {
return nil, myerr.WrapErr(err) return nil, myerr.WrapErr(err)
} }
......
...@@ -89,3 +89,15 @@ func GetUserMedal(db *gorm.DB, userId mysql.ID) ([]uint32, error) { ...@@ -89,3 +89,15 @@ func GetUserMedal(db *gorm.DB, userId mysql.ID) ([]uint32, error) {
} }
return result, nil 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
}
...@@ -108,4 +108,32 @@ type UserPropertyLog struct { ...@@ -108,4 +108,32 @@ type UserPropertyLog struct {
Type property_e.UserPropertyLogType Type property_e.UserPropertyLogType
AddSecond *mysql.Num AddSecond *mysql.Num
UpdateEndTime *time.Time 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
}
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
}*/
...@@ -6,6 +6,7 @@ import ( ...@@ -6,6 +6,7 @@ import (
"hilo-user/_const/enum/user_e" "hilo-user/_const/enum/user_e"
"hilo-user/myerr" "hilo-user/myerr"
"hilo-user/myerr/bizerr" "hilo-user/myerr/bizerr"
"time"
) )
//用户信息 //用户信息
...@@ -106,3 +107,40 @@ func GetUserByExtId(model *domain.Model, externalId string) (*User, error) { ...@@ -106,3 +107,40 @@ func GetUserByExtId(model *domain.Model, externalId string) (*User, error) {
user.Model = model user.Model = model
return &user, nil 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
}
...@@ -43,3 +43,24 @@ func GetVip(db *gorm.DB, userId uint64) (*UserVip, error) { ...@@ -43,3 +43,24 @@ func GetVip(db *gorm.DB, userId uint64) (*UserVip, error) {
} }
return nil, nil 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
}
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
}
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
})
}
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
})
}
...@@ -14,6 +14,8 @@ import ( ...@@ -14,6 +14,8 @@ import (
func EventInit() { func EventInit() {
UserBagSendEvent() UserBagSendEvent()
CpGiftEvent()
CpSpaceVisitEvent()
} }
func UserBagSendEvent() { func UserBagSendEvent() {
......
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
})
}
...@@ -19,6 +19,16 @@ require ( ...@@ -19,6 +19,16 @@ require (
gorm.io/gorm v1.23.8 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 ( require (
github.com/KyleBanks/depth v1.2.1 // indirect github.com/KyleBanks/depth v1.2.1 // indirect
github.com/PuerkitoBio/purell v1.1.1 // indirect github.com/PuerkitoBio/purell v1.1.1 // indirect
...@@ -37,7 +47,8 @@ require ( ...@@ -37,7 +47,8 @@ require (
github.com/go-playground/universal-translator v0.17.0 // indirect github.com/go-playground/universal-translator v0.17.0 // indirect
github.com/go-playground/validator/v10 v10.2.0 // indirect github.com/go-playground/validator/v10 v10.2.0 // indirect
github.com/go-sql-driver/mysql v1.7.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/consul/api v1.7.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.1 // indirect github.com/hashicorp/go-cleanhttp v0.5.1 // indirect
github.com/hashicorp/go-hclog v0.12.0 // indirect github.com/hashicorp/go-hclog v0.12.0 // indirect
...@@ -47,11 +58,10 @@ require ( ...@@ -47,11 +58,10 @@ require (
github.com/hashicorp/golang-lru v0.5.0 // indirect github.com/hashicorp/golang-lru v0.5.0 // indirect
github.com/hashicorp/serf v0.9.3 // indirect github.com/hashicorp/serf v0.9.3 // indirect
github.com/jinzhu/inflection v1.0.0 // 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/jmespath/go-jmespath v0.4.0 // indirect
github.com/joho/godotenv v1.3.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/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/leodido/go-urn v1.2.0 // indirect
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible // indirect github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible // indirect
github.com/lestrrat-go/strftime v1.0.6 // indirect github.com/lestrrat-go/strftime v1.0.6 // indirect
...@@ -63,11 +73,16 @@ require ( ...@@ -63,11 +73,16 @@ require (
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.1 // indirect github.com/modern-go/reflect2 v1.0.1 // indirect
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 // 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 github.com/ugorji/go/codec v1.1.7 // indirect
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 // indirect golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 // indirect
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
golang.org/x/text v0.3.6 // 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 google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/ini.v1 v1.63.2 // indirect gopkg.in/ini.v1 v1.63.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect
......
This diff is collapsed.
...@@ -3,6 +3,7 @@ package main ...@@ -3,6 +3,7 @@ package main
import ( import (
"fmt" "fmt"
"git.hilo.cn/hilo-common/resource/consul" "git.hilo.cn/hilo-common/resource/consul"
"hilo-user/cron"
"hilo-user/domain/service/event_s" "hilo-user/domain/service/event_s"
"hilo-user/route" "hilo-user/route"
) )
...@@ -14,7 +15,7 @@ const ( ...@@ -14,7 +15,7 @@ const (
) )
func main() { func main() {
//cron.Init() // 开启定时任务 cron.Init() // 开启定时任务
event_s.EventInit() // 注册事件(内部事件+mysql拟kafka) event_s.EventInit() // 注册事件(内部事件+mysql拟kafka)
r := route.InitRouter() // 注册路由 r := route.InitRouter() // 注册路由
consul.RegisterToConsul(PORT, RegisterName, RegisterTag) // 服务注册 consul.RegisterToConsul(PORT, RegisterName, RegisterTag) // 服务注册
......
...@@ -20,30 +20,12 @@ var ( ...@@ -20,30 +20,12 @@ var (
DiamondFrequency = myerr.NewBusinessCode(4001, "Diamond operation frequency too high", myerr.BusinessData{}) DiamondFrequency = myerr.NewBusinessCode(4001, "Diamond operation frequency too high", myerr.BusinessData{})
DiamondAccountFrozen = myerr.NewBusinessCode(4004, "Diamond Account Frozen", myerr.BusinessData{}) DiamondAccountFrozen = myerr.NewBusinessCode(4004, "Diamond Account Frozen", myerr.BusinessData{})
// 游戏 ResPropertyDiamondNoUse = myerr.NewBusinessCode(5004, "Property can not buy", myerr.BusinessData{}) //头饰不能买
GameInvalidParameter = myerr.NewGameError(10002, "invalid parameter")
GameTokenInvalid = myerr.NewGameError(1004, "user token invalid")
GameTokenExpire = myerr.NewGameError(1005, "user token expire")
CpNotRelation = myerr.NewBusinessCode(6000, "cp not relation", myerr.BusinessData{})
// 群组 // 群组
GroupNotFound = myerr.NewBusinessCode(14001, "Group not found", myerr.BusinessData{}) // 找不到该群 GroupNotFound = myerr.NewBusinessCode(14001, "Group not found", myerr.BusinessData{}) // 找不到该群
// 游戏服务的错误码,6位,50XXX CpAlreadyInvite = myerr.NewBusinessCode(50120, "Already invited", myerr.BusinessData{}) // 已经发送过邀请了
GameAddNoPermissions = myerr.NewBusinessCode(50100, "Only room administrators can create users", myerr.BusinessData{}) // 权限不足 CpHaveCancelNoDeal = myerr.NewBusinessCode(50121, "You have a cancel apply", 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{}) // 加入失败
) )
...@@ -2,6 +2,7 @@ package myerr ...@@ -2,6 +2,7 @@ package myerr
import ( import (
"fmt" "fmt"
"git.hilo.cn/hilo-common/myerr"
"git.hilo.cn/hilo-common/mylogrus" "git.hilo.cn/hilo-common/mylogrus"
"github.com/pkg/errors" "github.com/pkg/errors"
"strconv" "strconv"
...@@ -226,35 +227,10 @@ func WrapErr(err error) error { ...@@ -226,35 +227,10 @@ func WrapErr(err error) error {
} }
} }
func WrapGameErr(err error) error { func ToLocal(err *myerr.BusinessError) error {
if err == nil { return &BusinessError{
return err code: err.GetCode(),
} message: err.GetMsg(),
if h, ok := err.(*GameError); ok { err: err.GetErr(),
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 + "}"),
} }
} }
This diff is collapsed.
...@@ -10,6 +10,18 @@ import ( ...@@ -10,6 +10,18 @@ import (
"hilo-user/myerr/bizerr" "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) { func GetUserId(c *gin.Context) (mysql.ID, error) {
if userIdStr, ok := c.Keys[mycontext.USERID]; ok { if userIdStr, ok := c.Keys[mycontext.USERID]; ok {
userId := userIdStr.(uint64) userId := userIdStr.(uint64)
...@@ -18,6 +30,20 @@ func GetUserId(c *gin.Context) (mysql.ID, error) { ...@@ -18,6 +30,20 @@ func GetUserId(c *gin.Context) (mysql.ID, error) {
return 0, bizerr.ParaMissing 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 // 获取userId和externalId
func GetUserIdAndExtId(c *gin.Context, myContext *mycontext.MyContext) (mysql.ID, string, error) { func GetUserIdAndExtId(c *gin.Context, myContext *mycontext.MyContext) (mysql.ID, string, error) {
if userIdStr, ok := c.Keys[mycontext.USERID]; ok { if userIdStr, ok := c.Keys[mycontext.USERID]; ok {
......
...@@ -5,6 +5,7 @@ import ( ...@@ -5,6 +5,7 @@ import (
"git.hilo.cn/hilo-common/mycontext" "git.hilo.cn/hilo-common/mycontext"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"hilo-user/myerr" "hilo-user/myerr"
"hilo-user/req"
"net/http" "net/http"
) )
...@@ -42,27 +43,23 @@ func ResponseOk(c *gin.Context, data interface{}) { ...@@ -42,27 +43,23 @@ func ResponseOk(c *gin.Context, data interface{}) {
c.JSON(http.StatusOK, response) c.JSON(http.StatusOK, response)
} }
func GameResponseOk(c *gin.Context, data interface{}) { // 分页返回
// always return http.StatusOK // 客户端入参 req.PageReqBase
response := GameResponse{ // 服务端返回 req.PageRespBase
RetCode: 0, func ResponsePageBaseOk(c *gin.Context, data interface{}, nextPageIndex int, hasNextPage bool) {
RetMsg: myerr.GetSuccessMsg(), if data == nil {
SdkErrorCode: 0, data = make([]interface{}, 0)
Data: data,
} }
printResponseBody(c, &response) response := Response{
c.JSON(http.StatusOK, response) Code: myerr.GetSuccessCode(),
} Message: myerr.GetSuccessMsg(),
OperationMessage: myerr.GetSuccessMsg(),
func GameResponseFail(c *gin.Context, err *myerr.GameError) { Data: req.PageRespBase{
// always return http.StatusOK NextPageIndex: nextPageIndex,
response := GameResponse{ HasNextPage: hasNextPage,
RetCode: err.Code(), Data: data,
RetMsg: err.Error(), },
SdkErrorCode: err.Code(),
Data: nil,
} }
printResponseBody(c, &response)
c.JSON(http.StatusOK, response) c.JSON(http.StatusOK, response)
} }
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -37,9 +37,6 @@ func wrapper(handler HandlerFunc) func(c *gin.Context) { ...@@ -37,9 +37,6 @@ func wrapper(handler HandlerFunc) func(c *gin.Context) {
userId, _ := req.GetUserId(c) userId, _ := req.GetUserId(c)
switch h := err.(type) { 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: case *myerr.BusinessError:
myContext.Log.Warnf("request err -> url:%v, method:%v, userId:%v, err :%+v\n", reqUri, method, userId, h.GetErr()) myContext.Log.Warnf("request err -> url:%v, method:%v, userId:%v, err :%+v\n", reqUri, method, userId, h.GetErr())
resp.ResponseBusiness(c, h) resp.ResponseBusiness(c, h)
......
...@@ -102,6 +102,12 @@ func LoggerHandle(c *gin.Context) { ...@@ -102,6 +102,12 @@ func LoggerHandle(c *gin.Context) {
c.Set(mycontext.APP_VERSION, appVersion) c.Set(mycontext.APP_VERSION, appVersion)
c.Set(mycontext.URL, reqUri) c.Set(mycontext.URL, reqUri)
c.Set(mycontext.METHOD, method) 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) userId, _ := req.GetUserId(c)
......
...@@ -11,6 +11,7 @@ import ( ...@@ -11,6 +11,7 @@ import (
_ "hilo-user/docs" _ "hilo-user/docs"
"hilo-user/domain/model/msg_m" "hilo-user/domain/model/msg_m"
"hilo-user/resp" "hilo-user/resp"
"hilo-user/route/cp_r"
"hilo-user/route/user_r" "hilo-user/route/user_r"
) )
...@@ -22,17 +23,42 @@ func InitRouter() *gin.Engine { ...@@ -22,17 +23,42 @@ func InitRouter() *gin.Engine {
needLogin := r.Group("") needLogin := r.Group("")
needLogin.Use(ExceptionHandle, LoggerHandle, JWTApiHandle) needLogin.Use(ExceptionHandle, LoggerHandle, JWTApiHandle)
v1 := needLogin.Group("/v1") v1 := needLogin.Group("/v1")
v2 := needLogin.Group("/v2")
user := v1.Group("/user") user := v1.Group("/user")
{ {
user.GET("/nameplate", wrapper(user_r.UserNameplate)) user.GET("/nameplate", wrapper(user_r.UserNameplate))
user.GET("/bag/:resType", wrapper(user_r.UserBag)) 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 := r.Group("/inner")
inner.Use(ExceptionHandle, LoggerHandle) inner.Use(ExceptionHandle, LoggerHandle)
innerUser := inner.Group("/user") innerUser := inner.Group("/user")
{ {
innerUser.GET("/levels", wrapper(user_r.MGetUserLevels)) innerUser.GET("/levels", wrapper(user_r.MGetUserLevels))
innerUser.GET("/bag/id", wrapper(user_r.GetUserBagId)) 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") innerProp := inner.Group("/prop")
......
...@@ -4,6 +4,7 @@ import ( ...@@ -4,6 +4,7 @@ import (
"git.hilo.cn/hilo-common/domain" "git.hilo.cn/hilo-common/domain"
"git.hilo.cn/hilo-common/mycontext" "git.hilo.cn/hilo-common/mycontext"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"hilo-user/_const/enum/cp_e"
"hilo-user/_const/enum/res_e" "hilo-user/_const/enum/res_e"
"hilo-user/cv/user_cv" "hilo-user/cv/user_cv"
"hilo-user/domain/model/bag_m" "hilo-user/domain/model/bag_m"
...@@ -44,7 +45,7 @@ func UserBag(c *gin.Context) (*mycontext.MyContext, error) { ...@@ -44,7 +45,7 @@ func UserBag(c *gin.Context) (*mycontext.MyContext, error) {
} }
for _, bagGift := range userBagGifts { for _, bagGift := range userBagGifts {
if gift, ok := allGifts[bagGift.ResId]; ok { if gift, ok := allGifts[bagGift.ResId]; ok {
results = append(results, user_cv.UserBag{ info := user_cv.UserBag{
BagId: bagGift.ID, BagId: bagGift.ID,
ResType: res_e.ResUserBagGift, ResType: res_e.ResUserBagGift,
ResId: gift.ID, ResId: gift.ID,
...@@ -55,7 +56,16 @@ func UserBag(c *gin.Context) (*mycontext.MyContext, error) { ...@@ -55,7 +56,16 @@ func UserBag(c *gin.Context) (*mycontext.MyContext, error) {
SvgaUrl: gift.SvagUrl, SvgaUrl: gift.SvagUrl,
Count: bagGift.Count, Count: bagGift.Count,
RemainDays: int(bagGift.EndTime.Sub(time.Now()).Hours() / 24), 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: default:
......
This diff is collapsed.
MODE=local
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)
}
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment