Эх сурвалжийг харах

Merge branch 'feature/snapshot' into dev

qinchaoyue 4 өдөр өмнө
parent
commit
3b7ae2f620
27 өөрчлөгдсөн 546 нэмэгдсэн , 44 устгасан
  1. 3 39
      src/Hotline.Api/Controllers/Snapshot/SnapshotBulletinController.cs
  2. 10 0
      src/Hotline.Api/Controllers/Snapshot/SnapshotController.cs
  3. 11 0
      src/Hotline.Application/Snapshot/Contracts/INotificationApplication.cs
  4. 2 0
      src/Hotline.Application/Snapshot/Contracts/ISnapshotApplication.cs
  5. 25 1
      src/Hotline.Application/Snapshot/Contracts/ISnapshotBulletinApplication.cs
  6. 0 0
      src/Hotline.Application/Snapshot/DefaultSnapshotApplication.cs
  7. 12 0
      src/Hotline.Application/Snapshot/NotificationApplication.cs
  8. 15 1
      src/Hotline.Application/Snapshot/Notifications/SnapshotHandler.cs
  9. 17 1
      src/Hotline.Application/Snapshot/SnapshotApplicationBase.cs
  10. 83 2
      src/Hotline.Application/Snapshot/SnapshotBulletinApplication.cs
  11. 0 0
      src/Hotline.Application/Snapshot/ZiGongSnapshotApplication.cs
  12. 19 0
      src/Hotline.Repository.SqlSugar/Snapshot/NotificationReceiverRepository.cs
  13. 19 0
      src/Hotline.Repository.SqlSugar/Snapshot/NotificationRepository.cs
  14. 62 0
      src/Hotline.Share/Dtos/Snapshot/NotifyDto.cs
  15. 5 0
      src/Hotline.Share/Dtos/Snapshot/SnapshotBulletinDto.cs
  16. 17 0
      src/Hotline.Share/Enums/Snapshot/ENotificationType.cs
  17. 5 0
      src/Hotline.Share/Mq/EventNames.Snapshot.cs
  18. 5 0
      src/Hotline.Share/Tools/DateTimeExtensions.cs
  19. 19 0
      src/Hotline/Snapshot/Contracts/INotificationDomainService.cs
  20. 0 0
      src/Hotline/Snapshot/IRepository/IIndustryLogRepository.cs
  21. 12 0
      src/Hotline/Snapshot/IRepository/INotificationReceiverRepository.cs
  22. 12 0
      src/Hotline/Snapshot/IRepository/INotificationRepository.cs
  23. 27 0
      src/Hotline/Snapshot/Notification.cs
  24. 27 0
      src/Hotline/Snapshot/NotificationReceiver.cs
  25. 40 0
      src/Hotline/Snapshot/Services/NotificationDomainService.cs
  26. 19 0
      src/Hotline/Snapshot/SnapshotBulletin.cs
  27. 80 0
      test/Hotline.Tests/Application/SnapshotBulletionApplicationTest.cs

+ 3 - 39
src/Hotline.Api/Controllers/Snapshot/SnapshotBulletinController.cs

@@ -25,6 +25,7 @@ public class SnapshotBulletinController : BaseController
     private readonly ISessionContext _sessionContext;
     private readonly ISystemDicDataCacheManager _systemDicDataCacheManager;
     private readonly ISystemOrganizeRepository _organizeRepository;
+    private readonly INotificationApplication _notificationApplication;
 
     public SnapshotBulletinController(ISnapshotBulletinRepository bulletinRepository, ISessionContext sessionContext, ISystemDicDataCacheManager systemDicDataCacheManager, ISystemOrganizeRepository organizeRepository, ISnapshotBulletinApplication bulletinApplication)
     {
@@ -91,7 +92,6 @@ public class SnapshotBulletinController : BaseController
         await _bulletinRepository.UpdateAsync(bulletin, HttpContext.RequestAborted);
     }
 
-
     /// <summary>
     /// 公告审核
     /// </summary>
@@ -99,37 +99,7 @@ public class SnapshotBulletinController : BaseController
     /// <returns></returns>
     [HttpPost("bulletin/examine")]
     public async Task ExamineBulletin([FromBody] ExamineBulletinDto dto)
-    {
-        var bulletin = await _bulletinRepository.GetAsync(dto.Id, HttpContext.RequestAborted);
-        if (bulletin == null)
-            throw UserFriendlyException.SameMessage("无效数据");
-
-        if (bulletin.BulletinState != EBulletinState.InReview)
-            throw UserFriendlyException.SameMessage("当前状态不能审核");
-
-        if (dto.IsPass)
-        {
-            bulletin.BulletinState = Share.Enums.Article.EBulletinState.ReviewPass;
-            bulletin.BulletinTime = DateTime.Now;
-            bulletin.ExaminOpinion = dto.Reason;
-            bulletin.ExaminTime = DateTime.Now;
-            bulletin.ExaminManId = _sessionContext.RequiredUserId;
-            await _bulletinRepository.UpdateAsync(bulletin, HttpContext.RequestAborted);
-            var publishBulletin = bulletin.Adapt<PublishBulletinDto>();
-
-            //await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlinePushBulletin, publishBulletin, cancellationToken: HttpContext.RequestAborted);
-
-            //todo await _bulletinService.PushBulletin(publishBulletin, HttpContext.RequestAborted);
-        }
-        else
-        {
-            bulletin.ExaminOpinion = dto.Reason;
-            bulletin.ExaminTime = DateTime.Now;
-            bulletin.ExaminManId = _sessionContext.RequiredUserId;
-            bulletin.BulletinState = Share.Enums.Article.EBulletinState.ReviewNoPass;
-            await _bulletinRepository.UpdateAsync(bulletin, HttpContext.RequestAborted);
-        }
-    }
+        => await _bulletinApplication.ExamineBulletinAsync(dto, HttpContext.RequestAborted);
 
     /// <summary>
     /// 提交公告
@@ -195,13 +165,7 @@ public class SnapshotBulletinController : BaseController
     /// <returns></returns>
     [HttpPost("bulletin/add")]
     public async Task AddBulletin([FromBody] AddSnapshotBulletinInDto dto)
-    {
-        var model = dto.Adapt<SnapshotBulletin>();
-        model.BulletinState = Share.Enums.Article.EBulletinState.Draft;
-        model.ReadedNum = 0;
-        if (model.BulletinTime.HasValue == false) model.BulletinTime = DateTime.Now;
-        await _bulletinRepository.AddAsync(model, HttpContext.RequestAborted);
-    }
+        => await _bulletinApplication.AddBulletinAsync(dto, HttpContext.RequestAborted);
 
     /// <summary>
     /// 列表页面基础数据

+ 10 - 0
src/Hotline.Api/Controllers/Snapshot/SnapshotController.cs

@@ -373,6 +373,16 @@ public class SnapshotController : BaseController
             await _snapshotApplication.PostOrderGuiderSystemAsync(orderId, HttpContext.RequestAborted);
         }
     }
+
+    /// <summary>
+    /// 获取消息
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <returns></returns>
+    [HttpGet("notification")]
+    public async Task<IList<GetNotifyOutDto>> GetNotificationAsync([FromQuery] GetNotifyInDto dto)
+        => await _snapshotApplication.GetNotificationAsync(dto, HttpContext.RequestAborted);
+
     #region 积分
     /// <summary>
     /// 积分榜

+ 11 - 0
src/Hotline.Application/Snapshot/Contracts/INotificationApplication.cs

@@ -0,0 +1,11 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hotline.Application.Snapshot.Contracts;
+
+public interface INotificationApplication
+{
+}

+ 2 - 0
src/Hotline.Application/Snapshot/Contracts/ISnapshotApplication.cs

@@ -233,4 +233,6 @@ public interface ISnapshotApplication
     /// <param name="requestAborted"></param>
     /// <returns></returns>
     Task<int> GetPointsTotalAsync(PointItemsInDto dto, CancellationToken requestAborted);
+
+    Task<IList<GetNotifyOutDto>> GetNotificationAsync(GetNotifyInDto dto, CancellationToken requestAborted);
 }

+ 25 - 1
src/Hotline.Application/Snapshot/Contracts/ISnapshotBulletinApplication.cs

@@ -1,4 +1,5 @@
-using Hotline.Share.Dtos.Snapshot;
+using Hotline.Share.Dtos.Article;
+using Hotline.Share.Dtos.Snapshot;
 using SqlSugar;
 using System;
 using System.Collections.Generic;
@@ -9,6 +10,29 @@ using System.Threading.Tasks;
 namespace Hotline.Application.Snapshot.Contracts;
 public interface ISnapshotBulletinApplication
 {
+    /// <summary>
+    /// 新增公告
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <param name="requestAborted"></param>
+    /// <returns></returns>
+    Task<string> AddBulletinAsync(AddSnapshotBulletinInDto dto, CancellationToken requestAborted);
+
+    /// <summary>
+    /// 审核公告
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <param name="requestAborted"></param>
+    /// <returns></returns>
+    Task ExamineBulletinAsync(ExamineBulletinDto dto, CancellationToken requestAborted);
+
+    /// <summary>
+    /// 发送通知公告
+    /// </summary>
+    /// <param name="bullutionId"></param>
+    /// <param name="token"></param>
+    /// <returns></returns>
+    Task NotifyUserAsync(string bullutionId, CancellationToken token);
 
     /// <summary>
     /// 处理通知公告图片附件路径

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
src/Hotline.Application/Snapshot/DefaultSnapshotApplication.cs


+ 12 - 0
src/Hotline.Application/Snapshot/NotificationApplication.cs

@@ -0,0 +1,12 @@
+using Hotline.Application.Snapshot.Contracts;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hotline.Application.Snapshot;
+
+public class NotificationApplication : INotificationApplication
+{
+}

+ 15 - 1
src/Hotline.Application/Snapshot/Notifications/SnapshotHandler.cs

@@ -38,10 +38,12 @@ namespace Hotline.Application.Snapshot.Notifications;
 public class SnapshotHandler : ICapSubscribe, IScopeDependency
 {
     private readonly ISnapshotApplication _snapshotApplication;
+    private readonly ISnapshotBulletinApplication _snapshotBulletinApplication;
 
-    public SnapshotHandler(ISnapshotApplication snapshotApplication)
+    public SnapshotHandler(ISnapshotApplication snapshotApplication, ISnapshotBulletinApplication snapshotBulletinApplication)
     {
         _snapshotApplication = snapshotApplication;
+        _snapshotBulletinApplication = snapshotBulletinApplication;
     }
 
     /// <summary>
@@ -55,6 +57,18 @@ public class SnapshotHandler : ICapSubscribe, IScopeDependency
     {
         await _snapshotApplication.GuiderSystemReplyDelayAsync(message.OrderId, cancellationToken);
     }
+
+    /// <summary>
+    /// 通知公告审核通过
+    /// </summary>
+    /// <param name="bulletinId"></param>
+    /// <param name="token"></param>
+    /// <returns></returns>
+    [CapSubscribe(EventNames.BulletinIsPass, Group = "snapshot")]
+    public async Task BulletinIsPassHandler(string bulletinId, CancellationToken token)
+    {
+        await _snapshotBulletinApplication.NotifyUserAsync(bulletinId, token);
+    }
 }
 
 /// <summary>

+ 17 - 1
src/Hotline.Application/Snapshot/SnapshotApplicationBase.cs

@@ -87,8 +87,9 @@ public abstract class SnapshotApplicationBase
     private readonly ICitizenRepository _citizenRepository;
     private readonly ISnapshotPointsRecordRepository _pointsRecordRepository;
     private readonly IOptionsSnapshot<MqConfiguration> _mqConfiguration;
+    private readonly INotificationReceiverRepository _notificationReceiverRepository;
 
-    public SnapshotApplicationBase(IThirdIdentiyService thirdLoginService, IIndustryRepository industryRepository, ISnapshotBulletinRepository bulletinRepository, ISessionContext sessionContext, IRedPackRecordRepository redPackRecordRepository, IRepository<Order> orderRepository, IThirdAccountRepository thirdAccountRepository, IOrderSnapshotRepository orderSnapshotRepository, ISystemSettingCacheManager systemSettingCacheManager, ISystemAreaDomainService systemAreaDomainService, IFileRepository fileRepository, ISystemDicDataCacheManager systemDicDataCacheManager, ISnapshotOrderPublishRepository snapshotOrderPublishRepository, IRepository<WorkflowTrace> workflowTraceRepository, IPractitionerRepository practitionerRepository, IRepository<SystemArea> systemAreaRepository, IVolunteerRepository volunteerRepository, IVolunteerReportRepository volunteerReportRepository, ISystemLogRepository systemLog, IGuiderSystemService guiderSystemService, ICapPublisher capPublisher, Publisher publisher, IFileDomainService fileDomainService, ICommunityInfoRepository communityInfoRepository, IRedPackAuditRepository redPackAuditRepository, IOrderVisitRepository orderVisitRepository, IOrderVisitDetailRepository orderVisitDetailRepository, IRedPackGuiderAuditRepository redPackGuiderAuditRepository, IInviteCodeRecordRepository inviteCodeRecordRepository, IInviteCodeRepository inviteCodeRepository, ICitizenRepository citizenRepository, ISnapshotPointsRecordRepository snapshotPointsRecordRepository, IOptionsSnapshot<MqConfiguration> mqConfiguration)
+    public SnapshotApplicationBase(IThirdIdentiyService thirdLoginService, IIndustryRepository industryRepository, ISnapshotBulletinRepository bulletinRepository, ISessionContext sessionContext, IRedPackRecordRepository redPackRecordRepository, IRepository<Order> orderRepository, IThirdAccountRepository thirdAccountRepository, IOrderSnapshotRepository orderSnapshotRepository, ISystemSettingCacheManager systemSettingCacheManager, ISystemAreaDomainService systemAreaDomainService, IFileRepository fileRepository, ISystemDicDataCacheManager systemDicDataCacheManager, ISnapshotOrderPublishRepository snapshotOrderPublishRepository, IRepository<WorkflowTrace> workflowTraceRepository, IPractitionerRepository practitionerRepository, IRepository<SystemArea> systemAreaRepository, IVolunteerRepository volunteerRepository, IVolunteerReportRepository volunteerReportRepository, ISystemLogRepository systemLog, IGuiderSystemService guiderSystemService, ICapPublisher capPublisher, Publisher publisher, IFileDomainService fileDomainService, ICommunityInfoRepository communityInfoRepository, IRedPackAuditRepository redPackAuditRepository, IOrderVisitRepository orderVisitRepository, IOrderVisitDetailRepository orderVisitDetailRepository, IRedPackGuiderAuditRepository redPackGuiderAuditRepository, IInviteCodeRecordRepository inviteCodeRecordRepository, IInviteCodeRepository inviteCodeRepository, ICitizenRepository citizenRepository, ISnapshotPointsRecordRepository snapshotPointsRecordRepository, IOptionsSnapshot<MqConfiguration> mqConfiguration, INotificationReceiverRepository notificationReceiverRepository)
     {
         _thirdLoginService = thirdLoginService;
         _industryRepository = industryRepository;
@@ -123,6 +124,7 @@ public abstract class SnapshotApplicationBase
         _citizenRepository = citizenRepository;
         _pointsRecordRepository = snapshotPointsRecordRepository;
         _mqConfiguration = mqConfiguration;
+        _notificationReceiverRepository = notificationReceiverRepository;
     }
 
     #region 小程序
@@ -392,6 +394,20 @@ public abstract class SnapshotApplicationBase
             .SumAsync(m => m.Points);
     }
 
+    public async Task<IList<GetNotifyOutDto>> GetNotificationAsync(GetNotifyInDto dto, CancellationToken requestAborted)
+    {
+        var items = await _notificationReceiverRepository.Queryable()
+            .LeftJoin<Notification>((m , notify) => m.NotificationId == notify.Id)
+            .Where(m => m.ReceiverId == _sessionContext.UserId)
+            .Select((m, notify) => new GetNotifyOutDto 
+            {
+                NotificationId = m.NotificationId,
+                Title = notify.Title ,
+                CreationTime = m.CreationTime
+            }, true)
+            .ToFixedListAsync(dto, requestAborted);
+        return items;
+    }
 
     /// <summary>
     /// 获取工单详情

+ 83 - 2
src/Hotline.Application/Snapshot/SnapshotBulletinApplication.cs

@@ -1,9 +1,16 @@
-using Hotline.Application.Snapshot.Contracts;
+using DotNetCore.CAP;
+using Hotline.Application.Snapshot.Contracts;
 using Hotline.Caching.Interfaces;
 using Hotline.Settings;
 using Hotline.Share.Attributes;
+using Hotline.Share.Dtos.Article;
 using Hotline.Share.Dtos.Snapshot;
+using Hotline.Share.Enums.Article;
+using Hotline.Share.Tools;
+using Hotline.Snapshot;
+using Hotline.Snapshot.Contracts;
 using Hotline.Snapshot.IRepository;
+using Mapster;
 using Microsoft.AspNetCore.Http;
 using SqlSugar;
 using System;
@@ -12,7 +19,9 @@ using System.Linq;
 using System.Text;
 using System.Text.RegularExpressions;
 using System.Threading.Tasks;
+using XF.Domain.Authentications;
 using XF.Domain.Dependency;
+using XF.Domain.Exceptions;
 
 namespace Hotline.Application.Snapshot;
 public class SnapshotBulletinApplication : ISnapshotBulletinApplication, IScopeDependency
@@ -20,11 +29,74 @@ public class SnapshotBulletinApplication : ISnapshotBulletinApplication, IScopeD
 
     private readonly ISystemSettingCacheManager _systemSettingCacheManager;
     private readonly ISnapshotBulletinRepository _bulletinRepository;
+    private readonly INotificationDomainService _notificationDomainService;
+    private readonly ISessionContext _sessionContext;
+    private readonly ISafetyTypeRepository _safetyTypeRepository;
+    private readonly ICapPublisher _capPublisher;
 
-    public SnapshotBulletinApplication(ISystemSettingCacheManager systemSettingCacheManager, ISnapshotBulletinRepository bulletinRepository)
+    public SnapshotBulletinApplication(ISystemSettingCacheManager systemSettingCacheManager, ISnapshotBulletinRepository bulletinRepository, INotificationDomainService notificationDomainService, ISessionContext sessionContext, ISafetyTypeRepository safetyTypeRepository, ICapPublisher capPublisher)
     {
         _systemSettingCacheManager = systemSettingCacheManager;
         _bulletinRepository = bulletinRepository;
+        _notificationDomainService = notificationDomainService;
+        _sessionContext = sessionContext;
+        _safetyTypeRepository = safetyTypeRepository;
+        _capPublisher = capPublisher;
+    }
+
+    public async Task ExamineBulletinAsync(ExamineBulletinDto dto, CancellationToken token)
+    {
+        var bulletin = await _bulletinRepository.GetAsync(dto.Id, token)
+            ?? throw UserFriendlyException.SameMessage("无效数据");
+        if (bulletin.BulletinState != EBulletinState.InReview)
+            throw UserFriendlyException.SameMessage("当前状态不能审核");
+
+        if (dto.IsPass == false)
+        {
+            bulletin.ExaminOpinion = dto.Reason;
+            bulletin.ExaminTime = DateTime.Now;
+            bulletin.ExaminManId = _sessionContext.RequiredUserId;
+            bulletin.BulletinState = EBulletinState.ReviewNoPass;
+            await _bulletinRepository.UpdateAsync(bulletin, token);
+            return;
+        }
+
+        bulletin.BulletinState = EBulletinState.ReviewPass;
+        bulletin.BulletinTime = DateTime.Now;
+        bulletin.ExaminOpinion = dto.Reason;
+        bulletin.ExaminTime = DateTime.Now;
+        bulletin.ExaminManId = _sessionContext.RequiredUserId;
+        await _bulletinRepository.UpdateAsync(bulletin, token);
+
+        var citizens = await _safetyTypeRepository.Queryable()
+            .Includes(m => m.Citizens.Select(c => c.Id))
+            .Where(m => m.Id == bulletin.SafetyTypeId)
+            .ToListAsync(token);
+
+        await _capPublisher.PublishAsync(Share.Mq.EventNames.BulletinIsPass, bulletin.Id, cancellationToken: token);
+
+    }
+
+    /// <summary>
+    /// 发送通知公告
+    /// </summary>
+    /// <param name="bullutionId"></param>
+    /// <param name="token"></param>
+    /// <returns></returns>
+    public async Task NotifyUserAsync(string bullutionId, CancellationToken token)
+    {
+        await _bulletinRepository.GetAsync(bullutionId, token)
+            .Then(async bulletion => { 
+                await _safetyTypeRepository.Queryable().Where(m => m.Id == bulletion.SafetyTypeId)
+                .Includes(m => m.Citizens.Select(c => c.Id))
+                .FirstAsync(token)
+                .Then(async safetyType => 
+                {
+                    var inDto = bulletion.Adapt<AddNotifyInDto>();
+                    inDto.UserIds = [.. safetyType.Citizens.Select(m => m.Id)];
+                    await _notificationDomainService.AddNotifyAsync(inDto, token);
+                });
+            });
     }
 
     /// <summary>
@@ -88,4 +160,13 @@ public class SnapshotBulletinApplication : ISnapshotBulletinApplication, IScopeD
             .Select<SnapshotBulletinItemsOutDto>();
         return query;
     }
+
+    public async Task<string> AddBulletinAsync(AddSnapshotBulletinInDto dto, CancellationToken token)
+    {
+        var model = dto.Adapt<SnapshotBulletin>();
+        model.BulletinState = Share.Enums.Article.EBulletinState.Draft;
+        model.ReadedNum = 0;
+        if (model.BulletinTime.HasValue == false) model.BulletinTime = DateTime.Now;
+        return await _bulletinRepository.AddAsync(model, token);
+    }
 }

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
src/Hotline.Application/Snapshot/ZiGongSnapshotApplication.cs


+ 19 - 0
src/Hotline.Repository.SqlSugar/Snapshot/NotificationReceiverRepository.cs

@@ -0,0 +1,19 @@
+using Hotline.Repository.SqlSugar.DataPermissions;
+using Hotline.Snapshot;
+using Hotline.Snapshot.IRepository;
+using SqlSugar;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using XF.Domain.Dependency;
+
+namespace Hotline.Repository.SqlSugar.Snapshot;
+
+public class NotificationReceiverRepository : BaseRepository<NotificationReceiver>, INotificationReceiverRepository, IScopeDependency
+{
+    public NotificationReceiverRepository(ISugarUnitOfWork<HotlineDbContext> uow, IDataPermissionFilterBuilder dataPermissionFilterBuilder, IServiceProvider serviceProvider) : base(uow, dataPermissionFilterBuilder, serviceProvider)
+    {
+    }
+}

+ 19 - 0
src/Hotline.Repository.SqlSugar/Snapshot/NotificationRepository.cs

@@ -0,0 +1,19 @@
+using Hotline.Repository.SqlSugar.DataPermissions;
+using Hotline.Snapshot;
+using Hotline.Snapshot.IRepository;
+using SqlSugar;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using XF.Domain.Dependency;
+
+namespace Hotline.Repository.SqlSugar.Snapshot;
+
+public class NotificationRepository : BaseRepository<Notification>, INotificationRepository, IScopeDependency
+{
+    public NotificationRepository(ISugarUnitOfWork<HotlineDbContext> uow, IDataPermissionFilterBuilder dataPermissionFilterBuilder, IServiceProvider serviceProvider) : base(uow, dataPermissionFilterBuilder, serviceProvider)
+    {
+    }
+}

+ 62 - 0
src/Hotline.Share/Dtos/Snapshot/NotifyDto.cs

@@ -0,0 +1,62 @@
+using Hotline.Share.Enums.Snapshot;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hotline.Share.Dtos.Snapshot;
+
+public class NotifyDto
+{
+}
+
+public class AddNotifyInDto
+{
+
+    /// <summary>
+    /// 标题
+    /// </summary>
+    public string Title { get; set; }
+
+    /// <summary>
+    /// 内容
+    /// </summary>
+    public string Content { get; set; }
+
+    /// <summary>
+    /// 类型
+    /// </summary>
+    public ENotificationType NotifyType { get; set; }
+
+    /// <summary>
+    /// 用户
+    /// </summary>
+    public IList<string> UserIds { get; set; }
+}
+
+public class GetNotifyInDto : QueryFixedDto
+{
+    /// <summary>
+    /// 消息类型
+    /// </summary>
+    public ENotificationType NotifyType { get; set; }
+}
+
+public class GetNotifyOutDto
+{
+    /// <summary>
+    /// 通知Id
+    /// </summary>
+    public string NotificationId { get; set; }
+
+    /// <summary>
+    /// 标题
+    /// </summary>
+    public string Title { get; set; }
+
+    /// <summary>
+    /// 创建时间
+    /// </summary>
+    public DateTime CreationTime { get; set; }
+}

+ 5 - 0
src/Hotline.Share/Dtos/Snapshot/SnapshotBulletinDto.cs

@@ -279,4 +279,9 @@ public class AddSnapshotBulletinInDto
     /// 视频地址
     /// </summary>
     public string? VideoPath { get; set; }
+
+    /// <summary>
+    /// 视频封面
+    /// </summary>
+    public string? VideoCoverImgUrl { get; set; }
 }

+ 17 - 0
src/Hotline.Share/Enums/Snapshot/ENotificationType.cs

@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hotline.Share.Enums.Snapshot;
+
+public enum ENotificationType
+{
+    [Description("消息")]
+    Message = 0,
+
+    [Description("视频")]
+    Video = 1
+}

+ 5 - 0
src/Hotline.Share/Mq/EventNames.Snapshot.cs

@@ -11,4 +11,9 @@ public partial class  EventNames
     /// 延迟检查网格员系统是否回复
     /// </summary>
     public const string GuiderSystemReplyDelay = "snapshot.guider.reply.delay";
+
+    /// <summary>
+    /// 通知公告审核通过
+    /// </summary>
+    public const string BulletinIsPass = "snapshot.bulletin.pass";
 }

+ 5 - 0
src/Hotline.Share/Tools/DateTimeExtensions.cs

@@ -35,6 +35,11 @@ public static class DateTimeExtensions
         return dateTime.ToString("yyyy-MM-dd HH:mm:ss");
     }
 
+    public static string ToShortDateTimeString(this DateTime dateTime)
+    {
+        return dateTime.ToString("yyyy-MM-dd");
+    }
+
     public static long ToUnixTimeMilliseconds(this DateTime value)
     {
         return new DateTimeOffset(value).ToUnixTimeMilliseconds();

+ 19 - 0
src/Hotline/Snapshot/Contracts/INotificationDomainService.cs

@@ -0,0 +1,19 @@
+using Hotline.Share.Dtos.Snapshot;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hotline.Snapshot.Contracts;
+
+public interface INotificationDomainService
+{
+    /// <summary>
+    /// 添加通知
+    /// </summary>
+    /// <param name="inDto"></param>
+    /// <param name="token"></param>
+    /// <returns></returns>
+    Task AddNotifyAsync(AddNotifyInDto inDto, CancellationToken token);
+}

+ 0 - 0
src/Hotline/Snapshot/IRepository/IndustryLogRepository.cs → src/Hotline/Snapshot/IRepository/IIndustryLogRepository.cs


+ 12 - 0
src/Hotline/Snapshot/IRepository/INotificationReceiverRepository.cs

@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using XF.Domain.Repository;
+
+namespace Hotline.Snapshot.IRepository;
+
+public interface INotificationReceiverRepository : IRepository<NotificationReceiver>
+{
+}

+ 12 - 0
src/Hotline/Snapshot/IRepository/INotificationRepository.cs

@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using XF.Domain.Repository;
+
+namespace Hotline.Snapshot.IRepository;
+
+public interface INotificationRepository : IRepository<Notification>
+{
+}

+ 27 - 0
src/Hotline/Snapshot/Notification.cs

@@ -0,0 +1,27 @@
+using Hotline.Share.Enums.Snapshot;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using XF.Domain.Repository;
+
+namespace Hotline.Snapshot;
+
+public class Notification : CreationSoftDeleteEntity
+{
+    /// <summary>
+    /// 标题
+    /// </summary>
+    public string Title { get; set; }
+
+    /// <summary>
+    /// 内容
+    /// </summary>
+    public string Content { get; set; }
+
+    /// <summary>
+    /// 类型
+    /// </summary>
+    public ENotificationType NotifyType { get; set; }
+}

+ 27 - 0
src/Hotline/Snapshot/NotificationReceiver.cs

@@ -0,0 +1,27 @@
+using SqlSugar;
+using System.ComponentModel;
+using XF.Domain.Repository;
+
+namespace Hotline.Snapshot;
+
+[Description("通知关联消息")]
+public class NotificationReceiver : CreationSoftDeleteEntity
+{
+    /// <summary>
+    /// 通知ID
+    /// </summary>
+    [SugarColumn(ColumnDescription = "通知Id")]
+    public string NotificationId { get; set; }
+
+    /// <summary>
+    /// 接收人ID
+    /// </summary>
+    [SugarColumn(ColumnDescription = "接收人ID")]
+    public string ReceiverId { get; set; }
+
+    /// <summary>
+    /// 是否已读
+    /// </summary>
+    [SugarColumn(ColumnDescription = "是否已读")]
+    public bool IsRead { get; set; }
+}

+ 40 - 0
src/Hotline/Snapshot/Services/NotificationDomainService.cs

@@ -0,0 +1,40 @@
+using Hotline.Share.Dtos.Snapshot;
+using Hotline.Snapshot.Contracts;
+using Hotline.Snapshot.IRepository;
+using Mapster;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using XF.Domain.Dependency;
+
+namespace Hotline.Snapshot.Services;
+
+public class NotificationDomainService : INotificationDomainService, IScopeDependency
+{
+    private readonly INotificationRepository _notificationRepository;
+    private readonly INotificationReceiverRepository _notificationReceiverRepository;
+
+    public NotificationDomainService(INotificationRepository notificationRepository, INotificationReceiverRepository notificationReceiverRepository)
+    {
+        _notificationRepository = notificationRepository;
+        _notificationReceiverRepository = notificationReceiverRepository;
+    }
+
+    public async Task AddNotifyAsync(AddNotifyInDto inDto, CancellationToken token)
+    {
+        var entity = inDto.Adapt<Notification>();
+        var id = await _notificationRepository.AddAsync(entity, token);
+        var items = new List<NotificationReceiver>();
+        foreach (var userId in inDto.UserIds)
+        {
+            items.Add(new NotificationReceiver { 
+                NotificationId = id,
+                ReceiverId = userId,
+                IsRead = false
+            });
+        }
+        await _notificationReceiverRepository.AddRangeAsync(items, token);
+    }
+}

+ 19 - 0
src/Hotline/Snapshot/SnapshotBulletin.cs

@@ -175,5 +175,24 @@ public class SnapshotBulletin : CreationEntity
     {
         ReadedNum++;
     }
+
+    /// <summary>
+    /// 是否随手拍
+    /// </summary>
+    [SugarColumn(ColumnDescription = "是否随手拍")]
+    public bool? IsSnapshot { get; set; }
+
+    /// <summary>
+    /// 志愿者类型Id
+    /// </summary>
+    [SugarColumn(ColumnDescription = "志愿者类型Id")]
+    public string? SafetyTypeId { get; set; }
+
+    /// <summary>
+    /// 视频封面
+    /// </summary>
+    [SugarColumn(ColumnDescription = "视频封面")]
+    public string? VideoCoverImgUrl { get; set; }
+
 }
 

+ 80 - 0
test/Hotline.Tests/Application/SnapshotBulletionApplicationTest.cs

@@ -0,0 +1,80 @@
+using Hotline.Api.Controllers;
+using Hotline.Application.Snapshot.Contracts;
+using Hotline.Caching.Interfaces;
+using Hotline.Identity.Accounts;
+using Hotline.Identity.Roles;
+using Hotline.Repository.SqlSugar.Snapshot;
+using Hotline.Settings;
+using Hotline.Share.Dtos.Article;
+using Hotline.Share.Dtos.Snapshot;
+using Hotline.Share.Tools;
+using Hotline.Snapshot.IRepository;
+using Hotline.ThirdAccountDomainServices;
+using Hotline.ThirdAccountDomainServices.Interfaces;
+using Hotline.Users;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.DependencyInjection;
+using Shouldly;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using XF.Domain.Authentications;
+using XF.Domain.Cache;
+using XF.Domain.Repository;
+
+namespace Hotline.Tests.Application;
+
+public class SnapshotBulletionApplicationTest : TestBase
+{
+    private readonly ISnapshotBulletinApplication _snapshotBulletinApplication;
+    private readonly ISystemDicDataCacheManager _systemDicDataCacheManager;
+    private readonly ISafetyTypeRepository _safetyTypeRepository;
+    private readonly ISnapshotUserApplication _snapshotUserApplication;
+    private readonly ISessionContext _sessionContext;
+    private readonly ISnapshotApplication _snapshotApplication;
+
+    public SnapshotBulletionApplicationTest(IAccountRepository accountRepository, IRepository<Role> roleRepository, UserController userController, IServiceScopeFactory scopeFactory, IRepository<User> userRepository, IHttpContextAccessor httpContextAccessor, IThirdIdentiyService thirdIdentiyService, IThirdAccountRepository thirdAccountRepository, ITypedCache<SystemSetting> cacheSettingData, ThirdAccounSupplierFactory thirdAccountDomainFactory, IServiceProvider serviceProvider, ISnapshotBulletinApplication snapshotBulletinApplication, ISystemDicDataCacheManager systemDicDataCacheManager, ISafetyTypeRepository safetyTypeRepository, ISnapshotUserApplication snapshotUserApplication, ISessionContext sessionContext, ISnapshotApplication snapshotApplication) : base(accountRepository, roleRepository, userController, scopeFactory, userRepository, httpContextAccessor, thirdIdentiyService, thirdAccountRepository, cacheSettingData, thirdAccountDomainFactory, serviceProvider)
+    {
+        _snapshotBulletinApplication = snapshotBulletinApplication;
+        _systemDicDataCacheManager = systemDicDataCacheManager;
+        _safetyTypeRepository = safetyTypeRepository;
+        _snapshotUserApplication = snapshotUserApplication;
+        _sessionContext = sessionContext;
+        _snapshotApplication = snapshotApplication;
+    }
+
+    [Fact]
+    public async Task Bulletin_Test()
+    {
+        SetWeiXin();
+        var bulletinType =  _systemDicDataCacheManager.SnapshotBulletinType.First();
+        var safetyType = await _safetyTypeRepository.Queryable().FirstAsync();
+        var addRelationInDto = new AddCitizenRelationSafetyTypeInDto { SafetyTypeId = safetyType.Id, CitizenIds = [_sessionContext.UserId] };
+        await _snapshotUserApplication.AddCitizenRelationSafetyType(addRelationInDto, CancellationToken.None);
+        var inDto = new AddSnapshotBulletinInDto
+        {
+            Title = "测试公告" + DateTime.Now.ToShortDateTimeString(),
+            Content = "没什么内容",
+            BulletinTime = DateTime.Now,
+            IsSnapshot = true,
+            BulletinTypeId = bulletinType.DicDataValue,
+            BulletinTypeName = bulletinType.DicDataName,
+            SafetyTypeId = safetyType.Id,
+        };
+        var bulletinId = await _snapshotBulletinApplication.AddBulletinAsync(inDto, CancellationToken.None);
+        var examineDto = new ExamineBulletinDto
+        {
+            Id = bulletinId,
+            IsPass = true,
+            Reason = "审核通过"
+        };
+        await _snapshotBulletinApplication.ExamineBulletinAsync(examineDto, CancellationToken.None);
+        await _snapshotBulletinApplication.NotifyUserAsync(bulletinId, CancellationToken.None);
+
+
+        var notifyItems = await _snapshotApplication.GetNotificationAsync(new GetNotifyInDto(), CancellationToken.None);
+        notifyItems.Any(m => m.Title == inDto.Title).ShouldBeTrue();
+    }
+}

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно