From 957c06ca1a687b704fec7d7396dacb4338275032 Mon Sep 17 00:00:00 2001 From: zchengo <1933757688@qq.com> Date: Mon, 2 Jan 2023 11:00:29 +0800 Subject: [PATCH] feat: system notice --- server/api/notice.go | 63 +++++++++++++ server/api/subscribe.go | 2 +- server/api/user.go | 2 +- server/initialize/router.go | 6 ++ server/models/notice.go | 34 +++++++ server/service/common.go | 8 ++ server/service/notice.go | 71 ++++++++++++++ server/service/subscribe.go | 22 ++++- server/service/user.go | 20 ++++ web/src/api/notice.js | 37 ++++++++ web/src/views/Home.vue | 183 ++++++++++++++++++++++++++---------- 11 files changed, 389 insertions(+), 59 deletions(-) create mode 100644 server/api/notice.go create mode 100644 server/models/notice.go create mode 100644 server/service/notice.go create mode 100644 web/src/api/notice.js diff --git a/server/api/notice.go b/server/api/notice.go new file mode 100644 index 0000000..236fa17 --- /dev/null +++ b/server/api/notice.go @@ -0,0 +1,63 @@ +package api + +import ( + "crm/models" + "crm/response" + "crm/service" + "fmt" + "strconv" + + "github.com/gin-gonic/gin" +) + +type NoticeApi struct { + noticeService *service.NoticeService +} + +func NewNoticeApi() *NoticeApi { + noticeApi := NoticeApi{ + noticeService: &service.NoticeService{}, + } + return ¬iceApi +} + +func (n *NoticeApi) Update(context *gin.Context) { + var param models.NoticeUpdateParam + if err := context.ShouldBind(¶m); err != nil { + fmt.Println(err) + response.Result(response.ErrCodeParamInvalid, nil, context) + return + } + errCode := n.noticeService.Update(¶m) + response.Result(errCode, nil, context) +} + +func (n *NoticeApi) GetCount(context *gin.Context) { + uid, _ := strconv.Atoi(context.Request.Header.Get("uid")) + if uid <= 0 { + response.Result(response.ErrCodeParamInvalid, nil, context) + return + } + unReadNotice, errCode := n.noticeService.UnReadCount(int64(uid)) + response.Result(errCode, unReadNotice, context) +} + +func (n *NoticeApi) Delete(context *gin.Context) { + var param models.NoticeDeleteParam + if err := context.ShouldBind(¶m); err != nil { + response.Result(response.ErrCodeParamInvalid, nil, context) + return + } + errCode := n.noticeService.Delete(¶m) + response.Result(errCode, nil, context) +} + +func (n *NoticeApi) GetList(context *gin.Context) { + uid, _ := strconv.Atoi(context.Request.Header.Get("uid")) + if uid <= 0 { + response.Result(response.ErrCodeParamInvalid, nil, context) + return + } + noticeList, errCode := n.noticeService.GetList(int64(uid)) + response.Result(errCode, noticeList, context) +} diff --git a/server/api/subscribe.go b/server/api/subscribe.go index 0398442..82b36e4 100644 --- a/server/api/subscribe.go +++ b/server/api/subscribe.go @@ -16,7 +16,7 @@ type SubscribeApi struct { func NewSubscribeApi() *SubscribeApi { subscribeApi := SubscribeApi{ - subscribeService: &service.SubscribeService{}, + subscribeService: service.NewSubscribeService(), } return &subscribeApi } diff --git a/server/api/user.go b/server/api/user.go index 1933544..9778c27 100644 --- a/server/api/user.go +++ b/server/api/user.go @@ -16,7 +16,7 @@ type UserApi struct { func NewUserApi() *UserApi { userApi := UserApi{ - userService: &service.UserService{}, + userService: service.NewUserService(), } return &userApi } diff --git a/server/initialize/router.go b/server/initialize/router.go index 639194a..bda8bf9 100644 --- a/server/initialize/router.go +++ b/server/initialize/router.go @@ -66,6 +66,12 @@ func Router() { route.GET("/subscribe/info", api.NewSubscribeApi().GetInfo) route.POST("/subscribe/pay", api.NewSubscribeApi().Pay) route.POST("/subscribe/notify", api.NewSubscribeApi().Notify) + + // 通知模块 + route.GET("/notice/list", api.NewNoticeApi().GetList) + route.GET("/notice/count", api.NewNoticeApi().GetCount) + route.PUT("/notice/update", api.NewNoticeApi().Update) + route.DELETE("/notice/delete", api.NewNoticeApi().Delete) } // 启动、监听端口 diff --git a/server/models/notice.go b/server/models/notice.go new file mode 100644 index 0000000..f05ad00 --- /dev/null +++ b/server/models/notice.go @@ -0,0 +1,34 @@ +package models + +type Notice struct { + Id int64 `gorm:"primaryKey"` + Content string `gorm:"content"` + Status int `gorm:"status"` + Creator int64 `gorm:"creator"` + Created int64 `gorm:"created"` + Updated int64 `gorm:"updated"` +} + +type NoticeParam struct { + Content string `json:"content"` + Creator int64 `gorm:"creator"` +} + +type NoticeUpdateParam struct { + Id int64 `json:"id" binding:"required,gt=0"` +} + +type NoticeDeleteParam struct { + Ids []int64 `json:"ids" binding:"required"` +} + +type UnReadNotice struct { + Count int `json:"count"` +} + +type NoticeList struct { + Id int64 `json:"id"` + Content string `json:"content"` + Status int `json:"status"` + Created int64 `json:"created"` +} diff --git a/server/service/common.go b/server/service/common.go index 30bc2cf..5115325 100644 --- a/server/service/common.go +++ b/server/service/common.go @@ -19,6 +19,7 @@ const ( CONTRACT = "contract" PRODUCT = "product" SUBSCRIBE = "subscribe" + NOTICE = "notice" ) const ( @@ -31,6 +32,13 @@ const ( Prod = "prod" ) +const ( + REGISTER_NOTICE_TEMPLATE = "你注册了账号" + LOGIN_NOTICE_TEMPLATE = "你登录了账号" + SUBSCRIBE_NOTICE_TEMPLATE1 = "你订阅了专业版" + SUBSCRIBE_NOTICE_TEMPLATE2 = "你订阅了高级版" +) + var ctx = context.Background() // RestPage 分页查询 diff --git a/server/service/notice.go b/server/service/notice.go new file mode 100644 index 0000000..115a672 --- /dev/null +++ b/server/service/notice.go @@ -0,0 +1,71 @@ +package service + +import ( + "crm/global" + "crm/models" + "crm/response" + "time" +) + +const ( + Read = 1 // 已读 + UnRead = 2 // 未读 +) + +type NoticeService struct { +} + +// 新建通知 +func (n *NoticeService) Create(param *models.NoticeParam) int { + notice := models.Notice{ + Content: param.Content, + Status: UnRead, + Creator: param.Creator, + Created: time.Now().Unix(), + } + if err := global.Db.Create(¬ice).Error; err != nil { + return response.ErrCodeFailed + } + return response.ErrCodeSuccess +} + +// 更新通知 +func (n *NoticeService) Update(param *models.NoticeUpdateParam) int { + notice := models.Notice{ + Id: param.Id, + Status: Read, + Updated: time.Now().Unix(), + } + if err := global.Db.Model(¬ice).Updates(¬ice).Error; err != nil { + return response.ErrCodeFailed + } + return response.ErrCodeSuccess +} + +// 获取未读通知数量 +func (n *NoticeService) UnReadCount(uid int64) (models.UnReadNotice, int) { + var unRead models.UnReadNotice + raw := "select count(*) from notice where status = 2 and creator = ?" + if err := global.Db.Raw(raw, uid).Scan(&unRead.Count).Error; err != nil { + return unRead, response.ErrCodeFailed + } + return unRead, response.ErrCodeSuccess +} + +// 删除通知 +func (n *NoticeService) Delete(param *models.NoticeDeleteParam) int { + err := global.Db.Delete(&models.Notice{}, param.Ids).Error + if err != nil { + return response.ErrCodeFailed + } + return response.ErrCodeSuccess +} + +// 获取通知列表 +func (n *NoticeService) GetList(uid int64) ([]*models.NoticeList, int) { + noticeList := make([]*models.NoticeList, 0) + if err := global.Db.Table(NOTICE).Where("creator = ?", uid).Order("status desc").Order("created desc").Find(¬iceList).Error; err != nil { + return nil, response.ErrCodeFailed + } + return noticeList, response.ErrCodeSuccess +} diff --git a/server/service/subscribe.go b/server/service/subscribe.go index e7947bf..3b21b6b 100644 --- a/server/service/subscribe.go +++ b/server/service/subscribe.go @@ -20,6 +20,14 @@ const ( ) type SubscribeService struct { + noticeService *NoticeService +} + +func NewSubscribeService() *SubscribeService { + subscribeService := SubscribeService{ + noticeService: &NoticeService{}, + } + return &subscribeService } // 订阅专业版,发起支付 @@ -96,11 +104,14 @@ func (s *SubscribeService) Callback(outTradeNo string) (string, int) { // 创建订阅信息 var expired int64 + var content string switch order.Version { case 2: expired = time.Now().Unix() + int64(2592000) + content = SUBSCRIBE_NOTICE_TEMPLATE1 case 3: expired = time.Now().Unix() + int64(31104000) + content = SUBSCRIBE_NOTICE_TEMPLATE2 } subscribe := models.Subscribe{ Version: order.Version, @@ -121,11 +132,12 @@ func (s *SubscribeService) Callback(outTradeNo string) (string, int) { } } - // 修改支付状态 - key := fmt.Sprintf("uid:%v:pay:status", order.Uid) - if err := global.Rdb.Set(ctx, key, Payed, time.Hour*10).Err(); err != nil { - return StringNull, response.ErrCodeFailed - } + // 订阅通知 + s.noticeService.Create(&models.NoticeParam{ + Content: content, + Creator: order.Uid, + }) + return global.Config.Alipay.PaySuccessURL, response.ErrCodeSuccess } diff --git a/server/service/user.go b/server/service/user.go index 9ec3cba..1a9583a 100644 --- a/server/service/user.go +++ b/server/service/user.go @@ -19,6 +19,14 @@ const ( ) type UserService struct { + noticeService *NoticeService +} + +func NewUserService() *UserService { + userService := UserService{ + noticeService: &NoticeService{}, + } + return &userService } // 用户注册 @@ -75,6 +83,12 @@ func (u *UserService) Register(param *models.UserCreateParam) int { } } + // 注册通知 + u.noticeService.Create(&models.NoticeParam{ + Content: REGISTER_NOTICE_TEMPLATE, + Creator: newUser.Id, + }) + return response.ErrCodeSuccess } @@ -106,6 +120,12 @@ func (u *UserService) Login(param *models.UserLoginParam) (*models.UserInfo, int Token: token, } + // 登录通知 + u.noticeService.Create(&models.NoticeParam{ + Content: LOGIN_NOTICE_TEMPLATE, + Creator: userInfo.Uid, + }) + return &userInfo, response.ErrCodeSuccess } diff --git a/web/src/api/notice.js b/web/src/api/notice.js new file mode 100644 index 0000000..201de69 --- /dev/null +++ b/web/src/api/notice.js @@ -0,0 +1,37 @@ +import request from '../axios/index' + +// 获取通知列表 +export function updateNotice(param) { + return request({ + url: '/notice/update', + method: 'put', + data: param, + }) +} + +// 获取通知列表 +export function getNoticeCount(param) { + return request({ + url: '/notice/count', + method: 'get', + params: param, + }) +} + +// 删除全部通知 +export function deleteNotice(param) { + return request({ + url: '/notice/delete', + method: 'delete', + data: param, + }) +} + +// 获取通知列表 +export function getNoticeList(param) { + return request({ + url: '/notice/list', + method: 'get', + params: param, + }) +} \ No newline at end of file diff --git a/web/src/views/Home.vue b/web/src/views/Home.vue index baa9c83..7356d22 100644 --- a/web/src/views/Home.vue +++ b/web/src/views/Home.vue @@ -22,46 +22,67 @@
- - - - - - - - - - - - + + + + + - U + + + + + + U + +
- +
@@ -92,10 +113,12 @@ import { useRouter } from 'vue-router' import { useStore } from '../store/index'; import { message } from 'ant-design-vue'; import { getUserInfo, getVerifyCode, userDelete } from '../api/user'; +import { updateNotice, getNoticeCount, getNoticeList, deleteNotice } from '../api/notice'; import { DashboardOutlined, SmileOutlined, MehOutlined, ShoppingOutlined } from '@ant-design/icons-vue'; -import { CrownOutlined, MenuUnfoldOutlined, MenuFoldOutlined, QuestionCircleFilled } from '@ant-design/icons-vue'; -import { SmileFilled, BellFilled } from '@ant-design/icons-vue'; -import { ExclamationCircleOutlined, CrownFilled } from '@ant-design/icons-vue'; +import { CrownOutlined, MenuUnfoldOutlined, MenuFoldOutlined, WechatFilled } from '@ant-design/icons-vue'; +import { QuestionCircleFilled, BellFilled } from '@ant-design/icons-vue'; +import { ExclamationCircleOutlined, CrownFilled, LogoutOutlined, DownCircleOutlined, DeleteOutlined } from '@ant-design/icons-vue'; +import moment from 'moment' export default { components: { @@ -106,11 +129,14 @@ export default { CrownOutlined, MenuUnfoldOutlined, MenuFoldOutlined, + WechatFilled, QuestionCircleFilled, - SmileFilled, BellFilled, ExclamationCircleOutlined, CrownFilled, + LogoutOutlined, + DownCircleOutlined, + DeleteOutlined }, setup() { // 菜单选项 @@ -160,6 +186,11 @@ export default { const selectedKeys = ref([store.selectedKeys]) const collapsed = ref(false) + const data = reactive({ + noticeCount: 0, + noticeList: [] + }) + store.$subscribe((mutation, state) => { selectedKeys.value = [state.selectedKeys] }) @@ -175,9 +206,7 @@ export default { }) const visible = ref(false) - const popoverVisible = ref(false) const visibleLogo = ref(false) - const delUserVisible = ref(false) const loading = ref(false) const disabled = ref(false) const buttonText = ref('获取验证码') @@ -186,6 +215,7 @@ export default { onBeforeMount(() => { store.selectedKeys = 'dashboard' router.push('dashboard') + noticeCount() }) // 点击用户头像 @@ -196,7 +226,6 @@ export default { user.email = res.data.data.email user.version = res.data.data.version } - popoverVisible = true }) } @@ -223,12 +252,6 @@ export default { }) } - // 点击注销账号 - const onDelete = () => { - popoverVisible.value = false - delUserVisible.value = true - } - // 点击确认注销账号 const onConfirm = () => { let param = { @@ -243,6 +266,60 @@ export default { }) } + // 日期格式化 + const formatDate = (timeStamp) => { + let now = (Date.parse(new Date())) / 1000 + if (now - timeStamp < 60) { + return "刚刚" + } + if ((new Date().getDate()) == (new Date(timeStamp * 1000).getDate())) { + return "今天 " + moment(timeStamp * 1000).format('HH:mm') + } + return moment(timeStamp * 1000).format('YYYY-MM-DD') + } + + // 点击读取通知 + const onReadNotice = (id) => { + updateNotice({ id: id }).then((res) => { + if (res.data.code == 0) { + onNotice() + noticeCount() + } + }) + } + + // 获取通知数量 + const noticeCount = () => { + getNoticeCount().then((res) => { + if (res.data.code == 0) { + data.noticeCount = res.data.data.count + } + }) + } + + // 获取通知列表 + const onNotice = () => { + getNoticeList().then((res) => { + if (res.data.code == 0) { + data.noticeList = res.data.data + } + }) + } + + // 删除通知 + const onDeleteNotice = () => { + let ids = [] + for (let index = 0; index < data.noticeList.length; index++) { + ids[index] = data.noticeList[index].id + } + deleteNotice({ ids: ids }).then((res) => { + if (res.data.code == 0) { + data.noticeList = res.data.data + onNotice() + } + }) + } + // 点击退出账号 const onLogout = () => { localStorage.removeItem("uid") @@ -255,7 +332,6 @@ export default { disabled.value = false modalFormRef.value.resetFields() visible.value = false - delUserVisible.value = false }; return { @@ -266,18 +342,21 @@ export default { user, visible, visibleLogo, - popoverVisible, - delUserVisible, loading, disabled, buttonText, onUserAvatar, - onDelete, onLogout, onGetCode, onConfirm, + onNotice, + onReadNotice, + noticeCount, + onDeleteNotice, onCancel, store, + formatDate, + data }; }, } @@ -320,7 +399,7 @@ export default { color: #f56a00; background-color: #fde3cf; cursor: pointer; - margin-left: 10px; + margin-left: 20px; } .popover {