go-common/app/service/live/dao-anchor/dao/mysql.go

1232 lines
36 KiB
Go
Raw Normal View History

2019-04-22 10:49:16 +00:00
package dao
import (
"context"
"fmt"
"time"
"go-common/app/service/live/dao-anchor/model"
v1pb "go-common/app/service/live/dao-anchor/api/grpc/v1"
"go-common/library/ecode"
"go-common/library/log"
"github.com/pkg/errors"
)
//房間狀態常量
const (
LIVE_OPEN = 1
LIVE_CLOSE = 0
LIVE_ROUND = 2
)
//attr表相关常量定义
const (
ATTRID_POPULARITY = 1
ATTRID_REVENUE = 2
ATTRID_DANMU = 3
ATTRID_RANK_LIST = 4
ATTRID_VALID_LIVE_DAYS = 5
)
const (
//ATTRSUBID_RANK_HOUR 小时榜
ATTRSUBID_RANK_HOUR = 1
)
const (
//ATTRSUBID_POPULARITY_REALTIME 实时人气值
ATTRSUBID_POPULARITY_REALTIME = 1
//ATTRSUBID_POPULARITY_MAX_TO_ARG_7 7日人气峰值的均值
ATTRSUBID_POPULARITY_MAX_TO_ARG_7 = 2
//ATTRSUBID_POPULARITY_MAX_TO_ARG_30 30日人气峰值的均值
ATTRSUBID_POPULARITY_MAX_TO_ARG_30 = 3
)
const (
//ATTRSUBID_REVENUE_MINUTE_NUM_15 15分钟营收
ATTRSUBID_REVENUE_MINUTE_NUM_15 = 1
//ATTRSUBID_REVENUE_MINUTE_NUM_30 30分钟营收
ATTRSUBID_REVENUE_MINUTE_NUM_30 = 2
//ATTRSUBID_REVENUE_MINUTE_NUM_45 45分钟营收
ATTRSUBID_REVENUE_MINUTE_NUM_45 = 3
//ATTRSUBID_REVENUE_MINUTE_NUM_60 60分钟营收
ATTRSUBID_REVENUE_MINUTE_NUM_60 = 4
)
const (
//ATTRSUBID_DANMU_MINUTE_NUM_15 15分钟弹幕
ATTRSUBID_DANMU_MINUTE_NUM_15 = 1
//ATTRSUBID_DANMU_MINUTE_NUM_30 30分钟弹幕
ATTRSUBID_DANMU_MINUTE_NUM_30 = 2
//ATTRSUBID_DANMU_MINUTE_NUM_45 45分钟弹幕
ATTRSUBID_DANMU_MINUTE_NUM_45 = 3
//ATTRSUBID_DANMU_MINUTE_NUM_60 60分钟弹幕
ATTRSUBID_DANMU_MINUTE_NUM_60 = 4
)
//有效开播天数
const (
VALID_LIVE_DAYS_TYPE_1 = 1 //一次开播大于5分钟
VALID_LIVE_DAYS_TYPE_2 = 2 // 累加大于等于120分钟
)
const (
//ATTRSUBID_VALID_LIVE_DAYS_TYPE_1_DAY_7 近7天的有效天数,(有效天数一次开播大于5分钟)
ATTRSUBID_VALID_LIVE_DAYS_TYPE_1_DAY_7 = 1
//ATTRSUBID_VALID_LIVE_DAYS_TYPE_1_DAY_14 近14天的有效天数,(有效天数一次开播大于5分钟)
ATTRSUBID_VALID_LIVE_DAYS_TYPE_1_DAY_14 = 2
//ATTRSUBID_VALID_LIVE_DAYS_TYPE_2_DAY_7 近7天的有效天数,(有效天数累加大于等于120分钟)
ATTRSUBID_VALID_LIVE_DAYS_TYPE_2_DAY_7 = 3
//ATTRSUBID_VALID_LIVE_DAYS_TYPE_2_DAY_30 近30天的有效天数,(有效天数累加大于等于120分钟)
ATTRSUBID_VALID_LIVE_DAYS_TYPE_2_DAY_30 = 4
)
//tag表相关常量定义
const (
TAGID_PK = 1
TAGID_LOTTERY = 2
ROOM_EXT_SHARDING = 10
EXP_2_SCORE_RATE = 100
)
const (
_roomTable = "room"
_roomExtTablePrefix = "room_extend"
_anchorTable = "anchor"
_tagTable = "tag"
_attrTable = "attr"
_shortTable = "ap_short_room"
_subAreaTable = "ap_room_area_v2"
// add room info
_addRoomInfo1 = "insert into `%s` (`uid`) values (?)"
// add room info
_addRoomInfo2 = "insert into `%s` (`uid`,`room_id`) values (?,?)"
// add room extend info
_addRoomExtInfo = "insert into `%s_%d` (`room_id`) values (?)"
// add anchor info
_addAnchorInfo = "insert into `%s` (`uid`,`room_id`,`san_score`) values (?,?,12)"
// update room info
_updateRoomInfo = "update `%s` set %s where `room_id`=?"
// update room extend info
_updateRoomExtInfo = "update `%s_%d` set %s where `room_id`=?"
// update anchor info
_updateAnchorInfo = "update `%s` set %s where `uid`=?"
// tag create info
_tagCreateInfo = "insert ignore into `%s` (`room_id`,`tag_id`,`tag_sub_id`,`tag_value`,`tag_ext`,`tag_expire_at`) values (?,?,?,?,?,?) on duplicate key update `tag_value`=?,`tag_ext`=?,`tag_expire_at`=?"
// attr create info
_attrCreateInfo = "insert ignore into `%s` (`room_id`,`attr_id`,`attr_sub_id`,`attr_value`,`attr_ext`) values (?,?,?,?,?) on duplicate key update `attr_value`=?,`attr_ext`=?"
// attr set ex info
_attrSetRoomId = "update `%s` set `room_id`=? where `attr_id`=? and `attr_sub_id`=? and `attr_value`=?"
// attr set ex info
_attrSetValue = "update `%s` set `attr_value`=? where `room_id`=? and `attr_id`=? and `attr_sub_id`=?"
// attr select room_id
_attrSelectRoomId = "select `room_id` from `%s` where `attr_id`=? and `attr_sub_id`=? and `attr_value`=?"
//
_attrSelectValue = "select `attr_value` from `%s` where `room_id`=? and `attr_id`=? and `attr_sub_id`=?"
// query online room info
_queryOnlineRoomInfo = "select `room_id`,`uid`,`title`,`description`,`tags`,`background`,`cover`,`lock_status`,`lock_time`,`hidden_time`,`record_switch`,`round_switch`,`live_start_time`,`live_screen_type`,`live_area_id`,`live_area_parent_id`,`live_type` from `room` where `live_start_time`!=0 order by `room_id` limit ?,?"
// TODO 在播房间是否要处理轮播场景
// query online room by area info
_queryOnlineRoomByAreaInfo = "select `room_id` from `room` where `live_start_time`!=0 %s order by `live_start_time` desc"
_queryOnlineRoomByAreaCond = "and (`live_area_id`=%d or `live_area_parent_id`=%d)"
// query room info
_queryRoomInfo = "select `room_id`,`uid`,`title`,`description`,`tags`,`background`,`cover`,`lock_status`,`lock_time`,`hidden_time`,`record_switch`,`round_switch`,`live_start_time`,`live_screen_type`,`live_area_id`,`live_area_parent_id`,`live_type` from `room` where `room_id` in (%s)"
// query anchor info
_queryAnchorInfo = "select `room_id`,`san_score`,`profile_type`,`round_status`,`record_status`,`exp` from `%s` where `uid` in (%s)"
// query tag info
_queryTagInfo = "select `room_id`,`tag_id`,`tag_sub_id`,`tag_value`,`tag_ext`,`tag_expire_at` from `%s` where `room_id` in (%s) and `tag_expire_at`>?"
// query room ext info
_queryRoomExtInfo = "select `room_id`,`keyframe`,`popularity_count` from `%s_%d` where `room_id` in (%s)"
// get parent area id
_queryParentAreaID = "select `parent_id` from `%s` where `id`=?"
// get short id
_queryShortID = "select `short_id`,`roomid` from `%s` where `roomid` in (%s)"
// filter out short room-id and its corresponding room-id
_filterShortID = "select `short_id`,`roomid` from `%s` where `short_id` in (%s) and `status`=1"
// query attr info
_queryAttrInfo = "select `room_id`,`attr_value` from `%s` where `attr_id`=? and `attr_sub_id`=? and `room_id` in (%s)"
// query attr info
_queryAttrInfo2 = "select `room_id`,`attr_sub_id`,`attr_value` from `%s` where `attr_id`=? order by `attr_sub_id`"
// delete attr info
_deleteAttrInfo = "delete from `%s` where `attr_id`=? and `attr_sub_id`=?"
// query area info
_queryAreaInfo = "select `id`,`name`,`parent_id` from `%s` where `id`=?"
// query sub-areas for a given area
_querySubAreaInfo = "select `id`,`name` from `%s` where `parent_id`=?"
)
// Turns short-id, if any, into room-id
func (d *Dao) dbNormalizeRoomIDs(ctx context.Context, roomIDs []int64) (resp []int64, err error) {
if len(roomIDs) <= 0 {
return
}
resp = make([]int64, len(roomIDs))
// Resort to cache first, and for miss roomid, we save pair (roomid, index) to preserve order
// of our final result
agnosticRoomIds := make(map[int64]int)
for i, roomID := range roomIDs {
xid, ok := d.shortIDMapping.Get(fmt.Sprintf("%d", roomID))
if ok {
resp[i] = xid.(int64)
} else {
agnosticRoomIds[roomID] = i
}
}
// We are done.
if len(agnosticRoomIds) <= 0 {
return
}
var queryValues string
for roomID := range agnosticRoomIds {
if len(queryValues) > 0 {
queryValues += ","
}
queryValues += fmt.Sprintf("%d", roomID)
}
sql := fmt.Sprintf(_filterShortID, _shortTable, queryValues)
rows, err := d.dbLiveApp.Query(ctx, sql)
if err != nil {
log.Error("[dao.dao-anchor.mysql|dbNormalizeRoomIDs] query short id record error(%v), sql(%s)", err, sql)
return nil, err
}
defer rows.Close()
shortIDMapping := make(map[int64]int64)
for rows.Next() {
var shortID int64
var roomID int64
err = rows.Scan(&shortID, &roomID)
if err != nil {
log.Error("[dao.dao-anchor.mysql|dbNormalizeRoomIDs] scan short id record error(%v), roomIDs(%v)",
err, roomIDs)
return nil, err
}
shortIDMapping[shortID] = roomID
}
for roomID, index := range agnosticRoomIds {
xid, ok := shortIDMapping[roomID]
if ok {
resp[index] = xid
d.shortIDMapping.Put(fmt.Sprintf("%d", roomID), xid)
} else {
resp[index] = roomID
d.shortIDMapping.Put(fmt.Sprintf("%d", roomID), roomID)
}
}
return
}
func (d *Dao) dbDealWithStatus(ctx context.Context, data *v1pb.RoomData) (err error) {
if data == nil {
return
}
// 处理开播状态
if data.LiveStartTime > 0 {
// 如果开播
data.LiveStatus = LIVE_OPEN
} else if data.AnchorRoundSwitch == 1 {
// 如果轮播
data.LiveStatus = LIVE_ROUND
} else {
data.LiveStatus = LIVE_CLOSE
}
// 处理隐藏状态
if data.HiddenTime > time.Now().Unix() {
data.HiddenStatus = 1
}
// 获取分区名称
areaInfo := &model.AreaInfo{}
if data.AreaId > 0 {
areaInfo, err = d.dbFetchAreaInfo(ctx, data.AreaId)
if err != nil {
log.Error("[dao.dao-anchor.mysql|dbDealWithStatus] fetch area info error(%v), data(%v), areaid(%v)", err, data, data.AreaId)
return
}
data.AreaName = areaInfo.AreaName
}
if areaInfo.ParentAreaID > 0 {
areaInfo, err = d.dbFetchAreaInfo(ctx, areaInfo.ParentAreaID)
if err != nil {
log.Error("[dao.dao-anchor.mysql|dbDealWithStatus] fetch area info error(%v), data(%v), parent areaid(%v)", err, data, areaInfo.ParentAreaID)
return
}
data.ParentAreaName = areaInfo.AreaName
}
return
}
// dbFetchRoomIDByUID implementation
// dbFetchRoomIDByUID 查询主播房间号
func (d *Dao) dbFetchRoomIDByUID(ctx context.Context, uid int64) (roomID int64) {
uids := []int64{uid}
res := make(map[int64]*v1pb.RoomData)
err := d.dbFetchAnchorInfo(ctx, uids, res, false)
if err != nil {
log.Error("[dao.dao-anchor.mysql|dbFetchRoomIDByUID] get room ID error(%v), uid(%v)", err, uid)
return
}
if len(res) <= 1 {
return
}
for _, v := range res {
if v.Uid == uid {
roomID = v.RoomId
}
}
return
}
// dbFetchAreaInfo implementation
// dbFetchAreaInfo 查询分区信息
func (d *Dao) dbFetchAreaInfo(ctx context.Context, areaID int64) (info *model.AreaInfo, err error) {
if areaID <= 0 {
return
}
key := fmt.Sprintf("%d", areaID)
res, ok := d.areaInfoMapping.Get(key)
if ok {
return res.(*model.AreaInfo), nil
}
info = &model.AreaInfo{}
sql := fmt.Sprintf(_queryAreaInfo, _subAreaTable)
err = d.dbLiveApp.QueryRow(ctx, sql, areaID).Scan(&info.AreaID, &info.AreaName, &info.ParentAreaID)
if err == nil {
d.areaInfoMapping.Put(key, info)
} else {
// 尝试从配置中心拿到一级分区名称
if confInfo, ok := d.c.FirstAreas[key]; ok {
info.AreaID = areaID
info.AreaName = confInfo.Name
info.ParentAreaID = 0
err = nil
}
}
return
}
func (d *Dao) dbFetchAnchorInfo(ctx context.Context, uids []int64, resp map[int64]*v1pb.RoomData, overwrite bool) (err error) {
if len(uids) <= 0 {
return
}
valueList := ""
for i, id := range uids {
if i > 0 {
valueList += ","
}
valueList += fmt.Sprintf("%d", id)
}
sql := fmt.Sprintf(_queryAnchorInfo, _anchorTable, valueList)
rows, err := d.db.Query(ctx, sql)
if err != nil {
log.Error("[dao.dao-anchor.mysql|dbFetchRoomByIDs] get anchor record error(%v), uids(%v)", err, uids)
return err
}
defer rows.Close()
for rows.Next() {
data := &v1pb.RoomData{
AnchorLevel: new(v1pb.AnchorLevel),
}
var exp int64
err = rows.Scan(&data.RoomId, &data.AnchorSan, &data.AnchorProfileType, &data.AnchorRoundStatus, &data.AnchorRecordStatus, &exp)
if err != nil {
log.Error("[dao.dao-anchor.mysql|dbFetchRoomByIDs] scan anchor record error(%v), uids(%v)", err, uids)
return err
}
data.AnchorLevel.Score = exp / EXP_2_SCORE_RATE
data.AnchorLevel.Level, err = model.GetAnchorLevel(data.AnchorLevel.Score)
if err != nil {
log.Error("[dao.dao-anchor.mysql|dbFetchRoomByIDs] Failed to get anchor level (%v), level(%d)", err, data.AnchorLevel.Score)
return err
}
data.AnchorLevel.Left, data.AnchorLevel.Right, err = model.GetLevelScoreInfo(data.AnchorLevel.Level)
if err != nil {
log.Error("[dao.dao-anchor.mysql|dbFetchRoomByIDs] Failed to get anchor level score (%v), level(%d)", err, data.AnchorLevel.Level)
return err
}
data.AnchorLevel.Color, err = model.GetAnchorLevelColor(data.AnchorLevel.Level)
if err != nil {
log.Error("[dao.dao-anchor.mysql|dbFetchRoomByIDs] Failed to get anchor level color (%v), level(%d)", err, data.AnchorLevel.Level)
return err
}
data.AnchorLevel.MaxLevel = model.MaxAnchorLevel
if overwrite {
d := resp[data.RoomId]
d.AnchorSan = data.AnchorSan
d.AnchorProfileType = data.AnchorProfileType
d.AnchorRoundStatus = data.AnchorRoundStatus
d.AnchorRecordStatus = data.AnchorRecordStatus
d.AnchorLevel = data.AnchorLevel
} else {
resp[data.RoomId] = data
}
}
return
}
func (d *Dao) dbFetchRoomInfo(ctx context.Context, roomIDs []int64, resp map[int64]*v1pb.RoomData, overwrite bool) (err error) {
if len(roomIDs) <= 0 {
return
}
valueList := ""
for i, id := range roomIDs {
if i > 0 {
valueList += ","
}
valueList += fmt.Sprintf("%d", id)
}
sql := fmt.Sprintf(_queryRoomInfo, valueList)
rows, err := d.db.Query(ctx, sql)
if err != nil {
log.Error("[dao.dao-anchor.mysql|dbFetchRoomInfo] get room record error(%v), roomIDs(%v)", err, roomIDs)
return err
}
defer rows.Close()
for rows.Next() {
data := &v1pb.RoomData{}
err = rows.Scan(&data.RoomId, &data.Uid, &data.Title, &data.Description, &data.Tags, &data.Background, &data.Cover, &data.LockStatus, &data.LockTime, &data.HiddenTime, &data.AnchorRecordSwitch, &data.AnchorRoundSwitch, &data.LiveStartTime, &data.LiveScreenType, &data.AreaId, &data.ParentAreaId, &data.LiveType)
if err != nil {
log.Error("[dao.dao-anchor.mysql|dbFetchRoomInfo] scan room record error(%v), roomIDs(%v)", err, roomIDs)
return err
}
if overwrite {
d := resp[data.RoomId]
d.Uid = data.Uid
d.Title = data.Title
d.Description = data.Description
d.Tags = data.Tags
d.Background = data.Background
d.Cover = data.Cover
d.LockStatus = data.LockStatus
d.LockTime = data.LockTime
d.HiddenTime = data.HiddenTime
d.AnchorRecordSwitch = data.AnchorRecordSwitch
d.AnchorRoundSwitch = data.AnchorRoundSwitch
d.LiveStartTime = data.LiveStartTime
d.LiveScreenType = data.LiveScreenType
d.AreaId = data.AreaId
d.ParentAreaId = data.ParentAreaId
d.LiveType = data.LiveType
} else {
resp[data.RoomId] = data
}
d.dbDealWithStatus(ctx, resp[data.RoomId])
}
return
}
func (d *Dao) dbFetchTagInfo(ctx context.Context, roomIDs []int64, resp map[int64]*v1pb.RoomData) (err error) {
if len(roomIDs) <= 0 {
return
}
valueList := ""
for i, id := range roomIDs {
if i > 0 {
valueList += ","
}
valueList += fmt.Sprintf("%d", id)
}
sql := fmt.Sprintf(_queryTagInfo, _tagTable, valueList)
rows, err := d.db.Query(ctx, sql, time.Now().Unix())
if err != nil {
log.Error("[dao.dao-anchor.mysql|dbFetchTagInfo] get room record error(%v), roomIDs(%v)", err, roomIDs)
return err
}
defer rows.Close()
for rows.Next() {
var roomID int64
data := &v1pb.TagData{}
err = rows.Scan(&roomID, &data.TagId, &data.TagSubId, &data.TagValue, &data.TagExt, &data.TagExpireAt)
if err != nil {
log.Error("[dao.dao-anchor.mysql|dbFetchTagInfo] scan room record error(%v), roomIDs(%v)", err, roomIDs)
return err
}
if resp[roomID] == nil {
resp[roomID] = &v1pb.RoomData{}
}
if resp[roomID].TagList == nil {
resp[roomID].TagList = make([]*v1pb.TagData, 0)
}
resp[roomID].TagList = append(resp[roomID].TagList, data)
}
return
}
func (d *Dao) dbFetchExtInfo(ctx context.Context, roomIDs []int64, resp map[int64]*v1pb.RoomData) (err error) {
if len(roomIDs) <= 0 {
return
}
sharding := make(map[int64][]int64)
for i := 0; i < 10; i++ {
sharding[int64(i)] = make([]int64, 0, len(roomIDs))
}
for _, id := range roomIDs {
sharding[id%ROOM_EXT_SHARDING] = append(sharding[id%ROOM_EXT_SHARDING], id)
}
for shard, ids := range sharding {
if len(ids) <= 0 {
continue
}
valueList := ""
for i, id := range ids {
if i > 0 {
valueList += ","
}
valueList += fmt.Sprintf("%d", id)
}
sql := fmt.Sprintf(_queryRoomExtInfo, _roomExtTablePrefix, shard, valueList)
rows, err := d.db.Query(ctx, sql)
if err != nil {
log.Error("[dao.dao-anchor.mysql|dbFetchExtInfo] get room ext record error(%v), roomIDs(%v)", err, ids)
return err
}
defer rows.Close()
for rows.Next() {
var roomID int64
var keyframe string
var popularityCount int64
err = rows.Scan(&roomID, &keyframe, &popularityCount)
if err != nil {
log.Error("[dao.dao-anchor.mysql|dbFetchExtInfo] scan room ext record error(%v), roomIDs(%v)", err, ids)
return err
}
resp[roomID].Keyframe = keyframe
resp[roomID].PopularityCount = popularityCount
}
}
return
}
// dbFetchRoomByIDs implementation
// dbFetchRoomByIDs 查询房间信息
func (d *Dao) dbFetchRoomByIDs(ctx context.Context, req *v1pb.RoomByIDsReq) (resp *v1pb.RoomByIDsResp, err error) {
resp = &v1pb.RoomByIDsResp{
RoomDataSet: make(map[int64]*v1pb.RoomData),
}
roomIDs := make([]int64, 0, len(req.Uids))
if len(req.RoomIds) > 0 {
for _, id := range req.RoomIds {
roomIDs = append(roomIDs, id)
}
// 先查room表
err = d.dbFetchRoomInfo(ctx, roomIDs, resp.RoomDataSet, false)
if err != nil {
log.Error("[dao.dao-anchor.mysql|dbFetchRoomByIDs] get room record error(%v), req(%v)", err, req)
return nil, err
}
uids := make([]int64, 0, len(req.RoomIds))
for _, v := range resp.RoomDataSet {
uids = append(uids, v.Uid)
}
err = d.dbFetchAnchorInfo(ctx, uids, resp.RoomDataSet, true)
if err != nil {
log.Error("[dao.dao-anchor.mysql|dbFetchRoomByIDs] get anchor record error(%v), req(%v)", err, req)
return nil, err
}
} else if len(req.Uids) > 0 {
// 先查anchor表
uids := make([]int64, 0, len(req.Uids))
for _, id := range req.Uids {
uids = append(uids, id)
}
err = d.dbFetchAnchorInfo(ctx, uids, resp.RoomDataSet, false)
if err != nil {
log.Error("[dao.dao-anchor.mysql|dbFetchRoomByIDs] get anchor record error(%v), req(%v)", err, req)
return nil, err
}
for _, v := range resp.RoomDataSet {
roomIDs = append(roomIDs, v.RoomId)
}
err = d.dbFetchRoomInfo(ctx, roomIDs, resp.RoomDataSet, true)
if err != nil {
log.Error("[dao.dao-anchor.mysql|dbFetchRoomByIDs] get room record error(%v), req(%v)", err, req)
return nil, err
}
}
// Ext Info
err = d.dbFetchExtInfo(ctx, roomIDs, resp.RoomDataSet)
if err != nil {
log.Error("[dao.dao-anchor.mysql|dbFetchRoomByIDs] get room ext record error(%v), req(%v)", err, req)
return nil, err
}
// Tag List
err = d.dbFetchTagInfo(ctx, roomIDs, resp.RoomDataSet)
if err != nil {
log.Error("[dao.dao-anchor.mysql|dbFetchRoomByIDs] get tag record error(%v), req(%v)", err, req)
return nil, err
}
// TODO @wangyao需要处理short_id
return
}
// dbOnlineListByArea implementation
// dbOnlineListByArea 分区在线房间列表
func (d *Dao) dbOnlineListByArea(ctx context.Context, areaId int64) (roomIDs []int64, err error) {
cond := ""
if areaId > 0 {
cond = fmt.Sprintf(_queryOnlineRoomByAreaCond, areaId, areaId)
}
sql := fmt.Sprintf(_queryOnlineRoomByAreaInfo, cond)
rows, err := d.db.Query(ctx, sql)
if err != nil {
log.Error("[dao.dao-anchor.mysql|dbOnlineListByArea] get online room list by area error(%v), areaId(%v)", err, areaId)
return nil, err
}
defer rows.Close()
roomIDs = make([]int64, 0)
for rows.Next() {
var roomid int64
err = rows.Scan(&roomid)
if err != nil {
log.Error("[dao.dao-anchor.mysql|dbOnlineListByArea] scan online room list by area error(%v), areaId(%v)", err, areaId)
return nil, err
}
roomIDs = append(roomIDs, roomid)
}
return
}
// roomCreate implementation
// roomCreate 房间创建
func (d *Dao) roomCreate(ctx context.Context, req *v1pb.RoomCreateReq) (resp *v1pb.RoomCreateResp, err error) {
resp = &v1pb.RoomCreateResp{}
tx, err := d.db.Begin(ctx)
if err != nil {
err = errors.WithStack(err)
return resp, err
}
if req.RoomId != 0 {
resp.RoomId = req.RoomId
sql := fmt.Sprintf(_addRoomInfo2, _roomTable)
_, err = tx.Exec(sql, req.Uid, req.RoomId)
if err != nil {
if e := tx.Rollback(); e != nil {
log.Error("[dao.dao-anchor.mysql|roomCreate] create room record rollback error(%v), req(%v)", e, req)
}
// unique key exists error
log.Error("[dao.dao-anchor.mysql|roomCreate] create room record error(%v), req(%v)", err, req)
return resp, err
}
} else {
sql := fmt.Sprintf(_addRoomInfo1, _roomTable)
res, err := tx.Exec(sql, req.Uid)
if err != nil {
if e := tx.Rollback(); e != nil {
log.Error("[dao.dao-anchor.mysql|roomCreate] create room record rollback error(%v), req(%v)", e, req)
}
// unique key exists error
log.Error("[dao.dao-anchor.mysql|roomCreate] create room record error(%v), req(%v)", err, req)
return resp, err
}
if resp.RoomId, err = res.LastInsertId(); err != nil {
err = errors.WithStack(err)
log.Error("[dao.dao-anchor.mysql|AddGuard] get last insert id error(%v), req(%v)", err, req)
}
}
sql := fmt.Sprintf(_addRoomExtInfo, _roomExtTablePrefix, resp.RoomId%ROOM_EXT_SHARDING)
if _, err = tx.Exec(sql, resp.RoomId); err != nil {
if e := tx.Rollback(); e != nil {
log.Error("[dao.dao-anchor.mysql|roomCreate] create room extend record rollback error(%v), req(%v)", e, req)
}
log.Error("[dao.dao-anchor.mysql|roomCreate] create room extend record error(%v), req(%v)", err, req)
return resp, err
}
sql = fmt.Sprintf(_addAnchorInfo, _anchorTable)
if _, err = tx.Exec(sql, req.Uid, resp.RoomId); err != nil {
if e := tx.Rollback(); e != nil {
log.Error("[dao.dao-anchor.mysql|roomCreate] create anchor record rollback error(%v), req(%v)", e, req)
}
log.Error("[dao.dao-anchor.mysql|roomCreate] create anchor record error(%v), req(%v)", err, req)
return resp, err
}
if err = tx.Commit(); err != nil {
log.Error("[dao.dao-anchor.mysql|roomCreate] commit error(%v), req(%v)", err, req)
return resp, err
}
return
}
// roomUpdate implementation
// roomUpdate 房间信息更新
func (d *Dao) roomUpdate(ctx context.Context, req *v1pb.RoomUpdateReq) (resp *v1pb.UpdateResp, err error) {
updateSub := ""
args := make([]interface{}, len(req.Fields)+1)
args[len(req.Fields)] = req.RoomId
for i, f := range req.Fields {
switch f {
case "title":
args[i] = req.Title
case "cover":
args[i] = req.Cover
case "tags":
args[i] = req.Tags
case "background":
args[i] = req.Background
case "description":
args[i] = req.Description
case "live_start_time":
args[i] = req.LiveStartTime
if req.LiveStartTime > 0 {
if i > 0 {
updateSub += ","
}
updateSub += "`live_mark`=1"
if i == 0 {
updateSub += ","
}
}
case "live_screen_type":
args[i] = req.LiveScreenType
case "live_type":
args[i] = req.LiveType
case "lock_status":
args[i] = req.LockStatus
case "lock_time":
args[i] = req.LockTime
case "hidden_time":
args[i] = req.HiddenTime
case "area_id":
f = "live_area_id"
args[i] = req.AreaId
areaInfo := &model.AreaInfo{}
// 审核后台会设置成无分区
if req.AreaId > 0 {
areaInfo, err = d.dbFetchAreaInfo(ctx, req.AreaId)
if err != nil {
log.Error("[dao.dao-anchor.mysql|roomUpdate] fetch area info error(%v), req(%v), areaid(%v)", err, req, req.AreaId)
return
}
}
if i > 0 {
updateSub += ","
}
updateSub += fmt.Sprintf("`live_area_parent_id`=%d", areaInfo.ParentAreaID)
if i == 0 {
updateSub += ","
}
case "anchor_round_switch":
f = "round_switch"
args[i] = req.AnchorRoundSwitch
case "anchor_record_switch":
f = "record_switch"
args[i] = req.AnchorRecordSwitch
default:
log.Error("[dao.dao-anchor.mysql|roomUpdate] unsupported field(%v), req(%v)", f, req)
err = ecode.InvalidParam
return
}
if i > 0 {
updateSub += ","
}
updateSub += fmt.Sprintf("`%s`=?", f)
}
resp = &v1pb.UpdateResp{}
sql := fmt.Sprintf(_updateRoomInfo, _roomTable, updateSub)
res, err := d.db.Exec(ctx, sql, args...)
if err != nil {
log.Error("[dao.dao-anchor.mysql|roomUpdate] update room record error(%v), req(%v)", err, req)
return
}
resp.AffectedRows, err = res.RowsAffected()
return
}
// roomExtendUpdate implementation
// roomExtendUpdate 房间扩展信息更新
func (d *Dao) roomExtendUpdate(ctx context.Context, req *v1pb.RoomExtendUpdateReq) (resp *v1pb.UpdateResp, err error) {
if len(req.Fields) <= 0 {
log.Error("[dao.dao-anchor.mysql|roomExtendUpdate] no fields, req(%v)", req)
err = ecode.InvalidParam
return
}
updateSub := ""
args := make([]interface{}, len(req.Fields)+1)
args[len(req.Fields)] = req.RoomId
for i, f := range req.Fields {
switch f {
case "keyframe":
args[i] = req.Keyframe
case "danmu_count":
args[i] = req.DanmuCount
case "popularity_count":
args[i] = req.PopularityCount
case "audience_count":
args[i] = req.AudienceCount
case "gift_count":
args[i] = req.GiftCount
case "gift_gold_amount":
args[i] = req.GiftGoldAmount
case "gift_gold_count":
args[i] = req.GiftGoldCount
default:
log.Error("[dao.dao-anchor.mysql|roomExtendUpdate] unsupported field(%v), req(%v)", f, req)
err = ecode.InvalidParam
return
}
if i > 0 {
updateSub += ","
}
updateSub += fmt.Sprintf("`%s`=?", f)
}
resp = &v1pb.UpdateResp{}
sql := fmt.Sprintf(_updateRoomExtInfo, _roomExtTablePrefix, req.RoomId%10, updateSub)
res, err := d.db.Exec(ctx, sql, args...)
if err != nil {
log.Error("[dao.dao-anchor.mysql|roomExtendUpdate] update room extend record error(%v), req(%v)", err, req)
return
}
resp.AffectedRows, err = res.RowsAffected()
return
}
// roomExtendIncre implementation
// roomExtendIncre 房间扩展信息增量更新
func (d *Dao) roomExtendIncre(ctx context.Context, req *v1pb.RoomExtendIncreReq) (resp *v1pb.UpdateResp, err error) {
if len(req.Fields) <= 0 {
log.Error("[dao.dao-anchor.mysql|roomExtendIncre] no fields, req(%v)", req)
err = ecode.InvalidParam
return
}
// TODO: req_id
updateSub := ""
args := make([]interface{}, len(req.Fields)+1)
args[len(req.Fields)] = req.RoomId
for i, f := range req.Fields {
switch f {
case "danmu_count":
args[i] = req.DanmuCount
case "popularity_count":
args[i] = req.PopularityCount
case "audience_count":
args[i] = req.AudienceCount
case "gift_count":
args[i] = req.GiftCount
case "gift_gold_amount":
args[i] = req.GiftGoldAmount
case "gift_gold_count":
args[i] = req.GiftGoldCount
default:
log.Error("[dao.dao-anchor.mysql|roomExtendIncre] unsupported field(%v), req(%v)", f, req)
err = ecode.InvalidParam
return
}
if i > 0 {
updateSub += ","
}
updateSub += fmt.Sprintf("`%s`=`%s`+(?)", f, f)
}
resp = &v1pb.UpdateResp{}
sql := fmt.Sprintf(_updateRoomExtInfo, _roomExtTablePrefix, req.RoomId%10, updateSub)
res, err := d.db.Exec(ctx, sql, args...)
if err != nil {
log.Error("[dao.dao-anchor.mysql|roomExtendIncre] update room extend increment record error(%v), req(%v)", err, req)
return
}
resp.AffectedRows, err = res.RowsAffected()
return
}
// roomTagCreate implementation
// roomTagCreate 房间Tag创建
func (d *Dao) roomTagCreate(ctx context.Context, req *v1pb.RoomTagCreateReq) (resp *v1pb.UpdateResp, err error) {
resp = &v1pb.UpdateResp{}
sql := fmt.Sprintf(_tagCreateInfo, _tagTable)
_, err = d.db.Exec(ctx, sql, req.RoomId, req.TagId, req.TagSubId, req.TagValue, req.TagExt, req.TagExpireAt, req.TagValue, req.TagExt, req.TagExpireAt)
if err != nil {
log.Error("[dao.dao-anchor.mysql|roomTagCreate] create room tag error(%v), req(%v)", err, req)
return
}
resp.AffectedRows = 1
return
}
// roomAttrCreate implementation
// roomAttrCreate 房间Attr创建
func (d *Dao) roomAttrCreate(ctx context.Context, req *v1pb.RoomAttrCreateReq) (resp *v1pb.UpdateResp, err error) {
resp = &v1pb.UpdateResp{}
sql := fmt.Sprintf(_attrCreateInfo, _attrTable)
_, err = d.db.Exec(ctx, sql, req.RoomId, req.AttrId, req.AttrSubId, req.AttrValue, req.AttrExt, req.AttrValue, req.AttrExt)
if err != nil {
log.Error("[dao.dao-anchor.mysql|roomAttrCreate] create room attr error(%v), req(%v)", err, req)
return
}
resp.AffectedRows = 1
return
}
// roomAttrSetEx implementation
// roomAttrSetEx 房间Attr更新/插入
func (d *Dao) roomAttrSetEx(ctx context.Context, req *v1pb.RoomAttrSetExReq) (resp *v1pb.UpdateResp, err error) {
needSet, err := RoomAttrNeedSet(d, ctx, req)
if err != nil {
log.Error("[dao.dao-anchor.mysql|roomAttrSetEx] set room attr error(%v), req(%v)", err, req)
return
}
if needSet <= 0 {
return
}
if needSet == NEED_INSERT {
reqCreate := &v1pb.RoomAttrCreateReq{}
reqCreate.RoomId = req.RoomId
reqCreate.AttrId = req.AttrId
reqCreate.AttrSubId = req.AttrSubId
reqCreate.AttrValue = req.AttrValue
reqCreate.AttrExt = req.AttrExt
resp, err = d.roomAttrCreate(ctx, reqCreate)
} else {
if req.AttrId == ATTRID_RANK_LIST {
resp, err = d.roomAttrSetRoomId(ctx, req)
} else {
resp, err = d.roomAttrSetValue(ctx, req)
}
}
return
}
const (
NEED_INSERT = 1
NEED_UPDATE = 2
)
// roomAttrNeedSet 内部函数 0 不需要set 1 需要insert 2 需要update
func RoomAttrNeedSet(d *Dao, ctx context.Context, req *v1pb.RoomAttrSetExReq) (resp int, err error) {
attrId := req.AttrId
//排行榜设计:更新roomID
resp = 0
if attrId == ATTRID_RANK_LIST {
roomID := 0
sql := fmt.Sprintf(_attrSelectRoomId, _attrTable)
err = d.db.QueryRow(ctx, sql, req.AttrId, req.AttrSubId, req.AttrValue).Scan(&roomID)
if err != nil && err.Error() == "sql: no rows in result set" {
err = nil
resp = NEED_INSERT
return
}
if err != nil {
log.Error("[dao.dao-anchor.mysql|RoomAttrNeedSet] set room attr_rank error(%v), req(%v)", err, req)
return
}
if int64(roomID) != req.RoomId {
resp = NEED_UPDATE
}
} else {
value := 0
sql := fmt.Sprintf(_attrSelectValue, _attrTable)
err = d.db.QueryRow(ctx, sql, req.AttrId, req.AttrSubId, req.AttrValue).Scan(value)
if err != nil && err.Error() == "sql: no rows in result set" {
err = nil
resp = NEED_INSERT
return
}
if err != nil {
log.Error("[dao.dao-anchor.mysql|RoomAttrNeedSet] set room attr error(%v), req(%v)", err, req)
return
}
if int64(value) != req.AttrValue {
resp = NEED_UPDATE
}
}
return
}
//roomAttrSet 更新value
func (d *Dao) roomAttrSetValue(ctx context.Context, req *v1pb.RoomAttrSetExReq) (resp *v1pb.UpdateResp, err error) {
resp = &v1pb.UpdateResp{}
sql := fmt.Sprintf(_attrSetValue, _attrTable)
res, err := d.db.Exec(ctx, sql, req.AttrValue, req.RoomId, req.AttrId, req.AttrSubId)
if err != nil {
log.Error("[dao.dao-anchor.mysql|roomAttrSetEx] set room attr error(%v), req(%v)", err, req)
return
}
resp.AffectedRows, err = res.RowsAffected()
return
}
//roomAttrSetRoomId 更新roomID
func (d *Dao) roomAttrSetRoomId(ctx context.Context, req *v1pb.RoomAttrSetExReq) (resp *v1pb.UpdateResp, err error) {
resp = &v1pb.UpdateResp{}
sql := fmt.Sprintf(_attrSetRoomId, _attrTable)
res, err := d.db.Exec(ctx, sql, req.RoomId, req.AttrId, req.AttrSubId, req.AttrValue)
if err != nil {
log.Error("[dao.dao-anchor.mysql|roomAttrSetEx] set room attr error(%v), req(%v)", err, req)
return
}
resp.AffectedRows, err = res.RowsAffected()
return
}
// anchorUpdate implementation
// anchorUpdate 主播信息更新
func (d *Dao) anchorUpdate(ctx context.Context, req *v1pb.AnchorUpdateReq) (resp *v1pb.UpdateResp, err error) {
updateSub := ""
args := make([]interface{}, len(req.Fields)+1)
args[len(req.Fields)] = req.Uid
for i, f := range req.Fields {
switch f {
case "profile_type":
args[i] = req.ProfileType
case "san_score":
args[i] = req.SanScore
case "round_status":
args[i] = req.RoundStatus
case "record_status":
args[i] = req.RecordStatus
case "exp":
args[i] = req.Exp
default:
log.Error("[dao.dao-anchor.mysql|anchorUpdate] unsupported field(%v), req(%v)", f, req)
err = ecode.InvalidParam
return
}
if i > 0 {
updateSub += ","
}
updateSub += fmt.Sprintf("`%s`=?", f)
}
resp = &v1pb.UpdateResp{}
sql := fmt.Sprintf(_updateAnchorInfo, _anchorTable, updateSub)
res, err := d.db.Exec(ctx, sql, args...)
if err != nil {
log.Error("[dao.dao-anchor.mysql|anchorUpdate] update anchor record error(%v), req(%v)", err, req)
return
}
resp.AffectedRows, err = res.RowsAffected()
return
}
// anchorIncre implementation
// anchorIncre 主播信息增量更新
func (d *Dao) anchorIncre(ctx context.Context, req *v1pb.AnchorIncreReq) (resp *v1pb.UpdateResp, err error) {
// TODO: req_id
updateSub := ""
args := make([]interface{}, len(req.Fields)+1)
args[len(req.Fields)] = req.Uid
for i, f := range req.Fields {
switch f {
case "san_score":
args[i] = req.SanScore
case "exp":
args[i] = req.Exp
default:
log.Error("[dao.dao-anchor.mysql|anchorIncre] unsupported field(%v), req(%v)", f, req)
err = ecode.InvalidParam
return
}
if i > 0 {
updateSub += ","
}
updateSub += fmt.Sprintf("`%s`=`%s`+(?)", f, f)
}
resp = &v1pb.UpdateResp{}
sql := fmt.Sprintf(_updateAnchorInfo, _anchorTable, updateSub)
res, err := d.db.Exec(ctx, sql, args...)
if err != nil {
log.Error("[dao.dao-anchor.mysql|anchorUpdate] update anchor increment record error(%v), req(%v)", err, req)
return
}
resp.AffectedRows, err = res.RowsAffected()
return
}
// fetchAreas implementation
// fetchAreas 根据父分区号查询子分区
// If the request area-id does not exist, the function returns with the `err` being set.
func (d *Dao) fetchAreas(ctx context.Context, req *v1pb.FetchAreasReq) (resp *v1pb.FetchAreasResp, err error) {
// Query parent area info first and fail fast in case the area doesn't exist.
areaInfo, err := d.dbFetchAreaInfo(ctx, req.AreaId)
if err != nil {
log.Error("[dao.dao-anchor.mysql|fetchAreas] fetch main area info error(%v), req area(%d)", err, req.AreaId)
return nil, err
}
resp = &v1pb.FetchAreasResp{
Info: &v1pb.AreaInfo{
AreaId: req.AreaId,
AreaName: areaInfo.AreaName,
},
}
sql := fmt.Sprintf(_querySubAreaInfo, _subAreaTable)
rows, err := d.dbLiveApp.Query(ctx, sql, req.AreaId)
if err != nil {
log.Error("[dao.dao-anchor.mysql|fetchAreas] fetch area records error(%v), req area(%d)", err, req.AreaId)
return nil, err
}
defer rows.Close()
for rows.Next() {
var subAreaID int64
var subAreaName string
if err = rows.Scan(&subAreaID, &subAreaName); err != nil {
log.Error("[dao.dao-anchor.mysql|fetchAreas] fetch subarea info error(%v), req area(%d)", err, req.AreaId)
return nil, err
}
resp.Areas = append(resp.Areas, &v1pb.AreaInfo{
AreaId: subAreaID,
AreaName: subAreaName,
})
}
return
}
// fetchAttrByIDs implementation
// fetchAttrByIDs 批量根据房间号查询指标
func (d *Dao) fetchAttrByIDs(ctx context.Context, req *v1pb.FetchAttrByIDsReq) (resp *v1pb.FetchAttrByIDsResp, err error) {
if len(req.RoomIds) <= 0 {
return
}
resp = &v1pb.FetchAttrByIDsResp{
Attrs: make(map[int64]*v1pb.AttrData),
}
valueList := ""
for i, id := range req.RoomIds {
if i > 0 {
valueList += ","
}
valueList += fmt.Sprintf("%d", id)
}
sql := fmt.Sprintf(_queryAttrInfo, _attrTable, valueList)
rows, err := d.db.Query(ctx, sql, req.AttrId, req.AttrSubId)
if err != nil {
log.Error("[dao.dao-anchor.mysql|fetchAttrByIDs] get attr record error(%v), req(%v)", err, req)
return nil, err
}
defer rows.Close()
for rows.Next() {
data := &v1pb.AttrData{
AttrId: req.AttrId,
AttrSubId: req.AttrSubId,
}
err = rows.Scan(&data.RoomId, &data.AttrValue)
if err != nil {
log.Error("[dao.dao-anchor.mysql|fetchAttrByIDs] scan attr record error(%v), req(%v)", err, req)
return nil, err
}
resp.Attrs[data.RoomId] = data
}
return
}
// deleteAttr implementation
// deleteAttr 删除一个指标
func (d *Dao) deleteAttr(ctx context.Context, req *v1pb.DeleteAttrReq) (resp *v1pb.UpdateResp, err error) {
sql := fmt.Sprintf(_deleteAttrInfo, _attrTable)
res, err := d.db.Exec(ctx, sql, req.AttrId, req.AttrSubId)
if err != nil {
log.Error("[dao.dao-anchor.mysql|roomExtendIncre] update room extend increment record error(%v), req(%v)", err, req)
return
}
resp = &v1pb.UpdateResp{}
resp.AffectedRows, err = res.RowsAffected()
return
}