7 Commity 83592960fa ... d5211fce79

Autor SHA1 Wiadomość Data
  qinchaoyue d5211fce79 Merge branch 'feature/snapshot' into dev 1 dzień temu
  qinchaoyue f92c55c953 如果前端没有选额外扣除积分原因就不额外扣除积分 1 dzień temu
  qinchaoyue 68ce149dd9 随手拍公告新增反惨 1 dzień temu
  qinchaoyue 633477aedc 公告新增反惨 1 dzień temu
  qinchaoyue d011da7be2 修复异常 1 dzień temu
  qinchaoyue 4f41f43850 修复异常 1 dzień temu
  qinchaoyue 8bb3172c39 修复积分排行榜 1 dzień temu

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

@@ -57,12 +57,21 @@ public class SnapshotBulletinController : BaseController
     {
         var model = await _bulletinRepository.Queryable()
             .Includes(x => x.ExaminMan)
-            .FirstAsync(x => x.Id == id, HttpContext.RequestAborted);
+            .FirstAsync(x => x.Id == id, HttpContext.RequestAborted)
+            ?? throw UserFriendlyException.SameMessage("公告不存在");
 
         if (model != null && !string.IsNullOrEmpty(model.Content))
             model.Content = _bulletinApplication.GetSiteUrls(model.Content);
 
-        return model.Adapt<SnapshotBulletinDetailOutDto>();
+        var outDto = model.Adapt<SnapshotBulletinDetailOutDto>();
+        if (outDto.SafetyTypeId.NotNullOrEmpty())
+        {
+            outDto.SafetyTypeNames = await _safetyTypeRepository.Queryable()
+                .Where(m => outDto.SafetyTypeId.Contains(m.Id))
+                .Select(m => m.Name)
+                .ToListAsync();
+        }
+        return outDto;
     }
 
     /// <summary>
@@ -180,7 +189,7 @@ public class SnapshotBulletinController : BaseController
     public async Task<object> BulletinAddBaseData()
     {
         var safetyTypes = await _safetyTypeRepository.Queryable()
-            .Select(m => new SystemDicDataOutDto { Id =  m.Id, DicDataName = m.Name , DicDataValue = m.Id})
+            .Select(m => new SystemDicDataOutDto { Id = m.Id, DicDataName = m.Name, DicDataValue = m.Id })
             .ToListAsync(HttpContext.RequestAborted);
         var rsp = new
         {

+ 2 - 1
src/Hotline.Application/Snapshot/RedPackApplication.cs

@@ -148,7 +148,8 @@ public class RedPackApplication : IRedPackApplication, IScopeDependency
             await _redPackRecordRepository.AddAsync(entity);
         }
         await _redPackAuditRepository.UpdateAsync(redPackAudit, token);
-        await _snapshotPointsDomainService.AddPointsAsync(order.Id, EPointsSource.Audit, dto.PointsStatus, dto.ExtraDeductedPoints);
+        await _snapshotPointsDomainService
+            .AddPointsAsync(order.Id, EPointsSource.Audit, dto.PointsStatus, dto.ExtraDeductedPoints, dto.ExtraDeductionPointsTypeName);
         if (dto.IsSendSms)
         {
             var smsTemplate = await _snapshotSMSTemplateRepository.GetAsync(dto.SMSTemplateId);

+ 23 - 9
src/Hotline.Application/Snapshot/SnapshotApplicationBase.cs

@@ -1065,6 +1065,14 @@ public abstract class SnapshotApplicationBase
             .LeftJoin<Notification>((receiver, notify) => notify.Id == receiver.NotificationId)
             .LeftJoin<SnapshotBulletin>((receiver, notify, bulletin) => bulletin.Id == notify.ExternalId)
             .Where((receiver, notify) => receiver.ReceiverId == _sessionContext.UserId)
+            .GroupBy((receiver, notify, bulletin) => new 
+            {
+                notify.Title,
+                notify.ExternalId,
+                bulletin.SnapshotBulletinTypeName,
+                bulletin.VideoCoverImgUrl,
+                notify.CreationTime
+            })
             .OrderByDescending((receiver, notify) => notify.CreationTime)
             .Select((receiver, notify, bulletin) => new VideoBulletinOutDto
             {
@@ -1075,15 +1083,21 @@ public abstract class SnapshotApplicationBase
                 VideoCoverImgUrl = bulletin.VideoCoverImgUrl,
             }).FirstAsync();
 
-        outDto.Bulletins = await _notificationReceiverRepository.Queryable()
-            .LeftJoin<Notification>((receiver, notify) => notify.Id == receiver.NotificationId)
-            .Select((receiver, notify) => new PointsBulletinOutDto 
-            {
-                BulletinId = notify.ExternalId,
-                Title = notify.Title,
-            }, true)
-            .Take(2)
-            .ToListAsync();
+        outDto.Bulletins = new PointsBulletinOutDto
+        {
+            Items = await _notificationReceiverRepository.Queryable()
+                .LeftJoin<Notification>((receiver, notify) => notify.Id == receiver.NotificationId)
+                .Select((receiver, notify) => new PointsBulletinItemsOutDto
+                {
+                    BulletinId = notify.ExternalId,
+                    Title = notify.Title,
+                }, true)
+                .Take(2)
+                .ToListAsync(),
+            UnReadCount = await _notificationReceiverRepository.Queryable()
+            .Where(m => m.IsRead == false && m.ReceiverId == _sessionContext.UserId)
+            .CountAsync()
+        };
 
         return outDto;
     }

+ 3 - 9
src/Hotline.Application/Snapshot/SnapshotBulletinApplication.cs

@@ -1,5 +1,4 @@
-using DocumentFormat.OpenXml.Vml.Office;
-using DotNetCore.CAP;
+using DotNetCore.CAP;
 using Hotline.Application.Snapshot.Contracts;
 using Hotline.Caching.Interfaces;
 using Hotline.Orders;
@@ -14,14 +13,9 @@ using Hotline.Snapshot;
 using Hotline.Snapshot.Contracts;
 using Hotline.Snapshot.IRepository;
 using Mapster;
-using Microsoft.AspNetCore.Http;
 using SqlSugar;
-using System;
-using System.Collections.Generic;
-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;
@@ -85,7 +79,7 @@ public class SnapshotBulletinApplication : ISnapshotBulletinApplication, IScopeD
         await _bulletinRepository.GetAsync(bullutionId, token)
             .Then(async bulletion =>
             {
-                if (bulletion!.IsSnapshot == false) return;
+                if (bulletion!.SnapshotBulletinTypeId != "aqws") return;
                 if (bulletion!.SafetyTypeId!.IsNullOrEmpty()) return;
                 foreach (var safetyTypeId in bulletion!.SafetyTypeId!)
                 {
@@ -99,7 +93,7 @@ public class SnapshotBulletinApplication : ISnapshotBulletinApplication, IScopeD
                         {
                             var inDto = bulletion.Adapt<AddNotifyInDto>();
                             inDto.UserIds = citizenIds;
-                            if (bulletion.VideoPath.NotNullOrEmpty())
+                            if (bulletion.Shape != null && bulletion.Shape == EBulletinShape.Video)
                             {
                                 inDto.NotifyType = ENotificationType.Video;
                             }

+ 3 - 1
src/Hotline.Share/Dtos/Snapshot/NotifyDto.cs

@@ -40,7 +40,9 @@ public class AddNotifyInDto
 public class GetNotifyInDto : QueryFixedDto
 {
     /// <summary>
-    /// 消息类型
+    /// 消息类型:
+    /// 0: 通知公告
+    /// 1: 宣传视频
     /// </summary>
     public ENotificationType NotifyType { get; set; }
 }

+ 14 - 1
src/Hotline.Share/Dtos/Snapshot/PointsDto.cs

@@ -124,10 +124,23 @@ public class PointsRankOutDto
     /// <summary>
     /// 通知公告
     /// </summary>
-    public IList<PointsBulletinOutDto> Bulletins { get; set; }
+    public PointsBulletinOutDto Bulletins { get; set; }
 }
 
 public class PointsBulletinOutDto
+{
+    /// <summary>
+    /// 未读个数
+    /// </summary>
+    public int UnReadCount { get; set; }
+
+    /// <summary>
+    /// Summary
+    /// </summary>
+    public IList<PointsBulletinItemsOutDto> Items { get; set; }
+}
+
+public class PointsBulletinItemsOutDto
 {
     /// <summary>
     /// Id

+ 10 - 2
src/Hotline.Share/Dtos/Snapshot/SnapshotBulletinDto.cs

@@ -1,5 +1,6 @@
 using Hotline.Share.Dtos.Users;
 using Hotline.Share.Enums.Article;
+using Hotline.Share.Enums.Snapshot;
 using Hotline.Share.Requests;
 using System;
 using System.Collections.Generic;
@@ -163,6 +164,11 @@ public class SnapshotBulletinDetailOutDto : UpdateSnapshotBulletinInDto
     /// 上下架
     /// </summary>
     public bool IsArrive { get; set; }
+
+    /// <summary>
+    /// 类型
+    /// </summary>
+    public IList<string> SafetyTypeNames { get; set; }
 }
 
 public class UpdateSnapshotBulletinInDto : AddSnapshotBulletinInDto
@@ -256,9 +262,11 @@ public class AddSnapshotBulletinInDto
     public bool IsPopup { get; set; }
 
     /// <summary>
-    /// 是否随手拍
+    /// 公告形式
+    /// 0: 消息
+    /// 1: 视频
     /// </summary>
-    public bool? IsSnapshot { get; set; }
+    public EBulletinShape? Shape { get; set; }
 
     /// <summary>
     /// 志愿者类型Id

+ 28 - 0
src/Hotline.Share/Enums/Snapshot/EBulletinShape.cs

@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hotline.Share.Enums.Snapshot;
+
+/// <summary>
+/// 公告形式
+/// 0: 消息
+/// 1: 视频
+/// </summary>
+public enum EBulletinShape
+{
+    /// <summary>
+    /// 消息
+    /// </summary>
+    [Description("消息")]
+    Message = 0,
+
+    /// <summary>
+    /// 视频
+    /// </summary>
+    [Description("视频")]
+    Video = 1
+}

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

@@ -9,9 +9,15 @@ namespace Hotline.Share.Enums.Snapshot;
 
 public enum ENotificationType
 {
+    /// <summary>
+    /// 通知公告
+    /// </summary>
     [Description("消息")]
     Message = 0,
 
+    /// <summary>
+    /// 宣传视频
+    /// </summary>
     [Description("视频")]
     Video = 1
 }

+ 6 - 0
src/Hotline.Share/Enums/Snapshot/EPointsSource.cs

@@ -21,6 +21,12 @@ public enum EPointsSource
     [Description("审核积分")]
     Audit = 1,
 
+    /// <summary>
+    /// 额外扣除
+    /// </summary>
+    [Description("额外扣除")]
+    AuditDeduct = 2,
+
     /// <summary>
     /// 积分兑换
     /// </summary>

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

@@ -10,6 +10,9 @@ public static class DataMaskExtensions
 {
     public static string Mask(this string original, int startIndex = 0, int length = 4)
     {
+        if (original.IsNullOrEmpty()) return original;
+        if (original.Length <= startIndex + length) return original;
+
         var headStr = original.Substring(0, startIndex);
         var tailStr = original.Substring(startIndex + length);
         var sb = new StringBuilder(length);
@@ -23,12 +26,14 @@ public static class DataMaskExtensions
 
     public static string MaskPhoneNumber(this string original)
     {
+        if (original.IsNullOrEmpty()) return original;
         if (original.Length < 11) return string.Empty;
         return original.Mask(3, 4);
     }
 
     public static string MaskIdCard(this string original)
     {
+        if (original.IsNullOrEmpty()) return original;
         return original.Mask(3, 12);
     }
 }

+ 2 - 1
src/Hotline/Snapshot/Contracts/ISnapshotPointsDomainService.cs

@@ -19,6 +19,7 @@ public interface ISnapshotPointsDomainService
     /// <param name="source"></param>
     /// <param name="status"></param>
     /// <param name="extraDeductedPoints"></param>
+    /// <param name="extraDeductionPointsTypeName">额外扣除原因</param>
     /// <returns></returns>
-    Task AddPointsAsync(string orderId, EPointsSource source, ESnapshotSMSStatus? status, int? extraDeductedPoints);
+    Task AddPointsAsync(string orderId, EPointsSource source, ESnapshotSMSStatus? status, int? extraDeductedPoints, string? extraDeductionPointsTypeName);
 }

+ 12 - 2
src/Hotline/Snapshot/Services/SnapshotPointsDomainService.cs

@@ -1,5 +1,6 @@
 using Hotline.Orders;
 using Hotline.Share.Enums.Snapshot;
+using Hotline.Share.Tools;
 using Hotline.Snapshot.Contracts;
 using Hotline.Snapshot.IRepository;
 using System;
@@ -23,7 +24,7 @@ public class SnapshotPointsDomainService : ISnapshotPointsDomainService, IScopeD
         _pointsRecordRepository = snapshotPointsRecordRepository;
     }
 
-    public async Task AddPointsAsync(string orderId, EPointsSource source, ESnapshotSMSStatus? status, int? extraDeductedPoints)
+    public async Task AddPointsAsync(string orderId, EPointsSource source, ESnapshotSMSStatus? status, int? extraDeductedPoints, string? extraDeductionPointsTypeName)
     {
         if (status == null) return;
         var order = await _orderSnapshotRepository.Queryable()
@@ -62,8 +63,17 @@ public class SnapshotPointsDomainService : ISnapshotPointsDomainService, IScopeD
         if (source == EPointsSource.Audit && status == ESnapshotSMSStatus.Refuse)
         {
             points.Direction = Share.Enums.CallCenter.EPointsDirection.Out;
-            points.Points = order.RefusePoints ?? 0 + extraDeductedPoints ?? 0;
+            points.Points = order.RefusePoints ?? 0;
             points.Points *= -1;
+
+            if (extraDeductionPointsTypeName.NotNullOrEmpty() && extraDeductedPoints != null && extraDeductedPoints != 0)
+            {
+                var ex = points.ToJson().FromJson<SnapshotPointsRecord>();
+                ex.Source = EPointsSource.AuditDeduct;
+                ex.Points = extraDeductedPoints.Value * -1;
+                ex.Remark = extraDeductionPointsTypeName;
+                await _pointsRecordRepository.AddAsync(ex);
+            }
         }
         await _pointsRecordRepository.AddAsync(points);
     }

+ 4 - 3
src/Hotline/Snapshot/SnapshotBulletin.cs

@@ -1,5 +1,6 @@
 using Hotline.Share.Dtos;
 using Hotline.Share.Enums.Article;
+using Hotline.Share.Enums.Snapshot;
 using Hotline.Users;
 using SqlSugar;
 using System.ComponentModel;
@@ -177,10 +178,10 @@ public class SnapshotBulletin : CreationEntity
     }
 
     /// <summary>
-    /// 是否随手拍
+    /// 公告形式
     /// </summary>
-    [SugarColumn(ColumnDescription = "是否随手拍")]
-    public bool? IsSnapshot { get; set; }
+    [SugarColumn(ColumnDescription = "公告形式")]
+    public EBulletinShape? Shape { get; set; }
 
     /// <summary>
     /// 志愿者类型Id

+ 5 - 0
src/Hotline/Snapshot/SnapshotPointsRecord.cs

@@ -37,4 +37,9 @@ public class SnapshotPointsRecord : CreationEntity
     public int Points { get; set; }
 
     public string OrderId { get; set; }
+
+    /// <summary>
+    /// 备注
+    /// </summary>
+    public string? Remark { get; set; }
 }

+ 4 - 4
test/Hotline.Tests/Application/PointsRecordApplicationTest.cs

@@ -5,6 +5,7 @@ using Hotline.Identity.Roles;
 using Hotline.Orders;
 using Hotline.Settings;
 using Hotline.Share.Dtos.Snapshot;
+using Hotline.Share.Tools;
 using Hotline.Snapshot.IRepository;
 using Hotline.ThirdAccountDomainServices;
 using Hotline.ThirdAccountDomainServices.Interfaces;
@@ -83,9 +84,8 @@ public class PointsRecordApplicationTest : TestBase
         SetWeiXin();
         var item = await _snapshotApplication.GetPointsRankAsync();
         item.ShouldNotBeNull();
-        foreach (var a in item.Ranks)
-        {
-            var s = a.PhoneNumber;
-        }
+        item.Bulletins.Items.Any(m => m.Title.IsNullOrEmpty()).ShouldBeFalse();
+        item.Bulletins.Items.Any(m => m.BulletinId.IsNullOrEmpty()).ShouldBeFalse();
+        item.VideoBulletin.ShouldNotBeNull();
     }
 }

+ 5 - 5
test/Hotline.Tests/Application/SnapshotBulletionApplicationTest.cs

@@ -51,7 +51,7 @@ public class SnapshotBulletionApplicationTest : TestBase
     public async Task Bulletin_Test()
     {
         SetWeiXin();
-        var bulletinType =  _systemDicDataCacheManager.SnapshotBulletinType.First();
+        var bulletinType =  _systemDicDataCacheManager.SnapshotBulletinType.First(m => m.DicDataName == "安全卫士");
         var safetyType = await _safetyTypeRepository.Queryable().FirstAsync();
         var addRelationInDto = new AddCitizenRelationSafetyTypeInDto { SafetyTypeId = safetyType.Id, CitizenIds = [_sessionContext.UserId] };
         await _snapshotUserApplication.AddCitizenRelationSafetyType(addRelationInDto, CancellationToken.None);
@@ -60,7 +60,7 @@ public class SnapshotBulletionApplicationTest : TestBase
             Title = "测试公告" + DateTime.Now.ToLongDateTimeString(),
             Content = "没什么内容",
             BulletinTime = DateTime.Now,
-            IsSnapshot = true,
+            Shape = Share.Enums.Snapshot.EBulletinShape.Video,
             BulletinTypeId = bulletinType.DicDataValue,
             BulletinTypeName = bulletinType.DicDataName,
             SafetyTypeId = [safetyType.Id],
@@ -69,7 +69,7 @@ public class SnapshotBulletionApplicationTest : TestBase
         };
         var bulletinId = await _snapshotBulletinApplication.AddBulletinAsync(inDto, CancellationToken.None);
         var bulletin = await _bulletinRepository.GetAsync(bulletinId);
-        bulletin.IsSnapshot.ShouldBe(true);
+        bulletin.Shape.ShouldBe(Share.Enums.Snapshot.EBulletinShape.Video);
         bulletin.SafetyTypeId.First().ShouldBe(safetyType.Id);
         bulletin.VideoPath.ShouldBe(inDto.VideoPath);
         bulletin.VideoCoverImgUrl.ShouldBe(inDto.VideoCoverImgUrl);
@@ -81,11 +81,11 @@ public class SnapshotBulletionApplicationTest : TestBase
             Reason = "审核通过"
         };
         await _snapshotBulletinApplication.ExamineBulletinAsync(examineDto, CancellationToken.None);
-        Thread.Sleep(5 * 1000);
+        Thread.Sleep(10 * 1000);
         //await _snapshotBulletinApplication.NotifyUserAsync(bulletinId, CancellationToken.None);
 
 
-        var notifyItems = await _snapshotApplication.GetNotificationAsync(new GetNotifyInDto(), CancellationToken.None);
+        var notifyItems = await _snapshotApplication.GetNotificationAsync(new GetNotifyInDto { NotifyType = Share.Enums.Snapshot.ENotificationType.Video}, CancellationToken.None);
         notifyItems.Any(m => m.Title == inDto.Title).ShouldBeTrue();
     }
 }