Skip to content

Commit

Permalink
Merge pull request #102 from decert-me/feature/ranking
Browse files Browse the repository at this point in the history
Feature 排行榜功能
  • Loading branch information
0xdwong authored Dec 28, 2023
2 parents 2f505e7 + 241be56 commit bb1a78f
Show file tree
Hide file tree
Showing 12 changed files with 737 additions and 4 deletions.
37 changes: 37 additions & 0 deletions internal/app/api/v1/collection.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,40 @@ func CheckQuestInCollection(c *gin.Context) {
OkWithData(res, c)
}
}

// GetCollectionFlashRank 获取合辑闪电榜
func GetCollectionFlashRank(c *gin.Context) {
address := c.GetString("address")
if data, err := srv.GetCollectionFlashRank(address, c.Param("id")); err != nil {
FailWithMessage(GetMessage(c, "FetchFailed"), c)
} else {
OkWithData(data, c)
}
}

// GetCollectionHighRank 获取合辑高分榜
func GetCollectionHighRank(c *gin.Context) {
address := c.GetString("address")
if data, err := srv.GetCollectionHighRank(address, c.Param("id")); err != nil {
FailWithMessage(GetMessage(c, "FetchFailed"), c)
} else {
OkWithData(data, c)
}
}

// GetCollectionHolderRank 获取合辑 Holder 榜单
func GetCollectionHolderRank(c *gin.Context) {
var searchInfo request.GetCollectionHolderRankRequest
_ = c.ShouldBindQuery(&searchInfo)
address := c.GetString("address")
if data, total, err := srv.GetCollectionHolderRank(address, c.Param("id"), searchInfo.Page, searchInfo.PageSize); err != nil {
FailWithMessage(GetMessage(c, "FetchFailed"), c)
} else {
OkWithDetailed(response.PageResult{
List: data,
Total: total,
Page: searchInfo.Page,
PageSize: searchInfo.PageSize,
}, GetMessage(c, "FetchSuccess"), c)
}
}
37 changes: 37 additions & 0 deletions internal/app/api/v1/quest.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,40 @@ func UpdateRecommend(c *gin.Context) {
Ok(c)
}
}

// GetQuestFlashRank 获取闪电榜
func GetQuestFlashRank(c *gin.Context) {
address := c.GetString("address")
if data, err := srv.GetQuestFlashRank(address, c.Param("id")); err != nil {
FailWithMessage(GetMessage(c, "FetchFailed"), c)
} else {
OkWithData(data, c)
}
}

// GetQuestHighRank 获取高分榜
func GetQuestHighRank(c *gin.Context) {
address := c.GetString("address")
if data, err := srv.GetQuestHighRank(address, c.Param("id")); err != nil {
FailWithMessage(GetMessage(c, "FetchFailed"), c)
} else {
OkWithData(data, c)
}
}

// GetQuestHolderRank 获取持有榜
func GetQuestHolderRank(c *gin.Context) {
var searchInfo request.GetQuestHolderRankRequest
_ = c.ShouldBindQuery(&searchInfo)
address := c.GetString("address")
if data, total, err := srv.GetQuestHolderRank(address, c.Param("id"), searchInfo.Page, searchInfo.PageSize); err != nil {
FailWithMessage(GetMessage(c, "FetchFailed"), c)
} else {
OkWithDetailed(response.PageResult{
List: data,
Total: total,
Page: searchInfo.Page,
PageSize: searchInfo.PageSize,
}, GetMessage(c, "FetchSuccess"), c)
}
}
227 changes: 227 additions & 0 deletions internal/app/dao/collection.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"backend-go/internal/app/model/request"
"backend-go/internal/app/model/response"
"errors"
"fmt"
"github.com/spf13/cast"
"gorm.io/gorm"
)
Expand Down Expand Up @@ -132,3 +133,229 @@ func (d *Dao) CheckQuestInCollection(r request.CheckQuestInCollectionRequest) (r
}
return
}

// GetCollectionFlashRank 获取合辑闪电榜
func (d *Dao) GetCollectionFlashRank(address, collectionID string) (res response.GetCollectionFlashRankRes, err error) {
// 查询合辑信息
var collection model.Collection
err = d.db.Model(&model.Collection{}).Where("id", collectionID).First(&collection).Error
if err != nil {
return res, err
}
// 合辑未完结直接返回
if collection.TokenId == 0 {
return res, nil
}
// 查询合辑内挑战列表
var questList []model.Quest
err = d.db.Model(&model.CollectionRelate{}).
Select("quest.token_id,quest.quest_data").
Joins("left join quest ON collection_relate.token_id=quest.token_id").
Where("collection_relate.collection_id", collection.ID).
Where("collection_relate.status = 1").Find(&questList).Error
if err != nil {
return res, err
}
// 区分开放题
var allTokenIDList []int64
for _, quest := range questList {
allTokenIDList = append(allTokenIDList, quest.TokenId)
}
allTokenIDCount := len(allTokenIDList)
var havingSQL string
rawSQL := `SELECT address
FROM user_challenge_log
WHERE token_id IN ? AND address !='' AND pass=true AND deleted_at IS NULL
GROUP BY address
HAVING COUNT(DISTINCT token_id) = ?`
havingSQL = d.db.Model(&model.Collection{}).ToSQL(func(tx *gorm.DB) *gorm.DB {
return tx.Raw(rawSQL, allTokenIDList, allTokenIDCount)
})
// 获取合辑闪电榜
rankListSQL := `
WITH total AS(
SELECT address,token_id, created_at
FROM user_challenge_log
WHERE token_id IN ? AND user_challenge_log.address !='' AND pass=true AND is_open_quest=false AND deleted_at IS NULL
UNION
SELECT address,token_id, created_at
FROM user_open_quest
WHERE token_id IN ? AND pass=true AND deleted_at IS NULL
),ranked AS(
SELECT total.address,token_id, created_at,ROW_NUMBER() OVER (PARTITION BY total.address,total.token_id ORDER BY created_at ASC) as rn
FROM total
INNER JOIN (
`
rankListSQL = rankListSQL + havingSQL + ` ) AS a ON total.address = a.address
),ranked_list AS(
SELECT address,token_id, created_at,ROW_NUMBER() OVER (PARTITION BY address ORDER BY created_at DESC) as rn
FROM ranked WHERE rn=1
)
SELECT ROW_NUMBER() OVER (ORDER BY ranked_list.created_at ASC) as rank,ranked_list.address,ranked_list.created_at as finish_time,users.avatar
FROM ranked_list
LEFT JOIN users ON ranked_list.address=users.address
WHERE rn=1 ORDER BY created_at asc LIMIT 10;
`
err = d.db.Raw(rankListSQL, allTokenIDList, allTokenIDList).Scan(&res.RankList).Error
if err != nil {
return res, err
}
// 查询用户排名
userRankSQL := `
WITH total AS(
SELECT address,token_id, created_at
FROM user_challenge_log
WHERE token_id IN ? AND user_challenge_log.address !='' AND pass=true AND is_open_quest=false AND deleted_at IS NULL
UNION
SELECT address,token_id, created_at
FROM user_open_quest
WHERE token_id IN ? AND pass=true AND deleted_at IS NULL
),ranked AS(
SELECT total.address,token_id, created_at,ROW_NUMBER() OVER (PARTITION BY total.address,total.token_id ORDER BY created_at ASC) as rn
FROM total
INNER JOIN (
`
userRankSQL = userRankSQL + havingSQL + ` ) AS a ON total.address = a.address
),ranked_list AS(
SELECT address,token_id, created_at,ROW_NUMBER() OVER (PARTITION BY address ORDER BY created_at DESC) as rn
FROM ranked WHERE rn=1
),ranked_score_user AS(
SELECT ROW_NUMBER() OVER (ORDER BY ranked_list.created_at ASC) as rank,ranked_list.address,ranked_list.created_at as finish_time
FROM ranked_list
WHERE rn=1
)
SELECT rank,ranked_score_user.address,ranked_score_user.finish_time,users.avatar
FROM ranked_score_user
LEFT JOIN users ON ranked_score_user.address=users.address
WHERE ranked_score_user.address = ? LIMIT 1;
`
err = d.db.Raw(userRankSQL, allTokenIDList, allTokenIDList, address).Scan(&res).Error
return
}

// GetCollectionHighRank 获取合辑高分榜
func (d *Dao) GetCollectionHighRank(address, collectionID string) (res response.GetCollectionHighRankRes, err error) {
// 查询合辑信息
var collection model.Collection
err = d.db.Model(&model.Collection{}).Where("id", collectionID).First(&collection).Error
if err != nil {
return res, err
}
// 合辑未完结直接返回
if collection.TokenId == 0 {
return res, nil
}
// 查询合辑内挑战列表
var questList []model.Quest
err = d.db.Model(&model.CollectionRelate{}).
Select("quest.token_id,quest.quest_data").
Joins("left join quest ON collection_relate.token_id=quest.token_id").
Where("collection_relate.collection_id", collection.ID).
Where("collection_relate.status = 1").Find(&questList).Error
if err != nil {
return res, err
}
// 区分开放题
var allTokenIDList []int64
for _, quest := range questList {
allTokenIDList = append(allTokenIDList, quest.TokenId)
}
allTokenIDCount := len(allTokenIDList)
var havingSQL string
rawSQL := `SELECT address
FROM user_challenge_log
WHERE token_id IN ? AND address !='' AND pass=true AND deleted_at IS NULL
GROUP BY address
HAVING COUNT(DISTINCT token_id) = ?`
havingSQL = d.db.Model(&model.Collection{}).ToSQL(func(tx *gorm.DB) *gorm.DB {
return tx.Raw(rawSQL, allTokenIDList, allTokenIDCount)
})

// 获取合辑高分榜
rankListSQL := `
WITH total AS(
SELECT address,token_id, created_at,user_score/100 as score
FROM user_challenge_log
WHERE token_id IN ? AND user_challenge_log.address !='' AND pass=true AND is_open_quest=false AND deleted_at IS NULL
UNION
SELECT address,token_id, created_at,open_quest_score as score
FROM user_open_quest
WHERE token_id IN ? AND pass=true AND deleted_at IS NULL
),ranked AS(
SELECT total.address,token_id, created_at,score,ROW_NUMBER() OVER (PARTITION BY total.address,total.token_id ORDER BY score DESC) as rn
FROM total
INNER JOIN (
`
rankListSQL = rankListSQL + havingSQL + `) AS a ON total.address = a.address
),ranked_score AS(
SELECT ranked.address, SUM(score) as total_score,max(created_at) as created_at
FROM ranked WHERE rn=1
GROUP BY ranked.address
)
SELECT ROW_NUMBER() OVER (ORDER BY ranked_score.total_score DESC,ranked_score.created_at ASC) as rank,total_score as score,ranked_score.address,ranked_score.created_at as finish_time,users.avatar
FROM ranked_score
LEFT JOIN users ON ranked_score.address=users.address
ORDER BY total_score DESC,ranked_score.created_at ASC LIMIT 10;
`
err = d.db.Raw(rankListSQL, allTokenIDList, allTokenIDList).Scan(&res.RankList).Error
if err != nil {
return res, err
}
// 查询用户排名
userRankSQL := `
WITH total AS(
SELECT address,token_id, created_at,user_score/100 as score
FROM user_challenge_log
WHERE token_id IN ? AND user_challenge_log.address !='' AND pass=true AND is_open_quest=false AND deleted_at IS NULL
UNION
SELECT address,token_id, created_at,open_quest_score as score
FROM user_open_quest
WHERE token_id IN ? AND pass=true AND deleted_at IS NULL
),ranked AS(
SELECT total.address,token_id, created_at,score,ROW_NUMBER() OVER (PARTITION BY total.address,total.token_id ORDER BY score DESC) as rn
FROM total
INNER JOIN (
`
userRankSQL = userRankSQL + havingSQL + ` ) AS a ON total.address = a.address
),ranked_score AS(
SELECT ranked.address, SUM(score) as total_score,max(created_at) as created_at
FROM ranked WHERE rn=1
GROUP BY ranked.address
),ranked_score_user AS(
SELECT ROW_NUMBER() OVER (ORDER BY ranked_score.total_score DESC,ranked_score.created_at ASC) as rank,total_score as score,ranked_score.address,ranked_score.created_at as finish_time
FROM ranked_score
)
SELECT rank,score,ranked_score_user.address,ranked_score_user.finish_time,users.avatar
FROM ranked_score_user
LEFT JOIN users ON ranked_score_user.address=users.address
WHERE ranked_score_user.address = ? LIMIT 1;
`
err = d.db.Raw(userRankSQL, allTokenIDList, allTokenIDList, address).Scan(&res).Error
return
}

// GetCollectionHolderRank 获取合辑 Holder 榜单
func (d *Dao) GetCollectionHolderRank(address string, id int64, page, pageSize int) (res []response.GetCollectionHolderListRes, total int64, err error) {
// 分页参数
limit := pageSize
offset := pageSize * (page - 1)
// 查询合辑的tokenID
var tokenId int64
err = d.db.Model(&model.Collection{}).Select("token_id").Where("id", id).Scan(&tokenId).Error
if err != nil {
return res, total, err
}
err = d.db.Model(&model.UserChallenges{}).Where("token_id", tokenId).Count(&total).Error
if err != nil {
return res, total, err
}
err = d.db.Model(&model.UserChallenges{}).
Select("ROW_NUMBER() OVER (ORDER BY user_challenges.add_ts ASC) as rank,users.*,to_timestamp(user_challenges.add_ts) as claim_time").
Joins("LEFT JOIN users ON user_challenges.address=users.address").
Where("user_challenges.token_id", tokenId).
Order("user_challenges.add_ts DESC").
Limit(limit).Offset(offset).
Find(&res).Error
fmt.Println(res)
return res, total, err
}
Loading

0 comments on commit bb1a78f

Please sign in to comment.