Przeglądaj źródła

Merge branch 'dev' into dev_dss

Dun.Jason 11 miesięcy temu
rodzic
commit
6c0354e3f6

+ 13 - 2
src/Hotline.Api/Controllers/BaseController.cs

@@ -1,6 +1,7 @@
 using Hotline.Api.Filter;
 using Microsoft.AspNetCore.Mvc;
 using MiniExcelLibs;
+using System.IO;
 
 namespace Hotline.Api.Controllers;
 
@@ -10,12 +11,22 @@ namespace Hotline.Api.Controllers;
 [LogFilter]
 public class BaseController : ControllerBase
 {
-    
+    protected FileStreamResult ExcelStreamResult(Stream stream, string fileName = null)
+    {
+        var tail = DateTime.Now.ToString("yyyyMMddhhmmss") + ".xlsx";
+        fileName = string.IsNullOrEmpty(fileName)
+            ? tail
+            : $"{fileName}_{tail}";
+        return new FileStreamResult(stream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
+        {
+            FileDownloadName = fileName
+        };
+    }
 }
 
 [ApiController]
 [Route("api/v1/[controller]")]
 public class OriginController : ControllerBase
 {
-    
+
 }

Plik diff jest za duży
+ 448 - 455
src/Hotline.Api/Controllers/OrderController.cs


+ 97 - 10
src/Hotline.Api/Controllers/TestController.cs

@@ -21,11 +21,14 @@ using Hotline.Repository.SqlSugar.System;
 using Hotline.Repository.SqlSugar.Ts;
 using Hotline.Settings;
 using Hotline.Settings.TimeLimits;
+using Hotline.Share.Dtos;
 using Hotline.Share.Dtos.FlowEngine;
 using Hotline.Share.Dtos.Realtime;
 using Hotline.Share.Enums.Settings;
 using Hotline.Share.Mq;
+using Hotline.Tools;
 using Hotline.Users;
+using MapsterMapper;
 using MediatR;
 using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Components.Routing;
@@ -88,6 +91,7 @@ public class TestController : BaseController
     private readonly IRepository<WorkflowStepHandler> _workflowStepHandleRepository;
 
     private readonly IRepository<SystemOrganize> _systemOrganizeRepository;
+    private readonly IMapper _mapper;
 
 
     //private readonly ITypedCache<List<User>> _cache;
@@ -123,7 +127,9 @@ public class TestController : BaseController
         IAiVisitService aiVisitService,
         IRepository<WorkflowTrace> workflowTraceRepository,
         IRepository<WorkflowStepHandler> workflowStepHandleRepository,
-        IRepository<SystemOrganize> systemOrganizeRepository)
+        IRepository<SystemOrganize> systemOrganizeRepository,
+        IMapper mapper
+        )
     {
         _logger = logger;
         _authorizeGenerator = authorizeGenerator;
@@ -151,6 +157,7 @@ public class TestController : BaseController
         _workflowTraceRepository = workflowTraceRepository;
         _workflowStepHandleRepository = workflowStepHandleRepository;
         _systemOrganizeRepository = systemOrganizeRepository;
+        _mapper = mapper;
     }
 
     [HttpGet("testo")]
@@ -165,7 +172,7 @@ public class TestController : BaseController
     [AllowAnonymous]
     public async Task InitUserOrgFullName()
     {
-        var list = await _userRepository.Queryable().Where(x => 1==1 && string.IsNullOrEmpty(x.FullOrgName)).ToListAsync();
+        var list = await _userRepository.Queryable().Where(x => 1 == 1 && string.IsNullOrEmpty(x.FullOrgName)).ToListAsync();
 
         foreach (var item in list)
         {
@@ -175,7 +182,7 @@ public class TestController : BaseController
             int strLength = orgId.Length;
             int segmentsCount = strLength / 3;
             List<string> strings = new List<string>();
-           
+
             for (int i = 0; i < segmentsCount; i++)
             {
                 int startIndex = i * 3;
@@ -187,17 +194,17 @@ public class TestController : BaseController
             {
                 orgFullCode = strings[0];
                 strings.Remove(orgFullCode);
-                
+
             }
 
             foreach (var str in strings)
             {
                 orgFullCode = orgFullCode + str;
-               
+
                 var org = await _systemOrganizeRepository.GetAsync(orgFullCode);
                 if (org != null)
                 {
-                    
+
                     orgFullName = orgFullName + org.Name + "/";
                 }
             }
@@ -394,7 +401,7 @@ public class TestController : BaseController
         //var list = await query
         //    .Select((t, w, o) => new { t, w, o })
         //    .ToListAsync(HttpContext.RequestAborted);
-        
+
         //var toUsers = list.Where(d => d.t.FlowAssignType == EFlowAssignType.User).ToList();
         //var userIds = toUsers.SelectMany(d => d.t.Handlers).Select(d => d.Key).Distinct().ToList();
         //var users = await _userRepository.Queryable()
@@ -449,10 +456,59 @@ public class TestController : BaseController
 
     [AllowAnonymous]
     [HttpPost("t3")]
-    public Task<int> TestDaprPubsub(int data)
+    public IActionResult TestExportExcel()
     {
-        _logger.LogDebug("receive dapr event, params: {0}", data);
-        return Task.FromResult(data);
+       //var students = new List<Student>
+       // {
+       //     new Student
+       //     {
+       //         Id = "student001",
+       //         Name = "student001Name",
+       //         School = new School
+       //         {
+       //             Id = "schoolABC",
+       //             Name = "schoolABCName",
+       //             Sponser = new Sponser
+       //             {
+       //                 Amount = 1000
+       //             }
+       //         }
+       //     },
+       //     new Student
+       //     {
+       //         Id = "student002",
+       //         Name = "student002Name",
+       //         School = new School
+       //         {
+       //             Id = "schoolABCD",
+       //             Name = "schoolABCDName",
+       //             Sponser = new Sponser
+       //             {
+       //                 Amount = 2000
+       //             }
+       //         }
+       //     }
+       // };
+
+       //var ps = new List<Kv>
+       //{
+       //    new("Id", "编号"),
+       //    new("SchoolId", "流程标题"),
+       //    new("SchoolSponserAmount", "金额"),
+       //};
+
+       //dynamic? dynamicClass = DynamicClassHelper.CreateDynamicClass(ps);
+
+       // var dtos = students.Select(stu => _mapper.Map(stu, typeof(Student), dynamicClass))
+       //     .Cast<object>()
+       //     .ToList();
+
+       // var stream = ExcelHelper.CreateStream(dtos);
+
+       // return ExcelStreamResult(stream);
+
+       throw new NotImplementedException();
+
     }
 
     [HttpGet("rsa")]
@@ -464,4 +520,35 @@ public class TestController : BaseController
         return $"{publicKey} \r\n {privateKey}";
     }
 
+}
+
+public class School
+{
+    public string Id { get; set; }
+    public string Name { get; set; }
+    public Sponser Sponser { get; set; }
+}
+
+public class Student
+{
+    public string Id { get; set; }
+    public string Name { get; set; }
+
+    public School School { get; set; }
+}
+
+public class Sponser
+{
+    public int Amount { get; set; }
+}
+
+public class StudentDto
+{
+    public string Id { get; set; }
+    public string Name { get; set; }
+
+    public string SchoolId { get; set; }
+    public string SchoolName { get; set; }
+
+    public int SchoolSponserAmount { get; set; }
 }

+ 7 - 0
src/Hotline.Application/Orders/IOrderApplication.cs

@@ -10,6 +10,7 @@ using Hotline.Share.Dtos.FlowEngine.Workflow;
 using Hotline.Share.Dtos.Order;
 using Hotline.Share.Enums.Order;
 using Hotline.Share.Enums.Settings;
+using SqlSugar;
 using XF.Domain.Entities;
 
 namespace Hotline.Application.Orders
@@ -48,5 +49,11 @@ namespace Hotline.Application.Orders
         /// 接收外部平台修改工单附件
         /// </summary>
         Task UpdateOrderFilesAnonymousAsync(UpdateOrderFilesDto dto, CancellationToken cancellationToken);
+
+        #region 工单办理
+
+        ISugarQueryable<Order> QueryOrders(QueryOrderDto dto);
+
+        #endregion
     }
 }

+ 74 - 23
src/Hotline.Application/Orders/OrderApplication.cs

@@ -16,6 +16,9 @@ using Hotline.Share.Enums.Settings;
 using Hotline.Tools;
 using Hotline.Users;
 using MapsterMapper;
+using MediatR;
+using SqlSugar;
+using XF.Domain.Authentications;
 using XF.Domain.Constants;
 using XF.Domain.Dependency;
 using XF.Domain.Entities;
@@ -35,6 +38,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
     private readonly IRepository<OrderWord> _orderWrodRepository;
     private readonly IRepositoryTextSearch<OrderTs> _repositoryts;
     private readonly IFileRepository _fileRepository;
+    private readonly ISessionContext _sessionContext;
 
     public OrderApplication(
         IOrderDomainService orderDomainService,
@@ -45,7 +49,8 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         IMapper mapper,
         IRepository<OrderWord> orderWrodRepository,
         IRepositoryTextSearch<OrderTs> repositoryts,
-        IFileRepository fileRepository
+        IFileRepository fileRepository,
+        ISessionContext sessionContext
         )
     {
         _orderDomainService = orderDomainService;
@@ -57,6 +62,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         _orderWrodRepository = orderWrodRepository;
         _repositoryts = repositoryts;
         _fileRepository = fileRepository;
+        _sessionContext = sessionContext;
     }
 
     /// <summary>
@@ -75,17 +81,17 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         order.ExpiredTime = expiredTimeConfig.ExpiredTime;
         order.NearlyExpiredTime = expiredTimeConfig.NearlyExpiredTime;
 
-		//if (string.IsNullOrEmpty(order.WorkflowId))
-		//    throw new UserFriendlyException("该工单流程id异常");
-		//var workflow = await _workflowDomainService.GetWorkflowAsync(order.WorkflowId, cancellationToken: cancellationToken);
-		//await _workflowDomainService.UpdateExpiredTimeAsync(workflow, expiredTimeConfig.ExpiredTime,
-		//    expiredTimeConfig.TimeText, expiredTimeConfig.Count, expiredTimeConfig.TimeType, expiredTimeConfig.NearlyExpiredTime, cancellationToken);
+        //if (string.IsNullOrEmpty(order.WorkflowId))
+        //    throw new UserFriendlyException("该工单流程id异常");
+        //var workflow = await _workflowDomainService.GetWorkflowAsync(order.WorkflowId, cancellationToken: cancellationToken);
+        //await _workflowDomainService.UpdateExpiredTimeAsync(workflow, expiredTimeConfig.ExpiredTime,
+        //    expiredTimeConfig.TimeText, expiredTimeConfig.Count, expiredTimeConfig.TimeType, expiredTimeConfig.NearlyExpiredTime, cancellationToken);
 
-		if (string.IsNullOrEmpty(order.WorkflowId))
-			throw new UserFriendlyException("该工单流程id异常");
-		await _workflowDomainService.UpdateUnhandleExpiredTimeAsync(order.WorkflowId, expiredTimeConfig.ExpiredTime, cancellationToken);
+        if (string.IsNullOrEmpty(order.WorkflowId))
+            throw new UserFriendlyException("该工单流程id异常");
+        await _workflowDomainService.UpdateUnhandleExpiredTimeAsync(order.WorkflowId, expiredTimeConfig.ExpiredTime, cancellationToken);
 
-		await _orderRepository.UpdateAsync(order, cancellationToken);
+        await _orderRepository.UpdateAsync(order, cancellationToken);
     }
 
     /// <summary>
@@ -263,22 +269,22 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         }
     }
 
-    public async Task OrderSensitiveParticiple(string inputStr, string orderId, CancellationToken cancellationToken) 
+    public async Task OrderSensitiveParticiple(string inputStr, string orderId, CancellationToken cancellationToken)
     {
-	    var words = await _orderWrodRepository.Queryable().Where(x => x.IsEnable == 1 && x.Classify.Contains("敏感标签")).Select(x => x.Tag).ToListAsync(cancellationToken);
-	    var res = new List<string>();
-	    if (words.Any()) res = ParticipleTool.SegMMDouble(inputStr, ref words);
-		if (res.Any())
+        var words = await _orderWrodRepository.Queryable().Where(x => x.IsEnable == 1 && x.Classify.Contains("敏感标签")).Select(x => x.Tag).ToListAsync(cancellationToken);
+        var res = new List<string>();
+        if (words.Any()) res = ParticipleTool.SegMMDouble(inputStr, ref words);
+        if (res.Any())
         {
             var intersect = words.Intersect(res).ToList();
-			await _orderRepository.Updateable().SetColumns(o=> new Order() { Sensitive  = intersect }).Where(o=>o.Id == orderId).ExecuteCommandAsync(cancellationToken);
-		}
-	}
-
-	/// <summary>
-	/// 接收外部平台工单
-	/// </summary>
-	public Task<AddOrderResponse> ReceiveOrderFromExternalAsync(AddOrderDto dto, CancellationToken cancellationToken)
+            await _orderRepository.Updateable().SetColumns(o => new Order() { Sensitive = intersect }).Where(o => o.Id == orderId).ExecuteCommandAsync(cancellationToken);
+        }
+    }
+
+    /// <summary>
+    /// 接收外部平台工单
+    /// </summary>
+    public Task<AddOrderResponse> ReceiveOrderFromExternalAsync(AddOrderDto dto, CancellationToken cancellationToken)
     {
         switch (dto.Source)
         {
@@ -313,6 +319,51 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         }
     }
 
+    public ISugarQueryable<Order> QueryOrders(QueryOrderDto dto)
+    {
+        var isCenter = _sessionContext.OrgIsCenter;
+
+        return _orderRepository.Queryable(viewFilter: isCenter ? false : true)
+            .Includes(x => x.OrderScreens)
+            .WhereIF(!string.IsNullOrEmpty(dto.Keyword), d => d.Title.Contains(dto.Keyword!)) //标题
+            .WhereIF(!string.IsNullOrEmpty(dto.ProvinceNo), d => d.ProvinceNo.Contains(dto.ProvinceNo)) //省本地编号
+            .WhereIF(!string.IsNullOrEmpty(dto.No), d => d.No.Contains(dto.No)) //工单编码
+                                                                                //.WhereIF(!string.IsNullOrEmpty(dto.Content), d => d.Content.Contains(dto.Content!))
+            .WhereIF(dto.AcceptTypes.Any(), d => dto.AcceptTypes.Contains(d.AcceptTypeCode)) //受理类型
+            .WhereIF(dto.Channels.Any(), d => dto.Channels.Contains(d.SourceChannelCode)) //来源渠道
+            .WhereIF(dto.HotspotIds.Any(), d => dto.HotspotIds.Contains(d.HotspotId)) //热点类型
+            .WhereIF(!string.IsNullOrEmpty(dto.TransferPhone), d => d.TransferPhone.Contains(dto.TransferPhone!)) //转接号码
+                                                                                                                  //.WhereIF(dto.OrgCodes.Any(), d => d.Workflow.Assigns.Any(s => dto.OrgCodes.Contains(s.OrgCode)))
+            .WhereIF(dto.OrgCodes.Any(), d => dto.OrgCodes.Contains(d.ActualHandleOrgCode)) //接办部门
+            .WhereIF(!string.IsNullOrEmpty(dto.NameOrNo), d => d.AcceptorName.Contains(dto.NameOrNo!) || d.AcceptorStaffNo.Contains(dto.NameOrNo!)) //受理人/坐席
+            .WhereIF(dto.CreationTimeStart.HasValue, d => d.CreationTime >= dto.CreationTimeStart) //受理时间开始
+            .WhereIF(dto.CreationTimeEnd.HasValue, d => d.CreationTime <= dto.CreationTimeEnd) //受理时间结束
+            .WhereIF(dto.EmergencyLevels.Any(), d => dto.EmergencyLevels.Contains(d.EmergencyLevel))  //紧急程度
+            .WhereIF(!string.IsNullOrEmpty(dto.FromPhone), d => d.FromPhone.Contains(dto.FromPhone)) //来电号码
+            .WhereIF(!string.IsNullOrEmpty(dto.PhoneNo), d => d.Contact.Contains(dto.PhoneNo!)) //联系电话
+            .WhereIF(!string.IsNullOrEmpty(dto.PushTypeCode), d => d.PushTypeCode == dto.PushTypeCode) //推送分类
+            .WhereIF(dto.ExpiredTimeStart.HasValue, d => d.ExpiredTime >= dto.ExpiredTimeStart) //超期时间开始
+            .WhereIF(dto.ExpiredTimeEnd.HasValue, d => d.ExpiredTime <= dto.ExpiredTimeEnd) //超期时间结束
+            .WhereIF(dto.Statuses.Any(), d => dto.Statuses.Contains(d.Status))  //工单状态
+            .WhereIF(dto.Statuses.Any(d => d == EOrderStatus.SpecialToUnAccept), d => d.Status <= EOrderStatus.SpecialToUnAccept)
+            .WhereIF(!string.IsNullOrEmpty(dto.ActualHandlerName), d => d.ActualHandlerName.Contains(dto.ActualHandlerName)) //接办人
+            .WhereIF(dto.IsScreen == true, d => d.OrderScreens.Any(x => x.Status != EScreenStatus.Refuse)) //有甄别
+            .WhereIF(dto.IsScreen == false, d => !d.OrderScreens.Any(x => x.Status != EScreenStatus.Refuse)) //无甄别
+            .WhereIF(!string.IsNullOrEmpty(dto.CurrentStepCode), d => d.ActualHandleStepCode == dto.CurrentStepCode) //当前办理节点
+            .WhereIF(dto.ActualHandleTimeStart.HasValue, d => d.ActualHandleTime >= dto.ActualHandleTimeStart) //办结时间开始
+            .WhereIF(dto.ActualHandleTimeEnd.HasValue, d => d.ActualHandleTime <= dto.ActualHandleTimeEnd) //办结时间结束
+            .WhereIF(dto.IsOverTime == true, d => (d.ExpiredTime < DateTime.Now && d.Status < EOrderStatus.Filed) || (d.ExpiredTime < d.ActualHandleTime && d.Status >= EOrderStatus.Filed)) //是 超期
+            .WhereIF(dto.IsOverTime == false, d => (d.ExpiredTime > DateTime.Now && d.Status < EOrderStatus.Filed) || (d.ExpiredTime > d.ActualHandleTime && d.Status >= EOrderStatus.Filed)) //否 超期
+            .WhereIF(dto.IdentityType != null, d => d.IdentityType == dto.IdentityType) //来电主体
+            .WhereIF(!string.IsNullOrEmpty(dto.FromName), d => d.FromName.Contains(dto.FromName)) //来电人姓名
+            .WhereIF(dto.AreaCodes.Any(), d => dto.AreaCodes.Contains(d.AreaCode)) //区域
+            .WhereIF(dto.IsProvinceOrder.HasValue && dto.IsProvinceOrder == true, x => x.IsProvince == true)
+            .WhereIF(dto.IsProvinceOrder.HasValue && dto.IsProvinceOrder == false, x => x.IsProvince == false)
+            .WhereIF(!string.IsNullOrEmpty(dto.SensitiveWord), x => SqlFunc.JsonArrayAny(x.Sensitive, dto.SensitiveWord))
+            .WhereIF(dto.IsSensitiveWord.HasValue && dto.IsSensitiveWord == true, x => x.Sensitive != null && SqlFunc.JsonArrayLength(x.Sensitive) > 0)
+            .OrderByDescending(d => d.CreationTime);
+    }
+
 
     #region private
 

+ 44 - 0
src/Hotline.Share/Dtos/Order/ExportExcelDto.cs

@@ -0,0 +1,44 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hotline.Share.Dtos.Order
+{
+    public record ExportExcelDto<TQueryDto>
+    {
+        /// <summary>
+        /// 查询参数
+        /// </summary>
+        public TQueryDto QueryDto { get; set; }
+
+        /// <summary>
+        /// 导出列信息
+        /// </summary>
+        public List<ColumnInfo> ColumnInfos { get; set; }
+
+        /// <summary>
+        /// 是否全量导出
+        /// </summary>
+        public bool IsExportAll { get; set; }
+    }
+
+    public class ColumnInfo
+    {
+        /// <summary>
+        /// 字段名称
+        /// </summary>
+        public string Prop { get; set; }
+
+        /// <summary>
+        /// 表头展示名称
+        /// </summary>
+        public string Name { get; set; }
+
+        /// <summary>
+        /// 排序
+        /// </summary>
+        public int Sort { get; set; }
+    }
+}

+ 53 - 0
src/Hotline/Tools/DynamicClassHelper.cs

@@ -0,0 +1,53 @@
+using System.Reflection;
+using System.Reflection.Emit;
+using Hotline.Share.Dtos;
+using Hotline.Share.Dtos.Order;
+using MiniExcelLibs.Attributes;
+
+namespace Hotline.Tools;
+
+public class DynamicClassHelper
+{
+    public static Type CreateDynamicClass(List<ColumnInfo> propInfos)
+    {
+        string className = "DynamicClass";
+        AssemblyName assemblyName = new AssemblyName("DynamicAssembly");
+        AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
+        ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
+        TypeBuilder typeBuilder = moduleBuilder.DefineType(className, TypeAttributes.Public);
+
+        foreach (var property in propInfos)
+        {
+            var propertyName = property.Prop;
+            var propertyType = typeof(string);
+
+            FieldBuilder fieldBuilder = typeBuilder.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);
+            PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null);
+
+            MethodAttributes getSetAttributes = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig;
+
+            MethodBuilder getMethodBuilder = typeBuilder.DefineMethod("get_" + propertyName, getSetAttributes, propertyType, Type.EmptyTypes);
+            ILGenerator getIL = getMethodBuilder.GetILGenerator();
+            getIL.Emit(OpCodes.Ldarg_0);
+            getIL.Emit(OpCodes.Ldfld, fieldBuilder);
+            getIL.Emit(OpCodes.Ret);
+
+            MethodBuilder setMethodBuilder = typeBuilder.DefineMethod("set_" + propertyName, getSetAttributes, null, new Type[] { propertyType });
+            ILGenerator setIL = setMethodBuilder.GetILGenerator();
+            setIL.Emit(OpCodes.Ldarg_0);
+            setIL.Emit(OpCodes.Ldarg_1);
+            setIL.Emit(OpCodes.Stfld, fieldBuilder);
+            setIL.Emit(OpCodes.Ret);
+
+            propertyBuilder.SetGetMethod(getMethodBuilder);
+            propertyBuilder.SetSetMethod(setMethodBuilder);
+
+            var columnNameCtorInfo = typeof(ExcelColumnNameAttribute).GetConstructor(new Type[] { typeof(string), typeof(string[]) });
+            var attributeBuilder = new CustomAttributeBuilder(columnNameCtorInfo, new object[] { property.Name, null });
+            propertyBuilder.SetCustomAttribute(attributeBuilder);
+        }
+
+        Type generatedType = typeBuilder.CreateType();
+        return generatedType;
+    }
+}

+ 21 - 0
src/Hotline/Tools/ExcelHelper.cs

@@ -0,0 +1,21 @@
+using MiniExcelLibs;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Mapster;
+
+namespace Hotline.Tools
+{
+    public class ExcelHelper
+    {
+        public static MemoryStream CreateStream(List<object> values)
+        {
+            var stream = new MemoryStream();
+            stream.SaveAs(values);
+            stream.Seek(0, SeekOrigin.Begin);
+            return stream;
+        }
+    }
+}

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików