完成用户签到功能

dev
truthhun 1 year ago
parent 16a378813d
commit 3456353b2e

@ -120,6 +120,14 @@ message ListUserDynamicReply {
repeated Dynamic dynamic = 2;
}
message Sign{
int64 id = 1;
int64 user_id = 2;
int32 sign_at = 3;
string ip = 4;
google.protobuf.Timestamp created_at = 5 [ (gogoproto.stdtime) = true ];
}
service UserAPI {
//
rpc Register(RegisterAndLoginRequest) returns (LoginReply) {
@ -231,6 +239,20 @@ service UserAPI {
};
}
//
rpc SignToday(google.protobuf.Empty) returns (Sign) {
option (google.api.http) = {
put : '/api/v1/user/sign',
};
}
//
rpc GetSignedToday(google.protobuf.Empty) returns (Sign) {
option (google.api.http) = {
get : '/api/v1/user/sign',
};
}
//
// rpc ListUserFans(ListUserFansRequest) returns (ListUserReply) {
// option (google.api.http) = {

@ -6,6 +6,7 @@ import (
"time"
pb "moredoc/api/v1"
v1 "moredoc/api/v1"
"moredoc/middleware/auth"
"moredoc/model"
"moredoc/util"
@ -172,23 +173,27 @@ func (s *UserAPIService) Logout(ctx context.Context, req *emptypb.Empty) (res *e
// GetUser 根据ID获取用户信息
// 对于非管理员,只能获取公开字段
func (s *UserAPIService) GetUser(ctx context.Context, req *pb.GetUserRequest) (*pb.User, error) {
if req.Id <= 0 {
return nil, status.Errorf(codes.InvalidArgument, "ID错误")
}
userId := req.Id
fields := s.dbModel.GetUserPublicFields()
_, err := s.checkPermission(ctx)
if err == nil {
userClaims, err := s.checkPermission(ctx)
if err == nil || (err != nil && userClaims != nil && userClaims.UserId == userId) {
// 有权限或者查的是用户自己的资料
fields = []string{}
}
user, err := s.dbModel.GetUser(req.Id, fields...)
if userId <= 0 {
if userClaims == nil {
return nil, status.Errorf(codes.InvalidArgument, "ID错误")
}
userId = userClaims.UserId
}
user, err := s.dbModel.GetUser(userId, fields...)
if err != nil {
return nil, status.Errorf(codes.Internal, err.Error())
}
pbUser := &pb.User{}
util.CopyStruct(&user, pbUser)
return pbUser, nil
}
@ -515,3 +520,42 @@ func (s *UserAPIService) CanIUploadDocument(ctx context.Context, req *emptypb.Em
return &emptypb.Empty{}, nil
}
func (s *UserAPIService) GetSignedToday(ctx context.Context, req *emptypb.Empty) (*v1.Sign, error) {
userClaims, err := checkGRPCLogin(s.dbModel, ctx)
if err != nil {
return nil, err
}
sign := s.dbModel.GetSignedToday(userClaims.UserId)
if sign.Id == 0 {
return nil, status.Errorf(codes.NotFound, "您今天还没有签到")
}
pbSign := &v1.Sign{}
util.CopyStruct(&sign, pbSign)
return pbSign, nil
}
func (s *UserAPIService) SignToday(ctx context.Context, req *emptypb.Empty) (*v1.Sign, error) {
userClaims, err := checkGRPCLogin(s.dbModel, ctx)
if err != nil {
return nil, err
}
if sign := s.dbModel.GetSignedToday(userClaims.UserId); sign.Id > 0 {
return nil, status.Errorf(codes.AlreadyExists, "您今天已经签到过了")
}
ip := ""
if ips, _ := util.GetGRPCRemoteIP(ctx); len(ips) > 0 {
ip = ips[0]
}
sign, err := s.dbModel.CreateSign(userClaims.UserId, ip)
if err != nil {
s.logger.Error("签到失败", zap.Error(err))
return nil, status.Errorf(codes.Internal, err.Error())
}
pbSign := &v1.Sign{}
util.CopyStruct(sign, pbSign)
return pbSign, nil
}

@ -16,16 +16,6 @@ type Dynamic struct {
UpdatedAt *time.Time `form:"updated_at" json:"updated_at,omitempty" gorm:"column:updated_at;type:datetime;comment:更新时间;"`
}
// 这里是proto文件中的结构体可以根据需要删除或者调整
//message Dynamic {
// int64 id = 1;
// int64 user_id = 2;
// string content = 3;
// int32 type = 4;
// = 0;
// = 0;
//}
const (
DynamicTypeComment = 1 // 发表评论
DynamicTypeFavorite = 2 // 收藏文档

@ -173,6 +173,7 @@ func (m *DBModel) SyncDB() (err error) {
&Favorite{},
&Comment{},
&Dynamic{},
&Sign{},
}
if err = m.db.AutoMigrate(tableModels...); err != nil {
m.logger.Fatal("SyncDB", zap.Error(err))

@ -0,0 +1,86 @@
package model
import (
"fmt"
"strconv"
"time"
"go.uber.org/zap"
"gorm.io/gorm"
)
type Sign struct {
Id int64 `form:"id" json:"id,omitempty" gorm:"primaryKey;autoIncrement;column:id;comment:;"`
UserId int64 `form:"user_id" json:"user_id,omitempty" gorm:"column:user_id;type:bigint(20);size:20;default:0;index:idx_user_id;index:idx_user_sign_at,unique;comment:签到的用户ID;"`
Ip string `form:"ip" json:"ip,omitempty" gorm:"column:ip;type:varchar(16);size:16;comment:签到的用户IP;"`
SignAt int `form:"sign_at" json:"sign_at,omitempty" gorm:"column:sign_at;type:int(11);size:11;default:0;index:idx_user_sign_at,unique;comment:签到时间格式20060102;"`
CreatedAt *time.Time `form:"created_at" json:"created_at,omitempty" gorm:"column:created_at;type:datetime;comment:创建时间;"`
UpdatedAt *time.Time `form:"updated_at" json:"updated_at,omitempty" gorm:"column:updated_at;type:datetime;comment:更新时间;"`
}
func (Sign) TableName() string {
return tablePrefix + "sign"
}
// CreateSign 用户签到
// 1. 创建签到记录
// 2. 更新用户签积分
func (m *DBModel) CreateSign(userId int64, ip string) (sign *Sign, err error) {
now := time.Now()
signAt, _ := strconv.Atoi(now.Format("20060102"))
sign = &Sign{
UserId: userId,
Ip: ip,
SignAt: signAt,
}
cfg := m.GetConfigOfScore(ConfigScoreSignIn)
tx := m.db.Begin()
defer func() {
if err != nil {
tx.Rollback()
} else {
tx.Commit()
}
}()
err = tx.Create(sign).Error
if err != nil {
m.logger.Error("CreateSign Create", zap.Error(err))
return
}
content := "完成了每日签到"
if cfg.SignIn > 0 {
// 1. 更新用户积分
err = tx.Model(&User{}).Where("id=?", userId).Update("credit_count", gorm.Expr("credit_count + ?", cfg.SignIn)).Error
if err != nil {
m.logger.Error("CreateSign Update", zap.Error(err))
return
}
content = fmt.Sprintf("签到获得 %d 个魔豆", cfg.SignIn)
}
dynamic := Dynamic{
UserId: userId,
Type: DynamicTypeSign,
Content: content,
}
err = tx.Create(&dynamic).Error
if err != nil {
m.logger.Error("CreateSign Create Dynamic", zap.Error(err))
return
}
return
}
// 用户今日是否已签到
func (m *DBModel) GetSignedToday(userId int64) (sign Sign) {
signAt, _ := strconv.Atoi(time.Now().Format("20060102"))
err := m.db.Where("user_id=? and sign_at=?", userId, signAt).First(&sign).Error
if err != nil && err != gorm.ErrRecordNotFound {
m.logger.Error("GetSignedToday", zap.Error(err))
}
return
}

@ -102,3 +102,17 @@ export const getUserPermissions = (params) => {
params,
})
}
export const getSignedToday = () => {
return service({
url: '/api/v1/user/sign',
method: 'get',
})
}
export const signToday = () => {
return service({
url: '/api/v1/user/sign',
method: 'put',
})
}

@ -239,7 +239,7 @@
</template>
<script>
import { mapGetters } from 'vuex'
import { mapActions, mapGetters } from 'vuex'
import DocumentSimpleList from '~/components/DocumentSimpleList.vue'
import { getDocument, downloadDocument } from '~/api/document'
import { getFavorite, createFavorite, deleteFavorite } from '~/api/favorite'
@ -317,6 +317,7 @@ export default {
methods: {
formatDatetime,
formatBytes,
...mapActions('user', ['getUser']),
async getDocument() {
const res = await getDocument({
id: this.documentId,
@ -406,6 +407,7 @@ export default {
id: this.documentId,
})
if (res.status === 200) {
this.getUser()
//
window.location.href = res.data.url
return

@ -104,14 +104,26 @@
<span>{{ user.credit_count || 0 }}</span>
</el-col>
</el-row>
<!-- <el-button class="btn-block" type="success">
<i class="fa fa-calendar-plus-o"></i>
每日签到</el-button
> -->
<el-button class="btn-block" type="success" disabled>
<el-button
v-if="sign.id > 0"
:key="'sign-' + sign.id"
class="btn-block"
type="success"
disabled
>
<i class="fa fa-calendar-check-o" aria-hidden="true"></i>
今日已签到
</el-button>
<el-button
v-else
:key="'sign-0'"
class="btn-block"
type="success"
@click="signToday"
>
<i class="fa fa-calendar-plus-o"></i>
每日签到</el-button
>
<div class="mgt-20px">
<div>个性签名</div>
<div class="help-block user-signature">
@ -266,6 +278,7 @@ import { mapActions, mapGetters } from 'vuex'
import UserAvatar from '~/components/UserAvatar.vue'
import { listBanner } from '~/api/banner'
import { listDocument, listDocumentForHome } from '~/api/document'
import { getSignedToday, signToday } from '~/api/user'
export default {
name: 'IndexPage',
components: { UserAvatar },
@ -277,6 +290,9 @@ export default {
search: {
wd: '',
},
sign: {
sign_at: 0,
},
}
},
head() {
@ -294,10 +310,11 @@ export default {
this.getRecommendDocuments(),
this.listBanner(),
this.getDocuments(),
this.getSignedToday(),
])
},
methods: {
...mapActions('user', ['logout']),
...mapActions('user', ['logout', 'getUser']),
async listBanner() {
const res = await listBanner({
enable: true,
@ -317,6 +334,22 @@ export default {
this.$router.push({ path: '/search', query: { wd } })
}
},
async getSignedToday() {
const res = await getSignedToday()
if (res.status === 200) {
this.sign = res.data || { id: 0 }
}
},
async signToday() {
const res = await signToday()
if (res.status === 200) {
this.sign = res.data || { id: 1 }
this.getUser()
this.$message.success('签到成功')
} else {
this.$message.error(res.message || res.data.message)
}
},
async getRecommendDocuments() {
const res = await listDocument({
field: ['id', 'title'],

@ -52,7 +52,7 @@ export const user = {
async getUser({ commit }) {
const res = await getUser()
if (res.status === 200) {
commit('setUser', res.data.user)
commit('setUser', res.data)
}
return res
},

Loading…
Cancel
Save