diff --git a/internal/routers/api/message.go b/internal/routers/api/message.go index ae508922..202e9a83 100644 --- a/internal/routers/api/message.go +++ b/internal/routers/api/message.go @@ -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, ¶m) + 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) +} diff --git a/internal/routers/router.go b/internal/routers/router.go index 61925756..52ee4089 100644 --- a/internal/routers/router.go +++ b/internal/routers/router.go @@ -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) diff --git a/internal/service/avatar.go b/internal/service/avatar.go index 2aa51a77..5c666133 100644 --- a/internal/service/avatar.go +++ b/internal/service/avatar.go @@ -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 { diff --git a/internal/service/message.go b/internal/service/message.go index 6b77fda3..2c420cd7 100644 --- a/internal/service/message.go +++ b/internal/service/message.go @@ -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) { diff --git a/paopao.sql b/paopao.sql index 8b267563..c134f55d 100644 --- a/paopao.sql +++ b/paopao.sql @@ -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; diff --git a/pkg/errcode/module_code.go b/pkg/errcode/module_code.go index 46d0100f..8d78aaca 100644 --- a/pkg/errcode/module_code.go +++ b/pkg/errcode/module_code.go @@ -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, "获取点赞列表失败") diff --git a/web/src/api/user.ts b/web/src/api/user.ts index 113a206e..46cfe889 100644 --- a/web/src/api/user.ts +++ b/web/src/api/user.ts @@ -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; +}; + /** * 绑定用户手机 * @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', diff --git a/web/src/components/auth.vue b/web/src/components/auth.vue index f8205dcf..0e7d50c8 100644 --- a/web/src/components/auth.vue +++ b/web/src/components/auth.vue @@ -1,6 +1,6 @@ @@ -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 { diff --git a/web/src/components/whisper.vue b/web/src/components/whisper.vue new file mode 100644 index 00000000..19f80fbb --- /dev/null +++ b/web/src/components/whisper.vue @@ -0,0 +1,105 @@ + + + + + \ No newline at end of file diff --git a/web/src/store/index.ts b/web/src/store/index.ts index d15cb834..1f985b90 100644 --- a/web/src/store/index.ts +++ b/web/src/store/index.ts @@ -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; diff --git a/web/src/types/NetParams.d.ts b/web/src/types/NetParams.d.ts index 7664c9db..96abf371 100644 --- a/web/src/types/NetParams.d.ts +++ b/web/src/types/NetParams.d.ts @@ -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, diff --git a/web/src/types/item.d.ts b/web/src/types/item.d.ts index c2c9b60f..a61da99f 100644 --- a/web/src/types/item.d.ts +++ b/web/src/types/item.d.ts @@ -64,6 +64,7 @@ declare module Item { post_id: number, created_on: number, breif: string + content?: string } interface AttachmentProps { diff --git a/web/src/views/User.vue b/web/src/views/User.vue index 78848db3..43e66299 100644 --- a/web/src/views/User.vue +++ b/web/src/views/User.vue @@ -23,13 +23,20 @@ size="small" secondary type="primary" - @click="doWhisper" + @click="openWhisper" > 私信 + + +