feat: 新增消息通知模块
This commit is contained in:
@@ -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
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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}]。。。";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user