新增下载量排行榜,第三方登录初步确定方案

pull/361/head
HXY 2 years ago
parent d4c5cd9f22
commit 74f827fae2

@ -21,6 +21,8 @@ type Pub interface {
Register(*web.RegisterReq) (*web.RegisterResp, mir.Error)
Login(*web.LoginReq) (*web.LoginResp, mir.Error)
Version() (*web.VersionResp, mir.Error)
ThirdPartyLogin(*web.ThirdPartyLoginReq) mir.Error
ThirdPartyUserData(req *web.ThirdPartyUserDataReq) mir.Error
mustEmbedUnimplementedPubServant()
}
@ -95,6 +97,36 @@ func RegisterPubServant(e *gin.Engine, s Pub) {
resp, err := s.Login(req)
s.Render(c, resp, err)
})
//微信登录路由
router.Handle("GET", "/auth/third/login", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
return
default:
}
req := new(web.ThirdPartyLoginReq)
if err := s.Bind(c, req); err != nil {
s.Render(c, nil, err)
return
}
s.Render(c, nil, s.ThirdPartyLogin(req))
})
//获取用户数据
router.Handle("GET", "/auth/third/user", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
return
default:
}
req := new(web.ThirdPartyUserDataReq)
if err := s.Bind(c, req); err != nil {
s.Render(c, nil, err)
return
}
s.Render(c, nil, s.ThirdPartyUserData(req))
})
router.Handle("GET", "/", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():

@ -1,6 +1,7 @@
package v1
import (
"fmt"
"github.com/alimy/mir/v4"
"github.com/gin-gonic/gin"
"github.com/rocboss/paopao-ce/internal/model/web"
@ -13,7 +14,7 @@ type Rank interface {
GetHighQuality() ([]*web.GetHighQualityRankingResp, mir.Error)
mustEmbedUnimplementedRankServant()
//GetRankList(req *web.GetRankListReq) (interface{}, interface{})
GetDownloadRankList(req *web.GetDownloadRankListReq) ([]web.GetDownloadRankListResp, mir.Error)
//Chain() gin.HandlersChain
}
@ -32,29 +33,32 @@ func RegisterRankServant(e *gin.Engine, s Rank) {
default:
}
//获取GetHighQuality完成后的数据然后将其放到GetHighQualityResp中并返回
resp := new(web.GetHighQualityResp)
resp := new(web.GetRankListResp)
highQualityRanking, err := s.GetHighQuality()
resp.List = highQualityRanking
s.Render(c, resp, err)
})
//获取所有的rank
//router.Handle("GET", "/rank/list", func(c *gin.Context) {
// select {
// case <-c.Request.Context().Done():
// return
// default:
// }
// req := new(web.GetRankListReq)
// var bv _binding_ = req
// if err := bv.Bind(c); err != nil {
// s.Render(c, nil, err)
// return
// }
// //返回值
// resp, err := s.GetRankList(req)
// s.Render(c, resp, err)
//}
router.Handle("GET", "/rank/list", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
return
default:
}
req := new(web.GetDownloadRankListReq)
var bv _binding_ = req
if err := bv.Bind(c); err != nil {
s.Render(c, nil, err)
return
}
fmt.Print(req.ListType)
resp := new(web.GetRankListResp)
downloadRankList, err := s.GetDownloadRankList(req)
resp.List = downloadRankList
//返回值
s.Render(c, resp, err)
})
}

@ -6,8 +6,10 @@ import (
type (
GetHighQualityRankingResp = dbr.GetHighQualityRankingResp
GetDownloadRankListResp = dbr.GetDownloadRankListResp
)
type RankService interface {
GetHighQualityRanking() ([]*GetHighQualityRankingResp, error)
GetDownloadRankList(listType int) ([]*GetDownloadRankListResp, error)
}

@ -17,8 +17,17 @@ type HighQualityRanking struct {
// GetHighQualityRankingResp 优质榜单返回数据
type GetHighQualityRankingResp struct {
UserName string `json:"name"`
NickName string `json:"nickname"`
Avatar string `json:"avatar"`
PostCount int64 `json:"post_count"`
LikesCount int64 `json:"likes"`
ComprehensiveScore int64 `json:"comprehensive_score"`
}
// GetDownloadRankListResp 下载榜单返回数据
type GetDownloadRankListResp struct {
UserName string `json:"name"`
NickName string `json:"nickname"`
Avatar string `json:"avatar"`
Download int64 `json:"download"`
}

@ -28,6 +28,7 @@ type UserScore struct {
UserId int64
//用户名
Username string
Nickname string
//用户头像
Avatar string
//帖子数量&得分一个帖子是1分
@ -64,12 +65,13 @@ func (s RankService) GetHighQualityRanking() ([]*core.GetHighQualityRankingResp,
//如果userScores中没有该用户的信息则新建一个用户信息
if _, ok := userScores[post.UserID]; !ok {
userScores[post.UserID] = &UserScore{}
err = s.db.Table("p_user").Select("id,username,avatar").Where("id = ?", post.UserID).Find(&user).Error
err = s.db.Table("p_user").Select("id,username,avatar,nickname").Where("id = ?", post.UserID).Find(&user).Error
if err != nil {
fmt.Print("get user info error")
return nil, err
}
userScores[post.UserID].Username = user.Username
userScores[post.UserID].Nickname = user.Nickname
userScores[post.UserID].Avatar = user.Avatar
}
@ -134,6 +136,7 @@ func (s RankService) GetHighQualityRanking() ([]*core.GetHighQualityRankingResp,
rank = append(rank, &core.GetHighQualityRankingResp{
UserName: userScore.Username,
Avatar: userScore.Avatar,
NickName: userScore.Nickname,
PostCount: userScore.PostCount,
LikesCount: userScore.LikeCount,
ComprehensiveScore: userScore.Score,
@ -141,3 +144,91 @@ func (s RankService) GetHighQualityRanking() ([]*core.GetHighQualityRankingResp,
}
return rank, nil
}
type ShareKeyInfo struct {
//用户id
Id int64
UserName string
AllDownloadCount int64
DownloadCountPreWeek int64
DownloadCountPreMonth int64
Status int
}
type ShareKeyInfoRank struct {
UserName string `gorm:"column:username"`
Avatar string
TotalDownloadCount int64 `gorm:"column:total_download_count"`
}
// GetDownloadRankList 获取下载榜单
func (s RankService) GetDownloadRankList(listType int) ([]*core.GetDownloadRankListResp, error) {
//查询p_share_key_info表获取所有的share_key_info
var shareKeyInfos []ShareKeyInfoRank
//根据listType判断是获取周榜单还是月榜单
fmt.Print("listType:" + strconv.Itoa(listType) + "\n")
if listType == 1 {
//获取总榜单
//对shareKeyInfos按照all_download_count进行排序
err := s.db.Table("p_share_key_info").Select("username, SUM(all_download_count) as total_download_count").
Where("status = 0").
Group("username").
Order("total_download_count desc").
Limit(5).
Find(&shareKeyInfos).Error
if err != nil {
return nil, err
}
}
if listType == 2 {
//获取周榜单
//对shareKeyInfos按照download_count_per_week进行排序
err := s.db.Table("p_share_key_info").Select("username, SUM(download_count_per_week) as total_download_count").
Where("status = 0").
Group("username").
Order("total_download_count desc").
Limit(5).
Find(&shareKeyInfos).Error
if err != nil {
return nil, err
}
}
if listType == 3 {
//获取月榜单
//对shareKeyInfos按照download_count_per_month进行排序
err := s.db.Table("p_share_key_info").Select("username, SUM(download_count_per_month) as total_download_count").
Where("status = 0").
Group("username").
Order("total_download_count desc").
Limit(5).
Find(&shareKeyInfos).Error
if err != nil {
return nil, err
}
}
//将shareKeyInfos中的数据放到rank中
var ranks []*core.GetDownloadRankListResp
for _, shareKeyInfo := range shareKeyInfos {
//根据username查询user表获取用户头像
var user ms.User
err := s.db.Table("p_user").Select("avatar,nickname").Where("username = ?", shareKeyInfo.UserName).First(&user).Error
if err != nil {
fmt.Print("查询用户失败")
return nil, err
}
shareKeyInfo.Avatar = user.Avatar
ranks = append(ranks, &core.GetDownloadRankListResp{
UserName: shareKeyInfo.UserName,
NickName: user.Nickname,
Avatar: shareKeyInfo.Avatar,
Download: shareKeyInfo.TotalDownloadCount,
})
fmt.Print(shareKeyInfo.UserName + " " + strconv.FormatInt(shareKeyInfo.TotalDownloadCount, 10) + " " + shareKeyInfo.Avatar + "\n")
}
//fmt.Print(rank)
return ranks, nil
}

@ -48,3 +48,14 @@ type RegisterResp struct {
UserId int64 `json:"id"`
Username string `json:"username"`
}
// ThirdPartyLoginReq 第三方登录请求参数
type ThirdPartyLoginReq struct {
RequestType string `json:"request_type" form:"request_type" binding:"required"`
}
// ThirdPartyUserDataReq 第三方登录获取用户数据请求参数
type ThirdPartyUserDataReq struct {
Type string `json:"type" form:"type" binding:"required"`
Code string `json:"code" form:"code" binding:"required"`
}

@ -6,32 +6,32 @@ import (
"github.com/rocboss/paopao-ce/pkg/convert"
)
// GetHighQualityResp 构造相应参数包含一个GetHighQualityRankingResp数组
type GetHighQualityResp struct {
// GetRankListResp 构造相应参数包含一个GetHighQualityRankingResp数组
type GetRankListResp struct {
List any `json:"list"`
}
type GetHighQualityRankingResp struct {
UserName string `json:"name"`
NickName string `json:"nickname"`
Avatar string `json:"avatar"`
PostCount int64 `json:"post_count"`
LikesCount int64 `json:"likes"`
ComprehensiveScore int64 `json:"comprehensive_score"`
}
type GetRankListReq struct {
ListType string `json:"list_type"`
type GetDownloadRankListReq struct {
ListType int `json:"list_type"`
}
type GetRankListResp struct {
type GetDownloadRankListResp struct {
UserName string `json:"name"`
NickName string `json:"nickname"`
Avatar string `json:"avatar"`
PostCount int64 `json:"post_count"`
LikesCount int64 `json:"likes"`
ComprehensiveScore int64 `json:"comprehensive_score"`
Download int64 `json:"download"`
}
func (g *GetRankListReq) Bind(c *gin.Context) mir.Error {
g.ListType = convert.StrTo(c.Query("list_type")).String()
func (g *GetDownloadRankListReq) Bind(c *gin.Context) mir.Error {
g.ListType = convert.StrTo(c.Query("list_type")).MustInt()
return nil
}

@ -107,5 +107,9 @@ var (
ErrGetHighQualityRankingFailed = xerror.NewError(12001, "获取优质帖子排行榜失败")
ErrGetThirdPartyLoginUrlFailed = xerror.NewError(13001, "获取第三方登录地址失败")
ErrGetThirdPartyUserDataFailed = xerror.NewError(13002, "获取第三方用户数据失败")
ErrGetDownloadRankListFailed = xerror.NewError(13003, "获取下载排行榜失败")
ErrNotImplemented = xerror.NewError(10501, "功能未实现")
)

@ -8,8 +8,12 @@ import (
"bytes"
"context"
"encoding/base64"
"encoding/json"
"fmt"
"image/color"
"image/png"
"io/ioutil"
"net/http"
"regexp"
"strings"
"unicode/utf8"
@ -43,6 +47,85 @@ type pubSrv struct {
*base.DaoServant
}
type ThirdPartyUserDataVo struct {
Code int `json:"code"`
Msg string `json:"msg"`
Type string `json:"type"`
AccessToken string `json:"access_token"`
SocialUid string `json:"social_uid"`
Faceimg string `json:"faceimg"`
Nickname string `json:"nickname"`
Location string `json:"location"`
Grnder string `json:"grnder"`
Ip string `json:"ip"`
}
func (s *pubSrv) ThirdPartyUserData(req *web.ThirdPartyUserDataReq) mir.Error {
requestUrl := fmt.Sprintf("https://uniqueker.top/connect.php?act=callback&appid=1971&appkey=192e4cdc5d860b4eca813f64eec17498&type=" + req.Type + "&code=" + req.Code)
resp, err := http.Get(requestUrl)
if err != nil {
return web.ErrGetThirdPartyUserDataFailed
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
fmt.Print("failed to get response: %s" + resp.Status)
return web.ErrGetThirdPartyUserDataFailed
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return web.ErrGetThirdPartyUserDataFailed
}
var user ThirdPartyUserDataVo
err = json.Unmarshal(body, &user)
if err != nil {
return web.ErrGetThirdPartyUserDataFailed
}
//if data["code"].(float64) != 200 {
// return web.ErrGetThirdPartyUserDataFailed
//}
fmt.Print(user)
return nil
}
type ThirdPartyLoginVo struct {
Code int `json:"code"`
Msg string `json:"msg"`
Type string `json:"type"`
URL string `json:"url"`
}
func (s *pubSrv) ThirdPartyLogin(req *web.ThirdPartyLoginReq) mir.Error {
requestUrl := fmt.Sprintf("https://uniqueker.top/connect.php?act=login&appid=1971&appkey=192e4cdc5d860b4eca813f64eec17498&type=" + req.RequestType + "&redirect_uri=http://s5qfht.natappfree.cc")
resp, err := http.Get(requestUrl)
if err != nil {
return web.ErrGetThirdPartyLoginUrlFailed
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
fmt.Print("failed to get response: %s" + resp.Status)
return web.ErrGetThirdPartyLoginUrlFailed
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return web.ErrGetThirdPartyLoginUrlFailed
}
var qq ThirdPartyLoginVo
err = json.Unmarshal(body, &qq)
if err != nil {
return web.ErrGetThirdPartyLoginUrlFailed
}
if qq.Code != 0 {
return web.ErrGetThirdPartyLoginUrlFailed
}
fmt.Print(qq.URL)
return nil
}
func (s *pubSrv) TweetDetail(req *web.TweetDetailReq) (*web.TweetDetailResp, mir.Error) {
post, err := s.Ds.GetPostByID(req.TweetId)
if err != nil {

@ -19,6 +19,25 @@ type rankSrv struct {
*base.DaoServant
}
func (s *rankSrv) GetDownloadRankList(req *web.GetDownloadRankListReq) ([]web.GetDownloadRankListResp, mir.Error) {
ranks, err := s.Ds.GetDownloadRankList(req.ListType)
if err != nil {
return []web.GetDownloadRankListResp{}, web.ErrGetDownloadRankListFailed
}
var rankInfos []web.GetDownloadRankListResp
for _, rank := range ranks {
rankInfos = append(rankInfos, web.GetDownloadRankListResp{
UserName: rank.UserName,
NickName: rank.NickName,
Avatar: rank.Avatar,
Download: rank.Download,
})
}
return rankInfos, nil
}
func (s *rankSrv) Chain() gin.HandlersChain {
return gin.HandlersChain{chain.JWT()}
}
@ -41,6 +60,7 @@ func (s *rankSrv) GetHighQuality() ([]*web.GetHighQualityRankingResp, mir.Error)
for _, rank := range ranks {
rankInfos = append(rankInfos, &web.GetHighQualityRankingResp{
UserName: rank.UserName,
NickName: rank.NickName,
Avatar: rank.Avatar,
PostCount: rank.PostCount,
LikesCount: rank.LikesCount,

@ -22,14 +22,25 @@ export const getTags = (
});
};
/** 获取优质帖榜 */
export const getHighQuailty = (): Promise<NetReq.PostGetHighQuailty> => {
/** 获取用户榜 */
export const getHighQuailty = (): Promise<NetReq.GetRankingData> => {
return request({
method: "get",
url: "/v1/rank/highquality",
});
}
/** 获取下载榜 */
export const getDownloadRank= (param: number): Promise<NetReq.GetRankingData> => {
return request({
method: "get",
url: "/v1/rank/list",
params: {
list_type: param
}
});
}
/** 获取动态详情 */
export const getPost = (
params: NetParams.PostGetPost

@ -85,51 +85,64 @@
</n-card>
<n-card
class="hottopic-wrap"
title="排行榜"
embedded
:bordered="false"
size="small"
>
<div class="ranking-header">
<div class="ranking-title">{{ rankingTitles[currentRankingType] }}</div>
<div class="toggle-button" @click="toggleRankingType">
<n-icon :component="ChevronForward" />
</div>
</div>
<n-spin :show="loading">
<div
class="ranking-item"
v-for="(highQuality, index) in rankingList"
:key="highQuality.name"
v-for="(item, index) in getCurrentRankingList"
:key="item.name"
>
<div class="ranking-number">{{ index + 1 }}</div> <!-- -->
<div class="ranking-number">{{ index + 1 }}</div>
<!-- -->
<div class="ranking-avatar">
<!-- -->
<img :src="highQuality.avatar" alt="User Avatar" />
<img :src="item.avatar" alt="User Avatar" />
</div>
<div class="ranking-info">
<div style="flex: 1">
<div class="user-name">
<router-link
class="hash-link"
:to="{
name: 'user',
query: {
s: highQuality.name
}
s: item.name,
},
}"
>
{{ formatUserName(highQuality.name) }}
{{ formatUserName(item.nickname) }}
</router-link>
</div>
<div class="ranking-info">
<div class="name-stats">
<div class="downloads">
<div class="download-box">
<div class="download-value">{{ highQuality.comprehensive_score }}</div>
<div class="score">
<div class="score-value" v-if="currentRankingType === 'highQuality'">
{{ item.comprehensive_score }}
</div>
<div class="score-value" v-else-if="currentRankingType !== 'highQuality'">
{{ item.download }}
</div>
</div>
</div>
<div class="stats">
<div class="name-stats">
<div class="stats" v-if="currentRankingType === 'highQuality'">
<div class="stat-item">
<div class="stat-value">{{ highQuality.post_count }} </div>
<div class="stat-value">📃{{ formatQuoteNumStats(item.post_count || item.download) }}</div>
</div>
<div class="stat-drop">·</div>
<!-- -->
<div class="stat-item">
<div class="stat-value">{{ highQuality.likes }} </div>
<div class="stat-value">{{ formatQuoteNumStats(item.likes) }}</div>
</div>
</div>
</div>
</div>
@ -144,7 +157,7 @@
import { ref, onMounted, computed, watch } from "vue";
import { useStore } from "vuex";
import { useRouter } from "vue-router";
import { getHighQuailty, getTags } from "@/api/post";
import { getDownloadRank, getHighQuailty, getTags } from "@/api/post";
import { Search } from "@vicons/ionicons5";
import { ChevronForward } from "@vicons/ionicons5";
@ -167,7 +180,8 @@ const rightHotTopicMaxSize = Number(
);
// 模拟排行榜数据
const rankingList = ref<Item.highqualityProps[]>([]);
const rankingList = ref<Item.RankingDataProps[]>([]);
const allDownloadRankingList = ref<Item.RankingDataProps[]>([]);
//获取排行榜数据
const locadHeighQuailtyRankingList = () => {
@ -180,8 +194,41 @@ const locadHeighQuailtyRankingList = () => {
.catch((err) => {
loading.value = false;
});
};
const loadDowmloadRankingByType = (type: number) => {
getDownloadRank(type)
.then((res) => {
allDownloadRankingList.value = res.list;
})
.catch((err) => {
loading.value = false;
});
};
const rankingTitles: { [key: string]: string } = {
highQuality: "用户榜",
downloadPreWeek: "下载周榜",
downloadPreMonth: "下载月榜",
downloadAll: "下载总榜",
hot: "热门排行榜",
};
const currentRankingType = ref("highQuality");
const getCurrentRankingList = computed(() => {
if (currentRankingType.value === "highQuality") {
return rankingList.value;
} else if (currentRankingType.value === "downloadAll") {
return allDownloadRankingList.value;
}
return [];
});
const toggleRankingType = () => {
currentRankingType.value =
currentRankingType.value === "highQuality" ? "downloadAll" : "highQuality";
};
const loadHotTags = () => {
loading.value = true;
@ -207,6 +254,15 @@ const formatQuoteNum = (num: number) => {
return num;
};
const formatQuoteNumStats = (num: number) => {
if (num >= 1000) {
const formattedNum = (num / 1000).toFixed(1); // Get one decimal place
return formattedNum + 'k';
} else {
return num.toString().padStart(3, '0'); // Ensure 3-digit format
}
};
const handleSearch = () => {
router.push({
name: "home",
@ -224,8 +280,10 @@ const showFollowTopics = computed({
},
});
const formatUserName = (name: string) => {
return name.length > 5 ? name.substring(0, 5) + '...' : name;
return name.length > 10 ? name.substring(0, 10) + "..." : name;
};
watch(
() => ({
refreshTopicFollow: store.state.refreshTopicFollow,
@ -240,6 +298,7 @@ watch(
onMounted(() => {
loadHotTags();
locadHeighQuailtyRankingList();
loadDowmloadRankingByType(1);
});
</script>
@ -329,18 +388,20 @@ onMounted(() => {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
display: flex;
align-items: center;
margin-top: 10px;
}
.ranking-info {
margin-left: auto; /* 将左边距设置为 auto使排行榜内容靠右 */
flex: 1;
display: flex;
align-items: center; /* 将 align-items 设置为 center */
justify-content: flex-end; /* 将 justify-content 设置为 flex-end */
flex: 1;
.name-stats {
display: flex;
flex-direction: column;
align-items: flex-end; /* 将 align-items 设置为 flex-end */
justify-content: right;
flex: 1;
@ -361,12 +422,12 @@ onMounted(() => {
}
}
.downloads {
.score {
margin-left: auto;
text-align: right;
margin-top: 13px;
align-items: center;
.download-value {
.score-value {
font-weight: bold;
font-size: 15px;
}
@ -374,6 +435,31 @@ onMounted(() => {
}
}
}
.ranking-card {
margin-bottom: 10px;
}
.ranking-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px;
}
.ranking-title {
font-size: 16px;
font-weight: bold;
}
.toggle-button {
display: flex;
align-items: center;
cursor: pointer;
}
.toggle-button n-icon {
margin-left: 5px;
}
.dark {
.hottopic-wrap {
background-color: #18181c;

@ -352,10 +352,12 @@ declare module Item {
description: string;
}
//优质榜参数
interface highqualityProps {
//排行榜参数
interface RankingDataProps {
//用户名
name: string;
//昵称
nickname: string;
//头像
avatar: string;
//发帖数量
@ -364,5 +366,7 @@ declare module Item {
likes: number;
//总得分
comprehensive_score: number;
//下载数量
download: number;
}
}

@ -167,9 +167,9 @@ declare module NetReq {
pager: Item.PagerProps;
}
interface PostGetHighQuailty {
/** 帖子列表 */
list: Item.highqualityProps[];
interface GetRankingData {
/** 排行榜数据 */
list: Item.RankingDataProps[];
}
type PostCreatePost = Item.PostProps;

Loading…
Cancel
Save