feat: add user whisper

pull/11/head
ROC 3 years ago
parent 5cd43cc156
commit f87f99ea5e

@ -62,3 +62,44 @@ func ReadMessage(c *gin.Context) {
response.ToResponse(nil)
}
func SendUserWhisper(c *gin.Context) {
param := service.WhisperReq{}
response := app.NewResponse(c)
valid, errs := app.BindAndValid(c, &param)
if !valid {
global.Logger.Errorf("app.BindAndValid errs: %v", errs)
response.ToErrorResponse(errcode.InvalidParams.WithDetails(errs.Errors()...))
return
}
userID, _ := c.Get("UID")
// 不允许发送私信给自己
if userID.(int64) == param.UserID {
response.ToErrorResponse(errcode.NoWhisperToSelf)
return
}
svc := service.New(c)
_, err := svc.CreateWhisper(&model.Message{
SenderUserID: userID.(int64),
ReceiverUserID: param.UserID,
Type: model.MESSAGE_WHISPER,
Breif: "给你发送新私信了",
Content: param.Content,
})
if err != nil {
global.Logger.Errorf("svc.CreateWhisper err: %v\n", err)
if err == errcode.TooManyWhisperNum {
response.ToErrorResponse(errcode.TooManyWhisperNum)
} else {
response.ToErrorResponse(errcode.SendWhisperFailed)
}
return
}
response.ToResponse(nil)
}

@ -80,6 +80,9 @@ func NewRouter() *gin.Engine {
// 标记消息已读
authApi.POST("/user/message/read", api.ReadMessage)
// 发送用户私信
authApi.POST("/user/whisper", api.SendUserWhisper)
// 获取用户收藏列表
authApi.GET("/user/collections", api.GetUserCollections)

@ -6,56 +6,56 @@ import (
)
var defaultAvatars = []string{
"https://paopao-assets.oss-cn-shanghai.aliyuncs.com/public/avatar/default/zoe.png",
"https://paopao-assets.oss-cn-shanghai.aliyuncs.com/public/avatar/default/william.png",
"https://paopao-assets.oss-cn-shanghai.aliyuncs.com/public/avatar/default/walter.png",
"https://paopao-assets.oss-cn-shanghai.aliyuncs.com/public/avatar/default/thomas.png",
"https://paopao-assets.oss-cn-shanghai.aliyuncs.com/public/avatar/default/taylor.png",
"https://paopao-assets.oss-cn-shanghai.aliyuncs.com/public/avatar/default/sophia.png",
"https://paopao-assets.oss-cn-shanghai.aliyuncs.com/public/avatar/default/sam.png",
"https://paopao-assets.oss-cn-shanghai.aliyuncs.com/public/avatar/default/ryan.png",
"https://paopao-assets.oss-cn-shanghai.aliyuncs.com/public/avatar/default/ruby.png",
"https://paopao-assets.oss-cn-shanghai.aliyuncs.com/public/avatar/default/quinn.png",
"https://paopao-assets.oss-cn-shanghai.aliyuncs.com/public/avatar/default/paul.png",
"https://paopao-assets.oss-cn-shanghai.aliyuncs.com/public/avatar/default/owen.png",
"https://paopao-assets.oss-cn-shanghai.aliyuncs.com/public/avatar/default/olivia.png",
"https://paopao-assets.oss-cn-shanghai.aliyuncs.com/public/avatar/default/norman.png",
"https://paopao-assets.oss-cn-shanghai.aliyuncs.com/public/avatar/default/nora.png",
"https://paopao-assets.oss-cn-shanghai.aliyuncs.com/public/avatar/default/natalie.png",
"https://paopao-assets.oss-cn-shanghai.aliyuncs.com/public/avatar/default/naomi.png",
"https://paopao-assets.oss-cn-shanghai.aliyuncs.com/public/avatar/default/miley.png",
"https://paopao-assets.oss-cn-shanghai.aliyuncs.com/public/avatar/default/mike.png",
"https://paopao-assets.oss-cn-shanghai.aliyuncs.com/public/avatar/default/lucas.png",
"https://paopao-assets.oss-cn-shanghai.aliyuncs.com/public/avatar/default/kylie.png",
"https://paopao-assets.oss-cn-shanghai.aliyuncs.com/public/avatar/default/julia.png",
"https://paopao-assets.oss-cn-shanghai.aliyuncs.com/public/avatar/default/joshua.png",
"https://paopao-assets.oss-cn-shanghai.aliyuncs.com/public/avatar/default/john.png",
"https://paopao-assets.oss-cn-shanghai.aliyuncs.com/public/avatar/default/jane.png",
"https://paopao-assets.oss-cn-shanghai.aliyuncs.com/public/avatar/default/jackson.png",
"https://paopao-assets.oss-cn-shanghai.aliyuncs.com/public/avatar/default/ivy.png",
"https://paopao-assets.oss-cn-shanghai.aliyuncs.com/public/avatar/default/isaac.png",
"https://paopao-assets.oss-cn-shanghai.aliyuncs.com/public/avatar/default/henry.png",
"https://paopao-assets.oss-cn-shanghai.aliyuncs.com/public/avatar/default/harry.png",
"https://paopao-assets.oss-cn-shanghai.aliyuncs.com/public/avatar/default/harold.png",
"https://paopao-assets.oss-cn-shanghai.aliyuncs.com/public/avatar/default/hanna.png",
"https://paopao-assets.oss-cn-shanghai.aliyuncs.com/public/avatar/default/grace.png",
"https://paopao-assets.oss-cn-shanghai.aliyuncs.com/public/avatar/default/george.png",
"https://paopao-assets.oss-cn-shanghai.aliyuncs.com/public/avatar/default/freddy.png",
"https://paopao-assets.oss-cn-shanghai.aliyuncs.com/public/avatar/default/frank.png",
"https://paopao-assets.oss-cn-shanghai.aliyuncs.com/public/avatar/default/finn.png",
"https://paopao-assets.oss-cn-shanghai.aliyuncs.com/public/avatar/default/emma.png",
"https://paopao-assets.oss-cn-shanghai.aliyuncs.com/public/avatar/default/emily.png",
"https://paopao-assets.oss-cn-shanghai.aliyuncs.com/public/avatar/default/edward.png",
"https://paopao-assets.oss-cn-shanghai.aliyuncs.com/public/avatar/default/clara.png",
"https://paopao-assets.oss-cn-shanghai.aliyuncs.com/public/avatar/default/claire.png",
"https://paopao-assets.oss-cn-shanghai.aliyuncs.com/public/avatar/default/chloe.png",
"https://paopao-assets.oss-cn-shanghai.aliyuncs.com/public/avatar/default/audrey.png",
"https://paopao-assets.oss-cn-shanghai.aliyuncs.com/public/avatar/default/arthur.png",
"https://paopao-assets.oss-cn-shanghai.aliyuncs.com/public/avatar/default/anna.png",
"https://paopao-assets.oss-cn-shanghai.aliyuncs.com/public/avatar/default/andy.png",
"https://paopao-assets.oss-cn-shanghai.aliyuncs.com/public/avatar/default/alfred.png",
"https://paopao-assets.oss-cn-shanghai.aliyuncs.com/public/avatar/default/alexa.png",
"https://paopao-assets.oss-cn-shanghai.aliyuncs.com/public/avatar/default/abigail.png",
"https://assets.paopao.info/public/avatar/default/zoe.png",
"https://assets.paopao.info/public/avatar/default/william.png",
"https://assets.paopao.info/public/avatar/default/walter.png",
"https://assets.paopao.info/public/avatar/default/thomas.png",
"https://assets.paopao.info/public/avatar/default/taylor.png",
"https://assets.paopao.info/public/avatar/default/sophia.png",
"https://assets.paopao.info/public/avatar/default/sam.png",
"https://assets.paopao.info/public/avatar/default/ryan.png",
"https://assets.paopao.info/public/avatar/default/ruby.png",
"https://assets.paopao.info/public/avatar/default/quinn.png",
"https://assets.paopao.info/public/avatar/default/paul.png",
"https://assets.paopao.info/public/avatar/default/owen.png",
"https://assets.paopao.info/public/avatar/default/olivia.png",
"https://assets.paopao.info/public/avatar/default/norman.png",
"https://assets.paopao.info/public/avatar/default/nora.png",
"https://assets.paopao.info/public/avatar/default/natalie.png",
"https://assets.paopao.info/public/avatar/default/naomi.png",
"https://assets.paopao.info/public/avatar/default/miley.png",
"https://assets.paopao.info/public/avatar/default/mike.png",
"https://assets.paopao.info/public/avatar/default/lucas.png",
"https://assets.paopao.info/public/avatar/default/kylie.png",
"https://assets.paopao.info/public/avatar/default/julia.png",
"https://assets.paopao.info/public/avatar/default/joshua.png",
"https://assets.paopao.info/public/avatar/default/john.png",
"https://assets.paopao.info/public/avatar/default/jane.png",
"https://assets.paopao.info/public/avatar/default/jackson.png",
"https://assets.paopao.info/public/avatar/default/ivy.png",
"https://assets.paopao.info/public/avatar/default/isaac.png",
"https://assets.paopao.info/public/avatar/default/henry.png",
"https://assets.paopao.info/public/avatar/default/harry.png",
"https://assets.paopao.info/public/avatar/default/harold.png",
"https://assets.paopao.info/public/avatar/default/hanna.png",
"https://assets.paopao.info/public/avatar/default/grace.png",
"https://assets.paopao.info/public/avatar/default/george.png",
"https://assets.paopao.info/public/avatar/default/freddy.png",
"https://assets.paopao.info/public/avatar/default/frank.png",
"https://assets.paopao.info/public/avatar/default/finn.png",
"https://assets.paopao.info/public/avatar/default/emma.png",
"https://assets.paopao.info/public/avatar/default/emily.png",
"https://assets.paopao.info/public/avatar/default/edward.png",
"https://assets.paopao.info/public/avatar/default/clara.png",
"https://assets.paopao.info/public/avatar/default/claire.png",
"https://assets.paopao.info/public/avatar/default/chloe.png",
"https://assets.paopao.info/public/avatar/default/audrey.png",
"https://assets.paopao.info/public/avatar/default/arthur.png",
"https://assets.paopao.info/public/avatar/default/anna.png",
"https://assets.paopao.info/public/avatar/default/andy.png",
"https://assets.paopao.info/public/avatar/default/alfred.png",
"https://assets.paopao.info/public/avatar/default/alexa.png",
"https://assets.paopao.info/public/avatar/default/abigail.png",
}
func (s *Service) GetRandomAvatar() string {

@ -1,16 +1,49 @@
package service
import (
"fmt"
"time"
"github.com/rocboss/paopao-ce/global"
"github.com/rocboss/paopao-ce/internal/model"
"github.com/rocboss/paopao-ce/pkg/convert"
"github.com/rocboss/paopao-ce/pkg/errcode"
)
// 当日单用户私信总数限制TODO 配置化、积分兑换等)
const MAX_WHISPER_NUM_DAILY = 20
type ReadMessageReq struct {
ID int64 `json:"id" binding:"required"`
}
type WhisperReq struct {
UserID int64 `json:"user_id" binding:"required"`
Content string `json:"content" binding:"required"`
}
// 创建私信
func (svc *Service) CreateWhisper(msg *model.Message) (*model.Message, error) {
whisperKey := fmt.Sprintf("WhisperTimes:%d", msg.SenderUserID)
// 今日频次限制
if res, _ := global.Redis.Get(svc.ctx, whisperKey).Result(); convert.StrTo(res).MustInt() >= MAX_WHISPER_NUM_DAILY {
return nil, errcode.TooManyWhisperNum
}
// 创建私信
msg, err := svc.dao.CreateMessage(msg)
if err != nil {
return nil, err
}
// 写入当日(自然日)计数缓存
global.Redis.Incr(svc.ctx, whisperKey).Result()
currentTime := time.Now()
endTime := time.Date(currentTime.Year(), currentTime.Month(), currentTime.Day(), 23, 59, 59, 0, currentTime.Location())
global.Redis.Expire(svc.ctx, whisperKey, endTime.Sub(currentTime))
func (svc *Service) CreateMessage(msg *model.Message) (*model.Message, error) {
return svc.dao.CreateMessage(msg)
return msg, err
}
func (svc *Service) GetUnreadCount(userID int64) (int64, error) {

@ -1,19 +1,3 @@
/*
Navicat Premium Data Transfer
Source Server : t-roc
Source Server Type : MySQL
Source Server Version : 80029
Source Host : localhost:3306
Source Schema : paopao
Target Server Type : MySQL
Target Server Version : 80029
File Encoding : 65001
Date: 26/05/2022 17:12:03
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

@ -44,6 +44,9 @@ var (
GetMessagesFailed = NewError(50001, "获取消息列表失败")
ReadMessageFailed = NewError(50002, "标记消息已读失败")
SendWhisperFailed = NewError(50003, "私信发送失败")
NoWhisperToSelf = NewError(50004, "不允许给自己发送私信")
TooManyWhisperNum = NewError(50005, "今日私信次数已达上限")
GetCollectionsFailed = NewError(60001, "获取收藏列表失败")
GetStarsFailed = NewError(60002, "获取点赞列表失败")

@ -26,6 +26,19 @@ export const sendCaptcha = (data: any) => {
});
};
/**
*
* @param {Object} data
* @returns Promise
*/
export const sendUserWhisper = (data: NetParams.UserWhisper) => {
return request({
method: 'post',
url: '/user/whisper',
data
}) as unknown as Promise<NetParams.UserWhisper>;
};
/**
*
* @param {Object} data
@ -213,7 +226,7 @@ export const getRecharge = (params: NetParams.UserGetRecharge) => {
* @param {Object} params
* @returns Promise
*/
export const getSuggestUsers = (params: {k: string}) => {
export const getSuggestUsers = (params: { k: string }) => {
return request({
method: 'get',
url: '/suggest/users',
@ -226,7 +239,7 @@ export const getSuggestUsers = (params: {k: string}) => {
* @param {Object} params
* @returns Promise
*/
export const getSuggestTags = (params: {k: string}) => {
export const getSuggestTags = (params: { k: string }) => {
return request({
method: 'get',
url: '/suggest/tags',

@ -1,6 +1,6 @@
<template>
<n-modal
v-model:show="store.state.authModelShow"
v-model:show="store.state.authModalShow"
class="auth-card"
preset="card"
size="small"

@ -51,14 +51,18 @@
>
<div class="breif-content">
{{ message.breif }}
<span
v-if="message.type !== 4"
@click.stop="viewDetail(message)"
class="hash-link view-link"
>
<n-icon><share-outline /></n-icon>
</span>
</div>
<div v-if="message.type === 4" class="whisper-content-wrap">
{{ message.content }}
</div>
</n-alert>
</template>
</n-thing>
@ -75,9 +79,12 @@ const defaultavatar =
'https://assets.paopao.info/public/avatar/default/admin.png';
const router = useRouter();
const props = withDefaults(defineProps<{
message: Item.MessageProps
}>(), {});
const props = withDefaults(
defineProps<{
message: Item.MessageProps;
}>(),
{}
);
const viewDetail = (message: Item.MessageProps) => {
handleReadMessage(message);
if (message.type === 1 || message.type === 2 || message.type === 3) {
@ -145,6 +152,10 @@ const handleReadMessage = (message: Item.MessageProps) => {
display: flex;
width: 100%;
}
.whisper-content-wrap {
margin-top: 12px;
text-decoration: underline;
}
}
.view-link {

@ -0,0 +1,105 @@
<template>
<n-modal
:show="show"
@update:show="closeModal"
class="whisper-card"
preset="card"
size="small"
title="私信"
:mask-closable="false"
:bordered="false"
:style="{
width: '360px',
}"
>
<div class="whisper-wrap">
<n-alert :show-icon="false">
:
<n-ellipsis style="max-width: 100%">
<n-gradient-text type="success">
{{ user.nickname }}@{{ user.username }}
</n-gradient-text>
</n-ellipsis>
</n-alert>
<div class="whisper-line">
<n-input
type="textarea"
placeholder="请输入私信内容(请勿发送不和谐内容,否则将会被封号)"
:autosize="{
minRows: 5,
maxRows: 10,
}"
v-model:value="content"
maxlength="200"
show-count
/>
</div>
<div class="whisper-line send-wrap">
<n-button
strong
secondary
type="primary"
:loading="loading"
@click="sendWhisper"
>
</n-button>
</div>
</div>
</n-modal>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { sendUserWhisper } from '@/api/user';
const props = withDefaults(
defineProps<{
show: boolean;
user: Item.UserInfo;
}>(),
{
show: false,
}
);
const content = ref('');
const loading = ref(false);
const emit = defineEmits<{
(e: 'success'): void;
}>();
const closeModal = () => {
emit('success');
};
const sendWhisper = () => {
loading.value = true;
sendUserWhisper({
user_id: props.user.id,
content: content.value,
})
.then((res: any) => {
window.$message.success('');
loading.value = false;
content.value = '';
closeModal();
})
.catch((err: any) => {
loading.value = false;
});
};
</script>
<style lang="less" scoped>
.whisper-wrap {
.whisper-line {
margin-top: 10px;
&.send-wrap {
.n-button {
width: 100%;
}
}
}
}
</style>

@ -6,7 +6,7 @@ export default createStore({
theme: localStorage.getItem('PAOPAO_THEME'),
collapsedLeft: document.body.clientWidth <= 821,
collapsedRight: document.body.clientWidth <= 821,
authModelShow: false,
authModalShow: false,
authModelTab: 'signin',
userInfo: {
id: 0,
@ -22,7 +22,7 @@ export default createStore({
state.theme = theme;
},
triggerAuth(state, status) {
state.authModelShow = status;
state.authModalShow = status;
},
triggerAuthKey(state, key) {
state.authModelTab = key;

@ -61,7 +61,12 @@ declare module NetParams {
}
interface UserGetCaptcha {
}
interface UserWhisper {
user_id: number,
content: string
}
interface PostGetPost {
@ -97,13 +102,13 @@ declare module NetParams {
interface PostGetTags {
type: "hot" | string,
num: number
num: number
}
interface PostGetPostComments {
id: number
}
interface PostCreatePost {
contents: {
content: string,

@ -64,6 +64,7 @@ declare module Item {
post_id: number,
created_on: number,
breif: string
content?: string
}
interface AttachmentProps {

@ -23,13 +23,20 @@
size="small"
secondary
type="primary"
@click="doWhisper"
@click="openWhisper"
>
</n-button>
</n-space>
</div>
</div>
<!-- -->
<whisper
:show="showWhisper"
:user="user"
@success="whisperSuccess"
/>
</n-spin>
<template #footer>
<div class="pagination-wrap" v-if="totalPage > 0">
@ -78,6 +85,7 @@ const user = reactive({
nickname: '',
});
const userLoading = ref(false);
const showWhisper = ref(false);
const list = ref<Item.PostProps[]>([]);
const username = ref(route.query.username || '');
const page = ref(+(route.query.p as string) || 1);
@ -126,8 +134,12 @@ const updatePage = (p: number) => {
loadPosts();
};
const doWhisper = () => {
window.$message.warning('');
const openWhisper = () => {
// window.$message.warning('您尚未获得私信权限');
showWhisper.value = true;
};
const whisperSuccess = () => {
showWhisper.value = false;
};
watch(
() => ({

Loading…
Cancel
Save