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

Merge branch 'test' of http://110.188.24.182:10023/Fengwo/hotline into test

tangjiang 3 өдөр өмнө
parent
commit
afb4b6a368
49 өөрчлөгдсөн 910 нэмэгдсэн , 76 устгасан
  1. 9 0
      src/Hotline.Api/Controllers/Snapshot/BiSnapshotController.cs
  2. 11 54
      src/Hotline.Api/Controllers/Snapshot/SnapshotBulletinController.cs
  3. 10 0
      src/Hotline.Api/Controllers/Snapshot/SnapshotController.cs
  4. 9 0
      src/Hotline.Api/Controllers/Snapshot/SnapshotUserController.cs
  5. 4 2
      src/Hotline.Api/Controllers/SysController.cs
  6. 2 2
      src/Hotline.Api/config/appsettings.Development.json
  7. 12 0
      src/Hotline.Application/Exam/Service/Questions/QuestionService.cs
  8. 24 2
      src/Hotline.Application/Snapshot/BiSnapshotApplication.cs
  9. 7 0
      src/Hotline.Application/Snapshot/Contracts/IBiSnapshotApplication.cs
  10. 11 0
      src/Hotline.Application/Snapshot/Contracts/INotificationApplication.cs
  11. 2 0
      src/Hotline.Application/Snapshot/Contracts/ISnapshotApplication.cs
  12. 26 1
      src/Hotline.Application/Snapshot/Contracts/ISnapshotBulletinApplication.cs
  13. 7 0
      src/Hotline.Application/Snapshot/Contracts/ISnapshotUserApplication.cs
  14. 0 0
      src/Hotline.Application/Snapshot/DefaultSnapshotApplication.cs
  15. 12 0
      src/Hotline.Application/Snapshot/NotificationApplication.cs
  16. 15 1
      src/Hotline.Application/Snapshot/Notifications/SnapshotHandler.cs
  17. 17 1
      src/Hotline.Application/Snapshot/SnapshotApplicationBase.cs
  18. 109 2
      src/Hotline.Application/Snapshot/SnapshotBulletinApplication.cs
  19. 32 1
      src/Hotline.Application/Snapshot/SnapshotPointsApplication.cs
  20. 17 0
      src/Hotline.Application/Snapshot/SnapshotUserApplication.cs
  21. 0 0
      src/Hotline.Application/Snapshot/ZiGongSnapshotApplication.cs
  22. 19 0
      src/Hotline.Repository.SqlSugar/Snapshot/NotificationReceiverRepository.cs
  23. 19 0
      src/Hotline.Repository.SqlSugar/Snapshot/NotificationRepository.cs
  24. 11 1
      src/Hotline.Share/Dtos/Settings/SystemCommonOpinionDto.cs
  25. 62 0
      src/Hotline.Share/Dtos/Snapshot/NotifyDto.cs
  26. 9 0
      src/Hotline.Share/Dtos/Snapshot/PointsDto.cs
  27. 6 1
      src/Hotline.Share/Dtos/Snapshot/SnapshotBulletinDto.cs
  28. 36 0
      src/Hotline.Share/Dtos/Snapshot/SnapshotUserInfoDto.cs
  29. 28 0
      src/Hotline.Share/Dtos/Snapshot/StatisticsDto.cs
  30. 17 0
      src/Hotline.Share/Enums/Snapshot/ENotificationType.cs
  31. 5 0
      src/Hotline.Share/Mq/EventNames.Snapshot.cs
  32. 5 0
      src/Hotline.Share/Tools/DateTimeExtensions.cs
  33. 19 0
      src/Hotline/Snapshot/Contracts/INotificationDomainService.cs
  34. 0 0
      src/Hotline/Snapshot/IRepository/IIndustryLogRepository.cs
  35. 12 0
      src/Hotline/Snapshot/IRepository/INotificationReceiverRepository.cs
  36. 12 0
      src/Hotline/Snapshot/IRepository/INotificationRepository.cs
  37. 27 0
      src/Hotline/Snapshot/Notification.cs
  38. 27 0
      src/Hotline/Snapshot/NotificationReceiver.cs
  39. 40 0
      src/Hotline/Snapshot/Services/NotificationDomainService.cs
  40. 14 3
      src/Hotline/Snapshot/Services/SnapshotPointsDomainService.cs
  41. 19 0
      src/Hotline/Snapshot/SnapshotBulletin.cs
  42. 0 1
      src/Hotline/Validators/Exams/Questions/UpdateQuestionTagDtoValidator.cs
  43. 9 0
      test/Hotline.Tests/Application/BiSnapshotApplicationTest.cs
  44. 35 3
      test/Hotline.Tests/Application/OrderSnapshotApplicationTest.cs
  45. 36 1
      test/Hotline.Tests/Application/PointsRecordApplicationTest.cs
  46. 91 0
      test/Hotline.Tests/Application/SnapshotBulletionApplicationTest.cs
  47. 8 0
      test/Hotline.Tests/Application/SnapshotUserApplicationTest.cs
  48. 2 0
      test/Hotline.Tests/Mock/Interfaces/IOrderServiceStartWorkflow.cs
  49. 6 0
      test/Hotline.Tests/Mock/OrderServiceStartWorkflow.cs

+ 9 - 0
src/Hotline.Api/Controllers/Snapshot/BiSnapshotController.cs

@@ -327,4 +327,13 @@ public class BiSnapshotController : BaseController
     [HttpGet("re_transact-statistics-detail")]
     public async Task<PagedDto<ReTransactStatisticsDetailsOutDto>> GetReTransactStatisticsDetailAsync([FromQuery] ReTransactStatisticsDetailsInDto dto)
         => (await _biSnapshotApplication.GetReTransactStatisticsDetail(dto).ToPagedListAsync(dto)).ToPaged();
+
+    /// <summary>
+    /// 随手拍区域积分统计
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <returns></returns>
+    [HttpGet("county_points")]
+    public async Task<IList<SnapshotCountyPointsStatisticsOutDto>> Get([FromQuery] SnapshotCountyPointsStatisticsInDto dto)
+        => await _biSnapshotApplication.GetAreaPointsStatistics(dto).ToListAsync(HttpContext.RequestAborted);
 }

+ 11 - 54
src/Hotline.Api/Controllers/Snapshot/SnapshotBulletinController.cs

@@ -15,6 +15,7 @@ using Hotline.Share.Tools;
 using XF.Utility.EnumExtensions;
 using Hotline.Snapshot.IRepository;
 using Hotline.Application.Snapshot.Contracts;
+using Hotline.Share.Dtos.Settings;
 
 namespace Hotline.Api.Controllers.Snapshot;
 
@@ -22,17 +23,17 @@ public class SnapshotBulletinController : BaseController
 {
     private readonly ISnapshotBulletinRepository _bulletinRepository;
     private readonly ISnapshotBulletinApplication _bulletinApplication;
-    private readonly ISessionContext _sessionContext;
     private readonly ISystemDicDataCacheManager _systemDicDataCacheManager;
     private readonly ISystemOrganizeRepository _organizeRepository;
+    private readonly ISafetyTypeRepository _safetyTypeRepository;
 
-    public SnapshotBulletinController(ISnapshotBulletinRepository bulletinRepository, ISessionContext sessionContext, ISystemDicDataCacheManager systemDicDataCacheManager, ISystemOrganizeRepository organizeRepository, ISnapshotBulletinApplication bulletinApplication)
+    public SnapshotBulletinController(ISnapshotBulletinRepository bulletinRepository, ISystemDicDataCacheManager systemDicDataCacheManager, ISystemOrganizeRepository organizeRepository, ISnapshotBulletinApplication bulletinApplication, ISafetyTypeRepository safetyTypeRepository)
     {
         _bulletinRepository = bulletinRepository;
-        _sessionContext = sessionContext;
         _systemDicDataCacheManager = systemDicDataCacheManager;
         _organizeRepository = organizeRepository;
         _bulletinApplication = bulletinApplication;
+        _safetyTypeRepository = safetyTypeRepository;
     }
 
     #region 公告
@@ -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>
     /// 提交公告
@@ -138,18 +108,7 @@ public class SnapshotBulletinController : BaseController
     /// <returns></returns>
     [HttpGet("bulletin/commit")]
     public async Task CommitBulletin(string id)
-    {
-        var bulletin = await _bulletinRepository.GetAsync(id, HttpContext.RequestAborted);
-        if (bulletin == null)
-            throw UserFriendlyException.SameMessage("无效数据");
-
-        if (bulletin.BulletinState != EBulletinState.Draft && bulletin.BulletinState != EBulletinState.ReviewNoPass)
-            throw UserFriendlyException.SameMessage("当前状态不能提交");
-
-        bulletin.BulletinState = EBulletinState.InReview;
-        bulletin.CommitTime = DateTime.Now;
-        await _bulletinRepository.UpdateAsync(bulletin, HttpContext.RequestAborted);
-    }
+        => await _bulletinApplication.CommitBulletinAsync(id, HttpContext.RequestAborted);
 
     /// <summary>
     /// 修改公告
@@ -195,13 +154,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>
     /// 列表页面基础数据
@@ -226,11 +179,15 @@ public class SnapshotBulletinController : BaseController
     [HttpGet("bulletin/addbasedata")]
     public async Task<object> BulletinAddBaseData()
     {
+        var safetyTypes = await _safetyTypeRepository.Queryable()
+            .Select(m => new SystemDicDataOutDto { Id =  m.Id, DicDataName = m.Name , DicDataValue = m.Id})
+            .ToListAsync(HttpContext.RequestAborted);
         var rsp = new
         {
             BulletinType = _systemDicDataCacheManager.SnapshotBulletinType,
             BulletinSource = _systemDicDataCacheManager.SnapshotBulletinSource,
             OrgsOptions = await _organizeRepository.GetOrgJson(),
+            SafetyTypes = safetyTypes
         };
         return rsp;
     }

+ 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>
     /// 积分榜

+ 9 - 0
src/Hotline.Api/Controllers/Snapshot/SnapshotUserController.cs

@@ -62,4 +62,13 @@ public class SnapshotUserController : BaseController
     [HttpDelete("citizen_relation")]
     public async Task DeleteCitizenRelationSafetyType([FromBody] DeleteCitizenRelationSafetyTypeInDto dto)
         => await _snapshotUserApplication.DeleteCitizenRelationSafetyAsync(dto);
+
+    /// <summary>
+    /// 搜索安全志愿者
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <returns></returns>
+    [HttpGet("citizen")]
+    public async Task<PagedDto<GetThirdCitizenOutDto>> GetThirdCitizenAsync([FromQuery] GetThirdCitizenInDto dto)
+        => (await _snapshotUserApplication.GetThirdCitizenAsync(dto).ToPagedListAsync(dto)).ToPaged();
 }

+ 4 - 2
src/Hotline.Api/Controllers/SysController.cs

@@ -38,6 +38,7 @@ using XF.Domain.Repository;
 using XF.Utility.EnumExtensions;
 using Hotline.Tools;
 using Hotline.Share.Dtos.TrCallCenter;
+using Hotline.Share.Dtos.WebPortal;
 
 namespace Hotline.Api.Controllers
 {
@@ -600,14 +601,15 @@ namespace Hotline.Api.Controllers
         /// </summary>
         /// <returns></returns>
         [HttpGet("open-common")]
-        public async Task<PagedDto<SystemCommonOpinion>> QueryOpenCommonOpinionList([FromQuery] QueryCommonDto dto)
+        public async Task<PagedDto<SystemCommonOpinionDto>> QueryOpenCommonOpinionList([FromQuery] QueryCommonDto dto)
         {
             var (total, items) = await _commonOpinionRepository.Queryable()
                 .WhereIF(dto.IsOpen != null, x => x.IsOpen == dto.IsOpen)
                 .WhereIF(!string.IsNullOrEmpty(dto.UserName), x => x.CreatorName.Contains(dto.UserName))
                 .WhereIF(dto.CommonType != null, x => x.CommonType == dto.CommonType)
                 .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
-            return new PagedDto<SystemCommonOpinion>(total, items);
+
+            return new PagedDto<SystemCommonOpinionDto>(total, _mapper.Map<IReadOnlyList<SystemCommonOpinionDto>>(items));
         }
 
         /// <summary>export

+ 2 - 2
src/Hotline.Api/config/appsettings.Development.json

@@ -68,13 +68,13 @@
     }
   },
     "ConnectionStrings": {
-        "Hotline": "PORT=5432;DATABASE=hotline;HOST=110.188.24.182;PASSWORD=fengwo11!!;USER ID=dev;"
+        "Hotline": "PORT=5432;DATABASE=hotline_dev;HOST=110.188.24.182;PASSWORD=fengwo11!!;USER ID=dev;"
     },
   "Cache": {
     "Host": "110.188.24.182",
     "Port": 50179,
     "Password": "fengwo123!$!$",
-    "Database": 3 //hl:3, dev:5, test:2, demo:4
+    "Database": 5 //hl:3, dev:5, test:2, demo:4
   },
   "Swagger": true,
   "AccLog":  false,

+ 12 - 0
src/Hotline.Application/Exam/Service/Questions/QuestionService.cs

@@ -753,6 +753,10 @@ namespace Hotline.Application.Exam.Service.Questions
 
             actionRequest.QuestionSourcewareDtos.ResolveOperationStatus(all);
 
+            _addQuestionDto.QuestionSourcewareDtos = _mapper.Map<List<UpdateQuestionSourcewareDto>, List<AddQuestionSourcewareDto>>(actionRequest.QuestionSourcewareDtos);
+
+            _addQuestionDto.QuestionSourcewareDtos.ResolveOperationStatus();
+
             questionSourcewares.AddRangeExt(await AddSourcewares(_addQuestionDto, cancellationToken));
 
             questionSourcewares.AddRangeExt(await UpdateSourcewares(actionRequest, all, cancellationToken));
@@ -776,6 +780,10 @@ namespace Hotline.Application.Exam.Service.Questions
 
             actionRequest.QuestionKnowladgeDtos.ResolveOperationStatus(all);
 
+            _addQuestionDto.QuestionKnowladgeDtos = _mapper.Map<List<UpdateQuestionKnowladgeDto>, List<AddQuestionKnowladgeDto>>(actionRequest.QuestionKnowladgeDtos);
+
+            _addQuestionDto.QuestionKnowladgeDtos.ResolveOperationStatus();
+
             questionKnowladges.AddRangeExt(await AddKnowladges(_addQuestionDto, cancellationToken));
 
             questionKnowladges.AddRangeExt(await UpdateKnowladges(actionRequest, all, cancellationToken));
@@ -860,6 +868,8 @@ namespace Hotline.Application.Exam.Service.Questions
 
             actionRequest.QuestionOptionsDtos.ResolveOperationStatus(all);
 
+            _addQuestionDto.QuestionOptionsDtos = _mapper.Map<List<UpdateQuestionOptionsDto>, List<AddQuestionOptionsDto>>(actionRequest.QuestionOptionsDtos);
+
             _addQuestionDto.QuestionOptionsDtos.ResolveOperationStatus();
 
             questionOptions.AddRangeExt(await AddQuestionOptions(_addQuestionDto, cancellationToken));
@@ -885,6 +895,8 @@ namespace Hotline.Application.Exam.Service.Questions
 
             actionRequest.QuestionTagDtos.ResolveOperationStatus(all);
 
+            _addQuestionDto.QuestionTagDtos = _mapper.Map<List<UpdateQuestionTagDto>, List<AddQuestionTagDto>>(actionRequest.QuestionTagDtos);
+
             questionTags.AddRangeExt(await AddQuestionTags(_addQuestionDto, cancellationToken));
 
             questionTags.AddRangeExt(await UpdateQuestionTags(actionRequest, all, cancellationToken));

+ 24 - 2
src/Hotline.Application/Snapshot/BiSnapshotApplication.cs

@@ -5,6 +5,7 @@ using Hotline.Settings;
 using Hotline.Settings.Hotspots;
 using Hotline.Share.Attributes;
 using Hotline.Share.Dtos.Snapshot;
+using Hotline.Share.Enums.CallCenter;
 using Hotline.Share.Enums.FlowEngine;
 using Hotline.Share.Enums.Order;
 using Hotline.Share.Enums.Snapshot;
@@ -36,8 +37,9 @@ public class BiSnapshotApplication : IBiSnapshotApplication, IScopeDependency
     private readonly ICommunityInfoRepository _communityInfoRepository;
     private readonly IRepository<SystemArea> _systemAreaRepository;
     private readonly IOptionsSnapshot<AppConfiguration> _appOptions;
+    private readonly ISnapshotPointsRecordRepository _snapshotPointsRecordRepository;
 
-    public BiSnapshotApplication(IOrderSnapshotRepository orderSnapshotRepository, IRedPackRecordRepository redPackRecordRepository, IIndustryRepository industryRepository, IIndustryCaseRepository industryCaseRepository, IRedPackAuditRepository redPackAuditRepository, IRepository<Hotspot> hotspotTypeRepository, ISessionContext sessionContext, IOrderRepository orderRepository, ICommunityInfoRepository communityInfoRepository, IRepository<SystemArea> systemAreaRepository, IOptionsSnapshot<AppConfiguration> appOptions)
+    public BiSnapshotApplication(IOrderSnapshotRepository orderSnapshotRepository, IRedPackRecordRepository redPackRecordRepository, IIndustryRepository industryRepository, IIndustryCaseRepository industryCaseRepository, IRedPackAuditRepository redPackAuditRepository, IRepository<Hotspot> hotspotTypeRepository, ISessionContext sessionContext, IOrderRepository orderRepository, ICommunityInfoRepository communityInfoRepository, IRepository<SystemArea> systemAreaRepository, IOptionsSnapshot<AppConfiguration> appOptions, ISnapshotPointsRecordRepository snapshotPointsRecordRepository)
     {
         _orderSnapshotRepository = orderSnapshotRepository;
         _redPackRecordRepository = redPackRecordRepository;
@@ -50,6 +52,7 @@ public class BiSnapshotApplication : IBiSnapshotApplication, IScopeDependency
         _communityInfoRepository = communityInfoRepository;
         _systemAreaRepository = systemAreaRepository;
         _appOptions = appOptions;
+        _snapshotPointsRecordRepository = snapshotPointsRecordRepository;
     }
 
     public ISugarQueryable<HotspotStatisticsOutDto> GetHotspotStatistics(HotspotStatisticsInDto dto)
@@ -734,7 +737,7 @@ public class BiSnapshotApplication : IBiSnapshotApplication, IScopeDependency
             .LeftJoin<OrderSendBackAudit>((snapshot, order, back) => snapshot.Id == back.OrderId && back.State == ESendBackAuditState.End)
             .LeftJoin<OrderVisit>((snapshot, order, back, visit) => snapshot.Id == visit.OrderId && visit.VisitState == EVisitState.Visited)
             .LeftJoin<OrderSecondaryHandling>((snapshot, order, back, visit, second) => snapshot.Id == second.OrderId && second.State == ESecondaryHandlingState.End)
-            .Where((snapshot, order) => snapshot.CreationTime >= dto.StartTime && snapshot.CreationTime <= dto.EndTime 
+            .Where((snapshot, order) => snapshot.CreationTime >= dto.StartTime && snapshot.CreationTime <= dto.EndTime
             && order.ActualHandleOrgCode != null && order.ActualHandleOrgCode != "001")
             .GroupBy((snapshot, order) => new
             {
@@ -972,4 +975,23 @@ public class BiSnapshotApplication : IBiSnapshotApplication, IScopeDependency
             .Select((snapshot, order) => new IndustryStatisticsDetailsOutDto(), true);
         return query;
     }
+
+    public ISugarQueryable<SnapshotCountyPointsStatisticsOutDto> GetAreaPointsStatistics(SnapshotCountyPointsStatisticsInDto dto)
+    {
+        var query = _snapshotPointsRecordRepository.Queryable(includeDeleted: true)
+            .LeftJoin<Order>((points, order) => points.OrderId == order.Id)
+            .Where((points, order) => order.CreationTime >= dto.StartTime && order.CreationTime <= dto.EndTime && order.County != "" && order.County != null)
+            .GroupBy((points, order) => order.County)
+            .Select((points, order) => new SnapshotCountyPointsStatisticsOutDto
+            {
+                CountyName = order.County,
+                Points = SqlFunc.AggregateSum(SqlFunc.IIF(points.Direction == EPointsDirection.In, points.Points, 0)),
+                ExchangePoints = SqlFunc.AggregateSum(SqlFunc.IIF(points.Source == EPointsSource.Exchange, points.Points, 0)),
+            });
+#if DEBUG
+        var sql = query.ToSqlString();
+#endif
+        return query;
+    }
+
 }

+ 7 - 0
src/Hotline.Application/Snapshot/Contracts/IBiSnapshotApplication.cs

@@ -111,4 +111,11 @@ public interface IBiSnapshotApplication
     /// <param name="dto"></param>
     /// <returns></returns>
     ISugarQueryable<IndustryStatisticsDetailsOutDto> GetIndustryStatisticsDetails(IndustryStatisticsDetailsInDto dto);
+
+    /// <summary>
+    /// 区域积分统计
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <returns></returns>
+    ISugarQueryable<SnapshotCountyPointsStatisticsOutDto> GetAreaPointsStatistics(SnapshotCountyPointsStatisticsInDto dto);
 }

+ 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);
 }

+ 26 - 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>
     /// 处理通知公告图片附件路径
@@ -17,4 +41,5 @@ public interface ISnapshotBulletinApplication
     /// <returns></returns>
     string GetSiteUrls(string sHtmlText);
     ISugarQueryable<SnapshotBulletinItemsOutDto> QueryBulletinItems(SnapshotBulletinItemsInDto dto);
+    Task CommitBulletinAsync(string id, CancellationToken requestAborted);
 }

+ 7 - 0
src/Hotline.Application/Snapshot/Contracts/ISnapshotUserApplication.cs

@@ -21,4 +21,11 @@ public interface ISnapshotUserApplication
     /// <param name="dto"></param>
     /// <returns></returns>
     Task DeleteCitizenRelationSafetyAsync(DeleteCitizenRelationSafetyTypeInDto dto);
+
+    /// <summary>
+    /// 获取第三方市民列表
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <returns></returns>
+    ISugarQueryable<GetThirdCitizenOutDto> GetThirdCitizenAsync(GetThirdCitizenInDto dto);
 }

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 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>
     /// 获取工单详情

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

@@ -1,9 +1,19 @@
-using Hotline.Application.Snapshot.Contracts;
+using DocumentFormat.OpenXml.Vml.Office;
+using DotNetCore.CAP;
+using Hotline.Application.Snapshot.Contracts;
 using Hotline.Caching.Interfaces;
+using Hotline.Orders;
 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.Enums.Snapshot;
+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 +22,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 +32,81 @@ 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);
+
+        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 =>
+            {
+                if (bulletion!.IsSnapshot == false) return;
+                if (bulletion!.SafetyTypeId!.IsNullOrEmpty()) return;
+                foreach (var safetyTypeId in bulletion!.SafetyTypeId!)
+                {
+                    await _safetyTypeRepository.Queryable()
+                        .LeftJoin<CitizenRelationSafetyType>((safety, relation) => safety.Id == relation.SafetyTypeId)
+                        .LeftJoin<Citizen>((safety, relation, citizen) => relation.CitizenId == citizen.Id)
+                        .Where((safety, relation, citizen) => safety.Id == safetyTypeId)
+                        .Select((safety, relation, citizen) => relation.CitizenId)
+                        .ToListAsync(token)
+                        .Then(async citizenIds =>
+                        {
+                            var inDto = bulletion.Adapt<AddNotifyInDto>();
+                            inDto.UserIds = citizenIds;
+                            if (bulletion.VideoPath.NotNullOrEmpty())
+                            {
+                                inDto.NotifyType = ENotificationType.Video;
+                            }
+                            await _notificationDomainService.AddNotifyAsync(inDto, token);
+                        });
+                }
+            });
     }
 
     /// <summary>
@@ -88,4 +170,29 @@ public class SnapshotBulletinApplication : ISnapshotBulletinApplication, IScopeD
             .Select<SnapshotBulletinItemsOutDto>();
         return query;
     }
+
+
+    public async Task CommitBulletinAsync(string id, CancellationToken requestAborted)
+    {
+        var bulletin = await _bulletinRepository.GetAsync(id, requestAborted);
+        if (bulletin == null)
+            throw UserFriendlyException.SameMessage("无效数据");
+
+        if (bulletin.BulletinState != EBulletinState.Draft && bulletin.BulletinState != EBulletinState.ReviewNoPass)
+            throw UserFriendlyException.SameMessage("当前状态不能提交");
+
+        bulletin.BulletinState = EBulletinState.InReview;
+        bulletin.CommitTime = DateTime.Now;
+        await _bulletinRepository.UpdateAsync(bulletin, requestAborted);
+    }
+
+
+    public async Task<string> AddBulletinAsync(AddSnapshotBulletinInDto dto, CancellationToken token)
+    {
+        var model = dto.Adapt<SnapshotBulletin>();
+        model.BulletinState = EBulletinState.Draft;
+        model.ReadedNum = 0;
+        if (model.BulletinTime.HasValue == false) model.BulletinTime = DateTime.Now;
+        return await _bulletinRepository.AddAsync(model, token);
+    }
 }

+ 32 - 1
src/Hotline.Application/Snapshot/SnapshotPointsApplication.cs

@@ -4,6 +4,7 @@ using Hotline.Share.Dtos.Snapshot;
 using Hotline.Share.Enums.CallCenter;
 using Hotline.Share.Enums.Snapshot;
 using Hotline.Share.Tools;
+using Hotline.Snapshot;
 using Hotline.Snapshot.IRepository;
 using SqlSugar;
 using System;
@@ -19,18 +20,23 @@ public class SnapshotPointsApplication : ISnapshotPointsApplication, IScopeDepen
 {
     private readonly ISnapshotPointsRecordRepository _snapshotPointsRecordRepository;
     private readonly ICitizenRepository _citizenRepository;
+    private readonly ISafetyTypeRepository _safetyTypeRepository;
 
-    public SnapshotPointsApplication(ISnapshotPointsRecordRepository snapshotPointsRecordRepository, ICitizenRepository citizenRepository)
+    public SnapshotPointsApplication(ISnapshotPointsRecordRepository snapshotPointsRecordRepository, ICitizenRepository citizenRepository, ISafetyTypeRepository safetyTypeRepository)
     {
         _snapshotPointsRecordRepository = snapshotPointsRecordRepository;
         _citizenRepository = citizenRepository;
+        _safetyTypeRepository = safetyTypeRepository;
     }
 
     public ISugarQueryable<PointsItemsOutDto> GetPointsItems(PointsItemsInDto dto)
     {
         var query = _snapshotPointsRecordRepository.Queryable()
             .LeftJoin<Citizen>((points, citizen) => points.UserId == citizen.Id)
+            .LeftJoin<Order>((points, citizen, order) => order.Id == points.OrderId)
             .WhereIF(dto.PhoneNumber.NotNullOrEmpty(), (points, citizen) => citizen.PhoneNumber.Contains(dto.PhoneNumber!))
+            .WhereIF(dto.County.NotNullOrEmpty(), (points, citizen, order) => order.County == dto.County)
+            .WhereIF(dto.StartTime != null && dto.EndTime != null, (points, citizen) => points.CreationTime >= dto.StartTime && points.CreationTime <= dto.EndTime)
             .Where((points, citizen) => points.CreationTime >= dto.StartTime && points.CreationTime <= dto.EndTime)
             .GroupBy((points, citizen) => new { points.UserId, citizen.IsSecurityMax, citizen.Name, citizen.PhoneNumber })
             .Select((points, citizen) => new PointsItemsOutDto
@@ -59,5 +65,30 @@ public class SnapshotPointsApplication : ISnapshotPointsApplication, IScopeDepen
             .SetColumns(citizen => citizen.IsSecurityMax, dto.IsSecurityMax)
             .Where(citizen => citizen.Id == dto.UserId)
             .ExecuteCommandAsync(token);
+        await _citizenRepository.Queryable()
+            .Includes(m => m.SafetyTypes)
+            .Where(m => m.Id == dto.UserId)
+            .FirstAsync(token)
+            .Then(async citizen =>
+            {
+                await _safetyTypeRepository.Queryable()
+                    .Where(m => m.Name == "安全卫士")
+                    .FirstAsync(token)
+                    .Then(async safetyType =>
+                    {
+                        if (dto.IsSecurityMax)
+                            citizen.SafetyTypes.Add(safetyType);
+                        else
+                        {
+                            var delete = citizen.SafetyTypes.Where(m => m.Name == "安全卫士").ToList();
+                            foreach (var item in delete)
+                            {
+                                citizen.SafetyTypes.Remove(item);
+                            }
+                        }
+                        await _citizenRepository.UpdateNav(citizen).Include(m => m.SafetyTypes)
+                                .ExecuteCommandAsync();
+                    });
+            });
     }
 }

+ 17 - 0
src/Hotline.Application/Snapshot/SnapshotUserApplication.cs

@@ -10,6 +10,7 @@ using Hotline.Snapshot.IRepository;
 using Hotline.ThirdAccountDomainServices;
 using Hotline.Tools;
 using Mapster;
+using NPOI.POIFS.Properties;
 using SqlSugar;
 using System;
 using System.Collections.Generic;
@@ -60,6 +61,9 @@ public class SnapshotUserApplication : ISnapshotUserApplication, IScopeDependenc
             .LeftJoin<CitizenRelationSafetyType>((citizen, relation) => citizen.Id == relation.CitizenId)
             .LeftJoin<SafetyType>((citizen, relation, safety) => relation.SafetyTypeId == safety.Id)
             .WhereIF(dto.SafetyTypeId.NotNullOrEmpty(), (citizen, relation, safety) => safety.Id == dto.SafetyTypeId)
+            .WhereIF(dto.Name.NotNullOrEmpty(), (citizen, relation, safety) => citizen.Name.Contains(dto.Name))
+            .WhereIF(dto.PhoneNumber.NotNullOrEmpty(), (citizen, relation, safety) => citizen.PhoneNumber.Contains(dto.PhoneNumber))
+            .Where((citizen, relation, safety) => safety.Id != null)
             .Select((citizen, relation, safety) => new CitizenRelationSafetyTypeOutDto
             {
                 CitizenId = citizen.Id,
@@ -88,4 +92,17 @@ public class SnapshotUserApplication : ISnapshotUserApplication, IScopeDependenc
                 .ExecuteCommand();
         }
     }
+
+    public ISugarQueryable<GetThirdCitizenOutDto> GetThirdCitizenAsync(GetThirdCitizenInDto dto)
+    {
+        var query = _citizenRepository.Queryable(includeDeleted: true)
+            .LeftJoin<ThirdAccount>((citizen, third) => citizen.Id == third.ExternalId)
+            .WhereIF(dto.PhoneNumber.NotNullOrEmpty(), (citizen, third) => citizen.PhoneNumber.Contains(dto.PhoneNumber))
+            .Select((citizen, third) => new GetThirdCitizenOutDto { CitizenId = citizen.Id}, true);
+#if DEBUG
+        var sql = query.ToSqlString();
+#endif
+
+        return query;
+    }
 }

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 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)
+    {
+    }
+}

+ 11 - 1
src/Hotline.Share/Dtos/Settings/SystemCommonOpinionDto.cs

@@ -1,4 +1,6 @@
 using Hotline.Share.Enums.Settings;
+using Novacode;
+using System.Data;
 
 namespace Hotline.Share.Dtos.Settings
 {
@@ -9,9 +11,17 @@ namespace Hotline.Share.Dtos.Settings
         public string? CommonTypeText { get; set; }
         public bool? IsOpen { get; set; }
         public string? IsOpenText => IsOpen == true ? "公开常用意见" : "个人常用意见";
+        public string? AreaId { get; set; }
+        public ECommonAttributionType? AttributionType { get; set; }
+        public string? AttributionTypeText { get; set; }
+        public string? CreatorId { get; set; }
         public string? CreatorName { get; set; }
+        public string? CreatorOrgId { get; set; }
+        public int? CreatorOrgLevel { get; set; }
         public string? CreatorOrgName { get; set; }
-
+        public string? Id { get; set; }
+        public string? OldId { get; set; }
+        public int OrderBy { get; set; }
         public DateTime? CreationTime { get; set; }
     }
 }

+ 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; }
+}

+ 9 - 0
src/Hotline.Share/Dtos/Snapshot/PointsDto.cs

@@ -38,6 +38,15 @@ public record PointsItemsInDto : PagedKeywordRequest
     /// 联系方式
     /// </summary>
     public string? PhoneNumber { get; set; }
+
+    /// <summary>
+    /// 区域名称
+    /// </summary>
+    public string? County { get; set; }
+
+    public DateTime? StartTime { get; set; }
+
+    public DateTime? EndTime { get; set; }
 }
 
 public class PointsItemsOutDto

+ 6 - 1
src/Hotline.Share/Dtos/Snapshot/SnapshotBulletinDto.cs

@@ -263,7 +263,7 @@ public class AddSnapshotBulletinInDto
     /// <summary>
     /// 志愿者类型Id
     /// </summary>
-    public string? SafetyTypeId { get; set; }
+    public IList<string>? SafetyTypeId { get; set; }
 
     /// <summary>
     /// 时间
@@ -279,4 +279,9 @@ public class AddSnapshotBulletinInDto
     /// 视频地址
     /// </summary>
     public string? VideoPath { get; set; }
+
+    /// <summary>
+    /// 视频封面
+    /// </summary>
+    public string? VideoCoverImgUrl { get; set; }
 }

+ 36 - 0
src/Hotline.Share/Dtos/Snapshot/SnapshotUserInfoDto.cs

@@ -57,6 +57,16 @@ public record CitizenRelationSafetyTypeInDto : PagedRequest
     /// 志愿者类型
     /// </summary>
     public string? SafetyTypeId { get; set; }
+
+    /// <summary>
+    /// 名字
+    /// </summary>
+    public string? Name { get; set; }
+
+    /// <summary>
+    /// 手机号码
+    /// </summary>
+    public string? PhoneNumber { get; set; }
 }
 
 public class CitizenRelationSafetyTypeOutDto
@@ -124,3 +134,29 @@ public class CitizenIdRelationSafetyTypeId
     /// </summary>
     public string SafetyTypeId { get; set; }
 }
+
+public record GetThirdCitizenInDto : PagedRequest
+{
+    /// <summary>
+    /// 电话号码
+    /// </summary>
+    public string? PhoneNumber { get; set; }
+}
+
+public class GetThirdCitizenOutDto
+{
+    /// <summary>
+    /// Id
+    /// </summary>
+    public string CitizenId { get; set; }
+
+    /// <summary>
+    /// Name
+    /// </summary>
+    public string Name { get; set; }
+
+    /// <summary>
+    /// 电话
+    /// </summary>
+    public string PhoneNumber { get; set; }
+}

+ 28 - 0
src/Hotline.Share/Dtos/Snapshot/StatisticsDto.cs

@@ -3113,3 +3113,31 @@ public class IndustryStatisticsDetailsOutDto
     public string FileOpinion { get; set; }
 
 }
+
+public class SnapshotCountyPointsStatisticsInDto
+{
+    [Required]
+    public DateTime StartTime { get; set; }
+
+    [Required]
+    public DateTime EndTime { get; set; }
+
+}
+
+public class SnapshotCountyPointsStatisticsOutDto
+{
+    /// <summary>
+    /// 区域名称
+    /// </summary>
+    public string CountyName { get; set; }
+
+    /// <summary>
+    /// 积分
+    /// </summary>
+    public long Points { get; set; }
+
+    /// <summary>
+    /// 兑换积分
+    /// </summary>
+    public long ExchangePoints { 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);
+    }
+}

+ 14 - 3
src/Hotline/Snapshot/Services/SnapshotPointsDomainService.cs

@@ -1,4 +1,5 @@
-using Hotline.Share.Enums.Snapshot;
+using Hotline.Orders;
+using Hotline.Share.Enums.Snapshot;
 using Hotline.Snapshot.Contracts;
 using Hotline.Snapshot.IRepository;
 using System;
@@ -27,11 +28,21 @@ public class SnapshotPointsDomainService : ISnapshotPointsDomainService, IScopeD
         if (status == null) return;
         var order = await _orderSnapshotRepository.Queryable()
             .LeftJoin<Industry>((snapshot, industry) => snapshot.IndustryId == industry.Id)
+            .LeftJoin<Order>((snapshot, industry, order) => order.Id == snapshot.Id)
             .Where((snapshot, industry) => snapshot.Id == orderId)
-            .Select((snapshot, industry) => new { snapshot.Id, industry.ReportPoints , industry.ArgeePoints , industry.RefusePoints,  industry.Name,
-                snapshot.CreatorId})
+            .Select((snapshot, industry, order) => new 
+            {
+                snapshot.Id,
+                industry.ReportPoints,
+                industry.ArgeePoints, 
+                industry.RefusePoints,
+                industry.Name,
+                snapshot.CreatorId,
+                order.HotspotId
+            })
             .FirstAsync();
         if (order == null) return;
+        if (order.HotspotId != null && order.HotspotId.StartsWith("18") == true) return;
         
         if (order.ReportPoints.HasValue == false)
             throw new UserFriendlyException($"{order.Name} 行业未配置积分");

+ 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", ColumnDataType = "json", IsJson = true)]
+    public List<string>? SafetyTypeId { get; set; }
+
+    /// <summary>
+    /// 视频封面
+    /// </summary>
+    [SugarColumn(ColumnDescription = "视频封面")]
+    public string? VideoCoverImgUrl { get; set; }
+
 }
 

+ 0 - 1
src/Hotline/Validators/Exams/Questions/UpdateQuestionTagDtoValidator.cs

@@ -13,7 +13,6 @@ namespace Hotline.Validators.Exams.Questions
         public UpdateQuestionTagDtoValidator()
         {
             RuleFor(m => m.TagId).NotEmpty().WithMessage(x => string.Format(ExamErrorMessage.IsRequired, typeof(ExamTag).GetDescription()));
-            RuleFor(m=>m.Id).NotEmpty().WithMessage(x => string.Format(ExamErrorMessage.IsRequired, x.GetType().GetDescription(nameof(ExamQuestionTag.Id))));
         }
     }
 }

+ 9 - 0
test/Hotline.Tests/Application/BiSnapshotApplicationTest.cs

@@ -13,6 +13,7 @@ using Mapster;
 using Microsoft.AspNetCore.Http;
 using Microsoft.Extensions.DependencyInjection;
 using Shouldly;
+using SqlSugar;
 using System;
 using System.Collections.Generic;
 using System.Linq;
@@ -110,6 +111,14 @@ public class BiSnapshotApplicationTest : TestBase
 
         var e = _biSnapshotApplication.GetIndustryStatistics(inDto.Adapt<IndustryStatisticsInDto>());
         e.ShouldNotBeNull();
+
+        var areaPointsInDto = new SnapshotCountyPointsStatisticsInDto
+        {
+            StartTime = DateTime.Now.AddDays(-30),
+            EndTime = DateTime.Now,
+        };
+        var areaPointsOutDto = _biSnapshotApplication.GetAreaPointsStatistics(areaPointsInDto).ToList();
+        areaPointsOutDto.ShouldNotBeNull();
     }
 
     [Fact]

+ 35 - 3
test/Hotline.Tests/Application/OrderSnapshotApplicationTest.cs

@@ -11,6 +11,7 @@ using Hotline.Repository.SqlSugar.Snapshot;
 using Hotline.Settings;
 using Hotline.Share.Dtos;
 using Hotline.Share.Dtos.Snapshot;
+using Hotline.Share.Enums.CallCenter;
 using Hotline.Share.Enums.Snapshot;
 using Hotline.Share.Requests;
 using Hotline.Share.Tools;
@@ -112,6 +113,37 @@ public class OrderSnapshotApplicationTest : TestBase
             });
     }
 
+    [Theory]
+    [InlineData(true)]
+    [InlineData(false)]
+    public async Task SnapshotOrder_AddPoints_Test(bool 是受理范围)
+    {
+        var order = _orderServiceMock.CreateSnapshotOrder(SetWeiXin, "安全隐患")
+            .StepHandle(async order =>
+            {
+                if (是受理范围 == false)
+                {
+                    var entity = _orderRepository.Get(order.Id);
+                    entity.HotspotId = "181301";
+                    await _orderRepository.UpdateAsync(entity);
+                }
+            })
+            .办理到一级部门(SetZuoXi)
+            .办理到归档(Set一级部门, data =>
+            {
+                data.CompliantType = ECompliantType.First;
+                data.VerifyType = "现场";
+                data.IsCheckList = true;
+            }).GetCreateResult();
+        var points = await _pointsRecordRepository.Queryable()
+            .Where(m => m.OrderId == order.Id && m.Direction == EPointsDirection.In && m.Source == EPointsSource.Report)
+            .FirstAsync();
+        if (是受理范围)
+            points.ShouldNotBeNull();
+        else
+            points.ShouldBeNull();
+    }
+
     /// <summary>
     /// 随手拍网格员超时:
     /// </summary>
@@ -267,7 +299,7 @@ public class OrderSnapshotApplicationTest : TestBase
                 baseData.AuditTypeCode.ShouldNotBeNull();
                 baseData.Amount.ShouldNotBeNull();
 
-                var specialRedAuditItems = await _redPackApplication.GetRedPackSpecialAuditItems(new SnapshotOrderAuditItemsInDto() { PageIndex = 1, PageSize = 10 , No = order.No, Status = 1}).ToListAsync();
+                var specialRedAuditItems = await _redPackApplication.GetRedPackSpecialAuditItems(new SnapshotOrderAuditItemsInDto() { PageIndex = 1, PageSize = 10, No = order.No, Status = 1 }).ToListAsync();
                 var specialRedAudit = specialRedAuditItems.FirstOrDefault();
                 specialRedAudit.ShouldNotBeNull();
                 var a = _systemDicDataCacheManager.SnapshotReplenishType.First();
@@ -288,7 +320,7 @@ public class OrderSnapshotApplicationTest : TestBase
                 };
 
                 await _redPackApplication.UpdateRedPackSpecialRecordAsync(inDto); // 补充发放
-                specialRedAuditItems = await _redPackApplication.GetRedPackSpecialAuditItems(new SnapshotOrderAuditItemsInDto() { PageIndex = 1, PageSize = 10 , Status = 1, No = order.No}).ToListAsync();
+                specialRedAuditItems = await _redPackApplication.GetRedPackSpecialAuditItems(new SnapshotOrderAuditItemsInDto() { PageIndex = 1, PageSize = 10, Status = 1, No = order.No }).ToListAsync();
                 specialRedAudit = specialRedAuditItems.First();
                 specialRedAudit.ShouldNotBeNull();
                 specialRedAudit.BankCardNo.ShouldBe(inDto.BankCardNo);
@@ -329,7 +361,7 @@ public class OrderSnapshotApplicationTest : TestBase
                 }); // 添加备注
 
                 SetWeiXin();
-                redPackItems =  await _snapshotApplication.GetRedPacksAsync(new RedPacksInDto() { Status = ERedPackPickupStatus.Received }, CancellationToken.None);
+                redPackItems = await _snapshotApplication.GetRedPacksAsync(new RedPacksInDto() { Status = ERedPackPickupStatus.Received }, CancellationToken.None);
                 redPackRecord = redPackItems.Where(m => m.OrderId == order.Id).FirstOrDefault();
                 redPackRecord.ShouldNotBeNull();
                 redPackRecord.Amount.ShouldBe(11);

+ 36 - 1
test/Hotline.Tests/Application/PointsRecordApplicationTest.cs

@@ -2,8 +2,10 @@
 using Hotline.Application.Snapshot.Contracts;
 using Hotline.Identity.Accounts;
 using Hotline.Identity.Roles;
+using Hotline.Orders;
 using Hotline.Settings;
 using Hotline.Share.Dtos.Snapshot;
+using Hotline.Snapshot.IRepository;
 using Hotline.ThirdAccountDomainServices;
 using Hotline.ThirdAccountDomainServices.Interfaces;
 using Hotline.Users;
@@ -15,6 +17,7 @@ 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;
 
@@ -24,10 +27,17 @@ public class PointsRecordApplicationTest : TestBase
 {
     private readonly ISnapshotPointsApplication _pointsRecordApplication;
     private readonly ISnapshotApplication _snapshotApplication;
-    public PointsRecordApplicationTest(IAccountRepository accountRepository, IRepository<Role> roleRepository, UserController userController, IServiceScopeFactory scopeFactory, IRepository<User> userRepository, IHttpContextAccessor httpContextAccessor, IThirdIdentiyService thirdIdentiyService, IThirdAccountRepository thirdAccountRepository, ITypedCache<SystemSetting> cacheSettingData, ThirdAccounSupplierFactory thirdAccountDomainFactory, ISnapshotPointsApplication pointsRecordApplication, ISnapshotApplication snapshotApplication, IServiceProvider serviceProvider) : base(accountRepository, roleRepository, userController, scopeFactory, userRepository, httpContextAccessor, thirdIdentiyService, thirdAccountRepository, cacheSettingData, thirdAccountDomainFactory, serviceProvider)
+    private readonly ISessionContext _sessionContext;
+    private readonly ICitizenRepository _citizenRepository;
+    private readonly ISafetyTypeRepository _safetyTypeRepository;
+
+    public PointsRecordApplicationTest(IAccountRepository accountRepository, IRepository<Role> roleRepository, UserController userController, IServiceScopeFactory scopeFactory, IRepository<User> userRepository, IHttpContextAccessor httpContextAccessor, IThirdIdentiyService thirdIdentiyService, IThirdAccountRepository thirdAccountRepository, ITypedCache<SystemSetting> cacheSettingData, ThirdAccounSupplierFactory thirdAccountDomainFactory, ISnapshotPointsApplication pointsRecordApplication, ISnapshotApplication snapshotApplication, IServiceProvider serviceProvider, ISessionContext sessionContext, ICitizenRepository citizenRepository, ISafetyTypeRepository safetyTypeRepository) : base(accountRepository, roleRepository, userController, scopeFactory, userRepository, httpContextAccessor, thirdIdentiyService, thirdAccountRepository, cacheSettingData, thirdAccountDomainFactory, serviceProvider)
     {
         _pointsRecordApplication = pointsRecordApplication;
         _snapshotApplication = snapshotApplication;
+        _sessionContext = sessionContext;
+        _citizenRepository = citizenRepository;
+        _safetyTypeRepository = safetyTypeRepository;
     }
 
     [Fact]
@@ -42,6 +52,31 @@ public class PointsRecordApplicationTest : TestBase
         items.ShouldNotBeNull();
     }
 
+    [Fact]
+    public async Task UpdateIsSecurityMax_Test()
+    {
+        SetWeiXin();
+        var inDto = new UpdateIsSecurityMaxAsync
+        {
+            UserId = _sessionContext.UserId,
+            IsSecurityMax = false
+        };
+        await _pointsRecordApplication.UpdateIsSecurityMaxAsync(inDto, CancellationToken.None);
+        var citizen = await _citizenRepository.Queryable()
+            .Where(m => m.Id == inDto.UserId)
+            .Includes(m => m.SafetyTypes)
+            .FirstAsync();
+        citizen.SafetyTypes.Any(m => m.Name == "安全卫士").ShouldBeFalse();
+
+        inDto.IsSecurityMax = true;
+        await _pointsRecordApplication.UpdateIsSecurityMaxAsync(inDto, CancellationToken.None);
+        citizen = await _citizenRepository.Queryable()
+            .Where(m => m.Id == inDto.UserId)
+            .Includes(m => m.SafetyTypes)
+            .FirstAsync();
+        citizen.SafetyTypes.Any(m => m.Name == "安全卫士").ShouldBeTrue();
+    }
+
     [Fact]
     public async Task GetPointsRank_Test()
     {

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

@@ -0,0 +1,91 @@
+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;
+    private readonly ISnapshotBulletinRepository _bulletinRepository;
+
+    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, ISnapshotBulletinRepository bulletinRepository) : base(accountRepository, roleRepository, userController, scopeFactory, userRepository, httpContextAccessor, thirdIdentiyService, thirdAccountRepository, cacheSettingData, thirdAccountDomainFactory, serviceProvider)
+    {
+        _snapshotBulletinApplication = snapshotBulletinApplication;
+        _systemDicDataCacheManager = systemDicDataCacheManager;
+        _safetyTypeRepository = safetyTypeRepository;
+        _snapshotUserApplication = snapshotUserApplication;
+        _sessionContext = sessionContext;
+        _snapshotApplication = snapshotApplication;
+        _bulletinRepository = bulletinRepository;
+    }
+
+    [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.ToLongDateTimeString(),
+            Content = "没什么内容",
+            BulletinTime = DateTime.Now,
+            IsSnapshot = true,
+            BulletinTypeId = bulletinType.DicDataValue,
+            BulletinTypeName = bulletinType.DicDataName,
+            SafetyTypeId = [safetyType.Id],
+            VideoCoverImgUrl = "222222",
+            VideoPath = "333333"
+        };
+        var bulletinId = await _snapshotBulletinApplication.AddBulletinAsync(inDto, CancellationToken.None);
+        var bulletin = await _bulletinRepository.GetAsync(bulletinId);
+        bulletin.IsSnapshot.ShouldBe(true);
+        bulletin.SafetyTypeId.First().ShouldBe(safetyType.Id);
+        bulletin.VideoPath.ShouldBe(inDto.VideoPath);
+        bulletin.VideoCoverImgUrl.ShouldBe(inDto.VideoCoverImgUrl);
+        await _snapshotBulletinApplication.CommitBulletinAsync(bulletinId, CancellationToken.None);
+        var examineDto = new ExamineBulletinDto
+        {
+            Id = bulletinId,
+            IsPass = true,
+            Reason = "审核通过"
+        };
+        await _snapshotBulletinApplication.ExamineBulletinAsync(examineDto, CancellationToken.None);
+        Thread.Sleep(5 * 1000);
+        //await _snapshotBulletinApplication.NotifyUserAsync(bulletinId, CancellationToken.None);
+
+
+        var notifyItems = await _snapshotApplication.GetNotificationAsync(new GetNotifyInDto(), CancellationToken.None);
+        notifyItems.Any(m => m.Title == inDto.Title).ShouldBeTrue();
+    }
+}

+ 8 - 0
test/Hotline.Tests/Application/SnapshotUserApplicationTest.cs

@@ -37,6 +37,13 @@ public class SnapshotUserApplicationTest : TestBase
     [Fact]
     public async Task SnapshotUserApplication_Test()
     {
+        var searchInDto = new GetThirdCitizenInDto
+        {
+            PhoneNumber = "33"
+        };
+        var citizenSearchItems = await _snapshotUserApplication.GetThirdCitizenAsync(searchInDto).ToListAsync();
+        citizenSearchItems.ShouldNotBeNull();
+        citizenSearchItems.Count.ShouldNotBe(0);
         var newSafetyType = new SafetyType { Name = "安全卫士" };
         var safetyType = await _safetyTypeRepository.Queryable().Where(m => m.Name == newSafetyType.Name).FirstAsync();
         safetyType ??= new SafetyType
@@ -94,6 +101,7 @@ public class SnapshotUserApplicationTest : TestBase
         await _snapshotUserApplication.DeleteCitizenRelationSafetyAsync(deleteInDto);
 
         items = await _snapshotUserApplication.GetCitizenRelationSafetyType(inDto).ToListAsync();
+        items.Any(m => m.SafetyTypeId.IsNullOrEmpty()).ShouldBeFalse();
         item = items.FirstOrDefault(m => m.CitizenId == citizen.Id && m.SafetyTypeId == safetyType.Id);
         item.ShouldBeNull();
         inDto = new CitizenRelationSafetyTypeInDto

+ 2 - 0
test/Hotline.Tests/Mock/Interfaces/IOrderServiceStartWorkflow.cs

@@ -13,4 +13,6 @@ public interface IOrderServiceStartWorkflow
     CreateOrderOutDto GetCreateResult();
     OrderServiceMock 办理到一级部门(Action action = null);
     OrderServiceMock 办理到归档(Action action = null);
+
+    IOrderServiceStartWorkflow StepHandle(Func<CreateOrderOutDto, Task> handle);
 }

+ 6 - 0
test/Hotline.Tests/Mock/OrderServiceStartWorkflow.cs

@@ -46,6 +46,12 @@ public class OrderServiceStartWorkflow : IOrderServiceStartWorkflow
         return _orderServiceMock.CreateOrderOutDto;
     }
 
+    public IOrderServiceStartWorkflow StepHandle(Func<CreateOrderOutDto, Task> handle)
+    {
+        handle(_orderServiceMock.CreateOrderOutDto).GetAwaiter().GetResult();
+        return this;
+    }
+
     public OrderServiceMock 办理到一级部门(Action action = null)
     {
         action?.Invoke();

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