feat: 新增消息通知模块

This commit is contained in:
橙子
2024-05-23 23:40:55 +08:00
parent 695989969d
commit ef220a5b36
20 changed files with 528 additions and 85 deletions

View File

@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Yi.Framework.Ddd.Application.Contracts;
namespace Yi.Framework.Bbs.Application.Contracts.Dtos.Notice
{
public class BbsNoticeGetListInputVo:PagedAllResultRequestDto
{
}
}

View File

@@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Yi.Framework.Bbs.Domain.Shared.Enums;
namespace Yi.Framework.Bbs.Application.Contracts.Dtos.Notice
{
public class BbsNoticeGetListOutputDto
{
/// <summary>
/// 消息,支持html
/// </summary>
public string Message { get; set; }
/// <summary>
/// 消息类型
/// </summary>
public NoticeTypeEnum NoticeType { get; }
/// <summary>
/// 是否已读
/// </summary>
public bool IsRead { get; private set; }
/// <summary>
/// 已读时间
/// </summary>
public DateTime? ReadTime { get; private set; }
/// <summary>
/// 消息创建时间
/// </summary>
public DateTime CreationTime { get; }
}
}

View File

@@ -0,0 +1,72 @@
using Mapster;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using SqlSugar;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Yi.Framework.Bbs.Application.Contracts.Dtos.Notice;
using Yi.Framework.Bbs.Domain.Entities;
using Yi.Framework.SqlSugarCore.Abstractions;
namespace Yi.Framework.Bbs.Application.Services
{
public class BbsNoticeService : ApplicationService
{
private ISqlSugarRepository<BbsNoticeAggregateRoot, Guid> _repository;
public BbsNoticeService(ISqlSugarRepository<BbsNoticeAggregateRoot, Guid> repository)
{
_repository = repository;
}
/// <summary>
/// 查询用户的消息,需登录
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
[Authorize]
public async Task<PagedResultDto<BbsNoticeGetListOutputDto>> GetListAsync(BbsNoticeGetListInputVo input)
{
RefAsync<int> total = 0;
var entities = await _repository._DbQueryable.Where(x => x.AcceptUserId == CurrentUser.Id)
.OrderByDescending(x => x.CreationTime)
.ToPageListAsync(input.SkipCount, input.MaxResultCount, total);
var output = entities.Adapt<List<BbsNoticeGetListOutputDto>>();
return new PagedResultDto<BbsNoticeGetListOutputDto>(total, output);
}
/// <summary>
/// 已读消息,不传guid代表一键已读需登录
/// </summary>
/// <returns></returns>
[Authorize]
[Route("bbs-notice/read/{noticeId?}")]
public async Task PutReadAsync(Guid? noticeId)
{
//一键已读
if (noticeId is null)
{
await _repository._Db.Updateable<BbsNoticeAggregateRoot>()
.SetColumns(it => it.IsRead == true)
.Where(x => x.AcceptUserId == CurrentUser.Id)
.Where(x => x.IsRead == false)
.ExecuteCommandAsync();
}
//已读一条
else
{
await _repository._Db.Updateable<BbsNoticeAggregateRoot>()
.SetColumns(it => it.IsRead == true)
.Where(x => x.AcceptUserId == CurrentUser.Id)
.Where(x => x.IsRead == false)
.Where(x => x.Id == noticeId)
.ExecuteCommandAsync();
}
}
}
}

View File

@@ -29,7 +29,6 @@ namespace Yi.Framework.Bbs.Application.Services.Forum
/// Todo: 可放入领域层
/// </summary>
/// <returns></returns>
[UnitOfWork]
[Authorize]
public async Task<AgreeDto> PostOperateAsync(Guid discussId)
{
@@ -53,9 +52,8 @@ namespace Yi.Framework.Bbs.Application.Services.Forum
}
else
{
//点赞过,删除即可,修改总点赞数量
await _repository.DeleteByIdAsync(entity.Id);
await _repository.DeleteAsync(entity);
var discussEntity = await _discssRepository.GetByIdAsync(discussId);
if (discussEntity is null)
{

View File

@@ -15,5 +15,9 @@ namespace Yi.Framework.Bbs.Domain.Shared.Consts
public const string No_Exist = "传入的主题id不存在";
public const string Privacy = "【私密】您无该主题权限,可联系作者申请开放";
public const string AgreeNotice = "您的主题[{0}]被[{1}]用户点赞!得到一致认可,发现宝藏内容!";
public const string CommentNotice = "您的主题[{0}]被[{1}]用户评论!评论内容:[{2}]。。。";
}
}

View File

@@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Yi.Framework.Bbs.Domain.Shared.Model
{
/// <summary>
/// 消息通知存储用户信息
/// </summary>
public class HubUserModel
{
public HubUserModel(string connnectionId,Guid userId)
{
ConnnectionId = connnectionId;
UserId = userId;
}
/// <summary>
/// 客户端连接Id
/// </summary>
public string? ConnnectionId { get; }
/// <summary>
/// 用户id
/// </summary>
public Guid? UserId { get; set; }
}
}

View File

@@ -8,6 +8,10 @@ namespace Yi.Framework.Bbs.Domain.Entities
[SugarTable("BbsNotice")]
public class BbsNoticeAggregateRoot : AggregateRoot<Guid>, IHasCreationTime
{
public BbsNoticeAggregateRoot()
{
}
public BbsNoticeAggregateRoot(NoticeTypeEnum noticeType, string message, Guid? acceptUserId = null)
{
this.NoticeType = noticeType;
@@ -47,7 +51,7 @@ namespace Yi.Framework.Bbs.Domain.Entities
/// </summary>
public DateTime? ReadTime { get; private set; }
public DateTime CreationTime { get; }
public DateTime CreationTime { get; set; }
}

View File

@@ -1,8 +1,12 @@
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Entities.Events;
using Volo.Abp.EventBus;
using Volo.Abp.EventBus.Local;
using Yi.Framework.Bbs.Domain.Entities;
using Yi.Framework.Bbs.Domain.Entities.Forum;
using Yi.Framework.Bbs.Domain.Shared.Consts;
using Yi.Framework.Bbs.Domain.Shared.Etos;
using Yi.Framework.Rbac.Domain.Entities;
using Yi.Framework.SqlSugarCore.Abstractions;
namespace Yi.Framework.Bbs.Domain.EventHandlers
@@ -13,49 +17,70 @@ namespace Yi.Framework.Bbs.Domain.EventHandlers
public class AgreeCreatedEventHandler : ILocalEventHandler<EntityCreatedEventData<AgreeEntity>>,
ITransientDependency
{
private ISqlSugarRepository<BbsUserExtraInfoEntity> _userRepository;
private ISqlSugarRepository<UserAggregateRoot> _userRepository;
private ISqlSugarRepository<BbsUserExtraInfoEntity> _userInfoRepository;
private ISqlSugarRepository<AgreeEntity> _agreeRepository;
public AgreeCreatedEventHandler(ISqlSugarRepository<BbsUserExtraInfoEntity> userRepository, ISqlSugarRepository<AgreeEntity> agreeRepository)
private ILocalEventBus _localEventBus;
public AgreeCreatedEventHandler(ISqlSugarRepository<BbsUserExtraInfoEntity> userInfoRepository, ISqlSugarRepository<AgreeEntity> agreeRepository, ILocalEventBus localEventBus, ISqlSugarRepository<UserAggregateRoot> userRepository)
{
_userRepository = userRepository;
_userInfoRepository = userInfoRepository;
_agreeRepository = agreeRepository;
_localEventBus = localEventBus;
_userRepository = userRepository;
}
public async Task HandleEventAsync(EntityCreatedEventData<AgreeEntity> eventData)
{
var agreeEntity = eventData.Entity;
var userId = await _agreeRepository._DbQueryable.LeftJoin<DiscussAggregateRoot>((agree, discuss) => agree.DiscussId == discuss.Id)
.Select((agree, discuss) => discuss.CreatorId).FirstAsync();
//给创建者发布数量+1
await _userRepository._Db.Updateable<BbsUserExtraInfoEntity>()
//查询主题的信息
var discussAndAgreeDto = await _agreeRepository._DbQueryable
.LeftJoin<DiscussAggregateRoot>((agree, discuss) => agree.DiscussId == discuss.Id)
.Select((agree, discuss) =>
new
{
DiscussTitle = discuss.Title,
DiscussCreatorId = discuss.CreatorId,
})
.FirstAsync();
//查询点赞者用户
var agreeUser = await _userRepository.GetFirstAsync(x => x.Id == agreeEntity.CreatorId);
//给创建者点赞数量+1
await _userInfoRepository._Db.Updateable<BbsUserExtraInfoEntity>()
.SetColumns(it => it.AgreeNumber == it.AgreeNumber + 1)
.Where(it => it.UserId == userId)
.Where(it => it.UserId == discussAndAgreeDto.DiscussCreatorId)
.ExecuteCommandAsync();
//通知主题作者,有人点赞
await _localEventBus.PublishAsync(new BbsNoticeEventArgs(discussAndAgreeDto.DiscussCreatorId!.Value, string.Format(DiscussConst.AgreeNotice, discussAndAgreeDto.DiscussTitle, agreeUser.UserName)), false);
}
}
/// <summary>
/// 取消点赞
/// </summary>
public class AgreeDeletedEventHandler : ILocalEventHandler<EntityCreatedEventData<AgreeEntity>>,
public class AgreeDeletedEventHandler : ILocalEventHandler<EntityDeletedEventData<AgreeEntity>>,
ITransientDependency
{
private ISqlSugarRepository<BbsUserExtraInfoEntity> _userRepository;
private ISqlSugarRepository<AgreeEntity> _agreeRepository;
public AgreeDeletedEventHandler(ISqlSugarRepository<BbsUserExtraInfoEntity> userRepository, ISqlSugarRepository<AgreeEntity> agreeRepository)
private ILocalEventBus _localEventBus;
public AgreeDeletedEventHandler(ISqlSugarRepository<BbsUserExtraInfoEntity> userRepository, ISqlSugarRepository<AgreeEntity> agreeRepository, ILocalEventBus localEventBus)
{
_userRepository = userRepository;
_agreeRepository = agreeRepository;
_localEventBus = localEventBus;
}
public async Task HandleEventAsync(EntityCreatedEventData<AgreeEntity> eventData)
public async Task HandleEventAsync(EntityDeletedEventData<AgreeEntity> eventData)
{
var agreeEntity = eventData.Entity;
var userId = await _agreeRepository._DbQueryable.LeftJoin<DiscussAggregateRoot>((agree, discuss) => agree.DiscussId == discuss.Id)
.Select((agree, discuss) => discuss.CreatorId).FirstAsync();
//给创建者发布数量-1
//给创建者点赞数量-1
await _userRepository._Db.Updateable<BbsUserExtraInfoEntity>()
.SetColumns(it => it.DiscussNumber == it.DiscussNumber - 1)
.SetColumns(it => it.AgreeNumber == it.AgreeNumber - 1)
.Where(it => it.UserId == userId)
.ExecuteCommandAsync();
}

View File

@@ -1,16 +0,0 @@
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Entities.Events;
using Volo.Abp.EventBus;
using Yi.Framework.Bbs.Domain.Entities;
namespace Yi.Framework.Bbs.Domain.EventHandlers
{
public class BbsNoticeCreatedEventHandler : ILocalEventHandler<EntityCreatedEventData<BbsNoticeAggregateRoot>>,
ITransientDependency
{
public Task HandleEventAsync(EntityCreatedEventData<BbsNoticeAggregateRoot> eventData)
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,47 @@
using Microsoft.AspNetCore.SignalR;
using SqlSugar;
using Volo.Abp.DependencyInjection;
using Volo.Abp.EventBus;
using Yi.Framework.Bbs.Domain.Entities;
using Yi.Framework.Bbs.Domain.Shared.Enums;
using Yi.Framework.Bbs.Domain.Shared.Etos;
using Yi.Framework.Bbs.Domain.SignalRHubs;
using Yi.Framework.SqlSugarCore.Abstractions;
namespace Yi.Framework.Bbs.Domain.EventHandlers
{
/// <summary>
/// bbs消息推送处理
/// </summary>
public class BbsNoticeSendEventHandler : ILocalEventHandler<BbsNoticeEventArgs>,
ITransientDependency
{
private IHubContext<BbsNoticeHub> _hubContext;
private ISqlSugarRepository<BbsNoticeAggregateRoot, Guid> _repository;
public BbsNoticeSendEventHandler(IHubContext<BbsNoticeHub> hubContext, ISqlSugarRepository<BbsNoticeAggregateRoot, Guid> sugarRepository)
{
_hubContext = hubContext;
_repository = sugarRepository;
}
public async Task HandleEventAsync(BbsNoticeEventArgs eventData)
{
//离线存储
await _repository.InsertAsync(new BbsNoticeAggregateRoot(eventData.NoticeType, eventData.Message, eventData.AcceptUserId));
switch (eventData.NoticeType)
{
case Shared.Enums.NoticeTypeEnum.Personal:
if (BbsNoticeHub.HubUserModels.TryGetValue(eventData.AcceptUserId.ToString(), out var hubUserModel))
{
_hubContext.Clients.Client(hubUserModel.ConnnectionId).SendAsync(NoticeTypeEnum.Personal.ToString(), eventData.Message);
}
break;
case Shared.Enums.NoticeTypeEnum.Broadcast:
_hubContext.Clients.All.SendAsync(NoticeTypeEnum.Broadcast.ToString(), eventData.Message);
break;
default:
break;
}
}
}
}

View File

@@ -1,8 +1,13 @@
using Volo.Abp.DependencyInjection;
using TencentCloud.Tbm.V20180129.Models;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Entities.Events;
using Volo.Abp.EventBus;
using Volo.Abp.EventBus.Local;
using Yi.Framework.Bbs.Domain.Entities;
using Yi.Framework.Bbs.Domain.Entities.Forum;
using Yi.Framework.Bbs.Domain.Shared.Consts;
using Yi.Framework.Bbs.Domain.Shared.Etos;
using Yi.Framework.Rbac.Domain.Entities;
using Yi.Framework.SqlSugarCore.Abstractions;
namespace Yi.Framework.Bbs.Domain.EventHandlers
@@ -13,10 +18,14 @@ namespace Yi.Framework.Bbs.Domain.EventHandlers
public class CommentCreatedEventHandler : ILocalEventHandler<EntityCreatedEventData<CommentAggregateRoot>>,
ITransientDependency
{
private ISqlSugarRepository<BbsUserExtraInfoEntity> _userRepository;
public CommentCreatedEventHandler(ISqlSugarRepository<BbsUserExtraInfoEntity> userRepository)
private ILocalEventBus _localEventBus;
private ISqlSugarRepository<DiscussAggregateRoot> _discussRepository;
private ISqlSugarRepository<UserAggregateRoot> _userRepository;
public CommentCreatedEventHandler(ILocalEventBus localEventBus, ISqlSugarRepository<DiscussAggregateRoot> discussRepository, ISqlSugarRepository<UserAggregateRoot> userRepository)
{
_userRepository = userRepository;
_localEventBus = localEventBus;
_discussRepository = discussRepository;
}
public async Task HandleEventAsync(EntityCreatedEventData<CommentAggregateRoot> eventData)
{
@@ -27,6 +36,24 @@ namespace Yi.Framework.Bbs.Domain.EventHandlers
.SetColumns(it => it.CommentNumber == it.CommentNumber + 1)
.Where(it => it.UserId == commentEntity.CreatorId)
.ExecuteCommandAsync();
var disucssDto = await _discussRepository._DbQueryable
.Where(x => x.Id == commentEntity.DiscussId)
.LeftJoin<UserAggregateRoot>((dicuss, user) => dicuss.CreatorId == user.Id)
.Select((dicuss, user) =>
new
{
DiscussId = user.Id,
DiscussTitle = dicuss.Title,
})
.FirstAsync();
var commentUser = await _userRepository.GetFirstAsync(x => x.Id == commentEntity.CreatorId);
//截取10个长度
var content = commentEntity.Content.Length >= 10 ? commentEntity.Content.Substring(0, 10) : commentEntity.Content;
//通知主题作者,有人评论
await _localEventBus.PublishAsync(new BbsNoticeEventArgs(disucssDto.DiscussId, string.Format(DiscussConst.CommentNotice, disucssDto.DiscussTitle, commentUser.UserName, content)), false);
}
}
}

View File

@@ -0,0 +1,44 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Volo.Abp.AspNetCore.SignalR;
using Yi.Framework.Bbs.Domain.Shared.Model;
namespace Yi.Framework.Bbs.Domain.SignalRHubs
{
[HubRoute("/hub/bbs-notice")]
[Authorize]
public class BbsNoticeHub : AbpHub
{
/// <summary>
/// hub连接与用户id的映射关系存储
/// </summary>
public static ConcurrentDictionary<string, HubUserModel> HubUserModels = new ConcurrentDictionary<string, HubUserModel>();
/// <summary>
/// 连接添加用户信息
/// </summary>
/// <returns></returns>
public override Task OnConnectedAsync()
{
var hubUser = new HubUserModel(Context.ConnectionId, CurrentUser.Id!.Value);
HubUserModels.TryAdd(CurrentUser.Id.Value.ToString(), hubUser);
return Task.CompletedTask;
}
/// <summary>
/// 断开连接,去除用户连接信息
/// </summary>
/// <param name="exception"></param>
/// <returns></returns>
public override Task OnDisconnectedAsync(Exception exception)
{
HubUserModels.TryRemove(CurrentUser.Id.Value.ToString(), out _);
return Task.CompletedTask;
}
}
}