瀏覽代碼

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

qinchaoyue 4 月之前
父節點
當前提交
6122a6969e
共有 83 個文件被更改,包括 2287 次插入488 次删除
  1. 7 0
      Hotline.sln
  2. 3 1
      src/Hotline.Ai.Jths/AiQualityService.cs
  3. 104 0
      src/Hotline.Ai.XingTang/AiQualityService.cs
  4. 15 0
      src/Hotline.Ai.XingTang/AiXingTangStartupExtensions.cs
  5. 20 0
      src/Hotline.Ai.XingTang/Hotline.Ai.XingTang.csproj
  6. 28 6
      src/Hotline.Api/Controllers/Bi/BiCallController.cs
  7. 168 118
      src/Hotline.Api/Controllers/Bi/BiOrderController.cs
  8. 194 0
      src/Hotline.Api/Controllers/Bi/BiQualityController.cs
  9. 9 5
      src/Hotline.Api/Controllers/Bigscreen/DataScreenController.cs
  10. 10 10
      src/Hotline.Api/Controllers/CommonPController.cs
  11. 84 4
      src/Hotline.Api/Controllers/ExportWordController.cs
  12. 93 74
      src/Hotline.Api/Controllers/KnowledgeCommonController.cs
  13. 56 35
      src/Hotline.Api/Controllers/KnowledgeController.cs
  14. 76 2
      src/Hotline.Api/Controllers/OrderController.cs
  15. 1 0
      src/Hotline.Api/Controllers/PlanController.cs
  16. 88 15
      src/Hotline.Api/Controllers/QualityController.cs
  17. 9 0
      src/Hotline.Api/Controllers/Snapshot/IndustryController.cs
  18. 18 0
      src/Hotline.Api/Controllers/Snapshot/InviteCodeController.cs
  19. 45 6
      src/Hotline.Api/Controllers/Snapshot/SnapshotController.cs
  20. 21 19
      src/Hotline.Api/Controllers/Snapshot/SnapshotOrderController.cs
  21. 38 24
      src/Hotline.Api/Controllers/TestController.cs
  22. 3 0
      src/Hotline.Api/Hotline.Api.csproj
  23. 3 1
      src/Hotline.Api/StartupExtensions.cs
  24. 二進制
      src/Hotline.Api/Template/QualityCertificate.doc
  25. 5 0
      src/Hotline.Api/config/appsettings.Development.json
  26. 1 1
      src/Hotline.Application.Tests/Application/InviteCodeApplicationTest.cs
  27. 2 2
      src/Hotline.Application.Tests/Application/OrderSnapshotApplicationTest.cs
  28. 1 1
      src/Hotline.Application.Tests/Application/RedPackApplicationTest.cs
  29. 3 1
      src/Hotline.Application/CallCenter/DefaultCallApplication.cs
  30. 23 9
      src/Hotline.Application/Caselibrary/CaseApplication.cs
  31. 3 1
      src/Hotline.Application/FlowEngine/WorkflowApplication.cs
  32. 1 0
      src/Hotline.Application/Hotline.Application.csproj
  33. 7 3
      src/Hotline.Application/Knowledge/KnowApplication.cs
  34. 6 0
      src/Hotline.Application/Mappers/SnapshotMapperConfigs.cs
  35. 4 3
      src/Hotline.Application/Orders/OrderApplication.cs
  36. 1 1
      src/Hotline.Application/Orders/OrderSecondaryHandlingApplication.cs
  37. 14 5
      src/Hotline.Application/Planlibrary/PlanApplication.cs
  38. 10 0
      src/Hotline.Application/Quality/IQualityApplication.cs
  39. 172 3
      src/Hotline.Application/Quality/QualityApplication.cs
  40. 7 0
      src/Hotline.Application/Snapshot/IIndustryApplication.cs
  41. 16 1
      src/Hotline.Application/Snapshot/IInviteCodeApplication.cs
  42. 7 7
      src/Hotline.Application/Snapshot/IOrderSnapshotApplication.cs
  43. 23 1
      src/Hotline.Application/Snapshot/IndustryApplication.cs
  44. 44 3
      src/Hotline.Application/Snapshot/InviteCodeApplication.cs
  45. 26 22
      src/Hotline.Application/Snapshot/OrderSnapshotApplication.cs
  46. 12 8
      src/Hotline.Application/Snapshot/RedPackApplication.cs
  47. 7 6
      src/Hotline.Application/Snapshot/SnapshotApplicationBase.cs
  48. 17 3
      src/Hotline.Application/StatisticalReport/CallReport/YiBinCallReportApplication.cs
  49. 10 5
      src/Hotline.Application/StatisticalReport/OrderReportApplication.cs
  50. 3 3
      src/Hotline.Repository.SqlSugar/CallCenter/TrCallRecordRepository.cs
  51. 1 1
      src/Hotline.Share/Dtos/Bigscreen/BigscreenDto.cs
  52. 7 3
      src/Hotline.Share/Dtos/CallCenter/BiQueryCallsDto.cs
  53. 10 0
      src/Hotline.Share/Dtos/CallCenter/QueryTelOperationsFixedDto.cs
  54. 1 1
      src/Hotline.Share/Dtos/Caselibrary/CaseListDto.cs
  55. 5 0
      src/Hotline.Share/Dtos/FlowEngine/NextStepsDto.cs
  56. 5 0
      src/Hotline.Share/Dtos/Order/OrderBiDto.cs
  57. 12 0
      src/Hotline.Share/Dtos/Order/OrderStartFlowDto.cs
  58. 6 1
      src/Hotline.Share/Dtos/Order/QueryOrderDto.cs
  59. 11 1
      src/Hotline.Share/Dtos/Planlibrary/PlanDataDto.cs
  60. 6 1
      src/Hotline.Share/Dtos/Planlibrary/PlanListDto.cs
  61. 109 0
      src/Hotline.Share/Dtos/Quality/BiQualityDto.cs
  62. 29 0
      src/Hotline.Share/Dtos/Quality/QualityDto.cs
  63. 145 0
      src/Hotline.Share/Dtos/QualityExportWord/QualityCertificate.cs
  64. 75 1
      src/Hotline.Share/Dtos/Snapshot/InviteCodeDto.cs
  65. 29 0
      src/Hotline.Share/Dtos/Snapshot/OrderDeclareDto.cs
  66. 22 22
      src/Hotline.Share/Dtos/Snapshot/OrderDto.cs
  67. 55 0
      src/Hotline.Share/Dtos/Transfer.cs
  68. 42 0
      src/Hotline.Share/Enums/Quality/EQuality.cs
  69. 5 0
      src/Hotline.Share/Requests/DepartmentKeyWordRequest.cs
  70. 21 1
      src/Hotline.Share/Requests/PagedKeywordRequest.cs
  71. 2 0
      src/Hotline/Ai/Quality/IAiQualityService.cs
  72. 2 1
      src/Hotline/Configurations/AppConfiguration.cs
  73. 2 2
      src/Hotline/FlowEngine/Workflows/WorkflowDomainService.cs
  74. 6 6
      src/Hotline/KnowledgeBase/KnowledgeImportTemplate.cs
  75. 6 0
      src/Hotline/Planlibrary/PlanList.cs
  76. 18 0
      src/Hotline/Quality/Quality.cs
  77. 40 0
      src/Hotline/Quality/QualityTransferRecords.cs
  78. 1 1
      src/Hotline/Settings/TimeLimitDomain/ExpireTimeHandler.cs
  79. 6 0
      src/Hotline/Snapshot/InviteCode.cs
  80. 22 0
      src/Hotline/Snapshot/OrderSnapshot.cs
  81. 36 36
      src/Hotline/Snapshot/SnapshotMessageTemplate.cs
  82. 1 1
      src/Hotline/Snapshot/SnapshotSMSTemplate.cs
  83. 31 0
      src/Hotline/dataview.md

+ 7 - 0
Hotline.sln

@@ -61,6 +61,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hotline.WeChat", "src\Hotli
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TianQue.Sdk", "src\TianQue.Sdk\TianQue.Sdk.csproj", "{6CF27647-D0E0-4D17-80FB-3EE57864A2B4}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hotline.Ai.XingTang", "src\Hotline.Ai.XingTang\Hotline.Ai.XingTang.csproj", "{8E4F64EF-314A-45BA-8BB2-46FF5B06F7D5}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -159,6 +161,10 @@ Global
 		{6CF27647-D0E0-4D17-80FB-3EE57864A2B4}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{6CF27647-D0E0-4D17-80FB-3EE57864A2B4}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{6CF27647-D0E0-4D17-80FB-3EE57864A2B4}.Release|Any CPU.Build.0 = Release|Any CPU
+		{8E4F64EF-314A-45BA-8BB2-46FF5B06F7D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{8E4F64EF-314A-45BA-8BB2-46FF5B06F7D5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{8E4F64EF-314A-45BA-8BB2-46FF5B06F7D5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{8E4F64EF-314A-45BA-8BB2-46FF5B06F7D5}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -190,6 +196,7 @@ Global
 		{37784861-ABC0-41F4-87B4-2E08A89A2C42} = {D041C554-B78E-4AAF-B597-E309DC8EEF4F}
 		{75215667-65AF-4B7B-85E7-3140239B30CC} = {D041C554-B78E-4AAF-B597-E309DC8EEF4F}
 		{6CF27647-D0E0-4D17-80FB-3EE57864A2B4} = {D041C554-B78E-4AAF-B597-E309DC8EEF4F}
+		{8E4F64EF-314A-45BA-8BB2-46FF5B06F7D5} = {D041C554-B78E-4AAF-B597-E309DC8EEF4F}
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
 		SolutionGuid = {4B8EA790-BD13-4422-8D63-D6DBB77B823F}

+ 3 - 1
src/Hotline.Ai.Jths/AiQualityService.cs

@@ -21,8 +21,10 @@ namespace Hotline.Ai.Jths
             _client = new RestClient();
             _baseUrl = baseUrl;
         }
+        public async Task<string> CreateAiOrderQualityTask(string filename, CancellationToken cancellationToken) { return ""; }
 
-        public async Task CreateAiOrderQualityTask(
+
+		public async Task CreateAiOrderQualityTask(
              string id,
              string audioFile,
              string fromNo,

+ 104 - 0
src/Hotline.Ai.XingTang/AiQualityService.cs

@@ -0,0 +1,104 @@
+using RestSharp;
+using Fw.Utility.UnifyResponse;
+using System.Text;
+using System.Security.Cryptography;
+using Hotline.Share.Dtos.Quality;
+using Hotline.Ai.Quality;
+
+namespace Hotline.Ai.XingTang
+{
+    public class AiQualityService : IAiQualityService
+    {
+        private readonly RestClient _client;
+        private readonly string _baseUrl;
+
+        public AiQualityService(string baseUrl)
+        {
+            _client = new RestClient();
+            _baseUrl = baseUrl;
+        }
+
+        public async Task CreateAiOrderQualityTask(
+            string id,
+            string audioFile,
+            string fromNo,
+            DateTime? callStartTime,
+            string viteRecordPrefix,
+            string ywlxString,
+            CancellationToken cancellationToken)
+        { 
+        }
+
+
+		public async Task<string> CreateAiOrderQualityTask(string filename, CancellationToken cancellationToken)
+        {
+            var url = _baseUrl + "/offlinerecog?filename=" + filename;
+			var baseUrl = new Uri(url);
+            return  await ExecuteAsync(baseUrl.ToString(), Method.Get, "", cancellationToken);
+        }
+
+        public async Task<ApiResponse<TResponse>> ExecuteAsync<TRequest, TResponse>(string path, Method httpMethod,
+            TRequest request, CancellationToken cancellationToken)
+            where TRequest : class
+        {
+            var req = new RestRequest(path, httpMethod);
+            if (httpMethod is Method.Get)
+            {
+                req.AddObject(request);
+            }
+            else
+            {
+                req.AddJsonBody(request);
+            }
+
+            try
+            {
+                var response = await _client.ExecuteAsync<ApiResponse<TResponse>>(req, cancellationToken);
+                return response.Data;
+            }
+            catch (Exception e)
+            {
+                throw new HttpRequestException($"智能质检平台错误,Error: {e.Message}");
+            }
+        }
+
+        public async Task<string> ExecuteAsync<TRequest>(string path, Method httpMethod, TRequest request,
+            CancellationToken cancellationToken)
+            where TRequest : class
+        {
+            var req = new RestRequest(path, httpMethod);
+            req.Timeout = new TimeSpan(0,30,0);
+
+            try
+            {
+                var response = await _client.ExecuteAsync<ApiResponse>(req, cancellationToken);
+                return response.Content;
+            }
+            catch (Exception e)
+            {
+                throw new HttpRequestException($"智能质检平台错误,Error: {e.Message}");
+            }
+        }
+
+        /// <summary>
+        /// MD5加密
+        /// </summary>
+        /// <param name="input">需要加密的字符串</param>
+        /// <returns></returns>
+        private static string MD5Encrypt(string? input)
+        {
+            using var md5 = MD5.Create();
+            var t = md5.ComputeHash(Encoding.UTF8.GetBytes(input));
+            var sb = new StringBuilder(32);
+            for (var i = 0; i < t.Length; i++)
+                sb.Append(t[i].ToString("x").PadLeft(2, '0'));
+            return sb.ToString();
+        }
+
+        private static string Base64En(string? model)
+        {
+            var bytes = Encoding.UTF8.GetBytes(model);
+            return Convert.ToBase64String(bytes);
+        }
+    }
+}

+ 15 - 0
src/Hotline.Ai.XingTang/AiXingTangStartupExtensions.cs

@@ -0,0 +1,15 @@
+using Hotline.Ai.Quality;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Hotline.Ai.XingTang
+{
+	public static class AiXingTangStartupExtensions
+	{
+		public  static IServiceCollection AddAiXingTang(this IServiceCollection services, string baseUrl)
+		{
+			services.AddSingleton<IAiQualityService, AiQualityService>(_ => new AiQualityService(baseUrl));
+
+			return services;
+		}
+	}
+}

+ 20 - 0
src/Hotline.Ai.XingTang/Hotline.Ai.XingTang.csproj

@@ -0,0 +1,20 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>net8.0</TargetFramework>
+    <ImplicitUsings>enable</ImplicitUsings>
+    <Nullable>enable</Nullable>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="Fw.Utility.UnifyResponse" Version="1.0.0" />
+    <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0" />
+    <PackageReference Include="RestSharp" Version="112.1.0" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\Hotline.Share\Hotline.Share.csproj" />
+    <ProjectReference Include="..\Hotline\Hotline.csproj" />
+  </ItemGroup>
+
+</Project>

+ 28 - 6
src/Hotline.Api/Controllers/Bi/BiCallController.cs

@@ -576,7 +576,7 @@ public class BiCallController : BaseController
             Date = "合计",
             PersonCallInCount = list.Sum(x=>x.PersonCallInCount),
             PersonCallInPutthroughCount = list.Sum(x=>x.PersonCallInPutthroughCount),
-            PersonRingOffCount = list.Sum(x=>x.PersonRingOffCount),//个人服务挂断
+            //PersonRingOffCount = list.Sum(x=>x.PersonRingOffCount),//个人服务挂断
             PersonQueueOffCount = list.Sum(x=>x.PersonQueueOffCount),//个人服务队列挂断
             PersonWaitOffCount = list.Sum(x=>x.PersonWaitOffCount) //个人服务等待挂断
         };
@@ -609,7 +609,7 @@ public class BiCallController : BaseController
             Date = "合计",
             EnterpriseCallInCount = list.Sum(x=>x.EnterpriseCallInCount),
             EnterpriseCallInPutthroughCount = list.Sum(x=>x.EnterpriseCallInPutthroughCount),
-            EnterpriseRingOffCount = list.Sum(x=>x.EnterpriseRingOffCount), //企业挂断
+            //EnterpriseRingOffCount = list.Sum(x=>x.EnterpriseRingOffCount), //企业挂断
             EnterpriseQueueOffCount = list.Sum(x=>x.EnterpriseQueueOffCount),//个人服务队列挂断
             EnterpriseWaitOffCount = list.Sum(x=>x.EnterpriseWaitOffCount) //个人服务等待挂断
         };
@@ -707,7 +707,7 @@ public class BiCallController : BaseController
     {
         return new
         {
-            SeatUser = _userRepository.Queryable().Where(x => x.UserType == Share.Enums.User.EUserType.Seat)            
+            SeatUser =await _userRepository.Queryable().Where(x => x.UserType == Share.Enums.User.EUserType.Seat).ToListAsync()            
         };
     }
 
@@ -721,7 +721,7 @@ public class BiCallController : BaseController
     =>  _exportApplication.GetExcelFile(
             dto,              
             await _callReportApplication.QuerySeatMonthCall(dto.QueryDto)
-            , "坐席月接通率统计", "Date");
+            , "坐席月接通率统计", "Name");
 
 
     /// <summary>
@@ -743,10 +743,32 @@ public class BiCallController : BaseController
     /// <param name="dto"></param>
     /// <returns></returns>
     [HttpPost("query-seat-monthcall-detail/export")]
-    public async Task<FileStreamResult> QuerySeatMonthCallDetailExport([FromBody]ExportExcelDto<QuerySeatMonthCallDetailRequest> dto)
+    public async Task<FileStreamResult> QuerySeatMonthCallDetailExport([FromBody] ExportExcelDto<QuerySeatMonthCallDetailRequest> dto)
     {
+        var query = _callReportApplication.QuerySeatMonthCallDetail(dto.QueryDto);
+        List<QuerySeatMonthCallDetailResp> data;
+        if (dto.IsExportAll)
+        {
+            data = await query.ToListAsync(HttpContext.RequestAborted);
+        }
+        else
+        {
+            var (_, items) = await query.ToPagedListAsync(dto.QueryDto, HttpContext.RequestAborted);
+            data = items;
+        }
+
+        var dataDtos = _mapper.Map<ICollection<QuerySeatMonthCallDetailResp>>(data);
+
+        dynamic? dynamicClass = DynamicClassHelper.CreateDynamicClass(dto.ColumnInfos);
+
+        var dtos = dataDtos
+            .Select(stu => _mapper.Map(stu, typeof(QuerySeatMonthCallDetailResp), dynamicClass))
+            .Cast<object>()
+            .ToList();
+
+        var stream = ExcelHelper.CreateStream(dtos);
 
-        return null;
+        return ExcelStreamResult(stream, "坐席月接通明细");
     }
 
     #endregion

+ 168 - 118
src/Hotline.Api/Controllers/Bi/BiOrderController.cs

@@ -966,6 +966,83 @@ namespace Hotline.Api.Controllers.Bi
             return ExcelStreamResult(stream, "部门延期统计明细数据");
         }
 
+
+        /// <summary>
+        /// 部门延期统计明细-单独菜单
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpGet("order_delay_detail")]
+        public async Task<PagedDto<OrderDelayDto>> QueryOrderDelayDataDetailAsync([FromQuery] QueryOrderDelayDataDetailRequest dto)
+        {
+            var query = _orderDelayRepository.Queryable()
+                 .Includes(x => x.Order)
+                 .WhereIF(dto.StartTime.HasValue, x => x.CreationTime >= dto.StartTime)
+                 .WhereIF(dto.EndTime.HasValue, x => x.CreationTime <= dto.EndTime)
+                .WhereIF(dto.TypeId != null && dto.TypeId == 1, x => x.Order.IdentityType == EIdentityType.Citizen)
+                .WhereIF(dto.TypeId != null && dto.TypeId == 2, x => x.Order.IdentityType == EIdentityType.Enterprise)
+                 .WhereIF(dto.Type is 1, x => x.DelayState == EDelayState.Pass)
+                 .WhereIF(dto.Type is 2, x => x.DelayState == EDelayState.NoPass)
+                 .WhereIF(dto.Type is 3, x => x.DelayState == EDelayState.Examining)
+                 .WhereIF(dto.Type is 4, x => x.DelayState < EDelayState.Withdraw)
+             .MergeTable();
+
+            if (_sessionContext.OrgIsCenter == false)
+                query = query.Where(x => x.ApplyOrgCode.StartsWith(_sessionContext.OrgId));
+
+            var (total, items) = await query.ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
+            return new PagedDto<OrderDelayDto>(total, _mapper.Map<IReadOnlyList<OrderDelayDto>>(items));
+        }
+
+        /// <summary>
+        /// 部门延期统计明细-单独菜单 导出
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpPost("order-delay-detail/exprot")]
+        public async Task<FileStreamResult> ExportQueryOrderDelayDataDetailAsync([FromBody] ExportExcelDto<QueryOrderDelayDataDetailRequest> dto)
+        {
+            var query = _orderDelayRepository.Queryable()
+                 .Includes(x => x.Order)
+                 .WhereIF(dto.QueryDto.StartTime.HasValue, x => x.CreationTime >= dto.QueryDto.StartTime)
+                 .WhereIF(dto.QueryDto.EndTime.HasValue, x => x.CreationTime <= dto.QueryDto.EndTime)
+                .WhereIF(dto.QueryDto.TypeId != null && dto.QueryDto.TypeId == 1, x => x.Order.IdentityType == EIdentityType.Citizen)
+                .WhereIF(dto.QueryDto.TypeId != null && dto.QueryDto.TypeId == 2, x => x.Order.IdentityType == EIdentityType.Enterprise)
+                 .WhereIF(dto.QueryDto.Type is 1, x => x.DelayState == EDelayState.Pass)
+                 .WhereIF(dto.QueryDto.Type is 2, x => x.DelayState == EDelayState.NoPass)
+                 .WhereIF(dto.QueryDto.Type is 3, x => x.DelayState == EDelayState.Examining)
+                 .WhereIF(dto.QueryDto.Type is 4, x => x.DelayState < EDelayState.Withdraw)
+             .MergeTable();
+
+            if (_sessionContext.OrgIsCenter == false)
+                query = query.Where(x => x.ApplyOrgCode.StartsWith(_sessionContext.OrgId));
+
+            List<OrderDelay> list;
+            if (dto.IsExportAll)
+            {
+                list = await query.ToListAsync(HttpContext.RequestAborted);
+            }
+            else
+            {
+                var (_, items) = await query.ToPagedListAsync(dto.QueryDto, HttpContext.RequestAborted);
+                list = items;
+            }
+
+
+            var orderDtos = _mapper.Map<ICollection<OrderDelayDto>>(list);
+
+            dynamic? dynamicClass = DynamicClassHelper.CreateDynamicClass(dto.ColumnInfos);
+
+            var dtos = orderDtos
+                .Select(stu => _mapper.Map(stu, typeof(OrderDelayDto), dynamicClass))
+                .Cast<object>()
+                .ToList();
+
+            var stream = ExcelHelper.CreateStream(dtos);
+            return ExcelStreamResult(stream, "部门延期统计明细数据");
+
+        }
+
         /// <summary>
         /// 特提统计
         /// </summary>
@@ -1916,11 +1993,11 @@ namespace Hotline.Api.Controllers.Bi
                 .Select(o => new CenterReportCallDto
                 {
                     EffectiveCount = SqlFunc.AggregateSum(SqlFunc.IIF(o.OnState == EOnState.On, 1, 0)),//有效
-					InTotal = SqlFunc.AggregateSum(SqlFunc.IIF(o.CallDirection == ECallDirection.In, 1, 0)),//呼入总量
-					OutTotal = SqlFunc.AggregateSum(SqlFunc.IIF(o.CallDirection == ECallDirection.Out, 1, 0)),//呼出总量
-					InConnectionQuantity = SqlFunc.AggregateSum(SqlFunc.IIF(o.CallDirection == ECallDirection.In && o.AnsweredTime != null, 1, 0)),//呼入接通量
-					OutConnectionQuantity = SqlFunc.AggregateSum(SqlFunc.IIF(o.TelNo != "0" && o.CallDirection == ECallDirection.Out && o.AnsweredTime != null, 1, 0)),//呼出接通量
-					InvalidCount = SqlFunc.AggregateSum(SqlFunc.IIF(o.OnState == EOnState.NoOn /*&& o.BeginIvrTime.HasValue && o.BeginQueueTime.HasValue && o.BeginRingTime.HasValue*/, 1, 0)), //无效(排除队列挂断和IVR挂断)
+                    InTotal = SqlFunc.AggregateSum(SqlFunc.IIF(o.CallDirection == ECallDirection.In, 1, 0)),//呼入总量
+                    OutTotal = SqlFunc.AggregateSum(SqlFunc.IIF(o.CallDirection == ECallDirection.Out, 1, 0)),//呼出总量
+                    InConnectionQuantity = SqlFunc.AggregateSum(SqlFunc.IIF(o.CallDirection == ECallDirection.In && o.AnsweredTime != null, 1, 0)),//呼入接通量
+                    OutConnectionQuantity = SqlFunc.AggregateSum(SqlFunc.IIF(o.TelNo != "0" && o.CallDirection == ECallDirection.Out && o.AnsweredTime != null, 1, 0)),//呼出接通量
+                    InvalidCount = SqlFunc.AggregateSum(SqlFunc.IIF(o.OnState == EOnState.NoOn /*&& o.BeginIvrTime.HasValue && o.BeginQueueTime.HasValue && o.BeginRingTime.HasValue*/, 1, 0)), //无效(排除队列挂断和IVR挂断)
                     QueueByeCount = SqlFunc.AggregateSum(SqlFunc.IIF(o.CallDirection == ECallDirection.In && o.QueueTims > 0 && o.RingTimes == 0 && o.OnState == EOnState.NoOn, 1, 0)), //队列挂断
                     IvrByeCount = SqlFunc.AggregateSum(SqlFunc.IIF(o.CallDirection == ECallDirection.In && o.BeginIvrTime.HasValue && !o.BeginQueueTime.HasValue && !o.BeginRingTime.HasValue && o.OnState == EOnState.NoOn, 1, 0)), //IVR挂断
                 })
@@ -2086,8 +2163,8 @@ namespace Hotline.Api.Controllers.Bi
             {
                 sourceChannel.Add(new CenterReportOrderSourceChannelDto
                 {
-					AllCountNum = sourceChannelCount,
-					Name = item.DicDataName,
+                    AllCountNum = sourceChannelCount,
+                    Name = item.DicDataName,
                     Code = item.DicTypeCode,
                     CountNum = sourceChannelData.Find(p => p.Code == item.DicDataValue)?.CountNum ?? 0
                 });
@@ -2495,14 +2572,14 @@ namespace Hotline.Api.Controllers.Bi
 
             if (enterpriseOrderDto2 != null)
             {
-				centerReportStatisticsDto.EnterpriseOrderDto.VisitdCount = enterpriseOrderDto2.VisitdCount;
-				centerReportStatisticsDto.EnterpriseOrderDto.Dissatisfied = enterpriseOrderDto2.Dissatisfied;
-				centerReportStatisticsDto.EnterpriseOrderDto.CityDissatisfied = enterpriseOrderDto2.CityDissatisfied;
-				centerReportStatisticsDto.EnterpriseOrderDto.CountyDissatisfied = enterpriseOrderDto2.CountyDissatisfied;
-				centerReportStatisticsDto.EnterpriseOrderDto.Satisfied = enterpriseOrderDto2.Satisfied;
-				centerReportStatisticsDto.EnterpriseOrderDto.CitySatisfied = enterpriseOrderDto2.CitySatisfied;
-				centerReportStatisticsDto.EnterpriseOrderDto.CountySatisfied = enterpriseOrderDto2.CountySatisfied;
-			}
+                centerReportStatisticsDto.EnterpriseOrderDto.VisitdCount = enterpriseOrderDto2.VisitdCount;
+                centerReportStatisticsDto.EnterpriseOrderDto.Dissatisfied = enterpriseOrderDto2.Dissatisfied;
+                centerReportStatisticsDto.EnterpriseOrderDto.CityDissatisfied = enterpriseOrderDto2.CityDissatisfied;
+                centerReportStatisticsDto.EnterpriseOrderDto.CountyDissatisfied = enterpriseOrderDto2.CountyDissatisfied;
+                centerReportStatisticsDto.EnterpriseOrderDto.Satisfied = enterpriseOrderDto2.Satisfied;
+                centerReportStatisticsDto.EnterpriseOrderDto.CitySatisfied = enterpriseOrderDto2.CitySatisfied;
+                centerReportStatisticsDto.EnterpriseOrderDto.CountySatisfied = enterpriseOrderDto2.CountySatisfied;
+            }
 
             if (listInfo != null && listInfo.Rows.Count > 0 && centerReportStatisticsDto.EnterpriseOrderDto != null)
             {
@@ -2525,65 +2602,65 @@ namespace Hotline.Api.Controllers.Bi
                 centerReportStatisticsDto.EnterpriseOrderDto.CountySatisfied = centerReportStatisticsDto.EnterpriseOrderDto.CountySatisfied + Convert.ToInt32(listInfo.Rows[0]["EnterpriseCountySatisfaction"]);
             }
 
-			#endregion
-
-			#region 企业信件分类
-			//信件来源
-			var enterpriseAcceptTypeData = await _orderRepository.Queryable(false, false, false)
-				.Where(p => p.CreationTime >= StartTime && p.CreationTime <= EndTime && p.IdentityType == EIdentityType.Enterprise)
-				.Select(it => new
-				{
-					AcceptTypeCode = SqlFunc.IIF(SqlFunc.IsNullOrEmpty(it.AcceptTypeCode), "40", it.AcceptTypeCode),
-					FileOrgIsCenter = it.FileOrgIsCenter.HasValue ? it.FileOrgIsCenter : true,
-					CreationTimeHandleDurationWorkday = it.CreationTimeHandleDurationWorkday.HasValue ? it.CenterToOrgHandleDurationWorkday : 0,
-					CenterToOrgHandleDurationWorkday = it.CenterToOrgHandleDurationWorkday.HasValue ? it.CenterToOrgHandleDurationWorkday : 0,
-				})
-				.MergeTable()//将查询出来的结果合并成一个新表
-				 .GroupBy(temp => new { temp.AcceptTypeCode })//对新表进行分组
-				 .Select(temp => new CenterReportOrderSourceChannelDto
-				 {
-					 Code = temp.AcceptTypeCode,
-					 CountNum = SqlFunc.AggregateCount(temp.AcceptTypeCode),
-					 CompletedAging = SqlFunc.AggregateSum(SqlFunc.IIF(temp.FileOrgIsCenter == true, temp.CreationTimeHandleDurationWorkday, temp.CenterToOrgHandleDurationWorkday))
-				 })
-				 .ToListAsync();
-			List<CenterReportOrderSourceChannelDto> enterpriseAcceptType = new();
-			var enterpriseAcceptTypeDic = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.AcceptType);
-			foreach (var item in enterpriseAcceptTypeDic)
-			{
-				enterpriseAcceptType.Add(new CenterReportOrderSourceChannelDto
-				{
-					AllCountNum = sourceChannelCount,
-					Name = item.DicDataName,
-					Code = item.DicTypeCode,
-					CountNum = enterpriseAcceptTypeData.Find(p => p.Code == item.DicDataValue)?.CountNum ?? 0,
-					CompletedAging = enterpriseAcceptTypeData.Find(p => p.Code == item.DicDataValue)?.CompletedAging ?? 0
-				});
-			}
-
-			if (listPurOld != null && listPurOld.Rows.Count > 0 && enterpriseAcceptType != null)
-			{
-				foreach (var item in enterpriseAcceptType)
-				{
-					if (item.Code == "10")
-						item.CountNum = item.CountNum + Convert.ToInt32(listPurOld.Rows[0]["Consult"]);            // 咨询
-					else if (item.Code == "15")
-						item.CountNum = item.CountNum + Convert.ToInt32(listPurOld.Rows[0]["Suggest"]);            // 建议
-					else if (item.Code == "20")
-						item.CountNum = item.CountNum + Convert.ToInt32(listPurOld.Rows[0]["SeekHelp"]);           // 求助
-					else if (item.Code == "25")
-						item.CountNum = item.CountNum + Convert.ToInt32(listPurOld.Rows[0]["Praise"]);             // 表扬
-					else if (item.Code == "30")
-						item.CountNum = item.CountNum + Convert.ToInt32(listPurOld.Rows[0]["Report"]);             // 举报
-					else if (item.Code == "35")
-						item.CountNum = item.CountNum + Convert.ToInt32(listPurOld.Rows[0]["Complaint"]);          // 投诉
-				}
-			}
-
-			centerReportStatisticsDto.EnterpriseCenterReportOrderAcceptTypes = enterpriseAcceptType;
-			#endregion
-
-			return centerReportStatisticsDto;
+            #endregion
+
+            #region 企业信件分类
+            //信件来源
+            var enterpriseAcceptTypeData = await _orderRepository.Queryable(false, false, false)
+                .Where(p => p.CreationTime >= StartTime && p.CreationTime <= EndTime && p.IdentityType == EIdentityType.Enterprise)
+                .Select(it => new
+                {
+                    AcceptTypeCode = SqlFunc.IIF(SqlFunc.IsNullOrEmpty(it.AcceptTypeCode), "40", it.AcceptTypeCode),
+                    FileOrgIsCenter = it.FileOrgIsCenter.HasValue ? it.FileOrgIsCenter : true,
+                    CreationTimeHandleDurationWorkday = it.CreationTimeHandleDurationWorkday.HasValue ? it.CenterToOrgHandleDurationWorkday : 0,
+                    CenterToOrgHandleDurationWorkday = it.CenterToOrgHandleDurationWorkday.HasValue ? it.CenterToOrgHandleDurationWorkday : 0,
+                })
+                .MergeTable()//将查询出来的结果合并成一个新表
+                 .GroupBy(temp => new { temp.AcceptTypeCode })//对新表进行分组
+                 .Select(temp => new CenterReportOrderSourceChannelDto
+                 {
+                     Code = temp.AcceptTypeCode,
+                     CountNum = SqlFunc.AggregateCount(temp.AcceptTypeCode),
+                     CompletedAging = SqlFunc.AggregateSum(SqlFunc.IIF(temp.FileOrgIsCenter == true, temp.CreationTimeHandleDurationWorkday, temp.CenterToOrgHandleDurationWorkday))
+                 })
+                 .ToListAsync();
+            List<CenterReportOrderSourceChannelDto> enterpriseAcceptType = new();
+            var enterpriseAcceptTypeDic = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.AcceptType);
+            foreach (var item in enterpriseAcceptTypeDic)
+            {
+                enterpriseAcceptType.Add(new CenterReportOrderSourceChannelDto
+                {
+                    AllCountNum = sourceChannelCount,
+                    Name = item.DicDataName,
+                    Code = item.DicTypeCode,
+                    CountNum = enterpriseAcceptTypeData.Find(p => p.Code == item.DicDataValue)?.CountNum ?? 0,
+                    CompletedAging = enterpriseAcceptTypeData.Find(p => p.Code == item.DicDataValue)?.CompletedAging ?? 0
+                });
+            }
+
+            if (listPurOld != null && listPurOld.Rows.Count > 0 && enterpriseAcceptType != null)
+            {
+                foreach (var item in enterpriseAcceptType)
+                {
+                    if (item.Code == "10")
+                        item.CountNum = item.CountNum + Convert.ToInt32(listPurOld.Rows[0]["Consult"]);            // 咨询
+                    else if (item.Code == "15")
+                        item.CountNum = item.CountNum + Convert.ToInt32(listPurOld.Rows[0]["Suggest"]);            // 建议
+                    else if (item.Code == "20")
+                        item.CountNum = item.CountNum + Convert.ToInt32(listPurOld.Rows[0]["SeekHelp"]);           // 求助
+                    else if (item.Code == "25")
+                        item.CountNum = item.CountNum + Convert.ToInt32(listPurOld.Rows[0]["Praise"]);             // 表扬
+                    else if (item.Code == "30")
+                        item.CountNum = item.CountNum + Convert.ToInt32(listPurOld.Rows[0]["Report"]);             // 举报
+                    else if (item.Code == "35")
+                        item.CountNum = item.CountNum + Convert.ToInt32(listPurOld.Rows[0]["Complaint"]);          // 投诉
+                }
+            }
+
+            centerReportStatisticsDto.EnterpriseCenterReportOrderAcceptTypes = enterpriseAcceptType;
+            #endregion
+
+            return centerReportStatisticsDto;
         }
 
 
@@ -5512,33 +5589,6 @@ namespace Hotline.Api.Controllers.Bi
             return ExcelStreamResult(stream, "扭转信件统计");
         }
 
-        /// <summary>
-        /// 部门延期统计明细-单独菜单
-        /// </summary>
-        /// <param name="dto"></param>
-        /// <returns></returns>
-        [HttpGet("order_delay_detail")]
-        public async Task<PagedDto<OrderDelayDto>> QueryOrderDelayDataDetailAsync([FromQuery] QueryOrderDelayDataDetailRequest dto)
-        {
-            var query = _orderDelayRepository.Queryable()
-                 .Includes(x => x.Order)
-                 .WhereIF(dto.StartTime.HasValue, x => x.CreationTime >= dto.StartTime)
-                 .WhereIF(dto.EndTime.HasValue, x => x.CreationTime <= dto.EndTime)
-                .WhereIF(dto.TypeId != null && dto.TypeId == 1, x => x.Order.IdentityType == EIdentityType.Citizen)
-                .WhereIF(dto.TypeId != null && dto.TypeId == 2, x => x.Order.IdentityType == EIdentityType.Enterprise)
-                 .WhereIF(dto.Type is 1, x => x.DelayState == EDelayState.Pass)
-                 .WhereIF(dto.Type is 2, x => x.DelayState == EDelayState.NoPass)
-                 .WhereIF(dto.Type is 3, x => x.DelayState == EDelayState.Examining)
-                 .WhereIF(dto.Type is 4, x => x.DelayState < EDelayState.Withdraw)
-             .MergeTable();
-
-            if (_sessionContext.OrgIsCenter == false)
-                query = query.Where(x => x.ApplyOrgCode.StartsWith(_sessionContext.OrgId));
-
-            var (total, items) = await query.ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
-            return new PagedDto<OrderDelayDto>(total, _mapper.Map<IReadOnlyList<OrderDelayDto>>(items));
-        }
-
         /// <summary>
         /// 工单热词分析
         /// </summary>
@@ -5597,16 +5647,16 @@ namespace Hotline.Api.Controllers.Bi
         public async Task<List<OrderTsDetails>> GetQueryOrderTsDetailslOrderList(string KeyWord)
         {
             return await _orderTsDetailsRepository.Queryable()
-                .LeftJoin<Order>((ot,o)=>ot.OrderId==o.Id)
+                .LeftJoin<Order>((ot, o) => ot.OrderId == o.Id)
                    .Where((ot, o) => ot.Terms == KeyWord)
-                   .Select((ot, o)=>new OrderTsDetails
+                   .Select((ot, o) => new OrderTsDetails
                    {
-                       Id=ot.Id,
-                       Terms=ot.Terms,
-                       OrderId=o.Id,
-                       Title=o.Title,
-                       No=ot.No
-                   } )
+                       Id = ot.Id,
+                       Terms = ot.Terms,
+                       OrderId = o.Id,
+                       Title = o.Title,
+                       No = ot.No
+                   })
                    .ToListAsync(HttpContext.RequestAborted);
         }
 
@@ -5662,21 +5712,21 @@ namespace Hotline.Api.Controllers.Bi
         /// <summary>
         /// 根据知识库引用查询工单
         /// </summary>
-        /// <param name="KeyWord"></param>
+        /// <param name="dto"></param>
         /// <returns></returns>
         [HttpGet("get_query_knowledge_quote_order_list")]
-        public async Task<List<KnowledgeQuote>> GetQueryKnowledgeQuoteOrderList(string KeyWord)
+        public async Task<List<KnowledgeQuote>> GetQueryKnowledgeQuoteOrderList([FromQuery] PagedKeywordRequest dto)
         {
             return await _knowledgeQuoteRepository.Queryable()
                    .LeftJoin<Order>((kn, o) => kn.OrderId == o.Id)
-                   .Where((kn, o) => kn.KnowledgeId == KeyWord)
-                   .Select((kn, o)=>new KnowledgeQuote
+                   .Where((kn, o) => kn.KnowledgeId == dto.Keyword && kn.CreationTime >= dto.StartTime && kn.CreationTime <= dto.EndTime)
+                   .Select((kn, o) => new KnowledgeQuote
                    {
-                       KnowledgeId=kn.Id,
-                       KnowledgeTitle=o.Title,
-                       OrderId=o.Id,
-                       Title=o.Title,
-                       No=o.No,
+                       KnowledgeId = kn.Id,
+                       KnowledgeTitle = o.Title,
+                       OrderId = o.Id,
+                       Title = o.Title,
+                       No = o.No,
                    })
                    .ToListAsync(HttpContext.RequestAborted);
         }

+ 194 - 0
src/Hotline.Api/Controllers/Bi/BiQualityController.cs

@@ -0,0 +1,194 @@
+using Hotline.Share.Dtos.Order;
+using Hotline.Share.Dtos;
+using Hotline.Share.Requests;
+using MapsterMapper;
+using Microsoft.AspNetCore.Mvc;
+using Hotline.Application.Quality;
+using Hotline.Repository.SqlSugar.Extensions;
+using Hotline.Share.Dtos.Quality;
+using Hotline.Tools;
+using Hotline.Quality;
+using Hotline.Share.Enums.Quality;
+using Hotline.Caching.Services;
+using Hotline.Settings;
+using Hotline.Share.Enums.FlowEngine;
+using Hotline.Share.Enums.Order;
+using XF.Utility.EnumExtensions;
+using Hotline.Caching.Interfaces;
+using Hotline.Users;
+using XF.Domain.Repository;
+
+namespace Hotline.Api.Controllers.Bi
+{
+	public class BiQualityController : BaseController
+	{
+		private readonly IMapper _mapper;
+		private readonly IQualityApplication _qualityApplication;
+		private readonly IQualityRepository _qualityRepository;
+		private readonly ISystemSettingCacheManager _systemSettingCacheManager;
+		private readonly IRepository<User> _userRepository;
+
+		public BiQualityController(
+			IMapper mapper,
+			IQualityApplication qualityApplication,
+			IQualityRepository qualityRepository,
+			ISystemSettingCacheManager systemSettingCacheManager,
+			IRepository<User> userRepository
+			)
+		{
+			_mapper = mapper;
+			_qualityApplication = qualityApplication;
+			_qualityRepository = qualityRepository;
+			_systemSettingCacheManager = systemSettingCacheManager;
+			_userRepository = userRepository;
+		}
+
+
+		/// <summary>
+		/// 坐席质检分析
+		/// </summary>
+		/// <param name="dto"></param>
+		/// <returns></returns>
+		[HttpGet("seats_quality_analyse")]
+		public async Task<PagedDto<SeatsQualityAnalyseDto>> SeatsQualityAnalyse([FromQuery] PagedKeywordRequest dto)
+		{
+			var (total, items) = await _qualityApplication.SeatsQualityAnalyse(dto, HttpContext.RequestAborted).ToPagedListAsync(dto, HttpContext.RequestAborted);
+
+			return new PagedDto<SeatsQualityAnalyseDto>(total, _mapper.Map<IReadOnlyList<SeatsQualityAnalyseDto>>(items));
+		}
+
+		/// <summary>
+		/// 坐席质检分析导出
+		/// </summary>
+		/// <param name="dto"></param>
+		/// <returns></returns>
+		[HttpPost("seats_quality_analyse/export")]
+		public async Task<FileStreamResult> SeatsQualityAnalyseExport([FromBody] ExportExcelDto<PagedKeywordRequest> dto)
+		{
+			var query = _qualityApplication.SeatsQualityAnalyse(dto.QueryDto, HttpContext.RequestAborted);
+
+			List<Hotline.Quality.Quality> lists;
+			if (dto.IsExportAll)
+			{
+				lists = await query.ToListAsync(HttpContext.RequestAborted);
+			}
+			else
+			{
+				var (_, items) = await query.ToPagedListAsync(dto.QueryDto, HttpContext.RequestAborted);
+				lists = items;
+			}
+
+			var listDtos = _mapper.Map<ICollection<SeatsQualityAnalyseDto>>(lists);
+
+			dynamic? dynamicClass = DynamicClassHelper.CreateDynamicClass(dto.ColumnInfos);
+
+			var dtos = listDtos
+				.Select(stu => _mapper.Map(stu, typeof(SeatsQualityAnalyseDto), dynamicClass))
+				.Cast<object>()
+				.ToList();
+
+			var stream = ExcelHelper.CreateStream(dtos);
+
+			return ExcelStreamResult(stream, "坐席质检分析");
+
+		}
+
+		#region 质检工作分析
+
+		/// <summary>
+		/// 质检工单概览
+		/// </summary>
+		/// <param name="dto"></param>
+		/// <returns></returns>
+		[HttpGet("quality_order_overview")]
+		public async Task<List<QualityOrderOverviewDto>> QualityOrderOverview([FromQuery] QualityWorkAnalysisRequest dto)
+		{
+			var allOrderNum =  await _qualityRepository.Queryable().Where(x => x.Source == EQualitySource.Accepted && x.QualityTime >= dto.StartTime && x.QualityTime <= dto.EndTime && x.State == EQualityState.End).CountAsync();
+			var items = await _qualityApplication.QualityOrderOverview(dto,allOrderNum, HttpContext.RequestAborted).ToListAsync(HttpContext.RequestAborted);
+			return items;
+		}
+
+		/// <summary>
+		/// 质检工单概览导出
+		/// </summary>
+		/// <param name="dto"></param>
+		/// <returns></returns>
+		[HttpPost("quality_order_overview/export")]
+		public async Task<FileStreamResult> QualityOrderOverviewExport([FromBody] ExportExcelDto<QualityWorkAnalysisRequest> dto)
+		{
+			var allOrderNum = await _qualityRepository.Queryable().Where(x => x.Source == EQualitySource.Accepted && x.QualityTime >= dto.QueryDto.StartTime && x.QualityTime <= dto.QueryDto.EndTime && x.State == EQualityState.End).CountAsync();
+			var query = _qualityApplication.QualityOrderOverview(dto.QueryDto, allOrderNum, HttpContext.RequestAborted);
+
+			List<QualityOrderOverviewDto> listDtos;
+
+			listDtos = await query.ToListAsync(HttpContext.RequestAborted);
+
+			//var listDtos = _mapper.Map<ICollection<SeatsQualityAnalyseDto>>(lists);
+
+			dynamic? dynamicClass = DynamicClassHelper.CreateDynamicClass(dto.ColumnInfos);
+
+			var dtos = listDtos
+				.Select(stu => _mapper.Map(stu, typeof(SeatsQualityAnalyseDto), dynamicClass))
+				.Cast<object>()
+				.ToList();
+
+			var stream = ExcelHelper.CreateStream(dtos);
+
+			return ExcelStreamResult(stream, "质检工单概览");
+
+		}
+
+		/// <summary>
+		/// 坐席质检分值分析
+		/// </summary>
+		/// <param name="dto"></param>
+		/// <returns></returns>
+		[HttpGet("seats_quality_grade_analyse")]
+		public async Task<SeatsQualityGradeAnalyseDto> SeatsQualityGradeAnalyse([FromQuery] QualityWorkAnalysisRequest dto)
+		{
+			var items = await _qualityApplication.SeatsQualityGradeAnalyse(dto, HttpContext.RequestAborted).FirstAsync(HttpContext.RequestAborted);
+			return items;
+		}
+
+
+		/// <summary>
+		/// 质检分值分析/坐席质检工单分析
+		/// </summary>
+		/// <param name="dto"></param>
+		/// <returns></returns>
+		[HttpGet("month_quality_grade")]
+		public async Task<List<MonthQualityGradeDto>> MonthQualityGrade([FromQuery] QualityWorkAnalysisRequest dto)
+		{
+			var items = await _qualityApplication.MonthQualityGrade(dto, HttpContext.RequestAborted);
+			return items;
+		}
+
+
+		/// <summary>
+		/// 列表页面基础数据
+		/// </summary>
+		/// <returns></returns>
+		[HttpGet("quality_work/base")]
+		public async Task<object> QualityWorkBaseData()
+		{
+			var setting = _systemSettingCacheManager.GetSetting(SettingConstants.RoleZuoXi);
+			var roles = setting?.SettingValue.ToList();
+			var users = await _userRepository.Queryable()
+				.Includes(d => d.Organization)
+				.Includes(d => d.Roles)
+				.Where(d => d.Roles.Any(x => roles.Contains(x.Name)))
+				.Select(x => new Kv {
+					Key = x.Id,
+					Value = x.Name
+				})
+				.ToListAsync(HttpContext.RequestAborted);
+
+			var rsp = new
+			{
+				Seats = users
+			};
+			return rsp;
+		}
+		#endregion
+	}
+}

+ 9 - 5
src/Hotline.Api/Controllers/Bigscreen/DataScreenController.cs

@@ -15,6 +15,7 @@ using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Mvc;
 using Microsoft.Extensions.Options;
 using SqlSugar;
+using System;
 using XF.Domain.Repository;
 
 namespace Hotline.Api.Controllers.Bigscreen
@@ -606,6 +607,7 @@ namespace Hotline.Api.Controllers.Bigscreen
         [HttpGet("order-secondary-statistics")]
         public async Task<SecondaryProcessingOrderStatisticsDto> OrderSecondaryStatistics(DateTime StartTime, DateTime EndTime)
         {
+            DateTime? dateTime = DateTime.Now;
             EndTime = EndTime.AddDays(1).AddSeconds(-1);
             var data = new SecondaryProcessingOrderStatisticsDto
             {
@@ -622,15 +624,17 @@ namespace Hotline.Api.Controllers.Bigscreen
 
                 OrderSoonOverdueCount = await _orderSecondaryHandlingRepository.Queryable()
                 .Includes(x => x.Order)
-                 .Where(x => x.Order.ExpiredStatus == EExpiredStatus.GoingToExpired && x.AuditTime >= StartTime && x.AuditTime <= EndTime
+                 .Where(x => x.Order.Status < EOrderStatus.Filed && dateTime > x.Order.NearlyExpiredTime && dateTime < x.Order.ExpiredTime)
+                 .Where(x => x.AuditTime >= StartTime && x.AuditTime <= EndTime
                  && x.State != ESecondaryHandlingState.NotApply
                  && x.State != ESecondaryHandlingState.Apply && x.State != ESecondaryHandlingState.Refuse)
                  .CountAsync()
             };
             var da = await _orderSecondaryHandlingRepository.Queryable()
-                .LeftJoin<OrderVisit>((os, ov) => os.VisitId == ov.Id)
-                    .LeftJoin<OrderVisitDetail>((os, ov, od) => os.VisitDetailId == od.Id)
-                    .Where((os, ov, od) => ov.VisitTime >= StartTime && ov.VisitTime <= EndTime && os.State != ESecondaryHandlingState.NotApply
+                .LeftJoin<OrderVisit>((os, ov) => os.OrderId == ov.OrderId)
+                    .LeftJoin<OrderVisitDetail>((os, ov, od) => ov.Id == od.VisitId)
+                    .Where((os, ov, od) => ov.VisitState == EVisitState.Visited && od.VisitTarget == EVisitTarget.Org && ov.VisitTime >= StartTime && ov.VisitTime <= EndTime
+                    && os.State != ESecondaryHandlingState.NotApply
                     && os.State != ESecondaryHandlingState.Apply && os.State != ESecondaryHandlingState.Refuse)
                     .Select((os, ov, od) => new SecondarySatisfactionDto()
                     {
@@ -652,7 +656,7 @@ namespace Hotline.Api.Controllers.Bigscreen
         public async Task<List<OrderSecondaryHandlingDto>> OrderSecondaryHandlingDetailQuery()
         {
             var quer = await _orderSecondaryHandlingRepository.Queryable()
-                 .Includes(x=>x.Order)
+                 .Includes(x => x.Order)
                   .Where(x => x.CreationTime.Date == DateTime.Now.Date)
                   .OrderByDescending(x => x.CreationTime)
                   .Take(50)

+ 10 - 10
src/Hotline.Api/Controllers/CommonPController.cs

@@ -432,20 +432,20 @@ namespace Hotline.Api.Controllers
 					.ToListAsync();
 				var waitedList = waitedDataList.Where(x => x.Status != EOrderStatus.Countersigning &&
 				 x.Time > DateTime.Now && x.Status < EOrderStatus.Filed && DateTime.Now < x.NearlyExpiredTime
-				).OrderBy(x=>x.Time).Take(40).ToList();
+				).OrderBy(x=>x.Time).ToList();
 				waitedList = waitedList.Count > 0 ? waitedList.Copy() : waitedList;
 				//allNum += waitedList.Count > 40 ? 40 : waitedList.Count;
 				//allList.AddRange(waitedList);
 				//已超期
 				var waitedExpiredDataList = waitedDataList.Where(x => (x.Time < DateTime.Now && x.Status < EOrderStatus.Filed) ||
 				                                                      (x.Time < x.ActualHandleTime && x.Status >= EOrderStatus.Filed))
-														  .OrderBy(x=> x.Time).Take(40).ToList();
+														  .OrderBy(x=> x.Time).ToList();
 				waitedExpiredDataList = waitedExpiredDataList.Count > 0 ? waitedExpiredDataList.Copy() : waitedExpiredDataList;
 				waitedExpiredDataList.ForEach(x => x.Type = "WaitedExpired");
 				//allNum += waitedExpiredDataList.Count;
 				//allList.AddRange(waitedExpiredDataList);
 				//会签待办
-				var signDataList = waitedDataList.Where(x => x.Status == EOrderStatus.Countersigning).OrderBy(x => x.Time).Take(40).ToList();
+				var signDataList = waitedDataList.Where(x => x.Status == EOrderStatus.Countersigning).OrderBy(x => x.Time).ToList();
 				signDataList = signDataList.Count > 0 ? signDataList.Copy() : signDataList;
 				signDataList.ForEach(x => x.Type = "Sign");
 				
@@ -467,7 +467,7 @@ namespace Hotline.Api.Controllers
 						Time = d.Order.ExpiredTime,
 						Status = d.Order.Status,
 						CounterSignType = d.Order.CounterSignType
-					}).Take(40)
+					})
 					.ToListAsync();
 				//allNum += screenDataList.Count;
 				//allList.AddRange(screenDataList);
@@ -487,7 +487,7 @@ namespace Hotline.Api.Controllers
 						Time = d.Order.ExpiredTime,
 						Status = d.Order.Status,
 						CounterSignType = d.Order.CounterSignType
-					}).Take(40)
+					})
 					.ToListAsync();
 				//allNum += delayDataList.Count;
 				//allList.AddRange(delayDataList);
@@ -518,7 +518,7 @@ namespace Hotline.Api.Controllers
 					Time = x.OrderVisit.Order.ExpiredTime,
 					Status = x.OrderVisit.Order.Status,
 					CounterSignType = x.OrderVisit.Order.CounterSignType
-				}).Take(40).ToListAsync();
+				}).ToListAsync();
 				//allNum += screenApplyDataList.Count;
 				//allList.AddRange(screenApplyDataList);
 				//退回待审批
@@ -538,7 +538,7 @@ namespace Hotline.Api.Controllers
 						Time = d.Order.ExpiredTime,
 						Status = d.Order.Status,
 						CounterSignType = d.Order.CounterSignType
-					}).Take(40)
+					})
 					.ToListAsync();
 				//allNum += sendBackAuditDataList.Count;
 				//allList.AddRange(sendBackAuditDataList);
@@ -564,14 +564,14 @@ namespace Hotline.Api.Controllers
 						Time = d.ExpiredTime,
 						Status = d.Status,
 						CounterSignType = d.CounterSignType
-					}).Take(40)
+					})
 					.ToListAsync();
 				return new
 				{
 					//AllNum = allNum,
 					//AllList = allList,
-					WaitedNum = waitedList.Count > 40 ? 40 : waitedList.Count,
-					WaitedList = waitedList.Take(40).ToList(),
+					WaitedNum = waitedList.Count,
+					WaitedList = waitedList.ToList(),
 					WaitedExpiredNum = waitedExpiredDataList.Count,
 					WaitedExpiredList = waitedExpiredDataList,
 					SignDataNum = signDataList.Count,

+ 84 - 4
src/Hotline.Api/Controllers/ExportWordController.cs

@@ -2,12 +2,15 @@
 using Hotline.Caching.Interfaces;
 using Hotline.Configurations;
 using Hotline.Orders;
+using Hotline.Quality;
 using Hotline.Settings;
 using Hotline.Share.Dtos.OrderExportWord;
+using Hotline.Share.Dtos.QualityExportWord;
 using Hotline.Share.Enums.Order;
 using MapsterMapper;
 using Microsoft.AspNetCore.Mvc;
 using Microsoft.Extensions.Options;
+using MongoDB.Driver.Linq;
 using XF.Domain.Repository;
 
 namespace Hotline.Api.Controllers
@@ -22,15 +25,17 @@ namespace Hotline.Api.Controllers
         private readonly ILogger<ExportWordController> _logger;
         private readonly IOptionsSnapshot<AppConfiguration> _appOptions;
         private readonly ISystemSettingCacheManager _systemSettingCacheManager;
+		private readonly IQualityRepository _qualityRepository;
 
-        public ExportWordController(IOrderRepository orderRepository,
+		public ExportWordController(IOrderRepository orderRepository,
             IWordHelperService wordHelperService,
             IMapper mapper,
            IRepository<OrderVisit> orderVisitRepository,
            IRepository<OrderVisitDetail> orderVisitedDetailRepository,
            ILogger<ExportWordController> logger,
             IOptionsSnapshot<AppConfiguration> appOptions,
-            ISystemSettingCacheManager systemSettingCacheManager)
+            ISystemSettingCacheManager systemSettingCacheManager,
+			IQualityRepository qualityRepository)
         {
             _orderRepository = orderRepository;
             _wordHelperService = wordHelperService;
@@ -40,7 +45,9 @@ namespace Hotline.Api.Controllers
             _logger = logger;
             _appOptions = appOptions;
             _systemSettingCacheManager = systemSettingCacheManager;
-        }
+			_qualityRepository = qualityRepository;
+
+		}
 
         /// <summary>
         /// 工单交办单导出         
@@ -120,5 +127,78 @@ namespace Hotline.Api.Controllers
             return File(fileBytes, "application/octet-stream", $"{name}.zip");
         }
 
-    }
+
+        /// <summary>
+        /// 质检单导出
+        /// </summary>
+        /// <param name="Ids"></param>
+        /// <returns></returns>
+		[HttpPost("quality_certificate")]
+		public async Task<IActionResult> QualityCertificate([FromBody] List<string> Ids) {
+
+			var streams = new Dictionary<string, Stream>();
+			var path = $"{Directory.GetCurrentDirectory()}/Template/QualityCertificate.doc";
+			int num = 1;
+			foreach (var item in Ids)
+			{
+                var quality = await _qualityRepository.GetAsync(item, HttpContext.RequestAborted);
+				if (quality == null)
+					continue;
+				var order = await _orderRepository.GetAsync(quality.OrderId, HttpContext.RequestAborted);
+				if (order == null)
+					continue;
+
+				var exportTest = _mapper.Map<QualityCertificate>(order);
+
+				if (_appOptions.Value.IsZiGong)
+					exportTest.CityName = "自贡市";
+				else if (_appOptions.Value.IsYiBin)
+					exportTest.CityName = "宜宾市";
+				else if (_appOptions.Value.IsLuZhou)
+					exportTest.CityName = "泸州市";
+
+                //质检信息
+                exportTest.Grade = quality.Grade;
+                exportTest.QualityContent = quality.Content;
+				exportTest.UserName = quality.UserName;
+				exportTest.QualityTime = quality.QualityTime.HasValue? quality.QualityTime.Value.ToString("yyyy-MM-dd HH:mm:ss") :string.Empty;
+
+				//查询回访信息
+				var visitData = await _orderVisitRepository.GetAsync(p => p.OrderId == order.Id && p.VisitState == EVisitState.Visited, HttpContext.RequestAborted);
+				if (visitData != null)
+				{
+					//回访明细
+					var visitDetail = await _orderVisitedDetailRepository.Queryable().Where(p => p.VisitId == visitData.Id && p.VisitTarget == Share.Enums.Order.EVisitTarget.Org).ToListAsync();
+					string visit = "";
+					foreach (var itemVisit in visitDetail)
+					{
+						visit += "回访部门:" + itemVisit.VisitOrgName;
+						visit += " \n办件结果:" + itemVisit.OrgProcessingResults?.Value + "    办事态度:" + itemVisit.OrgHandledAttitude?.Value + "\n";
+
+						if (itemVisit.VisitOrgCode == order.ActualHandleOrgCode)
+							exportTest.VisitContent = "回访内容:" + itemVisit.VisitContent;
+					}
+					exportTest.VisitOrg = visit;
+				}
+				if (Ids.Count > 1)
+				{
+					streams.Add(order.No + "_" + num + path.Substring(path.LastIndexOf(".")), _wordHelperService.WordStream(path, exportTest));
+					num = num + 1;
+				}
+				else
+				{
+					var btyes = _wordHelperService.WordByte(path, exportTest);
+					HttpContext.Response.Headers.Add("Access-Control-Expose-Headers", "Content-Disposition");
+					return File(btyes, "application/vnd.ms-word", order.No + path.Substring(path.LastIndexOf(".")));
+				}
+			}
+
+			//调用压缩方法 进行压缩 (接收byte[] 数据)
+			byte[] fileBytes = _wordHelperService.ConvertZipStream(streams);
+			HttpContext.Response.Headers.Add("Access-Control-Expose-Headers", "Content-Disposition");
+			var name = DateTime.Now.ToString("yyyyMMddHHmmss");
+			return File(fileBytes, "application/octet-stream", $"{name}.zip");
+		}
+
+	}
 }

+ 93 - 74
src/Hotline.Api/Controllers/KnowledgeCommonController.cs

@@ -24,19 +24,19 @@ namespace Hotline.Api.Controllers
         private readonly IRepository<KnowledgeStandard> _knowledgeStandardRepository;
         private readonly IMapper _mapper;
         private readonly ISessionContext _sessionContext;
-		private readonly ISystemOrganizeRepository _systemOrganizeRepository;
-		private readonly IRepository<Hotspot> _hotspotTypeRepository;
+        private readonly ISystemOrganizeRepository _systemOrganizeRepository;
+        private readonly IRepository<Hotspot> _hotspotTypeRepository;
 
-		/// <summary>
-		/// 
-		/// </summary>
-		/// <param name="knowledgeTypeRepository"></param>
-		/// <param name="knowledgeTypeDomainService"></param>
-		/// <param name="knowledgeStandardRepository"></param>
-		/// <param name="mapper"></param>
-		public KnowledgeCommonController(IRepository<KnowledgeType> knowledgeTypeRepository, IKnowledgeTypeDomainService knowledgeTypeDomainService,
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="knowledgeTypeRepository"></param>
+        /// <param name="knowledgeTypeDomainService"></param>
+        /// <param name="knowledgeStandardRepository"></param>
+        /// <param name="mapper"></param>
+        public KnowledgeCommonController(IRepository<KnowledgeType> knowledgeTypeRepository, IKnowledgeTypeDomainService knowledgeTypeDomainService,
             IRepository<KnowledgeStandard> knowledgeStandardRepository, ISessionContext sessionContext,
-			IMapper mapper, ISystemOrganizeRepository systemOrganizeRepository, IRepository<Hotspot> hotspotTypeRepository)
+            IMapper mapper, ISystemOrganizeRepository systemOrganizeRepository, IRepository<Hotspot> hotspotTypeRepository)
         {
             _knowledgeTypeRepository = knowledgeTypeRepository;
             _knowledgeTypeDomainService = knowledgeTypeDomainService;
@@ -45,7 +45,7 @@ namespace Hotline.Api.Controllers
             _sessionContext = sessionContext;
             _systemOrganizeRepository = systemOrganizeRepository;
             _hotspotTypeRepository = hotspotTypeRepository;
-		}
+        }
         #endregion
 
         #region 知识分类
@@ -93,8 +93,8 @@ namespace Hotline.Api.Controllers
         public async Task<KnowledgeType> GetType(string Id)
         {
             var types = await _knowledgeTypeRepository.Queryable()
-                .Includes(x=>x.KnowledgeTypeOrgs)
-                .Where(x=>x.Id  == Id)
+                .Includes(x => x.KnowledgeTypeOrgs)
+                .Where(x => x.Id == Id)
                 .FirstAsync(HttpContext.RequestAborted);
             if (types is null)
                 throw UserFriendlyException.SameMessage("查询失败!");
@@ -140,23 +140,42 @@ namespace Hotline.Api.Controllers
         /// <param name="IsEnable">不传查询所有</param>
         /// <returns></returns>
         [HttpGet("treelist")]
-        public async Task<List<KnowledgeTypeDto>> GetTreeList(bool? IsEnable,string? Attribution)
+        public async Task<List<KnowledgeTypeDto>> GetTreeList(bool? IsEnable, string? Attribution)
         {
-            return await _knowledgeTypeRepository.Queryable().WhereIF(IsEnable.HasValue, x =>x.IsEnable == IsEnable)
-				 //.Where(x => x.KnowledgeTypeOrgs.Any(to => to.OrgId == _sessionContext.RequiredOrgId) || x.KnowledgeTypeOrgs.Any() == false)
-				 .Where(x=> SqlFunc.Subqueryable<KnowledgeTypeOrg>().Where(to=>to.TypeId == x.Id && to.OrgId == _sessionContext.RequiredOrgId).Any()
-				 || SqlFunc.Subqueryable<KnowledgeTypeOrg>().Where(to => to.TypeId == x.Id).NotAny())
-				 .Select(x=> new KnowledgeTypeDto()
-				 {
+            var query = _knowledgeTypeRepository.Queryable();
+            if (!_sessionContext.OrgIsCenter)
+                query.Where(x => x.Orgs.Any(s => s.Id == _sessionContext.RequiredOrgId));
+            return await query.WhereIF(IsEnable.HasValue, x => x.IsEnable == IsEnable)
+                 .Select(x => new KnowledgeTypeDto()
+                 {
                      Id = x.Id.SelectAll(),
-                     KnowledgeNum = SqlFunc.Subqueryable<KnowledgeRelationType>().LeftJoin<Knowledge>((kr,k)=> kr.KnowledgeId == k.Id)
-	                     .Where((kr, k)=>kr.KnowledgeTypeSpliceName.StartsWith(x.SpliceName))
-	                     .WhereIF(!string.IsNullOrEmpty(Attribution),(kr,k)=>k.Attribution == Attribution).DistinctCount(kr=>kr.KnowledgeId)
+                     KnowledgeNum = SqlFunc.Subqueryable<KnowledgeRelationType>().LeftJoin<Knowledge>((kr, k) => kr.KnowledgeId == k.Id)
+                         .Where((kr, k) => kr.KnowledgeTypeSpliceName.StartsWith(x.SpliceName))
+                         .WhereIF(!string.IsNullOrEmpty(Attribution), (kr, k) => k.Attribution == Attribution).DistinctCount(kr => kr.KnowledgeId)
 
-				 })
-				 .OrderBy(x => x.Sort).ToTreeAsync(it => it.children, it => it.ParentId, null,it=> it.Id);
+                 })
+                 .OrderBy(x => x.Sort)
+                 .ToTreeAsync(it => it.children, it => it.ParentId, null, it => it.Id);
         }
 
+        //[HttpGet("treelist")]
+        //public async Task<List<KnowledgeTypeDto>> GetTreeList(bool? IsEnable, string? Attribution)
+        //{
+        //    return await _knowledgeTypeRepository.Queryable()
+        //        .WhereIF(IsEnable.HasValue, x => x.IsEnable == IsEnable)
+        //        .Where(x => SqlFunc.Subqueryable<KnowledgeTypeOrg>().Where(to => to.TypeId == x.Id && to.OrgId == _sessionContext.RequiredOrgId).Any()
+        //                    || SqlFunc.Subqueryable<KnowledgeTypeOrg>().Where(to => to.TypeId == x.Id).NotAny())
+        //        .Select(x => new KnowledgeTypeDto()
+        //        {
+        //            Id = x.Id.SelectAll(),
+        //            KnowledgeNum = SqlFunc.Subqueryable<KnowledgeRelationType>().LeftJoin<Knowledge>((kr, k) => kr.KnowledgeId == k.Id)
+        //                .Where((kr, k) => kr.KnowledgeTypeSpliceName.StartsWith(x.SpliceName))
+        //                .WhereIF(!string.IsNullOrEmpty(Attribution), (kr, k) => k.Attribution == Attribution).DistinctCount(kr => kr.KnowledgeId)
+
+        //        })
+        //        .OrderBy(x => x.Sort).ToTreeAsync(it => it.children, it => it.ParentId, null, it => it.Id);
+        //}
+
         /// <summary>
         /// 知识部门
         /// </summary>
@@ -166,25 +185,25 @@ namespace Hotline.Api.Controllers
         public async Task<List<KnowledgeOrgDto>> GetTreeList(string? Attribution)
         {
 
-	        //await Db.Queryable<SystemOrganize>()
-		       // .Where(it => it.Id.StartsWith(orgCode))
-		       // .OrderBy(d => d.Id)
-		       // .ToTreeAsync(it => it.Children, it => it.ParentId, orgCode.Length > 6 ? orgCode.Substring(0, orgCode.Length - 3) : null);
-	        var orgCode = _sessionContext.RequiredOrgId;
-			var query=  _systemOrganizeRepository.Queryable().WhereIF(!_sessionContext.OrgIsCenter, it => it.Id.StartsWith(_sessionContext.RequiredOrgId))
-					.Select(it=> new KnowledgeOrgDto()
-					{
-						Id = it.Id.SelectAll(),
-						KnowledgeNum = SqlFunc.Subqueryable<Knowledge>()
-							.Where(k => k.CreatorOrgId.StartsWith(it.Id))
-							.WhereIF(!string.IsNullOrEmpty(Attribution),  k => k.Attribution == Attribution).DistinctCount(k => k.Id)
+            //await Db.Queryable<SystemOrganize>()
+            // .Where(it => it.Id.StartsWith(orgCode))
+            // .OrderBy(d => d.Id)
+            // .ToTreeAsync(it => it.Children, it => it.ParentId, orgCode.Length > 6 ? orgCode.Substring(0, orgCode.Length - 3) : null);
+            var orgCode = _sessionContext.RequiredOrgId;
+            var query = _systemOrganizeRepository.Queryable().WhereIF(!_sessionContext.OrgIsCenter, it => it.Id.StartsWith(_sessionContext.RequiredOrgId))
+                    .Select(it => new KnowledgeOrgDto()
+                    {
+                        Id = it.Id.SelectAll(),
+                        KnowledgeNum = SqlFunc.Subqueryable<Knowledge>()
+                            .Where(k => k.CreatorOrgId.StartsWith(it.Id))
+                            .WhereIF(!string.IsNullOrEmpty(Attribution), k => k.Attribution == Attribution).DistinctCount(k => k.Id)
 
-					}).OrderBy(d => d.Id);
+                    }).OrderBy(d => d.Id);
 
-			if (_sessionContext.OrgIsCenter)
-			{
-				return await query.ToTreeAsync(x => x.Children, it => it.ParentId, null);
-			}
+            if (_sessionContext.OrgIsCenter)
+            {
+                return await query.ToTreeAsync(x => x.Children, it => it.ParentId, null);
+            }
             return await query.ToTreeAsync(it => it.Children, it => it.ParentId, orgCode.Length > 6 ? orgCode.Substring(0, orgCode.Length - 3) : null);
 
         }
@@ -195,20 +214,20 @@ namespace Hotline.Api.Controllers
         [HttpGet("treelist/hotspot")]
         public async Task<IReadOnlyList<KnowledgeHotSpotDto>> GetChildren([FromQuery] string? id, string? Attribution)
         {
-	        var list = await _hotspotTypeRepository.Queryable()
-		        .WhereIF(!string.IsNullOrEmpty(id), x => x.ParentId == id)
-		        .WhereIF(string.IsNullOrEmpty(id), x => x.ParentId == null || x.ParentId == "")
-		        .OrderBy(d => d.HotSpotName)
-		        .Select(x => new KnowledgeHotSpotDto
-				{
-					Id = x.Id.SelectAll(),
-					HasChild = SqlFunc.Subqueryable<Hotspot>().Where(d => d.ParentId == x.Id).NotAny(),
-					KnowledgeNum = SqlFunc.Subqueryable<Knowledge>().LeftJoin<Hotspot>((k,h)=>k.HotspotId == h.Id)
-						.Where((k,h) =>h.HotSpotFullName.StartsWith(x.HotSpotFullName))
-						.WhereIF(!string.IsNullOrEmpty(Attribution), (k,h) => k.Attribution == Attribution).DistinctCount(k => k.Id)
-				}).ToListAsync();
+            var list = await _hotspotTypeRepository.Queryable()
+                .WhereIF(!string.IsNullOrEmpty(id), x => x.ParentId == id)
+                .WhereIF(string.IsNullOrEmpty(id), x => x.ParentId == null || x.ParentId == "")
+                .OrderBy(d => d.HotSpotName)
+                .Select(x => new KnowledgeHotSpotDto
+                {
+                    Id = x.Id.SelectAll(),
+                    HasChild = SqlFunc.Subqueryable<Hotspot>().Where(d => d.ParentId == x.Id).NotAny(),
+                    KnowledgeNum = SqlFunc.Subqueryable<Knowledge>().LeftJoin<Hotspot>((k, h) => k.HotspotId == h.Id)
+                        .Where((k, h) => h.HotSpotFullName.StartsWith(x.HotSpotFullName))
+                        .WhereIF(!string.IsNullOrEmpty(Attribution), (k, h) => k.Attribution == Attribution).DistinctCount(k => k.Id)
+                }).ToListAsync();
 
-	        return list;
+            return list;
         }
 
 
@@ -220,27 +239,27 @@ namespace Hotline.Api.Controllers
         [HttpGet("treelist/children-hasname")]
         public async Task<IReadOnlyList<KnowledgeHotSpotDto>> GetChildrenHasName([FromQuery] string Name, string? Attribution)
         {
-	        var arr = _hotspotTypeRepository.Queryable()
-		        .WhereIF(!string.IsNullOrEmpty(Name), x => x.HotSpotFullName.Contains(Name)).Select(x => x.Id).ToList().Cast<object>().ToArray();
-	        //.ToTreeAsync(x => x.Children, it => it.ParentId, null);
-	        return await _hotspotTypeRepository.Queryable().Select(x => new KnowledgeHotSpotDto
-			{
-				Id = x.Id.SelectAll(),
-				HasChild = SqlFunc.Subqueryable<Hotspot>().Where(d => d.ParentId == x.Id).NotAny(),
-				KnowledgeNum = SqlFunc.Subqueryable<Knowledge>().LeftJoin<Hotspot>((k, h) => k.HotspotId == h.Id)
-					.Where((k, h) => h.HotSpotFullName.StartsWith(x.HotSpotFullName))
-					.WhereIF(!string.IsNullOrEmpty(Attribution), (k, h) => k.Attribution == Attribution).DistinctCount(k => k.Id)
-			}).ToTreeAsync(x => x.Children, it => it.ParentId, "", arr);
+            var arr = _hotspotTypeRepository.Queryable()
+                .WhereIF(!string.IsNullOrEmpty(Name), x => x.HotSpotFullName.Contains(Name)).Select(x => x.Id).ToList().Cast<object>().ToArray();
+            //.ToTreeAsync(x => x.Children, it => it.ParentId, null);
+            return await _hotspotTypeRepository.Queryable().Select(x => new KnowledgeHotSpotDto
+            {
+                Id = x.Id.SelectAll(),
+                HasChild = SqlFunc.Subqueryable<Hotspot>().Where(d => d.ParentId == x.Id).NotAny(),
+                KnowledgeNum = SqlFunc.Subqueryable<Knowledge>().LeftJoin<Hotspot>((k, h) => k.HotspotId == h.Id)
+                    .Where((k, h) => h.HotSpotFullName.StartsWith(x.HotSpotFullName))
+                    .WhereIF(!string.IsNullOrEmpty(Attribution), (k, h) => k.Attribution == Attribution).DistinctCount(k => k.Id)
+            }).ToTreeAsync(x => x.Children, it => it.ParentId, "", arr);
         }
 
 
-		/// <summary>
-		/// 知识分类- 只获取一级
-		/// </summary>
-		/// <param name="ParentId">不传默认查询第一级</param>
-		/// <param name="IsEnable">不传查询所有,包含已经禁用的</param>
-		/// <returns></returns>
-		[HttpGet("typelist")]
+        /// <summary>
+        /// 知识分类- 只获取一级
+        /// </summary>
+        /// <param name="ParentId">不传默认查询第一级</param>
+        /// <param name="IsEnable">不传查询所有,包含已经禁用的</param>
+        /// <returns></returns>
+        [HttpGet("typelist")]
         public async Task<List<KnowledgeType>> GetTypeList(string? ParentId, bool? IsEnable)
         {
             return await _knowledgeTypeRepository

+ 56 - 35
src/Hotline.Api/Controllers/KnowledgeController.cs

@@ -958,7 +958,7 @@ namespace Hotline.Api.Controllers
         }
 
         /// <summary>
-        /// 下载知识导入模板
+        /// 下载知识导入模板(new)
         /// </summary>
         /// <returns></returns>
         [HttpGet("dl-template")]
@@ -970,46 +970,67 @@ namespace Hotline.Api.Controllers
             }, "知识模板.xlsx");
         }
 
+        /// <summary>
+        /// 导入知识(new)
+        /// </summary>
+        /// <param name="file"></param>
+        /// <returns></returns>
         [HttpPost("import-knowledge")]
-        public async Task<object> ImportKnowledge(IFormFile file)
-        {
-            using (var stream = new MemoryStream())
+        public async Task<string> ImportKnowledge(IFormFile file)
+        {
+            using var stream = new MemoryStream();
+            await file.CopyToAsync(stream);
+            var items = stream.Query<KnowledgeImportTemplate>().ToList();
+            if (items == null || !items.Any()) return "未读取到数据";
+
+            if (items.Any(d => d.Attribution != "中心知识库" && d.Attribution != "部门知识库"))
+                return "请正确输入知识归属";
+            if (items.Any(d => d.IsPublic != "是" && d.IsPublic != "否"))
+                return "请正确输入是否公开";
+            if (items.Any(d => string.IsNullOrEmpty(d.Title)))
+                return "请输入标题";
+            if (items.Any(d => string.IsNullOrEmpty(d.Content)))
+                return "请输入内容";
+
+            var titles = items.Select(d => d.Title).ToList();
+            var exists = await _knowledgeRepository.Queryable()
+                .Where(d => titles.Contains(d.Title))
+                .ToListAsync(HttpContext.RequestAborted);
+            if (exists.Any())
+                return $"标题重复:{string.Join(',', exists.Select(d => d.Title))}";
+
+            var inputLevelOneTypes = items.Select(d => d.KnowledgeTypeLevelOne)
+                .Distinct().ToList();
+            var levelOneTypes = await _knowledgeTypeRepository.Queryable()
+                .Where(d => d.IsEnable && d.ParentId == null)
+                .ToListAsync(HttpContext.RequestAborted);
+            var existLevelOneNames = levelOneTypes.Select(d => d.Name);
+            var notExists = inputLevelOneTypes.Except(existLevelOneNames).ToList();
+            if (notExists.Any())
+                return $"不存在的一级分类名称:{string.Join(',', notExists)}";
+
+            var sb = new StringBuilder();
+            var kns = new List<Knowledge>();
+            foreach (var item in items)
             {
-                await file.CopyToAsync(stream);
-                var items = stream.Query<KnowledgeImportTemplate>().ToList();
-                if (items == null || !items.Any()) return "未读取到数据";
-
-                if (items.Any(d => d.Attribution != "中心知识库" && d.Attribution != "部门知识库"))
-                    return "请正确输入知识归属";
-                if (items.Any(d => d.IsPublic != "是" && d.IsPublic != "否"))
-                    return "请正确输入是否公开";
-                if (items.Any(d => string.IsNullOrEmpty(d.Title)))
-                    return "请输入标题";
-                if (items.Any(d => string.IsNullOrEmpty(d.Content)))
-                    return "请输入内容";
-
-                var titles = items.Select(d => d.Title).ToList();
-                var exists = await _knowledgeRepository.Queryable()
-                    .Where(d => titles.Contains(d.Title))
-                    .ToListAsync(HttpContext.RequestAborted);
-                if (exists.Any())
-                    return $"标题重复:{string.Join(',', exists.Select(d => d.Title))}";
-
-                var levelOneTypes = items.Select(d => d.KnowledgeTypeLevelOne).ToList();
-                _knowledgeTypeRepository.Queryable()
-                    .Where(d => d.IsEnable);//todo
-
-                foreach (var item in items)
+                var kn = _mapper.Map<Knowledge>(item);
+                var knType = levelOneTypes.FirstOrDefault(d => d.Name == item.KnowledgeTypeLevelOne);
+                if (knType is null)
                 {
-                    var kn = _mapper.Map<Knowledge>(item);
-
+                    sb.Append($"错误一级分类:{item.KnowledgeTypeLevelOne}");
+                    continue;
                 }
+                kn.KnowledgeTypes = new List<KnowledgeType> { knType };
+                kn.IsPublic = item.IsPublic == "是";
+                kns.Add(kn);
+            }
 
-                return new
-                {
+            await _knowledgeRepository.AddRangeAsync(kns, HttpContext.RequestAborted);
 
-                };
-            }
+            var err = sb.ToString();
+            return string.IsNullOrEmpty(err)
+                ? $"导入{items.Count}条,成功{kns.Count}条"
+                : $"导入{items.Count}条,成功{kns.Count}条,{err}";
         }
 
 

+ 76 - 2
src/Hotline.Api/Controllers/OrderController.cs

@@ -1985,11 +1985,11 @@ public class OrderController : BaseController
         if (int.Parse(setting?.SettingValue[0]) != 0 && !_sessionContext.OrgIsCenter)
         {
             int count = await _orderDelayRepository.CountAsync(x =>
-                x.OrderId == delaydto.OrderId && x.ApplyOrgCode == _sessionContext.RequiredOrgId && x.DelayState == EDelayState.Pass);
+                x.OrderId == delaydto.OrderId && x.ApplyOrgCode.Substring(SqlFunc.MappingColumn<int>("0"), SqlFunc.MappingColumn<int>("6")) == _sessionContext.RequiredOrgId.Substring(SqlFunc.MappingColumn<int>("0"), SqlFunc.MappingColumn<int>("6")) && x.DelayState == EDelayState.Pass);
             if (_appOptions.Value.IsZiGong)
             {
                 count = await _orderDelayRepository.CountAsync(x =>
-                    x.OrderId == delaydto.OrderId && x.ApplyOrgCode == _sessionContext.RequiredOrgId &&
+                    x.OrderId == delaydto.OrderId &&  x.ApplyOrgCode == _sessionContext.RequiredOrgId &&
                     (x.DelayState == EDelayState.Pass || x.DelayState == EDelayState.NoPass));
             }
 
@@ -2069,6 +2069,78 @@ public class OrderController : BaseController
         }
     }
 
+	/// <summary>
+	/// 批量审批延期
+	/// </summary>
+	[HttpPost("delay/batch_audit")]
+	[LogFilter("批量审批延期")]
+	public async Task<string> BatchAuditDelay([FromBody] BatchDelayNextFlowDto dto) {
+		var result = new StringBuilder();
+		var fail = 0;
+		var success = 0;
+		foreach (var item in dto.DelayId)
+		{
+			try
+			{
+                var workflow = dto.NextWorkflow;
+				var delay = await _orderDelayRepository.GetAsync(item, HttpContext.RequestAborted);
+				workflow.WorkflowId = delay.WorkflowId;
+				NextStepsWithOpinionDto<NextStepOption> next = null;
+
+				try
+				{
+					next = await _workflowApplication.GetNextStepsAsync(delay.WorkflowId, HttpContext.RequestAborted);
+				}
+				catch (UserFriendlyException e)
+				{
+					if (e.Message.Contains("未找到对应节点"))
+					{
+						result.Append("无权审核:" + delay.No);
+						fail++;
+					}
+					else
+					{
+						throw;
+					}
+				}
+				if (next == null) continue;
+               
+				var isBatch = next.Steps.Where(x => x.Value == workflow.NextStepName).Any();
+                if (isBatch)
+                {
+					var step = next.Steps.Where(x => x.Value == workflow.NextStepName).FirstOrDefault();
+					workflow.NextStepCode = step.Key;
+					workflow.NextStepName = step.Value;
+				}
+                else {
+					result.Append("无权审核:" + delay.No);
+					fail++;
+                    continue;
+				}
+
+				workflow.StepId = next.StepId;
+                workflow.ReviewResult = dto.IsPass ? EReviewResult.Approval : EReviewResult.Failed;
+
+				if (workflow.ReviewResult == EReviewResult.Approval) {
+					await _workflowDomainService.NextAsync(_sessionContext, workflow,cancellationToken: HttpContext.RequestAborted);
+				}
+				else
+				{
+					var reject = workflow.Adapt<RejectDto>();
+					await _workflowApplication.RejectAsync(reject, HttpContext.RequestAborted);
+				}
+				success++;
+			}
+			catch (UserFriendlyException e)
+			{
+				result.Append(e.Message);
+				fail++;
+			}
+		}
+		return $"总共: {dto.DelayId.Length}, 成功: {success}, 失败: {fail}, 失败原因: {result.ToString()}";
+
+	}
+
     /// <summary>
     ///  延期查询流程办理下一步可选节点
     /// </summary>
@@ -4033,6 +4105,7 @@ public class OrderController : BaseController
         if (orderId.NotNullOrEmpty())
         {
             outDto.Opinion = await _typeCache.GetAsync($"tmp_opinion_{orderId}{_sessionContext.UserId}", HttpContext.RequestAborted);
+            outDto.Content = (await _orderRepository.GetAsync(orderId, HttpContext.RequestAborted))?.Content;
         }
 
         //随手拍
@@ -4362,6 +4435,7 @@ public class OrderController : BaseController
             throw UserFriendlyException.SameMessage("该工单未开启流程");
         var dto = await _workflowApplication.GetNextStepsAsync(order.WorkflowId, HttpContext.RequestAborted);
         dto.ExpiredTime = order.ExpiredTime;
+        dto.Content = order.Content;
         var rsp = _mapper.Map<NextStepsWithOpinionDto<RecommendStepOption>>(dto);
         foreach (var step in rsp.Steps)
         {

+ 1 - 0
src/Hotline.Api/Controllers/PlanController.cs

@@ -29,6 +29,7 @@ namespace Hotline.Api.Controllers
     public class PlanController : BaseController
     {
 
+
         #region 注入
 
         private readonly IMapper _mapper;

+ 88 - 15
src/Hotline.Api/Controllers/QualityController.cs

@@ -21,6 +21,9 @@ using Hotline.Application.CallCenter;
 using Hotline.CallCenter.Configs;
 using Microsoft.Extensions.Options;
 using Hotline.Configurations;
+using Hotline.CallCenter.Calls;
+using System.Linq;
+using Hotline.Repository.SqlSugar.Quality;
 
 namespace Hotline.Api.Controllers
 {
@@ -47,8 +50,9 @@ namespace Hotline.Api.Controllers
         private readonly ICallApplication _callApplication;
         private readonly IOptionsSnapshot<AppConfiguration> _appOptions;
         private readonly IRepository<SystemLog> _logRepository;
+		private readonly IRepository<QualityTransferRecords> _qualityTransferRecordsRepository;
 
-        public QualityController(
+		public QualityController(
             ISessionContext sessionContext,
             IMapper mapper,
             IQualityRepository qualitey,
@@ -67,7 +71,8 @@ namespace Hotline.Api.Controllers
             ISystemSettingCacheManager systemSettingCacheManager,
             ICallApplication callApplication,
             IOptionsSnapshot<AppConfiguration> appOptions,
-            IRepository<SystemLog> logRepository)
+            IRepository<SystemLog> logRepository,
+			IRepository<QualityTransferRecords> qualityTransferRecordsRepository)
         {
             _sessionContext = sessionContext;
             _mapper = mapper;
@@ -88,7 +93,8 @@ namespace Hotline.Api.Controllers
             _callApplication = callApplication;
             _appOptions = appOptions;
             _logRepository = logRepository;
-        }
+            _qualityTransferRecordsRepository = qualityTransferRecordsRepository;
+		}
         #region 质检管理
         /// <summary>
         /// 删除质检
@@ -117,12 +123,25 @@ namespace Hotline.Api.Controllers
             await _qualityApplication.UpdateQualityAsync(dto, HttpContext.RequestAborted);
         }
 
-        /// <summary>
-        /// 获取质检列表
-        /// </summary>
-        /// <param name="dto"></param>
-        /// <returns></returns>
-        [HttpGet("list")]
+		/// <summary>
+		/// 更新质检
+		/// </summary>
+		/// <param name="dto"></param>
+		/// <returns></returns>
+		[HttpPut("transfer")]
+		public async Task TransferUpdate([FromBody] TransferUpdate dto)
+		{
+			await _qualitey.Updateable().SetColumns(x=> new Hotline.Quality.Quality { Transfer  = dto.Transfer }).Where(x=>x.Id == dto.Id).ExecuteCommandAsync(HttpContext.RequestAborted);
+		}
+
+
+
+		/// <summary>
+		/// 获取质检列表
+		/// </summary>
+		/// <param name="dto"></param>
+		/// <returns></returns>
+		[HttpGet("list")]
         public async Task<PagedDto<QualityDto>> List([FromQuery] QualityListDto dto)
         {
             var (total, items) = await _qualitey.Queryable()
@@ -611,12 +630,66 @@ namespace Hotline.Api.Controllers
             return rsp;
         }
 
-        /// <summary>
-        /// 智能质检结果返回接收 
-        /// </summary>
-        /// <param name="dto"></param>
-        /// <returns></returns>
-        [AllowAnonymous]
+		/// <summary>
+		/// 智能质检转写_兴唐
+		/// </summary>
+		/// <param name="dto"></param>
+		/// <returns></returns>
+		[HttpPost("aitransfer_xt")]
+		[LogFilter("智能质检转写_兴唐")]
+		public async Task AiTransfer_XT([FromBody] List<AiQualityXTDto> dto)
+		{
+			var transfers = new List<QualityTransferRecords>();
+			foreach (var item in dto)
+			{
+                var records =  await _qualityTransferRecordsRepository.Queryable().Where(x=>x.QualityId == item.Id  &&  x.IsFinished == false).AnyAsync();
+                if (records)
+                    continue;
+
+				var transfer = new QualityTransferRecords();
+				transfer.QualityId = item.Id;
+				transfer.IsFinished = false;
+				transfer.TransferState = EQualityTransferState.NotStarted;
+				transfers.Add(transfer);
+				await _qualitey.Updateable().SetColumns(x => new Hotline.Quality.Quality { TransferState = EQualityTransferState.Translating }).Where(x => x.Id == item.Id).ExecuteCommandAsync();
+			}
+			await _qualityTransferRecordsRepository.AddRangeAsync(transfers);
+		}
+
+		/// <summary>
+		/// 智能质检转写
+		/// </summary>
+		/// <param name="dto"></param>
+		/// <returns></returns>
+		[AllowAnonymous]
+		[HttpPost("transfer")]
+		[LogFilter("智能质检转写_兴唐_定时调用")]
+		public async Task AiTransfer_XT()
+		{
+            var translatings = await _qualityTransferRecordsRepository.Queryable().Where(x => x.IsFinished == false && x.TransferState == EQualityTransferState.Translating).OrderBy(x => x.TransferTime).ToListAsync();
+            if (translatings.Any())
+            {
+                var transfer = translatings.FirstOrDefault();
+                if ((DateTime.Now - transfer.TransferTime.Value).TotalMinutes >= 30)
+                {
+                    await _qualityTransferRecordsRepository.Updateable().SetColumns(x => new QualityTransferRecords { TransferState = EQualityTransferState.Lose }).Where(x => x.Id == transfer.Id).ExecuteCommandAsync();
+                }
+                return;
+            }
+            else {
+				var notStarted = await _qualityTransferRecordsRepository.Queryable().Where(x => x.IsFinished == false && x.TransferState == EQualityTransferState.NotStarted).OrderBy(x => x.TransferTime).FirstAsync();
+				await _qualityTransferRecordsRepository.Updateable().SetColumns(x => new QualityTransferRecords { TransferState = EQualityTransferState.Translating , TransferTime = DateTime.Now }).Where(x => x.Id == notStarted.Id).ExecuteCommandAsync();
+				_qualityApplication.Transfer_XT(notStarted.QualityId, HttpContext.RequestAborted);
+			}
+		}
+
+
+		/// <summary>
+		/// 智能质检结果返回接收 
+		/// </summary>
+		/// <param name="dto"></param>
+		/// <returns></returns>
+		[AllowAnonymous]
         [HttpPost("AiResult")]
         [LogFilter("智能质检结果返回接收")]
         public async Task AiResult([FromBody] List<AiQualityResultDto> dto)

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

@@ -176,6 +176,15 @@ public class IndustryController : BaseController
     [HttpGet("sms_template/{id}")]
     public async Task<SnapshotSMSTemplateItemsOutDto> GetSMSTemplateDetailAsync(string id)
         => await _industryApplication.GetSMSTemplateDetailAsync(id);
+
+    /// <summary>
+    /// 删除短信模板
+    /// </summary>
+    /// <param name="ids"></param>
+    /// <returns></returns>
+    [HttpDelete("sms_template")]
+    public async Task DeleteSmsTemplateAsync([FromBody] IList<string> ids)
+        => await _industryApplication.DeleteSMSTemplateAsync(ids);
     #endregion
 
     #region 区域从业人员

+ 18 - 0
src/Hotline.Api/Controllers/Snapshot/InviteCodeController.cs

@@ -45,6 +45,15 @@ public class InviteCodeController : BaseController
     public async Task<PagedDto<InviteCode>> GetInviteCodeItemsAsync([FromQuery] GetInviteCodeItemsInDto dto)
         => (await _inviteCodeApplication.GetInviteCodeItemsAsync().ToPagedListAsync(dto)).ToPaged();
 
+
+    /// <summary>
+    /// 删除邀请码
+    /// </summary>
+    /// <returns></returns>
+    [HttpDelete]
+    public async Task DeleteInviteCodeAsync([FromBody] IList<string> ids)
+        => await _inviteCodeApplication.DeleteInviteCodeAsync(ids);
+
     /// <summary>
     /// 邀请码详情
     /// </summary>
@@ -81,4 +90,13 @@ public class InviteCodeController : BaseController
     [HttpGet("statistic")]
     public async Task<IList<InviteCodeStatisticOutDto>> GetInviteCodeStatisticAsync([FromQuery] GetInviteCodeStatisticInDto dto)
         => await _inviteCodeApplication.GetInviteCodeStatisticAsync(dto);
+
+    /// <summary>
+    /// 邀请码统计明细
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <returns></returns>
+    [HttpGet("statistic/detail")]
+    public async Task<PagedDto<InviteCodeStatisticDetailOutDto>> GetInviteCodeStatisticDetailAsync([FromQuery] GetInviteCodeStatisticDetailInDto dto)
+        => (await _inviteCodeApplication.GetInviteCodeStatisticDetailAsync(dto).ToPagedListAsync(dto)).ToPaged();
 }

+ 45 - 6
src/Hotline.Api/Controllers/Snapshot/SnapshotController.cs

@@ -1,15 +1,19 @@
-using Hotline.Api.Filter;
+using AngleSharp.Dom;
+using Fw.Utility.UnifyResponse;
+using Hotline.Api.Filter;
 using Hotline.Application.Orders;
 using Hotline.Application.Snapshot;
 using Hotline.Caching.Interfaces;
 using Hotline.File;
 using Hotline.Orders;
+using Hotline.Repository.SqlSugar.Snapshot;
 using Hotline.Settings;
 using Hotline.Share.Dtos;
 using Hotline.Share.Dtos.Article;
 using Hotline.Share.Dtos.Order;
 using Hotline.Share.Dtos.Settings;
 using Hotline.Share.Dtos.Snapshot;
+using Hotline.Share.Dtos.WebPortal;
 using Hotline.Share.Enums.Order;
 using Hotline.Share.Enums.Snapshot;
 using Hotline.Share.Tools;
@@ -20,10 +24,12 @@ using Mapster;
 using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Mvc;
 using SqlSugar;
+using SqlSugar.Extensions;
 using System.ComponentModel.DataAnnotations;
 using System.Reflection;
 using XF.Domain.Authentications;
 using XF.Domain.Exceptions;
+using XF.Domain.Filters;
 using XF.Domain.Repository;
 
 namespace Hotline.Api.Controllers.Snapshot;
@@ -42,8 +48,9 @@ public class SnapshotController : BaseController
     private readonly IOrderSnapshotRepository _orderSnapshotRepository;
     private readonly ISystemDicDataCacheManager _systemDicDataCacheManager;
     private readonly ISessionContext _sessionContext;
+    private readonly IThirdAccountRepository _thirdAccountRepository;
 
-    public SnapshotController(IRepository<Order> orderRepository, ISnapshotApplication snapshotApplication, ISystemAreaDomainService systemAreaDomainService, IIndustryRepository industryRepository, IOrderDomainService orderDomainService, IFileRepository fileRepository, IOrderSnapshotRepository orderSnapshotRepository, ISystemDicDataCacheManager systemDicDataCacheManager, ISessionContext sessionContext)
+    public SnapshotController(IRepository<Order> orderRepository, ISnapshotApplication snapshotApplication, ISystemAreaDomainService systemAreaDomainService, IIndustryRepository industryRepository, IOrderDomainService orderDomainService, IFileRepository fileRepository, IOrderSnapshotRepository orderSnapshotRepository, ISystemDicDataCacheManager systemDicDataCacheManager, ISessionContext sessionContext, IThirdAccountRepository thirdAccountRepository)
     {
         _orderRepository = orderRepository;
         _snapshotApplication = snapshotApplication;
@@ -54,6 +61,7 @@ public class SnapshotController : BaseController
         _orderSnapshotRepository = orderSnapshotRepository;
         _systemDicDataCacheManager = systemDicDataCacheManager;
         _sessionContext = sessionContext;
+        _thirdAccountRepository = thirdAccountRepository;
     }
 
     /// <summary>
@@ -83,7 +91,7 @@ public class SnapshotController : BaseController
     [LogFilter("添加随手拍工单")]
     public async Task<AddSnapshotOrderOutDto> AddOrderAsync([FromBody] AddSnapshotOrderInDto dto)
     {
-        var ssp = _systemDicDataCacheManager.SourceChannel.FirstOrDefault(m => m.DicDataName == "随手拍") 
+        var ssp = _systemDicDataCacheManager.SourceChannel.FirstOrDefault(m => m.DicDataName == "随手拍")
             ?? throw UserFriendlyException.SameMessage("请添加[随手拍]来源.");
         var order = dto.Adapt<Order>();
         dto.ValidateObject();
@@ -110,6 +118,15 @@ public class SnapshotController : BaseController
         orderSnapshot.IndustryId = dto.IndustryId;
         orderSnapshot.IndustryName = industry.Name;
         orderSnapshot.CompanyName = dto.CompanyName;
+        if (dto.StartWorkTime.NotNullOrEmpty()) orderSnapshot.StartWorkTime = dto.StartWorkTime.ObjToDate();
+        if (dto.EndWorkTime.NotNullOrEmpty()) orderSnapshot.EndWorkTime = dto.EndWorkTime.ObjToDate();
+        if (dto.Name.NotNullOrEmpty())
+        {
+            await _thirdAccountRepository.Updateable()
+               .SetColumns(m => m.UserName, dto.Name)
+               .Where(m => m.Id == _sessionContext.UserId)
+               .ExecuteCommandAsync(HttpContext.RequestAborted);
+        }
         await _orderSnapshotRepository.AddAsync(orderSnapshot);
         return order.Adapt<AddSnapshotOrderOutDto>();
     }
@@ -258,7 +275,7 @@ public class SnapshotController : BaseController
     /// <param name="dtos"></param>
     /// <returns></returns>
     [HttpPost("practitioner")]
-    public async Task AddPractitionerAsync([FromBody]IList<AddBatchPractitionerInDto> dtos)
+    public async Task AddPractitionerAsync([FromBody] IList<AddBatchPractitionerInDto> dtos)
         => await _snapshotApplication.AddPractitionerAsync(dtos);
 
     /// <summary>
@@ -278,7 +295,7 @@ public class SnapshotController : BaseController
     /// <returns></returns>
     [HttpGet("practitioner")]
     [AllowAnonymous]
-    public async Task<IList<PractitionerItemOutDto>> GetPractitionerDetailAsync([FromQuery]PractitionerItemInDto dto)
+    public async Task<IList<PractitionerItemOutDto>> GetPractitionerDetailAsync([FromQuery] PractitionerItemInDto dto)
         => await _snapshotApplication.GetPractitionerItemsAsync(dto, HttpContext.RequestAborted);
 
     /// <summary>
@@ -298,7 +315,7 @@ public class SnapshotController : BaseController
     /// <returns></returns>
     [HttpGet("report/base")]
     public Dictionary<string, dynamic> GetReportBaseAsync()
-    { 
+    {
         return new Dictionary<string, dynamic>
         {
             { "jobType", _systemDicDataCacheManager.JobType },
@@ -332,6 +349,7 @@ public class SnapshotController : BaseController
         }
     }
 
+    #region 第三方系统
     /// <summary>
     /// 接收网格员系统回调
     /// </summary>
@@ -343,4 +361,25 @@ public class SnapshotController : BaseController
     {
         await _snapshotApplication.SaveGuiderSystemReplyAsync(dto, HttpContext.RequestAborted);
     }
+
+    /// <summary>
+    /// 电气焊申报系统申报工单查询
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <returns></returns>
+    [HttpPost("order/declare")]
+    [AllowAnonymous]
+    public async Task<OpenResponse> GetOrderDeclareItemsAsync([FromBody] OrderDeclareItemsInDto dto)
+    {
+        return OpenResponse.Ok(new List<WebPortalDeResponse<List<OrderDeclareItemsOutDto>>>()
+        {
+            new() { code = "1", msg = "ok", data =
+                [
+                    new OrderDeclareItemsOutDto()
+                ]
+            }
+        });
+    }
+
+    #endregion
 }

+ 21 - 19
src/Hotline.Api/Controllers/Snapshot/SnapshotOrderController.cs

@@ -112,57 +112,59 @@ public class SnapshotOrderController : BaseController
         => (await _orderSnapshotApplication.GetGuiderReplyItemsAsync(dto).ToPagedListAsync(dto)).ToPaged();
 
     /// <summary>
-    /// 获取工单标集合
+    /// 获取工单标集合
     /// </summary>
     /// <param name="dto"></param>
     /// <returns></returns>
-    [HttpGet("sign")]
-    public async Task<PagedDto<SignOrderSnapshotItemsOutDto>> GetSignOrderSnapshotItemsAsync([FromQuery] SignOrderSnapshotItemsInDto dto)
-        => (await _orderSnapshotApplication.GetSignOrderSnapshotItemsAsync(dto).ToPagedListAsync(dto)).ToPaged();
+    [HttpGet("label")]
+    public async Task<PagedDto<LabelOrderSnapshotItemsOutDto>> GetLabelOrderSnapshotItemsAsync([FromQuery] LabelOrderSnapshotItemsInDto dto)
+        => (await _orderSnapshotApplication.GetLabelOrderSnapshotItemsAsync(dto).ToPagedListAsync(dto)).ToPaged();
 
     /// <summary>
-    /// 获取工单已经标集合
+    /// 获取工单已经标集合
     /// </summary>
     /// <param name="dto"></param>
     /// <returns></returns>
-    [HttpGet("signed")]
-    public async Task<PagedDto<SignedOrderSnapshotItemsOutDto>> GetSignedOrderSnapshotItemsAsync([FromQuery] SignedOrderSnapshotItemsInDto dto)
-        => (await _orderSnapshotApplication.GetSignedOrderSnapshotItemsAsync(dto).ToPagedListAsync(dto)).ToPaged();
+    [HttpGet("labeled")]
+    public async Task<PagedDto<LabeledOrderSnapshotItemsOutDto>> GetLabeledOrderSnapshotItemsAsync([FromQuery] LabeledOrderSnapshotItemsInDto dto)
+        => (await _orderSnapshotApplication.GetLabeledOrderSnapshotItemsAsync(dto).ToPagedListAsync(dto)).ToPaged();
 
     /// <summary>
-    /// 标详情
+    /// 标详情
     /// </summary>
     /// <param name="id"></param>
     /// <returns></returns>
-    [HttpGet("sign/{id}")]
-    public async Task<SignOrderSnapshotDetailOutDto> GetSignOrderSnapshotDetailAsync(string id)
+    [HttpGet("label/{id}")]
+    public async Task<LabelOrderSnapshotDetailOutDto> GetLabelOrderSnapshotDetailAsync(string id)
     { 
         var order = await _orderRepository.Queryable()
             .LeftJoin<OrderSnapshot>((order, snapshot) => order.Id == snapshot.Id)
             .Where((order, snapshot) => order.Id == id)
-            .Select((order, snapshot) => new SignOrderSnapshotDetailOutDto { Id = order.Id, Title = order.Title, Lables =snapshot.Labels })
+            .Select((order, snapshot) => new LabelOrderSnapshotDetailOutDto { Id = order.Id, Title = order.Title, Lables =snapshot.Labels })
             .FirstAsync();
+        var snapshot = await _orderSnapshotRepository.GetAsync(id, HttpContext.RequestAborted);
+        order.Lables = snapshot.Labels;
         order.LabelsBaseData = _systemDicDataCacheManager.SnapshotOrderLabel;
         return order;
     }
 
     /// <summary>
-    /// 修改工单标
+    /// 修改工单标
     /// </summary>
     /// <param name="dto"></param>
     /// <returns></returns>
-    [HttpPut("sign")]
-    public async Task UpdateSignOrderSnapshotAsync([FromBody] UpdateSignOrderSnapshotInDto dto)
+    [HttpPut("label")]
+    public async Task UpdateLabelOrderSnapshotAsync([FromBody] UpdateLabelOrderSnapshotInDto dto)
         => await _orderSnapshotApplication.UpdateLabelAsync(dto.Id, dto.SnapshotLabels);
 
     /// <summary>
-    /// 获取工单标日志集合
+    /// 获取工单标日志集合
     /// </summary>
     /// <param name="dto"></param>
     /// <returns></returns>
-    [HttpGet("sign/log")]
-    public async Task<PagedDto<SignOrderSnapshotLogItemsOutDto>> GetSignOrderSnapshotLogItemsAsync([FromQuery] SignOrderSnapshotLogItemsInDto dto)
-        => (await _orderSnapshotApplication.GetSignOrderSnapshotLogItemsAsync(dto).ToPagedListAsync(dto)).ToPaged();
+    [HttpGet("label/log")]
+    public async Task<PagedDto<LabelOrderSnapshotLogItemsOutDto>> GetLabelOrderSnapshotLogItemsAsync([FromQuery] LabelOrderSnapshotLogItemsInDto dto)
+        => (await _orderSnapshotApplication.GetLabelOrderSnapshotLogItemsAsync(dto).ToPagedListAsync(dto)).ToPaged();
 
     /// <summary>
     /// 随手拍公开集合

+ 38 - 24
src/Hotline.Api/Controllers/TestController.cs

@@ -73,6 +73,7 @@ using static System.Runtime.InteropServices.JavaScript.JSType;
 using Order = Hotline.Orders.Order;
 using Hotline.Share.Dtos.Settings;
 using OrderDto = Hotline.Share.Dtos.Order.OrderDto;
+using Hotline.Ai.Quality;
 
 namespace Hotline.Api.Controllers;
 
@@ -144,6 +145,7 @@ public class TestController : BaseController
 	private readonly IOrderScreenRepository _orderScreenRepository;
 	private readonly IRepository<OrderVisit> _orderVisitRepository;
     private readonly IThirdIdentiyService _thirdIdentiyService;
+	private readonly IServiceProvider _serviceProvider;
 
 
 	public TestController(
@@ -201,7 +203,8 @@ ICallApplication callApplication,
         IRepository<OldSendProData> oldSendProDataRepository,
         IThirdIdentiyService thirdIdentiyService,
 		IOrderScreenRepository orderScreenRepository,
-		IRepository<OrderVisit> orderVisitRepository
+		IRepository<OrderVisit> orderVisitRepository,
+		IServiceProvider serviceProvider
 		)
     {
         _logger = logger;
@@ -255,6 +258,7 @@ ICallApplication callApplication,
         _thirdIdentiyService = thirdIdentiyService;
         _orderScreenRepository = orderScreenRepository;
         _orderVisitRepository = orderVisitRepository;
+        _serviceProvider = serviceProvider;
 	}
 
     /// <summary>
@@ -547,8 +551,9 @@ ICallApplication callApplication,
         //var r = _timeLimitDomainService.CalcExpiredTime(DateTime.Now, EFlowDirection.CenterToCenter, batchId);
         //var r = _timeLimitDomainService.CalcEndTime(DateTime.Parse("2024-09-12 14:45:47"), Share.Enums.Settings.ETimeType.WorkDay, 2, 80, 50);
         //_capPublisher.PublishDelay((DateTime.Now.AddMinutes(2) - DateTime.Now), EventNames.OrderRelateCall, "123");
-        var times =await _expireTime.CalcExpiredTime(DateTime.Parse("2024-12-31 13:25:13.137977"), DateTime.Parse("2024-12-31 13:25:13.137977"), EFlowDirection.CenterToOrg,new OrderTimeClacInfo() { AcceptTypeCode= "10" });
-        //var times = await _expireTime.CalcWorkTimeToDecimal(DateTime.Parse("2024-12-16 21:36:27"), DateTime.Parse("2024-12-17 12:47:05"), false);
+        //var times =await _expireTime.CalcExpiredTime(DateTime.Parse("2024-12-31 13:25:13.137977"), DateTime.Parse("2024-12-31 13:25:13.137977"), EFlowDirection.CenterToOrg,new OrderTimeClacInfo() { AcceptTypeCode= "10" });
+        //await _expireTime.CalcWorkTimeToDecimal(visit.VisitTime.Value, DateTime.Now, false);
+        var times = await _expireTime.CalcWorkTimeToDecimal(DateTime.Parse("2024-12-16 21:36:27"), DateTime.Parse("2024-12-17 12:47:05"), false);
         //await _capPublisher.PublishDelay(EventNames.OrderRelateCall, "123", cancellationToken: HttpContext.RequestAborted);
         return OpenResponse.Ok(times);
     }
@@ -1373,33 +1378,35 @@ ICallApplication callApplication,
         }
     }
 
-    /// <summary>
-    /// 老系统数据同步
-    /// </summary>
-    /// <returns></returns>
-	[HttpGet("old_data_synchronization/{no}")]
+	/// <summary>
+	/// 老系统数据同步
+	/// </summary>
+	/// <returns></returns>
+	[HttpGet("old_data_synchronization")]
 	[AllowAnonymous]
-	public async Task oldDataSynchronization(string no) 
-    {
-        var orders = new List<Order>();
-        if (string.IsNullOrEmpty(no))
-        {
-            orders = await _orderRepository.Queryable().Where(x => x.No.Length == 12 && x.ActualHandleTime == null  &&  x.CenterToOrgTime == null).ToListAsync();
-        }
-        else {
+	public async Task oldDataSynchronization([FromQuery] string? no)
+	{
+		var orders = new List<Order>();
+		if (string.IsNullOrEmpty(no))
+		{
+			orders = await _orderRepository.Queryable().Where(x => x.No.Length == 12 && x.ActualHandleTime == null && x.CenterToOrgTime == null).ToListAsync();
+		}
+		else
+		{
 			orders = await _orderRepository.Queryable().Where(x => x.No == no).ToListAsync();
 		}
-        foreach (var order in orders) {
+		foreach (var order in orders)
+		{
 
-            var  steps = await _workflowStepRepository.Queryable().Where(x=>x.ExternalId == order.Id).ToListAsync();
+			var steps = await _workflowStepRepository.Queryable().Where(x => x.ExternalId == order.Id).ToListAsync();
 
-            var actualHandleStep = steps.Where(x => x.HandlerOrgId == order.ActualHandleOrgCode).OrderByDescending(x => x.CreationTime).FirstOrDefault();
-            var CenterToOrgStep = steps.Where(x => x.AssignerOrgId == "001" && x.HandlerOrgId  != "001").OrderByDescending(x => x.CreationTime).FirstOrDefault();
+			var actualHandleStep = steps.Where(x => x.HandlerOrgId == order.ActualHandleOrgCode).OrderByDescending(x => x.CreationTime).FirstOrDefault();
+			var CenterToOrgStep = steps.Where(x => x.AssignerOrgId == "001" && x.HandlerOrgId != "001").OrderByDescending(x => x.CreationTime).FirstOrDefault();
 
 			order.ActualHandleTime = actualHandleStep.HandleTime;
-            order.CenterToOrgTime = CenterToOrgStep.CreationTime;
+			order.CenterToOrgTime = CenterToOrgStep.CreationTime;
 
-            await _orderRepository.Updateable().SetColumns(x => new Order { ActualHandleTime = order.ActualHandleTime, CenterToOrgTime = order.CenterToOrgTime }).ExecuteCommandAsync();
+			await _orderRepository.Updateable().SetColumns(x => new Order { ActualHandleTime = order.ActualHandleTime, CenterToOrgTime = order.CenterToOrgTime }).Where(x => x.Id == order.Id).ExecuteCommandAsync();
 
 
 			var now = order.FiledTime.Value;
@@ -1442,8 +1449,15 @@ ICallApplication callApplication,
 			order.File(now, handleDuration, fileDuration, allDuration, creationTimeHandleDurationWorkday, centerToOrgHandleDurationWorkday);
 
 		}
-       
-    }
 
+	}
+
+	[HttpGet("aiXingTang")]
+	[AllowAnonymous]
+	public async Task aiXingTang() {
+		var aiQualityService = _serviceProvider.GetRequiredService<IAiQualityService>();
+        await aiQualityService.CreateAiOrderQualityTask("cs202501030002.mp3", HttpContext.RequestAborted);
+
+	}
 
 }

+ 3 - 0
src/Hotline.Api/Hotline.Api.csproj

@@ -35,6 +35,9 @@
     <None Update="Template\AssignmentForm.doc">
       <CopyToOutputDirectory>Always</CopyToOutputDirectory>
     </None>
+    <None Update="Template\QualityCertificate.doc">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </None>
     <None Remove="logs\**" />
   </ItemGroup>
 

+ 3 - 1
src/Hotline.Api/StartupExtensions.cs

@@ -47,6 +47,7 @@ using XF.Domain.Repository.Events;
 using Hotline.Orders.DatabaseEventHandler;
 using Hotline.Snapshot;
 using Hotline.WeChat;
+using Hotline.Ai.XingTang;
 
 
 namespace Hotline.Api;
@@ -157,7 +158,8 @@ internal static class StartupExtensions
                 services.AddProxiedScoped<ISnapshotApplication, DefaultSnapshotApplication>();
                 break;
             case AppDefaults.AppScope.ZiGong:
-                services.AddProxiedScoped<ISnapshotApplication, ZiGongSnapshotApplication>();
+				services.AddAiXingTang(appConfiguration.ZiGong.AiQuality.Url);
+				services.AddProxiedScoped<ISnapshotApplication, ZiGongSnapshotApplication>();
                 break;
             case AppDefaults.AppScope.LuZhou:
                 services.AddProxiedScoped<ISnapshotApplication, DefaultSnapshotApplication>();

二進制
src/Hotline.Api/Template/QualityCertificate.doc


+ 5 - 0
src/Hotline.Api/config/appsettings.Development.json

@@ -25,6 +25,11 @@
       }
     },
     "ZiGong": {
+      //智能质检
+      "AiQuality": {
+        "Url": "http://175.10.86.234:10095" // 正式
+        //"Url": "http://118.122.73.80:19072/", // 测试
+      },
       "AreaCode": "510300",
       "CallCenterType": "XingTang"
     },

+ 1 - 1
src/Hotline.Application.Tests/Application/InviteCodeApplicationTest.cs

@@ -42,7 +42,7 @@ public class InviteCodeApplicationTest : TestBase
 
         var statics = await _inviteCodeApplication.GetInviteCodeStatisticAsync(new GetInviteCodeStatisticInDto 
         {
-            BeginTime = DateTime.Now.AddDays(-1),
+            StartTime = DateTime.Now.AddDays(-1),
             EndTime = DateTime.Now
         });
         statics.Count.ShouldNotBe(0);

+ 2 - 2
src/Hotline.Application.Tests/Application/OrderSnapshotApplicationTest.cs

@@ -65,11 +65,11 @@ public class OrderSnapshotApplicationTest : TestBase
 
         snapshot.IsSafetyDepartment.ShouldBe(false);
 
-        var labelLog = await _orderSnapshotApplication.GetSignOrderSnapshotLogItemsAsync(new SignOrderSnapshotLogItemsInDto()).ToPagedListAsync(new PagedRequest());
+        var labelLog = await _orderSnapshotApplication.GetLabelOrderSnapshotLogItemsAsync(new LabelOrderSnapshotLogItemsInDto()).ToPagedListAsync(new PagedRequest());
         labelLog.Items.Any(m => m.Title.IsNullOrEmpty()).ShouldBe(false);
         labelLog.Items.Any(m => m.SourceChannel.IsNullOrEmpty()).ShouldBe(false);
 
-        var sigedItems = await _orderSnapshotApplication.GetSignedOrderSnapshotItemsAsync(new SignedOrderSnapshotItemsInDto()).ToListAsync();
+        var sigedItems = await _orderSnapshotApplication.GetLabeledOrderSnapshotItemsAsync(new LabeledOrderSnapshotItemsInDto()).ToListAsync();
         sigedItems.Any(m => m.OrderId == order.Id).ShouldBe(true);
     }
 }

+ 1 - 1
src/Hotline.Application.Tests/Application/RedPackApplicationTest.cs

@@ -41,7 +41,7 @@ public class RedPackApplicationTest : TestBase
     {
         var items = await _redPackApplication.GetRedPackAuditItemsAsync(new SnapshotOrderAuditItemsInDto(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 0)).ToListAsync();
         var audit = items.First();
-        var smsTemplate = await _redPackApplication.GetRedPackAuditSMSTemplateAsync(new GetRedPackAuditSMSTemplateInDto(audit.OrderId, ERedPackAuditStatus.Agree));
+        var smsTemplate = await _redPackApplication.GetRedPackAuditSMSTemplateAsync(new GetRedPackAuditSMSTemplateInDto(audit.OrderId, ESnapshotSMSStatus.Agree));
         var inDto = new UpdateRedPackAuditInDto
         {
             Status = ERedPackAuditStatus.Agree,

+ 3 - 1
src/Hotline.Application/CallCenter/DefaultCallApplication.cs

@@ -299,6 +299,8 @@ public abstract class DefaultCallApplication : ICallApplication
             .WhereIF(!string.IsNullOrEmpty(dto.StaffNo), d => d.StaffNo == dto.StaffNo)
             .WhereIF(!string.IsNullOrEmpty(dto.GroupId), d => d.GroupId == dto.GroupId)
             .WhereIF(dto.OperateState != null, d => d.OperateState == dto.OperateState)
+            .WhereIF(dto.OperateTimeStart.HasValue, x => x.OperateTime >= dto.OperateTimeStart)
+            .WhereIF(dto.OperateTimeEnd.HasValue, x => x.OperateTime <= dto.OperateTimeEnd)
             .OrderByDescending(d => d.Id)
             .ToFixedListAsync(dto, cancellationToken);
     }
@@ -597,7 +599,7 @@ public abstract class DefaultCallApplication : ICallApplication
             TrCallRecordDto = call.Adapt<TrCallDto>()
         }, cancellationToken: cancellationToken);
         var msg = $"原CallId: {orderCall.CallId}, 更新CallId: {call.Id}";
-        _systemLogRepository.Add("延迟更新工单通话", orderId, msg,status:1, ipUrl: orderCall.CallId);
+        _systemLogRepository.Add("延迟更新工单通话", orderId, msg, status: 1, ipUrl: orderCall.CallId);
         return msg + "(完成推省上)";
     }
 

+ 23 - 9
src/Hotline.Application/Caselibrary/CaseApplication.cs

@@ -213,17 +213,29 @@ namespace Hotline.Application.Caselibrary
                 .Includes(x => x.Order)
                 .Includes(x => x.Knowledge)
                 .Where(x => x.IsDeleted == false)
-                .WhereIF(pagedDto.IsPopular.HasValue, x => x.IsPopular == pagedDto.IsPopular)
+
+                .WhereIF(pagedDto.IsPopular == true, x => x.IsPopular == true)
+
                 .WhereIF(OrgSeedData.CenterId != pagedDto.CreateOrgId && !string.IsNullOrEmpty(pagedDto.CreateOrgId), x => x.CreatorOrgId != null && x.CreatorOrgId.StartsWith(pagedDto.CreateOrgId!))
-                .WhereIF(!string.IsNullOrEmpty(pagedDto.Title), x => x.Title.Contains(pagedDto.Title))
-                .WhereIF(!string.IsNullOrEmpty(pagedDto.Abstract), x => x.Abstract.Contains(pagedDto.Abstract))
-                .WhereIF(!string.IsNullOrEmpty(pagedDto.Describe), x => x.Describe.Contains(pagedDto.Describe))
-                .WhereIF(!string.IsNullOrEmpty(pagedDto.Result), x => x.Result.Contains(pagedDto.Result))
-                .WhereIF(!string.IsNullOrEmpty(pagedDto.Reason), x => x.Reason.Contains(pagedDto.Reason))
 
-                .WhereIF(!string.IsNullOrEmpty(pagedDto.Keyword), x => x.Title.Contains(pagedDto.Keyword!) ||
-                                                                  x.CreatorName!.Contains(pagedDto.Keyword!) ||
-                                                                  x.CreatorOrgName!.Contains(pagedDto.Keyword!))
+                .WhereIF(!string.IsNullOrEmpty(pagedDto.Title) &&
+                          string.IsNullOrEmpty(pagedDto.Keywords) &&
+                          string.IsNullOrEmpty(pagedDto.Abstract), x => x.Title.Contains(pagedDto.Title!))
+
+                .WhereIF(string.IsNullOrEmpty(pagedDto.Title) &&
+                        !string.IsNullOrEmpty(pagedDto.Keywords) &&
+                         string.IsNullOrEmpty(pagedDto.Abstract), x => x.Keywords.Contains(pagedDto.Keywords!))
+
+                .WhereIF(string.IsNullOrEmpty(pagedDto.Title) &&
+                         string.IsNullOrEmpty(pagedDto.Keywords) &&
+                        !string.IsNullOrEmpty(pagedDto.Abstract), x => x.Abstract.Contains(pagedDto.Abstract!))
+
+                .WhereIF(!string.IsNullOrEmpty(pagedDto.Title) &&
+                         !string.IsNullOrEmpty(pagedDto.Keywords) &&
+                         !string.IsNullOrEmpty(pagedDto.Abstract)
+                                                                , x => x.Title.Contains(pagedDto.Title!) ||
+                                                                  x.Keywords!.Contains(pagedDto.Keywords!) ||
+                                                                  x.Abstract!.Contains(pagedDto.Abstract!))
 
                 .WhereIF(pagedDto.Status.HasValue && pagedDto.Status == ECaseStatus.OnShelf, x => x.Status == ECaseStatus.OnShelf)
                 .WhereIF(pagedDto.Status.HasValue && pagedDto.Status == ECaseStatus.OffShelf, x => x.Status == ECaseStatus.OffShelf)
@@ -290,6 +302,8 @@ namespace Hotline.Application.Caselibrary
             if (any)
                 throw UserFriendlyException.SameMessage("当前案例标题存在重复标题!");
 
+            pList.InitId();
+
             if (dto.Files != null && dto.Files.Count > 0)
                 pList.FileJson = await _fileRepository.AddFileAsync(dto.Files, pList.Id, "", cancellationToken);
             await _caseListRepository.AddAsync(pList, cancellationToken);

+ 3 - 1
src/Hotline.Application/FlowEngine/WorkflowApplication.cs

@@ -861,7 +861,9 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
             NextStepOption nextStepOption;
 
             //汇总节点只能选择对应节点办理对象
-            if (workflow.FlowType is EFlowType.Handle && stepDefine.StepType is EStepType.Summary)
+            if (workflow.FlowType is EFlowType.Handle 
+                && stepDefine.StepType is EStepType.Summary
+                && stepDefine.BusinessType is EBusinessType.Seat or EBusinessType.Send)
             {
                 var handler = _workflowDomainService.GetSummaryTargetFlowStepHandler(workflow, stepDefine.SummaryTargetCode);
 

+ 1 - 0
src/Hotline.Application/Hotline.Application.csproj

@@ -17,6 +17,7 @@
 
   <ItemGroup>
     <ProjectReference Include="..\Hotline.Ai.Jths\Hotline.Ai.Jths.csproj" />
+    <ProjectReference Include="..\Hotline.Ai.XingTang\Hotline.Ai.XingTang.csproj" />
     <ProjectReference Include="..\Hotline.Application.Contracts\Hotline.Application.Contracts.csproj" />
     <ProjectReference Include="..\Hotline.NewRock\Hotline.NewRock.csproj" />
     <ProjectReference Include="..\Hotline.Repository.SqlSugar\Hotline.Repository.SqlSugar.csproj" />

+ 7 - 3
src/Hotline.Application/Knowledge/KnowApplication.cs

@@ -108,7 +108,7 @@ namespace Hotline.Application.Knowledge
                     switch (pagedDto.ModuleCode)
                     {
                         case "add":
-                            query.Where(d => d.KnowledgeApproves.Any(x => x.KnowledgeApproveType == EKnowledgeApproveType.Add 
+                            query.Where(d => d.KnowledgeApproves.Any(x => x.KnowledgeApproveType == EKnowledgeApproveType.Add
                                                                           && x.KnowledgeApproveStatus == EKnowledgeApproveStatus.Unhandle));
                             break;
                         case "update":
@@ -148,6 +148,10 @@ namespace Hotline.Application.Knowledge
                     throw new ArgumentOutOfRangeException();
             }
 
+            if (!_sessionContext.OrgIsCenter)
+                query.Where(x => x.KnowledgeTypes.Any(t => t.Orgs.Any(to => to.Id == _sessionContext.RequiredOrgId)));
+
+
             var (total, temp) = await query
                 //.Includes(x => x.User)
                 .Includes(x => x.SystemOrganize)
@@ -163,8 +167,8 @@ namespace Hotline.Application.Knowledge
                 //                                     || t.KnowledgeType.Orgs.Any() == false))
                 //.Where(x => x.KnowledgeTypes.Any(t => t.KnowledgeTypeOrgs.Any(to => to.OrgId == _sessionContext.RequiredOrgId)
                 //                                      || t.KnowledgeTypeOrgs.Any() == false))
-                .Where(x => x.KnowledgeTypes.Any(t => t.Orgs.Any(to => to.Id == _sessionContext.RequiredOrgId)
-                                                      || t.Orgs.Any() == false))
+                //.Where(x => x.KnowledgeTypes.Any(t => t.Orgs.Any(to => to.Id == _sessionContext.RequiredOrgId)
+                //                                      /*|| t.Orgs.Any() == false*/))
                 //.Where(x => (x.Status == EKnowledgeStatus.Drafts && x.CreatorId == _sessionContext.UserId) || (x.Status != EKnowledgeStatus.Drafts))
                 .WhereIF(!string.IsNullOrEmpty(pagedDto.Title), x => x.Title.Contains(pagedDto.Title!))
                 .WhereIF(!string.IsNullOrEmpty(pagedDto.Keyword), x => x.Title.Contains(pagedDto.Keyword!) || x.CreatorName.Contains(pagedDto.Keyword!) || x.CreatorOrgName.Contains(pagedDto.Keyword!) || x.SourceOrganize.Name.Contains(pagedDto.Keyword!))

+ 6 - 0
src/Hotline.Application/Mappers/SnapshotMapperConfigs.cs

@@ -18,6 +18,9 @@ public class SnapshotMapperConfigs : IRegister
 {
     public void Register(TypeAdapterConfig config)
     {
+        config.ForType<AddSnapshotOrderInDto, OrderSnapshot>()
+           .Ignore(m => m.StartWorkTime)
+           .Ignore(m => m.EndWorkTime);
 
         config.ForType<SystemDicData, Kv>()
             .Map(m => m.Key, b => b.DicDataValue)
@@ -56,6 +59,9 @@ public class SnapshotMapperConfigs : IRegister
         config.ForType<IndustryFileDto, Hotline.File.File>()
             .Map(m => m.Additions, n => n.AdditionId);
 
+        config.ForType<SnapshotFileInDto, File.File>()
+            .Map(m => m.Additions, n => n.AdditionId);
+
         config.ForType<AddSnapshotOrderInDto, Order>()
             .Map(m => m.Contact, n => n.PhoneNumber)
             .Map(m => m.FromName, n => n.Name)

+ 4 - 3
src/Hotline.Application/Orders/OrderApplication.cs

@@ -1434,8 +1434,9 @@ public class OrderApplication : IOrderApplication, IScopeDependency
              d.SourceChannelCode == "SZMHD" && d.IsProvince == true) //政民互动
          .WhereIF(!string.IsNullOrEmpty(dto.ProvinceChannel) && dto.ProvinceChannel == "3", d => d.Source == ESource.ProvinceStraight &&
              d.SourceChannelCode == "S12345" && d.IsProvince == true) //省12345
-         .WhereIF(!string.IsNullOrEmpty(dto.ContentRetrieval),
-             d => d.Title.Contains(dto.ContentRetrieval) || d.Content.Contains(dto.ContentRetrieval) || d.FileOpinion.Contains(dto.ContentRetrieval) || d.ActualOpinion.Contains(dto.ContentRetrieval))
+         //.WhereIF(!string.IsNullOrEmpty(dto.ContentRetrieval),d => d.Title.Contains(dto.ContentRetrieval) || d.Content.Contains(dto.ContentRetrieval) || d.FileOpinion.Contains(dto.ContentRetrieval) || d.ActualOpinion.Contains(dto.ContentRetrieval))
+         .WhereIF(!string.IsNullOrEmpty(dto.ContentRetrieval), d=> d.Content.Contains(dto.ContentRetrieval!))
+         .WhereIF(!string.IsNullOrEmpty(dto.FileOption),d=>d.FileOpinion.Contains(dto.FileOption!))
          .WhereIF(dto.IsSgin.HasValue && dto.IsSgin == true, d => d.CurrentStepAcceptTime != null)
          .WhereIF(dto.IsSgin.HasValue && dto.IsSgin == false, d => d.CurrentStepAcceptTime == null)
          .WhereIF(dto.FiledType is FiledType.CenterFiled, d => d.FileOrgIsCenter == true)//d => d.ProcessType == EProcessType.Zhiban
@@ -3250,7 +3251,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         query = query.WhereIF(dto.VisitStateQuery == EVisitStateQuery.Visited, d => d.VisitState == EVisitState.Visited)
             .WhereIF(dto.VisitStateQuery == EVisitStateQuery.SMSUnsatisfied, d => d.VisitState == EVisitState.SMSUnsatisfied)
             .WhereIF(dto.VisitStateQuery == EVisitStateQuery.SMSVisiting, d => d.VisitState == EVisitState.SMSVisiting)
-            .WhereIF(dto.VisitStateQuery == EVisitStateQuery.NoPutThrough, d => d.IsPutThrough == false && d.VisitState != EVisitState.Visited)
+            .WhereIF(dto.VisitStateQuery == EVisitStateQuery.NoPutThrough, d => d.IsPutThrough == false && d.VisitState != EVisitState.Visited && d.VisitState != EVisitState.None)
             .WhereIF(dto.VisitStateQuery == EVisitStateQuery.ChipVoiceVisiting, d => d.VisitState == EVisitState.AiVisiting)//任务 162:回访状态快捷查询条件
             .WhereIF(!string.IsNullOrEmpty(dto.Keyword), d => d.Order.Title.Contains(dto.Keyword!))
             .WhereIF(!string.IsNullOrEmpty(dto.No), d => d.No == dto.No)

+ 1 - 1
src/Hotline.Application/Orders/OrderSecondaryHandlingApplication.cs

@@ -249,7 +249,7 @@ namespace Hotline.Application.Orders
                 .Where(x => x.OrderVisit.Order.IsProvince == false)
                 .LeftJoin<OrderScreen>((x, s) => x.Id == s.VisitDetailId && s.Status < EScreenStatus.End && s.IsDeleted == false)
                 //.Where((x, s) => s.Id == null && (x.SecondaryHandling.State == ESecondaryHandlingState.NotApply || x.SecondaryHandling.Id == null))
-                .Where(x=> SqlFunc.Subqueryable<OrderSecondaryHandling>().Where(osh => osh.VisitDetailId == x.Id &&  osh.State == ESecondaryHandlingState.NotApply).NotAny())
+                .Where(x=> SqlFunc.Subqueryable<OrderSecondaryHandling>().Where(osh => osh.VisitDetailId == x.Id &&  osh.State != ESecondaryHandlingState.NotApply).NotAny())
                 //.Where(x => x.OrderVisit.VisitTime < dto.CreationTimeEnd && x.OrderVisit.VisitTime > dto.CreationTimeStart)
                 .WhereIF(!string.IsNullOrEmpty(dto.No), x => x.OrderVisit.Order!.No!.Contains(dto.No!))
                 .WhereIF(dto.IsProvince.HasValue, x => x.OrderVisit.Order!.IsProvince == dto.IsProvince)

+ 14 - 5
src/Hotline.Application/Planlibrary/PlanApplication.cs

@@ -221,11 +221,18 @@ namespace Hotline.Application.Planlibrary
                 .WhereIF(pagedDto.IsPublic.HasValue, x => x.IsPublic == pagedDto.IsPublic)
                 .WhereIF(OrgSeedData.CenterId != pagedDto.CreateOrgId && !string.IsNullOrEmpty(pagedDto.CreateOrgId), x => x.CreatorOrgId != null && x.CreatorOrgId.StartsWith(pagedDto.CreateOrgId!))
                 .WhereIF(!string.IsNullOrEmpty(pagedDto.Attribution), x => x.Attribution == pagedDto.Attribution)
-                .WhereIF(!string.IsNullOrEmpty(pagedDto.Title), x => x.Title.Contains(pagedDto.Title))
-                .WhereIF(!string.IsNullOrEmpty(pagedDto.Content), x => x.Content.Contains(pagedDto.Content))
-                .WhereIF(!string.IsNullOrEmpty(pagedDto.Keywords), x => x.Title.Contains(pagedDto.Keywords!) ||
-                                                                  x.CreatorName!.Contains(pagedDto.Keywords!) ||
-                                                                  x.CreatorOrgName!.Contains(pagedDto.Keywords!))
+
+                // 预案库没有关键词、去掉关键词查询
+
+                .WhereIF(!string.IsNullOrEmpty(pagedDto.Title) &&
+                          string.IsNullOrEmpty(pagedDto.Content), x => x.Title.Contains(pagedDto.Title))
+
+                .WhereIF(string.IsNullOrEmpty(pagedDto.Title) &&
+                        !string.IsNullOrEmpty(pagedDto.Content), x => x.Content.Contains(pagedDto.Content))
+
+                .WhereIF(!string.IsNullOrEmpty(pagedDto.Title) &&
+                         !string.IsNullOrEmpty(pagedDto.Content), x => x.Title.Contains(pagedDto.Title!) ||
+                                                                   x.Content!.Contains(pagedDto.Content!))
 
                 .WhereIF(pagedDto.Status.HasValue && pagedDto.Status == EPlanStatus.OnShelf, x => x.Status == EPlanStatus.OnShelf)
                 .WhereIF(pagedDto.Status.HasValue && pagedDto.Status == EPlanStatus.OffShelf, x => x.Status == EPlanStatus.OffShelf)
@@ -287,6 +294,8 @@ namespace Hotline.Application.Planlibrary
             if (any)
                 throw UserFriendlyException.SameMessage("当前预案标题存在重复标题!");
 
+            pList.InitId();
+
             if (dto.Files != null && dto.Files.Count > 0)
                 pList.FileJson = await _fileRepository.AddFileAsync(dto.Files, pList.Id, "", cancellationToken);
             await _planListRepository.AddAsync(pList, cancellationToken);

+ 10 - 0
src/Hotline.Application/Quality/IQualityApplication.cs

@@ -1,5 +1,7 @@
 using Hotline.Share.Dtos.Quality;
 using Hotline.Share.Enums.Quality;
+using Hotline.Share.Requests;
+using SqlSugar;
 
 namespace Hotline.Application.Quality
 {
@@ -11,5 +13,13 @@ namespace Hotline.Application.Quality
 		Task UpdateQualityAsync(UpdateQualityDto model, CancellationToken cancellationToken);
 		Task AiResultAsync(List<AiQualityResultDto> model, CancellationToken cancellationToken);
 
+		Task Transfer_XT(string Id, CancellationToken cancellationToken);
+		ISugarQueryable<Hotline.Quality.Quality> SeatsQualityAnalyse(PagedKeywordRequest dto, CancellationToken cancellationToken);
+
+		ISugarQueryable<QualityOrderOverviewDto> QualityOrderOverview(QualityWorkAnalysisRequest dto, int allOrderNum, CancellationToken cancellationToken);
+		ISugarQueryable<SeatsQualityGradeAnalyseDto> SeatsQualityGradeAnalyse(QualityWorkAnalysisRequest dto, CancellationToken cancellationToken);
+
+		Task<List<MonthQualityGradeDto>> MonthQualityGrade(QualityWorkAnalysisRequest dto, CancellationToken cancellationToken);
+
 	}
 }

+ 172 - 3
src/Hotline.Application/Quality/QualityApplication.cs

@@ -27,6 +27,19 @@ using XF.Domain.Repository;
 using Hotline.Share.Enums.CallCenter;
 using Hotline.Configurations;
 using Hotline.Tools;
+using Hotline.Share.Dtos;
+using MathNet.Numerics;
+using Microsoft.Extensions.DependencyInjection;
+using NPOI.HPSF;
+using System.Threading;
+using Hotline.Share.Requests;
+using Hotline.Repository.SqlSugar.TextSearch;
+using SqlSugar;
+using Hotline.Share.Enums.Order;
+using Hotline.Repository.SqlSugar;
+using DocumentFormat.OpenXml.Wordprocessing;
+using Novacode;
+using NPOI.HSSF.Record.Chart;
 
 namespace Hotline.Application.Quality
 {
@@ -45,8 +58,13 @@ namespace Hotline.Application.Quality
         private readonly Publisher _publisher;
         private readonly IRepository<TrCallRecord> _trCallRecordRepository;
         private readonly IRepository<OrderWord> _orderWordRepository;
+		private readonly IServiceProvider _serviceProvider;
+		private readonly IRepository<QualityItem> _qualiteyItem;
+		private readonly IRepository<QualityTransferRecords> _qualityTransferRecordsRepository;
+		protected ISugarUnitOfWork<HotlineDbContext> Uow { get; }
+		protected ISqlSugarClient Db { get; }
 
-        public QualityApplication(
+		public QualityApplication(
             ISessionContext sessionContext,
             IMapper mapper,
             IRepository<QualityDetail> qualiteyDetail,
@@ -59,7 +77,11 @@ namespace Hotline.Application.Quality
             ILogger<Hotline.Quality.Quality> logger,
             Publisher publisher,
             IRepository<TrCallRecord> trCallRecordRepository,
-            IRepository<OrderWord> orderWordRepository)
+			IServiceProvider serviceProvider,
+			IRepository<QualityItem> qualiteyItem,
+			IRepository<OrderWord> orderWordRepository,
+			IRepository<QualityTransferRecords> qualityTransferRecordsRepository,
+			ISugarUnitOfWork<HotlineDbContext> uow)
         {
             _sessionContext = sessionContext;
             _mapper = mapper;
@@ -74,7 +96,12 @@ namespace Hotline.Application.Quality
             _publisher = publisher;
             _trCallRecordRepository = trCallRecordRepository;
             _orderWordRepository = orderWordRepository;
-        }
+            _serviceProvider = serviceProvider;
+			_qualiteyItem = qualiteyItem;
+            _qualityTransferRecordsRepository = qualityTransferRecordsRepository;
+			Uow = uow;
+			Db = uow.Db;
+		}
 
         /// <summary>
         /// 新增质检
@@ -369,6 +396,148 @@ namespace Hotline.Application.Quality
 			}
 		}
 
+        /// <summary>
+        /// 异步转写
+        /// </summary>
+        /// <returns></returns>
+        public async Task Transfer_XT(string Id, CancellationToken cancellationToken) {
+
+			var quality = await _qualityRepository.Queryable().Includes(x => x.Order).Where(x => x.Id == Id).FirstAsync();
+			var call = await _callApplication.GetCallAsync(quality.Order.CallId, cancellationToken);
+			var items = await _qualiteyItem.Queryable().Where(x => x.IsEnable == 0 && x.IsDeleted == true && x.IsIntelligent == 1).ToListAsync();
+
+			var transfers = new List<Transfer>();
+			if (!string.IsNullOrEmpty(call?.AudioFile))
+			{
+				var filename = Path.GetFileName(call.AudioFile);
+				try
+				{
+					var aiQualityService = _serviceProvider.GetRequiredService<IAiQualityService>();
+					var res = await aiQualityService.CreateAiOrderQualityTask(filename, cancellationToken);
+					if (!string.IsNullOrEmpty(res))
+					{
+						var rowList = res.Split("\n");
+						var sort = 0;
+						foreach (var row in rowList)
+						{
+							var model = new Transfer();
+							var statement = row.Split(":");
+							if (statement.Count() == 2)
+							{
+								model.Sort = sort;
+								model.Type = statement[0].ToString() == "spk_01" ? EQualityTransferType.Seats : EQualityTransferType.Citizen;
+								model.Content = statement[1].ToString();
+								model.ReviseContent = model.Content;
+								if (!string.IsNullOrEmpty(model.Content))
+								{
+									var hitWord = items.Where(x => model.Content.Contains(x.Name)).ToList();
+									List<QualityDetail> details = new List<QualityDetail>();
+									foreach (var item in hitWord)
+                                    {
+										QualityDetail detail = new QualityDetail
+										{
+											QualityId = quality.Id,
+											Second = 0,
+											EndSecond = 0,
+											Name = item.Name,
+											Content = item.Name,
+											Intelligent = true,
+											Grade = item.Grade,
+											Check = true,
+										};
+                                        details.Add(detail);
+									}
+									model.Prohibited = string.Join(";", hitWord);
+                                    await _qualiteyDetail.AddRangeAsync(details);
+								}
+							}
+							transfers.Add(model);
+							sort++;
+						}
+					}
+					await _qualityRepository.Updateable().SetColumns(x => new Hotline.Quality.Quality { TransferState = EQualityTransferState.Succeed,Transfer = transfers }).Where(x => x.Id == quality.Id).ExecuteCommandAsync();
+					await _qualityTransferRecordsRepository.Updateable().SetColumns(x => new QualityTransferRecords { TransferState = EQualityTransferState.Succeed,IsFinished = true }).Where(x => x.QualityId == quality.Id).ExecuteCommandAsync();
+				}
+				catch (Exception)
+				{
+					await _qualityRepository.Updateable().SetColumns(x => new Hotline.Quality.Quality { TransferState = EQualityTransferState.Lose}).Where(x => x.Id == quality.Id).ExecuteCommandAsync();
+					await _qualityTransferRecordsRepository.Updateable().SetColumns(x => new QualityTransferRecords { TransferState = EQualityTransferState.Lose, IsFinished = true }).Where(x => x.QualityId == quality.Id).ExecuteCommandAsync();
+					throw;
+				}
+			}
+		}
+
+
+        /// <summary>
+        /// 坐席质检分析
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        public ISugarQueryable<Hotline.Quality.Quality> SeatsQualityAnalyse(PagedKeywordRequest dto, CancellationToken cancellationToken) {
+
+            var query = _qualityRepository.Queryable()
+                .Includes(x => x.Order)
+                .Includes(x => x.QualityDetails)
+                .Where(x => x.Source == EQualitySource.Accepted && x.QualityTime >= dto.StartTime && x.QualityTime <= dto.EndTime && x.State == EQualityState.End);
+            return query;
+        }
+
+        /// <summary>
+        /// 质检工单概览
+        /// </summary>
+        /// <returns></returns>
+        public  ISugarQueryable<QualityOrderOverviewDto>  QualityOrderOverview(QualityWorkAnalysisRequest dto, int allOrderNum, CancellationToken cancellationToken) {
+
+            var query = _qualiteyDetail.Queryable()
+                .Includes(x => x.Quality)
+				.Where(x => x.Quality.Source == EQualitySource.Accepted && x.Quality.QualityTime >= dto.StartTime && x.Quality.QualityTime <= dto.EndTime && x.Quality.State == EQualityState.End)
+				.GroupBy(x => x.Name)
+                .Select(x => new QualityOrderOverviewDto
+                {
+                    QualityItem = x.Name,
+                    AllOrderNum = allOrderNum,
+                    OrderNum = SqlFunc.AggregateDistinctCount(x.Quality.OrderId)
+                });
+            return query;
+        }
+
+		public ISugarQueryable<SeatsQualityGradeAnalyseDto> SeatsQualityGradeAnalyse(QualityWorkAnalysisRequest dto,CancellationToken cancellationToken)
+		{
+
+			var query = _qualityRepository.Queryable()
+                .Includes(x=>x.Order)
+				.Where(x => x.Source == EQualitySource.Accepted && x.QualityTime >= dto.StartTime && x.QualityTime <= dto.EndTime && x.State == EQualityState.End)
+                .WhereIF(!string.IsNullOrEmpty(dto.AcceptorId),x=>x.Order.AcceptorId == dto.AcceptorId)
+				.Select(x => new SeatsQualityGradeAnalyseDto
+				{
+                    NinetyGrade = SqlFunc.AggregateSum(SqlFunc.IIF(x.Grade >= 90, 1, 0)),
+                    EightyGrade = SqlFunc.AggregateSum(SqlFunc.IIF(x.Grade >= 80 &&  x.Grade < 90, 1, 0)),
+					SeventyGrade = SqlFunc.AggregateSum(SqlFunc.IIF(x.Grade >= 70 && x.Grade < 80, 1, 0)),
+					SixtyGrade = SqlFunc.AggregateSum(SqlFunc.IIF(x.Grade >= 60 && x.Grade < 70, 1, 0)),
+					FiftyGrade = SqlFunc.AggregateSum(SqlFunc.IIF(x.Grade < 60, 1, 0))
+				});
+			return query;
+		}
+
+		public async Task<List<MonthQualityGradeDto>> MonthQualityGrade(QualityWorkAnalysisRequest dto, CancellationToken cancellationToken)
+		{
+            var queryList = Db.Reportable(ReportableDateType.MonthsInLast1years).ToQueryable<DateTime>();
+
+            var qualityList = _qualityRepository.Queryable()
+                .Where(x => x.Source == EQualitySource.Accepted && x.State == EQualityState.End)
+                .WhereIF(dto.MinGrade.HasValue && dto.MaxGrade.HasValue, x => x.Grade >= dto.MinGrade && x.Grade <= dto.MaxGrade)
+                .WhereIF(!string.IsNullOrEmpty(dto.AcceptorId), x => x.Order.AcceptorId == dto.AcceptorId);
+
+
+            var query = await queryList.LeftJoin(qualityList, (x1, x2) => x2.QualityTime.Value.ToString("yyyy-MM") == x1.ColumnName.ToString("yyyy-MM"))
+                .GroupBy((x1, x2) => x1.ColumnName)
+                .Select((x1, x2) => new MonthQualityGradeDto
+				{
+					Month = x1.ColumnName.ToString("MM"),
+                    Num = SqlFunc.AggregateDistinctCount(x2.OrderId)
+				}).ToListAsync();
+			return query;
+		}
 
 	}
 }

+ 7 - 0
src/Hotline.Application/Snapshot/IIndustryApplication.cs

@@ -177,4 +177,11 @@ public interface IIndustryApplication
     /// <param name="dto"></param>
     /// <returns></returns>
     ISugarQueryable<IndustryLogItemsOutDto> GetIndustryLogItemsAsync(IndustryLogItemsInDto dto);
+
+    /// <summary>
+    /// 删除行业短信模板
+    /// </summary>
+    /// <param name="ids"></param>
+    /// <returns></returns>
+    Task DeleteSMSTemplateAsync(IList<string> ids);
 }

+ 16 - 1
src/Hotline.Application/Snapshot/IInviteCodeApplication.cs

@@ -1,4 +1,5 @@
-using Hotline.Share.Dtos.Snapshot;
+using Hotline.Share.Dtos;
+using Hotline.Share.Dtos.Snapshot;
 using Hotline.Snapshot;
 using SqlSugar;
 using System;
@@ -11,6 +12,13 @@ namespace Hotline.Application.Snapshot;
 public interface IInviteCodeApplication
 {
     Task AddInviteCodeAsync(AddInviteCodeInDto dto);
+
+    /// <summary>
+    /// 删除邀请码
+    /// </summary>
+    /// <param name="ids"></param>
+    /// <returns></returns>
+    Task DeleteInviteCodeAsync(IList<string> ids);
     ISugarQueryable<InviteCode> GetInviteCodeItemsAsync();
 
     /// <summary>
@@ -20,6 +28,13 @@ public interface IInviteCodeApplication
     /// <returns></returns>
     Task<IList<InviteCodeStatisticOutDto>> GetInviteCodeStatisticAsync(GetInviteCodeStatisticInDto dto);
 
+    /// <summary>
+    /// 邀请码统计明细
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <returns></returns>
+    ISugarQueryable<InviteCodeStatisticDetailOutDto> GetInviteCodeStatisticDetailAsync(GetInviteCodeStatisticDetailInDto dto);
+
     /// <summary>
     /// 修改邀请码
     /// </summary>

+ 7 - 7
src/Hotline.Application/Snapshot/IOrderSnapshotApplication.cs

@@ -56,11 +56,11 @@ public interface IOrderSnapshotApplication
     ISugarQueryable<GetOrderSnapshotPublishItemsOutDto> GetOrderSnapshotPublishItemsAsync(GetOrderSnapshotPublishItemsInDto dto);
 
     /// <summary>
-    /// 获取工单标集合
+    /// 获取工单标集合
     /// </summary>
     /// <param name="dto"></param>
     /// <returns></returns>
-    ISugarQueryable<SignOrderSnapshotItemsOutDto> GetSignOrderSnapshotItemsAsync(SignOrderSnapshotItemsInDto dto);
+    ISugarQueryable<LabelOrderSnapshotItemsOutDto> GetLabelOrderSnapshotItemsAsync(LabelOrderSnapshotItemsInDto dto);
 
     /// <summary>
     /// 批量设置随手拍公开申请不通过
@@ -75,7 +75,7 @@ public interface IOrderSnapshotApplication
     Task UpdateOrderSnapshotPublishStatusAsync(UpdateOrderSnapshotPublishStatusInDto dto);
 
     /// <summary>
-    /// 随手拍工单标
+    /// 随手拍工单标
     /// </summary>
     /// <param name="id"></param>
     /// <param name="snapshotLabels"></param>
@@ -90,11 +90,11 @@ public interface IOrderSnapshotApplication
     Task SaveOrderWorkflowInfo(NextWorkflowDto<OrderHandleFlowDto> dto);
 
     /// <summary>
-    /// 获取随手拍工单标日志
+    /// 获取随手拍工单标日志
     /// </summary>
     /// <param name="dto"></param>
     /// <returns></returns>
-    ISugarQueryable<SignOrderSnapshotLogItemsOutDto> GetSignOrderSnapshotLogItemsAsync(SignOrderSnapshotLogItemsInDto dto);
+    ISugarQueryable<LabelOrderSnapshotLogItemsOutDto> GetLabelOrderSnapshotLogItemsAsync(LabelOrderSnapshotLogItemsInDto dto);
 
     /// <summary>
     /// 返回办理页面基础数据
@@ -112,9 +112,9 @@ public interface IOrderSnapshotApplication
     ISugarQueryable<OrderSnapshotItemsOutDto> GetOrderSnapshotItemsAsync(OrderSnapshotItemsInDto dto);
 
     /// <summary>
-    /// 获取工单已经标集合
+    /// 获取工单已经标集合
     /// </summary>
     /// <param name="dto"></param>
     /// <returns></returns>
-    ISugarQueryable<SignedOrderSnapshotItemsOutDto> GetSignedOrderSnapshotItemsAsync(SignedOrderSnapshotItemsInDto dto);
+    ISugarQueryable<LabeledOrderSnapshotItemsOutDto> GetLabeledOrderSnapshotItemsAsync(LabeledOrderSnapshotItemsInDto dto);
 }

+ 23 - 1
src/Hotline.Application/Snapshot/IndustryApplication.cs

@@ -225,7 +225,10 @@ public class IndustryApplication : IIndustryApplication, IScopeDependency
         return await _snapshotSMSTemplateRepository.Queryable()
             .LeftJoin<Industry>((s, i) => s.IndustryId == i.Id)
             .Where((s, i) => s.Id == id)
-            .Select<SnapshotSMSTemplateItemsOutDto>()
+            .Select((s, i) => new SnapshotSMSTemplateItemsOutDto
+            { 
+                Status = s.Status
+            }, true)
             .FirstAsync();
     }
 
@@ -399,5 +402,24 @@ public class IndustryApplication : IIndustryApplication, IScopeDependency
         return query;
     }
 
+    /// <summary>
+    /// 删除行业短信模板
+    /// </summary>
+    /// <param name="ids"></param>
+    /// <returns></returns>
+    public async Task DeleteSMSTemplateAsync(IList<string> ids)
+    {
+        await _snapshotSMSTemplateRepository.Queryable()
+            .Where(m => ids.Contains(m.Id))
+            .ToListAsync()
+            .Then(async sms => {
+                for (int i = 0;i < sms.Count;i++)
+                {
+                    sms[i].IsDeleted = true;
+                    await _snapshotSMSTemplateRepository.UpdateAsync(sms[i]);
+                }
+            });
+    }
+
     #endregion
 }

+ 44 - 3
src/Hotline.Application/Snapshot/InviteCodeApplication.cs

@@ -1,4 +1,5 @@
 using Hotline.Share.Dtos.Snapshot;
+using Hotline.Share.Tools;
 using Hotline.Snapshot;
 using Hotline.Snapshot.Interfaces;
 using Hotline.Tools;
@@ -38,6 +39,26 @@ public class InviteCodeApplication : IInviteCodeApplication, IScopeDependency
         await _inviteCodeRepository.AddAsync(entity);
     }
 
+    /// <summary>
+    /// 删除邀请码
+    /// </summary>
+    /// <param name="ids"></param>
+    /// <returns></returns>
+    public async Task DeleteInviteCodeAsync(IList<string> ids)
+    {
+        await _inviteCodeRepository.Queryable()
+            .Where(m => ids.Contains(m.Id))
+            .ToListAsync()
+            .Then(async invite =>
+            {
+                for (int i = 0;i < invite.Count;i++)
+                {
+                    invite[i].IsDeleted = true;
+                    await _inviteCodeRepository.UpdateAsync(invite[i]);
+                }
+            });
+    }
+
     public ISugarQueryable<InviteCode> GetInviteCodeItemsAsync()
     {
         return _inviteCodeRepository.Queryable();
@@ -47,11 +68,11 @@ public class InviteCodeApplication : IInviteCodeApplication, IScopeDependency
     {
         dto.ValidateObject();
         var items = await _inviteCodeRecordRepository.Queryable()
-            .Where(m => m.CreationTime >= dto.BeginTime && m.CreationTime <= dto.EndTime)
-            .GroupBy(m => m.OrgName)
+            .Where(m => m.CreationTime >= dto.StartTime && m.CreationTime <= dto.EndTime)
+            .GroupBy(m => new { m.Id, m.OrgName })
             .Select(m => new InviteCodeStatisticOutDto
             { 
-                Id = m.OrgName, 
+                Id = m.Id, 
                 OrgName = m.OrgName,
                 Count = SqlFunc.AggregateCount(m.OrgName)
             })
@@ -59,6 +80,26 @@ public class InviteCodeApplication : IInviteCodeApplication, IScopeDependency
         return items;
     }
 
+    /// <summary>
+    /// 邀请码统计明细
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <returns></returns>
+    public ISugarQueryable<InviteCodeStatisticDetailOutDto> GetInviteCodeStatisticDetailAsync(GetInviteCodeStatisticDetailInDto dto)
+    {
+        dto.ValidateObject();
+        var query = _inviteCodeRecordRepository.Queryable()
+            .Where(m => m.CreationTime >= dto.StartTime && m.CreationTime <= dto.EndTime && m.OrgId == dto.OrgId)
+            .WhereIF(dto.Name.NotNullOrEmpty(), m => m.Name.Contains(dto.Name))
+            .WhereIF(dto.PhoneNumber.NotNullOrEmpty(), m => m.PhoneNumber.Contains(dto.PhoneNumber))
+            .WhereIF(dto.InviteCode.NotNullOrEmpty(), m => m.InviteCode.Contains(dto.InviteCode))
+            .Select(m => new InviteCodeStatisticDetailOutDto
+            { 
+                OpenId = m.WXOpenId
+            }, true);
+        return query;
+    }
+
     public async Task UpdateInviteCodeAsync(UpdateInviteCodeInDto dto)
     {
         var entity = await _inviteCodeRepository.GetAsync(dto.Id)

+ 26 - 22
src/Hotline.Application/Snapshot/OrderSnapshotApplication.cs

@@ -198,7 +198,7 @@ public class OrderSnapshotApplication : IOrderSnapshotApplication, IScopeDepende
     /// </summary>
     /// <param name="dto"></param>
     /// <returns></returns>
-    public ISugarQueryable<SignOrderSnapshotItemsOutDto> GetSignOrderSnapshotItemsAsync(SignOrderSnapshotItemsInDto dto)
+    public ISugarQueryable<LabelOrderSnapshotItemsOutDto> GetLabelOrderSnapshotItemsAsync(LabelOrderSnapshotItemsInDto dto)
     {
         var query = _orderSnapshotRepository.Queryable()
             .LeftJoin<Order>((snapshot, order) => snapshot.Id == order.Id)
@@ -209,7 +209,7 @@ public class OrderSnapshotApplication : IOrderSnapshotApplication, IScopeDepende
             .WhereIF(dto.No.NotNullOrEmpty(), (snapshot, order, step) => order.No.Contains(dto.No))
             .WhereIF(dto.Title.NotNullOrEmpty(), (snapshot, order, step) => order.Title.Contains(dto.Title))
             .OrderByDescending((snapshot, order, step) => snapshot.CreationTime)
-            .Select((snapshot, order) => new SignOrderSnapshotItemsOutDto
+            .Select((snapshot, order) => new LabelOrderSnapshotItemsOutDto
             {
                 OrderId = snapshot.Id,
                 No = order.No,
@@ -222,8 +222,8 @@ public class OrderSnapshotApplication : IOrderSnapshotApplication, IScopeDepende
                 NetworkENumber = snapshot.NetworkENumber,
                 NetworkRemark = snapshot.NetworkRemark,
                 ReplyDate = snapshot.ReplyDate,
-                SignTime = snapshot.SignTime,
-                SignUserName = snapshot.SignUserName
+                LabelTime = snapshot.LabelTime,
+                LabelUserName = snapshot.LabelUserName
             });
         return query;
     }
@@ -281,12 +281,16 @@ public class OrderSnapshotApplication : IOrderSnapshotApplication, IScopeDepende
             snapshot.IsTruthDepartment = true;
         if (labels.Any(m => m.Key == "bss"))
             snapshot.IsTruthDepartment = false;
+        snapshot.LabelTime = DateTime.Now;
+        snapshot.LabelUserName = _sessionContext.UserName;
+        snapshot.LabelUserId = _sessionContext.UserId;
         await _orderSnapshotRepository.UpdateAsync(snapshot);
 
-        var entity = new SnapshotLabelLog { 
-        OrderId = snapshot.Id,
-        LabelName = snapshot.LabelName,
-        Labels = labels
+        var entity = new SnapshotLabelLog
+        {
+            OrderId = snapshot.Id,
+            LabelName = snapshot.LabelName,
+            Labels = labels
         };
         await _snapshotLabelLogRepository.AddAsync(entity);
     }
@@ -346,23 +350,23 @@ public class OrderSnapshotApplication : IOrderSnapshotApplication, IScopeDepende
     /// </summary>
     /// <param name="dto"></param>
     /// <returns></returns>
-    public ISugarQueryable<SignOrderSnapshotLogItemsOutDto> GetSignOrderSnapshotLogItemsAsync(SignOrderSnapshotLogItemsInDto dto)
+    public ISugarQueryable<LabelOrderSnapshotLogItemsOutDto> GetLabelOrderSnapshotLogItemsAsync(LabelOrderSnapshotLogItemsInDto dto)
     {
         var query = _snapshotLabelLogRepository.Queryable()
             .LeftJoin<Order>((log, order) => log.OrderId == order.Id)
             .LeftJoin<OrderSnapshot>((log, order, snapshot) => log.OrderId == snapshot.Id)
             .WhereIF(dto.No.NotNullOrEmpty(), (log, order) => order.No.Contains(dto.No))
             .WhereIF(dto.Title.NotNullOrEmpty(), (log, order) => order.Title.Contains(dto.Title))
-            .WhereIF(dto.Contact.NotNullOrEmpty(), (log, order)=> order.Contact.Contains(dto.Contact))
-            .WhereIF(dto.FromName.NotNullOrEmpty(), (log, order)=> order.FromName.Contains(dto.FromName))
-            .WhereIF(dto.Label.NotNullOrEmpty(), (log, order)=> log.LabelName.Contains(dto.Label))
-            .WhereIF(dto.SignName.NotNullOrEmpty(), (log, order) => log.CreatorName.Contains(dto.SignName))
+            .WhereIF(dto.Contact.NotNullOrEmpty(), (log, order) => order.Contact.Contains(dto.Contact))
+            .WhereIF(dto.FromName.NotNullOrEmpty(), (log, order) => order.FromName.Contains(dto.FromName))
+            .WhereIF(dto.Label.NotNullOrEmpty(), (log, order) => log.LabelName.Contains(dto.Label))
+            .WhereIF(dto.LabelUserName.NotNullOrEmpty(), (log, order) => log.CreatorName.Contains(dto.LabelUserName))
             .WhereIF(dto.BeginSignTime.HasValue && dto.EndSignTime.HasValue, (log, order) => log.CreationTime >= dto.BeginSignTime && log.CreationTime <= dto.EndSignTime)
-            .Select((log, order, snapshot) => new SignOrderSnapshotLogItemsOutDto
+            .Select((log, order, snapshot) => new LabelOrderSnapshotLogItemsOutDto
             {
                 OrderId = order.Id,
-                SignName = log.CreatorName,
-                SignTime = log.CreationTime,
+                LabelUserName = log.CreatorName,
+                LabelTime = log.CreationTime,
                 IndustryName = snapshot.IndustryName
             }, true);
         return query;
@@ -425,7 +429,7 @@ public class OrderSnapshotApplication : IOrderSnapshotApplication, IScopeDepende
             .WhereIF(dto.No.NotNullOrEmpty(), (snapshot, order) => order.No.Contains(dto.No))
             .WhereIF(dto.Title.NotNullOrEmpty(), (snapshot, order) => order.Title.Contains(dto.Title))
             .WhereIF(dto.ActualHandleOrgName.NotNullOrEmpty(), (snapshot, order) => order.ActualHandleOrgName.Contains(dto.ActualHandleOrgName))
-            .Select((snapshot, order) => new OrderSnapshotItemsOutDto 
+            .Select((snapshot, order) => new OrderSnapshotItemsOutDto
             {
                 CreationTime = order.CreationTime
             }
@@ -438,7 +442,7 @@ public class OrderSnapshotApplication : IOrderSnapshotApplication, IScopeDepende
     /// </summary>
     /// <param name="dto"></param>
     /// <returns></returns>
-    public ISugarQueryable<SignedOrderSnapshotItemsOutDto> GetSignedOrderSnapshotItemsAsync(SignedOrderSnapshotItemsInDto dto)
+    public ISugarQueryable<LabeledOrderSnapshotItemsOutDto> GetLabeledOrderSnapshotItemsAsync(LabeledOrderSnapshotItemsInDto dto)
     {
         var query = _orderSnapshotRepository.Queryable()
             .LeftJoin<Order>((snapshot, order) => order.Id == snapshot.Id)
@@ -450,12 +454,12 @@ public class OrderSnapshotApplication : IOrderSnapshotApplication, IScopeDepende
             .WhereIF(dto.Label.NotNullOrEmpty(), (snapshot, order) => snapshot.LabelName.Contains(dto.Label))
             .WhereIF(dto.BeginSignTime.HasValue && dto.EndSignTime.HasValue, (snapshot, order) => snapshot.SignTime >= dto.BeginSignTime && snapshot.SignTime <= dto.EndSignTime)
             .OrderByDescending((snapshot, order) => snapshot.CreationTime)
-            .Select((snapshot, order) => new SignedOrderSnapshotItemsOutDto
-            { 
+            .Select((snapshot, order) => new LabeledOrderSnapshotItemsOutDto
+            {
                 LabelName = snapshot.LabelName,
                 OrderId = order.Id,
-                SignTime = snapshot.SignTime,
-                SignUserName = snapshot.SignUserName,
+                LabelTime = snapshot.LabelTime,
+                LabelUserName = snapshot.LabelUserName,
                 SourceChannel = order.SourceChannel,
             }, true);
 

+ 12 - 8
src/Hotline.Application/Snapshot/RedPackApplication.cs

@@ -156,8 +156,7 @@ public class RedPackApplication : IRedPackApplication, IScopeDependency
         if (count != 0)
             outDto.RedPackTxt += $"今天审批【{count}】个";
 
-        outDto.AuditComBox = EnumExts.GetDescriptions<ERedPackAuditStatus>()
-            .Where(m => m.Key.ToString() != "0").ToList();
+        outDto.AuditComBox = EnumExts.GetDescriptions<ESnapshotSMSStatus>();
         return outDto;
     }
 
@@ -347,13 +346,18 @@ public class RedPackApplication : IRedPackApplication, IScopeDependency
     /// <returns></returns>
     public async Task<IList<GetRedPackAuditSMSTemplateOutDto>> GetRedPackAuditSMSTemplateAsync(GetRedPackAuditSMSTemplateInDto dto)
     {
+        var industryId = await _orderSnapshotRepository.Queryable()
+            .Where(m => m.Id == dto.OrderId)
+            .Select(m => m.IndustryId).FirstAsync();
         var items = await _snapshotSMSTemplateRepository.Queryable(includeDeleted: true)
-            .LeftJoin<Industry>((sms, industry) => sms.IndustryId == industry.Id)
-            .LeftJoin<OrderSnapshot>((sms, industry, snapshot) => snapshot.IndustryId == industry.Id)
-            .Where((sms, industry, snapshot) => (sms.IsPublic == true || sms.IndustryId == snapshot.IndustryId)
-             && sms.Status == dto.Status && sms.IsEnable == true)
-            .Select<GetRedPackAuditSMSTemplateOutDto>()
-            .Distinct()
+            .Where(sms => (sms.IsPublic == true || sms.IndustryId == industryId)
+             && sms.Status == dto.Status && sms.IsEnable == true && sms.IsDeleted == false)
+            .Select(sms => new GetRedPackAuditSMSTemplateOutDto
+            {
+                Id = sms.Id,
+                Content = sms.Content
+            })
+            .GroupBy(sms => new { sms.Id, sms.Content })
             .ToListAsync();
         return items;
     }

+ 7 - 6
src/Hotline.Application/Snapshot/SnapshotApplicationBase.cs

@@ -131,13 +131,13 @@ public abstract class SnapshotApplicationBase
         items.ForEach(m =>
         {
             if (m.BackgroundImgUrl.NotNullOrEmpty())
-                m.BackgroundImgUrl = fileDownloadApi + m.BackgroundImgUrl;
+                m.BackgroundImgUrl = fileServiceUrl + m.BackgroundImgUrl;
             if (m.BannerImgUrl.NotNullOrEmpty())
-                m.BannerImgUrl = fileDownloadApi + m.BannerImgUrl;
+                m.BannerImgUrl = fileServiceUrl + m.BannerImgUrl;
             if (m.CareCellImgUrl.NotNullOrEmpty())
-                m.CareCellImgUrl = fileDownloadApi + m.CareCellImgUrl;
+                m.CareCellImgUrl = fileServiceUrl + m.CareCellImgUrl;
             if (m.CellImgUrl.NotNullOrEmpty())
-                m.CellImgUrl = fileDownloadApi + m.CellImgUrl;
+                m.CellImgUrl = fileServiceUrl + m.CellImgUrl;
         });
 
         return new HomePageOutDto
@@ -293,6 +293,9 @@ public abstract class SnapshotApplicationBase
     {
         var openId = _sessionContext.OpenId;
         var thirdAccount = await _thirdAccountRepository.GetByOpenIdAsync(openId);
+        if (thirdAccount.PhoneNumber.IsNullOrEmpty() && _sessionContext.Phone.NotNullOrEmpty())
+            thirdAccount.PhoneNumber = _sessionContext.Phone;
+
         var dayTime = DateTime.Now;
         var readPack = await _redPackRecordRepository.Queryable()
             .Where(m => m.WXOpenId == openId && m.PickupStatus == ERedPackPickupStatus.Received)
@@ -812,7 +815,6 @@ public abstract class SnapshotApplicationBase
     }
     #endregion
 
-
     #region 红包
     /// <summary>
     /// 生成用户红包审核数据
@@ -851,7 +853,6 @@ public abstract class SnapshotApplicationBase
     }
     #endregion
 
-
     #region 随手拍公告
     /// <summary>
     /// 添加随手拍公告

+ 17 - 3
src/Hotline.Application/StatisticalReport/CallReport/YiBinCallReportApplication.cs

@@ -315,9 +315,9 @@ public class YiBinCallReportApplication : CallReportApplicationBase, ICallReport
                   InHangupImmediateWhenAnswered = SqlFunc.AggregateSum(SqlFunc.IIF(c.AnsweredTime != null && c.Duration < connectByeTimes, 1, 0)),//接通秒挂
                   OverTimeImmediate = SqlFunc.AggregateSum(SqlFunc.IIF(c.AnsweredTime != null && c.RingTimes > ringTimes, 1, 0)),//超时接通量
                   InTimeImmediate = SqlFunc.AggregateSum(SqlFunc.IIF(c.AnsweredTime != null && c.RingTimes <= ringTimes, 1, 0)),//按时接通量
-                  InHanguped = SqlFunc.AggregateSum(SqlFunc.IIF(c.AnsweredTime == null, 1, 0)),
-                  InHangupImmediate = SqlFunc.AggregateSum(SqlFunc.IIF(c.AnsweredTime == null && c.RingTimes <= noConnectByeTimes, 1, 0)),
-                  OverTimeInHanguped = SqlFunc.AggregateSum(SqlFunc.IIF(c.AnsweredTime == null && c.RingTimes > noConnectByeTimes, 1, 0)),
+                  InHanguped = SqlFunc.AggregateSum(SqlFunc.IIF(c.AnsweredTime == null, 1, 0)), //呼入未接通
+                  InHangupImmediate = SqlFunc.AggregateSum(SqlFunc.IIF(c.AnsweredTime == null && c.RingTimes <= noConnectByeTimes, 1, 0)),//未接通秒挂量
+                  OverTimeInHanguped = SqlFunc.AggregateSum(SqlFunc.IIF(c.AnsweredTime == null && c.RingTimes > noConnectByeTimes, 1, 0)), //超时未接通量
               }).ToListAsync();
         return list;
     }
@@ -329,6 +329,12 @@ public class YiBinCallReportApplication : CallReportApplicationBase, ICallReport
     /// <returns></returns>
     public override ISugarQueryable<QuerySeatMonthCallDetailResp> QuerySeatMonthCallDetail(QuerySeatMonthCallDetailRequest dto)
     {
+        //获取配置
+        int noConnectByeTimes = _systemSettingCacheManager.NoConnectByeTimes;
+        int effectiveTimes = _systemSettingCacheManager.EffectiveTimes;
+        int connectByeTimes = _systemSettingCacheManager.ConnectByeTimes;
+        int ringTimes = _systemSettingCacheManager.RingTimes;
+
         return  _trCallRecordRepository.Queryable()
             .WhereIF(dto.RingStartTime.HasValue, x => x.BeginRingTime >= dto.RingStartTime)
             .WhereIF(dto.RingEndTime.HasValue, x => x.BeginRingTime <= dto.RingEndTime)
@@ -338,6 +344,14 @@ public class YiBinCallReportApplication : CallReportApplicationBase, ICallReport
             .WhereIF(!string.IsNullOrEmpty(dto.TelNo), x => x.TelNo.Contains(dto.TelNo!))
             .WhereIF(!string.IsNullOrEmpty(dto.Cpn),x=>x.CPN.Contains(dto.Cpn!))
             .WhereIF(!string.IsNullOrEmpty(dto.Cdpn), x => x.CDPN.Contains(dto.Cdpn!))
+            .WhereIF(dto.QueryType.HasValue && dto.QueryType == 1,x=>x.AnsweredTime != null) //呼入接通总量
+            .WhereIF(dto.QueryType.HasValue && dto.QueryType == 2,x=> x.AnsweredTime != null && x.Duration >= effectiveTimes) //有效接通量
+            .WhereIF(dto.QueryType.HasValue && dto.QueryType == 3,x=> x.AnsweredTime != null && x.Duration < connectByeTimes) //接通秒挂量
+            .WhereIF(dto.QueryType.HasValue && dto.QueryType == 4,x=> x.AnsweredTime != null && x.RingTimes > ringTimes) //超时接通量
+            .WhereIF(dto.QueryType.HasValue && dto.QueryType == 5,x=> x.AnsweredTime != null && x.RingTimes <= ringTimes) //按时接通量
+            .WhereIF(dto.QueryType.HasValue && dto.QueryType == 6,x=> x.AnsweredTime == null) //呼入未接通量
+            .WhereIF(dto.QueryType.HasValue && dto.QueryType == 7,x=> x.AnsweredTime == null && x.RingTimes <= noConnectByeTimes) //未接通秒挂量
+            .WhereIF(dto.QueryType.HasValue && dto.QueryType == 8,x=> x.AnsweredTime == null && x.RingTimes > noConnectByeTimes) //超时未接通量
             .Where(x => x.CallDirection == ECallDirection.In)
             .OrderBy(x => x.BeginRingTime)
             .MergeTable()

+ 10 - 5
src/Hotline.Application/StatisticalReport/OrderReportApplication.cs

@@ -1612,6 +1612,7 @@ namespace Hotline.Application.StatisticalReport
                     PassTotal = SqlFunc.AggregateSum(SqlFunc.IIF(x.DelayState == EDelayState.Pass, 1, 0)),
                     NoPassTotal = SqlFunc.AggregateSum(SqlFunc.IIF(x.DelayState == EDelayState.NoPass, 1, 0)),
                     ExaminingTotal = SqlFunc.AggregateSum(SqlFunc.IIF(x.DelayState == EDelayState.Examining, 1, 0)),
+                    WithdrawTotal = SqlFunc.AggregateSum(SqlFunc.IIF(x.DelayState == EDelayState.Withdraw,1,0)),
                     AllTotal = SqlFunc.AggregateSum(SqlFunc.IIF(x.DelayState == EDelayState.Pass, 1, 0)) + SqlFunc.AggregateSum(SqlFunc.IIF(x.DelayState == EDelayState.NoPass, 1, 0)) + SqlFunc.AggregateSum(SqlFunc.IIF(x.DelayState == EDelayState.Examining, 1, 0))
 
                 }).MergeTable();
@@ -1638,7 +1639,8 @@ namespace Hotline.Application.StatisticalReport
                  .WhereIF(dto.Type is 1, x => x.DelayState == EDelayState.Pass)
                  .WhereIF(dto.Type is 2, x => x.DelayState == EDelayState.NoPass)
                  .WhereIF(dto.Type is 3, x => x.DelayState == EDelayState.Examining)
-                 .WhereIF(dto.Type is 4, x => x.DelayState < EDelayState.Withdraw)
+                 //.WhereIF(dto.Type is 4, x => x.DelayState < EDelayState.Withdraw)
+                 .WhereIF(dto.Type is 5,x=>x.DelayState == EDelayState.Withdraw)
              .MergeTable();
 
         }
@@ -1654,9 +1656,11 @@ namespace Hotline.Application.StatisticalReport
 
             var centerOrgName = _systemSettingCacheManager.GetSetting(SettingConstants.CenterOrgName)?.SettingValue[0];
             var orderData = _orderRepository.Queryable()
-              .Where(it => it.CreationTime >= dto.StartTime && it.CreationTime <= dto.EndTime && it.Status >= EOrderStatus.Filed)
-            .WhereIF(dto.TypeId != null && dto.TypeId == 1, it => it.IdentityType == EIdentityType.Citizen)
-            .WhereIF(dto.TypeId != null && dto.TypeId == 2, it => it.IdentityType == EIdentityType.Enterprise)
+              .Where(it => it.Status >= EOrderStatus.Filed)
+              .WhereIF(dto.TimeType == 1,it=> it.StartTime >= dto.StartTime && it.StartTime<= dto.EndTime)
+              .WhereIF(dto.TimeType == 2, it => it.FiledTime >= dto.StartTime && it.FiledTime <= dto.EndTime)
+              .WhereIF(dto.TypeId != null && dto.TypeId == 1, it => it.IdentityType == EIdentityType.Citizen)
+              .WhereIF(dto.TypeId != null && dto.TypeId == 2, it => it.IdentityType == EIdentityType.Enterprise)
               .Select(it => new
               {
                   OrgCode = IsCenter == true ? it.ActualHandleOrgCode.Substring(SqlFunc.MappingColumn<int>("0"), SqlFunc.MappingColumn<int>("6")) : it.ActualHandleOrgCode.Substring(0, _sessionContext.RequiredOrgId.Length + 3),
@@ -1717,6 +1721,7 @@ namespace Hotline.Application.StatisticalReport
                     YjAllTimes = SqlFunc.AggregateSum(SqlFunc.IIF(it.AcceptTypeCode == "1" && it.AllTime != null, it.AllTime, 0)),
                     YjAcceptanceTypeCode = "1"
                 }).MergeTable();
+            Console.Write(orderData.ToSqlString());
             return orderData;
         }
 
@@ -1728,7 +1733,7 @@ namespace Hotline.Application.StatisticalReport
         public ISugarQueryable<Order> DepartmentAcceptanceTypeOrderList(DepartmentKeyWordRequest dto)
         {
             return _orderRepository.Queryable()
-                 .Where(p => p.CreationTime >= dto.StartTime && p.CreationTime <= dto.EndTime && p.Status >= EOrderStatus.Filed)
+                 .Where(p => p.FiledTime >= dto.StartTime && p.FiledTime <= dto.EndTime && p.Status >= EOrderStatus.Filed)
                  .WhereIF(!string.IsNullOrEmpty(dto.OrgCode) && dto.OrgCode == "001", p => p.ActualHandleOrgCode == dto.OrgCode)
                  .WhereIF(dto.TypeId != null && dto.TypeId == 1, p => p.IdentityType == EIdentityType.Citizen)
                  .WhereIF(dto.TypeId != null && dto.TypeId == 2, p => p.IdentityType == EIdentityType.Enterprise)

+ 3 - 3
src/Hotline.Repository.SqlSugar/CallCenter/TrCallRecordRepository.cs

@@ -323,7 +323,7 @@ namespace Hotline.Repository.SqlSugar.CallCenter
                     Date = x.CreatedTime.ToString("yyyy-MM-dd"),
                     PersonCallInCount = SqlFunc.AggregateSum(SqlFunc.IIF(x.IvrDtmf.Substring(x.IvrDtmf.Length - 1, 1) == "1", 1, 0)),
                     PersonCallInPutthroughCount = SqlFunc.AggregateSum(SqlFunc.IIF(x.IvrDtmf.Substring(x.IvrDtmf.Length - 1, 1) == "1" && x.OnState == EOnState.On, 1, 0)),
-                    PersonRingOffCount = SqlFunc.AggregateSum(SqlFunc.IIF(x.IvrDtmf.Substring(x.IvrDtmf.Length - 1, 1) == "1" && x.QueueTims > 0  && x.OnState == EOnState.NoOn, 1, 0)),//个人服务挂断
+                    //PersonRingOffCount = SqlFunc.AggregateSum(SqlFunc.IIF(x.IvrDtmf.Substring(x.IvrDtmf.Length - 1, 1) == "1" && x.OnState == EOnState.NoOn, 1, 0)),//个人服务挂断
                     PersonQueueOffCount = SqlFunc.AggregateSum(SqlFunc.IIF(x.IvrDtmf.Substring(x.IvrDtmf.Length - 1, 1) == "1" && x.QueueTims > 0 && x.RingTimes == 0 && x.OnState == EOnState.NoOn, 1, 0)),//个人服务队列挂断
                     PersonWaitOffCount = SqlFunc.AggregateSum(SqlFunc.IIF(x.IvrDtmf.Substring(x.IvrDtmf.Length -1,1) =="1" && x.RingTimes>0 && x.OnState == EOnState.NoOn,1,0)) //个人服务等待挂断
                 })
@@ -341,7 +341,7 @@ namespace Hotline.Repository.SqlSugar.CallCenter
                     Date = x.CreatedTime.ToString("yyyy-MM-dd"),
                     EnterpriseCallInCount = SqlFunc.AggregateSum(SqlFunc.IIF(x.IvrDtmf.Substring(x.IvrDtmf.Length - 1, 1) == "2", 1, 0)),
                     EnterpriseCallInPutthroughCount = SqlFunc.AggregateSum(SqlFunc.IIF(x.IvrDtmf.Substring(x.IvrDtmf.Length - 1, 1) == "2" && x.OnState == EOnState.On, 1, 0)),
-                    EnterpriseRingOffCount = SqlFunc.AggregateSum(SqlFunc.IIF(x.IvrDtmf.Substring(x.IvrDtmf.Length - 1, 1) == "2" && x.QueueTims > 0 && x.RingTimes == 0 && x.OnState == EOnState.NoOn, 1, 0)), //企业挂断
+                    //EnterpriseRingOffCount = SqlFunc.AggregateSum(SqlFunc.IIF(x.IvrDtmf.Substring(x.IvrDtmf.Length - 1, 1) == "2" && x.QueueTims > 0 && x.RingTimes == 0 && x.OnState == EOnState.NoOn, 1, 0)), //企业挂断
                     EnterpriseQueueOffCount = SqlFunc.AggregateSum(SqlFunc.IIF(x.IvrDtmf.Substring(x.IvrDtmf.Length - 1, 1) == "1" && x.QueueTims > 0 && x.RingTimes == 0 && x.OnState == EOnState.NoOn, 1, 0)),//个人服务队列挂断
                     EnterpriseWaitOffCount = SqlFunc.AggregateSum(SqlFunc.IIF(x.IvrDtmf.Substring(x.IvrDtmf.Length - 1, 1) == "1" && x.RingTimes > 0 && x.OnState == EOnState.NoOn, 1, 0)) //个人服务等待挂断
                 })
@@ -385,7 +385,7 @@ namespace Hotline.Repository.SqlSugar.CallCenter
                     EnterpriseCallOutPutthroughCount = a.EnterpriseCallOutPutthroughCount,
                     AiVisitCallOutPutthroughCount = b.AiVisitCallOutPutthroughCount,
                     AiCallOutPutthroughCount = 0,
-                }).ToListAsync();
+                }).OrderBy(x=>x.Date).ToListAsync();
             return list;
         }
 

+ 1 - 1
src/Hotline.Share/Dtos/Bigscreen/BigscreenDto.cs

@@ -263,7 +263,7 @@ namespace Hotline.Share.Dtos.Bigscreen
 
         public string CalcSatisfiedRate()
         {
-            if (Count == 0 || NoSatisfiedCount == 0)
+            if (Count == 0)
             {
                 return "0.000%";
             }

+ 7 - 3
src/Hotline.Share/Dtos/CallCenter/BiQueryCallsDto.cs

@@ -289,7 +289,7 @@ public class QueryCallDateStatisticsDetailResp
     /// <summary>
     /// 服务接通率
     /// </summary>
-    public double ServicePutthorughRate => PutthroughCount == 0 ? 0 : Math.Round(((double)PutthroughCount / CallInTotal) * 100, 2);
+    public double ServicePutthorughRate => (PersonCallInPutthroughCount + EnterpriseCallInPutthroughCount + AiCallInPutthroughCount) == 0 ? 0 : Math.Round(((double)PutthroughCount / (PersonCallInPutthroughCount + EnterpriseCallInPutthroughCount + AiCallInPutthroughCount)) * 100, 2);
 
     /// <summary>
     /// 服务接通率(文本)
@@ -328,7 +328,7 @@ public class QueryPersonCallDateStatisticsDetailResp
     /// <summary>
     /// 个人服务挂断总量
     /// </summary>
-    public int PersonRingOffCount { get; set; }
+    public int PersonRingOffCount => PersonQueueOffCount + PersonWaitOffCount;
 
     /// <summary>
     /// 个人服务队列挂断
@@ -372,7 +372,7 @@ public class QueryEnterpriseCallDateStatisticsDetailResp
     /// <summary>
     /// 企业服务挂断总量
     /// </summary>
-    public int EnterpriseRingOffCount { get; set; }
+    public int EnterpriseRingOffCount => EnterpriseQueueOffCount + EnterpriseWaitOffCount;
 
     /// <summary>
     /// 企业服务队列挂断
@@ -620,6 +620,10 @@ public record QuerySeatMonthCallDetailRequest:PagedRequest
     /// </summary>
     public DateTime? RingEndTime { get;set; }
 
+    /// <summary>
+    /// 点数进入明细类型(1:呼入接通总量 2:有效接通量 3:接通秒挂量 4:超时接通量 5:按时接通量 6:呼入未接通总量 7:未接通秒挂量 8:超时未接通量)
+    /// </summary>
+    public int? QueryType { get; set; }
 }
 
 public class QuerySeatMonthCallDetailResp

+ 10 - 0
src/Hotline.Share/Dtos/CallCenter/QueryTelOperationsFixedDto.cs

@@ -28,5 +28,15 @@ namespace Hotline.Share.Dtos.CallCenter
         /// 操作类型
         /// </summary>
         public int? OperateState { get; set; }
+
+        /// <summary>
+        /// 动作开始时间Star
+        /// </summary>
+        public DateTime? OperateTimeStart { get; set; }
+
+        /// <summary>
+        /// 动作开始时间End
+        /// </summary>
+        public DateTime? OperateTimeEnd { get; set; }
     }
 }

+ 1 - 1
src/Hotline.Share/Dtos/Caselibrary/CaseListDto.cs

@@ -19,7 +19,7 @@ namespace Hotline.Share.Dtos.Caselibrary
         /// <summary>
         /// 关键词
         /// </summary>
-        public string? Keyword { get; set; }
+        public string? Keywords { get; set; }
 
         /// <summary>
         /// 摘要

+ 5 - 0
src/Hotline.Share/Dtos/FlowEngine/NextStepsDto.cs

@@ -63,6 +63,11 @@ public class NextStepsDto
     /// 临时保存的办理意见
     /// </summary>
     public string? Opinion { get; set; }
+
+    /// <summary>
+    /// 受理类容
+    /// </summary>
+    public string? Content { get; set; }
 }
 
 public class NextStepsDto<TSteps> : NextStepsDto

+ 5 - 0
src/Hotline.Share/Dtos/Order/OrderBiDto.cs

@@ -213,6 +213,11 @@ namespace Hotline.Share.Dtos.Order
         /// </summary>
         public int ExaminingTotal { get; set; }
 
+        /// <summary>
+        /// 撤销次数
+        /// </summary>
+        public int WithdrawTotal { get; set; }
+
     }
 
 

+ 12 - 0
src/Hotline.Share/Dtos/Order/OrderStartFlowDto.cs

@@ -268,4 +268,16 @@ namespace Hotline.Share.Dtos.Order
 
         public NextWorkflowDto NextWorkflow { get; set; }
     }
+
+    public class BatchDelayNextFlowDto {
+
+        public string[] DelayId { get; set; }
+        
+		public NextWorkflowDto NextWorkflow { get; set; }
+
+		/// <summary>
+		/// 是否通过
+		/// </summary>
+		public bool IsPass { get; set; }
+	}
 }

+ 6 - 1
src/Hotline.Share/Dtos/Order/QueryOrderDto.cs

@@ -178,10 +178,15 @@ namespace Hotline.Share.Dtos.Order
         public string? ProvinceChannel { get; set; }
 
         /// <summary>
-        /// 内容检索
+        /// 内容检索(受理内容)
         /// </summary>
         public string? ContentRetrieval { get; set; }
 
+        /// <summary>
+        /// 内容检索(归档意见)
+        /// </summary>
+        public string? FileOption { get; set; }
+
         /// <summary>
         /// 归档方式
         /// </summary>

+ 11 - 1
src/Hotline.Share/Dtos/Planlibrary/PlanDataDto.cs

@@ -135,7 +135,17 @@ namespace Hotline.Share.Dtos.Planlibrary
             return "";
         }
 
-        public HotspotDto HotspotType { get; set; }
+        /// <summary>
+        /// 热点
+        /// </summary>
+        public string? HotspotId { get; set; }
+
+        /// <summary>
+        /// 外部数据(为前端提供级联功能)
+        /// </summary>
+        public string? HotspotExternal { get; set; }
+
+        public HotspotDto? HotspotType { get; set; }
 
         /// <summary>
         /// 热点

+ 6 - 1
src/Hotline.Share/Dtos/Planlibrary/PlanListDto.cs

@@ -179,6 +179,11 @@ namespace Hotline.Share.Dtos.Planlibrary
         /// </summary>
         public string? HotspotId { get; set; }
 
+        /// <summary>
+        /// 外部数据(为前端提供级联功能)
+        /// </summary>
+        public string? HotspotExternal { get; set; }
+
         /// <summary>
         /// 是否公开
         /// </summary>
@@ -371,7 +376,7 @@ namespace Hotline.Share.Dtos.Planlibrary
         /// <summary>
         /// 外部数据(为前端提供级联功能)
         /// </summary>
-        //public string HotspotExternal { get; set; }
+        public string HotspotExternal { get; set; }
 
         public PlanCollectDto Collect { get; set; }
 

+ 109 - 0
src/Hotline.Share/Dtos/Quality/BiQualityDto.cs

@@ -0,0 +1,109 @@
+using Hotline.Share.Dtos.Order;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hotline.Share.Dtos.Quality
+{
+	public class BiQualityDto
+	{
+	}
+
+	public class SeatsQualityAnalyseDto {
+
+		/// <summary>
+		///  工单
+		/// </summary>
+		public OrderDto? Order { get; set; }
+
+		/// <summary>
+		/// 受理人名称
+		/// </summary>
+		public string? AcceptorName => Order.AcceptorName;
+
+		/// <summary>
+		/// 工单编码
+		/// </summary>
+		public string? No => Order.No;
+
+		/// <summary>
+		///  质检明细
+		/// </summary>
+		public List<QualityDetailDto>? QualityDetails { get; set; }
+
+		/// <summary>
+		/// 质检项
+		/// </summary>
+		public string? QualityDetail => QualityDetails != null && QualityDetails.Any() ? string.Join(";", QualityDetails.Select(x => x.Name)) : string.Empty;
+
+
+		/// <summary>
+		/// 质检分数
+		/// </summary>
+		public int? Grade { get; set; }
+	}
+
+
+	public class QualityOrderOverviewDto 
+	{
+		/// <summary>
+		/// 质检项
+		/// </summary>
+		public string? QualityItem { get; set; }
+
+		/// <summary>
+		/// 质检工单数
+		/// </summary>
+		public int OrderNum { get; set; }
+
+		/// <summary>
+		/// 全量质检工单数
+		/// </summary>
+		public int AllOrderNum { get; set; }
+
+		/// <summary>
+		/// 占比
+		/// </summary>
+		public string? Rate => GetRate();
+
+		/// <summary>
+		/// 计算占比
+		/// </summary>
+		/// <returns></returns>
+		public string GetRate()
+		{
+			if (OrderNum != 0 && AllOrderNum != 0)
+			{
+				return Math.Round((OrderNum / (double)AllOrderNum) * 100, 2) + "%";
+			}
+			return "0%";
+		}
+
+	}
+
+
+	public class SeatsQualityGradeAnalyseDto 
+	{
+		public int NinetyGrade { get; set; }
+
+		public int EightyGrade { get; set; }
+
+		public int SeventyGrade { get; set; }
+
+		public int SixtyGrade { get; set; }
+
+		public int FiftyGrade { get; set; }
+	}
+
+	public class MonthQualityGradeDto
+	{
+		public string Month { get; set; }
+
+		public int Num { get; set; }
+	}
+
+
+
+}

+ 29 - 0
src/Hotline.Share/Dtos/Quality/QualityDto.cs

@@ -53,6 +53,18 @@ namespace Hotline.Share.Dtos.Quality
 	{
 		public string Id { get; set; }
 	}
+
+	public class TransferUpdate
+	{
+		/// <summary>
+		/// 质检ID
+		/// </summary>
+		public string Id { get; set; }
+		/// <summary>
+		/// 转写内容
+		/// </summary>
+		public List<Transfer>? Transfer { get; set; }
+	}
 	public class DeleteQualityDto
 	{
 		public List<string> Ids { get; set; }
@@ -172,6 +184,18 @@ namespace Hotline.Share.Dtos.Quality
 		/// </summary>
 		public bool AiQuality { get; set; }
 
+		/// <summary>
+		/// 转写状态
+		/// </summary>
+		public EQualityTransferState? TransferState { get; set; }
+
+		public string TransferStateText => TransferState.HasValue ? TransferState.GetDescription() : string.Empty;
+
+		/// <summary>
+		/// 转写内容
+		/// </summary>
+		public List<Transfer>? Transfer { get; set; }
+
 	}
 	public class QualityBaseDto {
 		public DateTime? LastModificationTime { get; set; }
@@ -230,4 +254,9 @@ namespace Hotline.Share.Dtos.Quality
 		/// </summary>
 		public int? MinGrade { get; set; }
 	}
+
+	public class AiQualityXTDto 
+	{
+		public string Id { get; set; }
+	}
 }

+ 145 - 0
src/Hotline.Share/Dtos/QualityExportWord/QualityCertificate.cs

@@ -0,0 +1,145 @@
+using Hotline.Share.Dtos.ExportWord;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hotline.Share.Dtos.QualityExportWord
+{
+	public class QualityCertificate : IWordExportTemplate
+	{
+		/// <summary>
+		/// 市州名称
+		/// </summary>
+		public string CityName { get; set; }
+
+		/// <summary>
+		/// 编号
+		/// </summary>
+		public string No { get; set; }
+
+		/// <summary>
+		/// 来信时间
+		/// </summary>
+		public string? CreationTime { get; set; }
+
+		/// <summary>
+		/// 来电/信人姓名
+		/// </summary>
+		public string? FromName { get; set; }
+
+		/// <summary>
+		/// 来电/信人性别
+		/// </summary>
+		public string? FromGender { get; set; }
+
+		/// <summary>
+		/// 来电号码
+		/// </summary>
+		public string? FromPhone { get; set; }
+
+		/// <summary>
+		/// 联系电话
+		/// </summary>
+		public string? Contact { get; set; }
+
+		/// <summary>
+		/// 来电地址
+		/// </summary>
+		public string? FullAddress { get; set; }
+
+		/// <summary>
+		/// 标题
+		/// </summary>
+		public string Title { get; set; }
+
+		/// <summary>
+		/// 内容
+		/// </summary>
+		public string Content { get; set; }
+
+		/// <summary>
+		/// 受理类型
+		/// </summary>
+		public string? AcceptType { get; set; }
+
+		/// <summary>
+		/// 热点
+		/// </summary>
+		public string? HotspotSpliceName { get; set; }
+
+		/// <summary>
+		/// 实际办理部门名称
+		/// </summary>
+		public string? ActualHandleOrgName { get; set; }
+
+		/// <summary>
+		/// 办理时间限制(如:24小时、7个工作日)
+		/// </summary>
+		public string? TimeLimit { get; set; }
+
+		/// <summary>
+		/// 交办意见
+		/// </summary>
+		public string? CenterToOrgOpinion { get; set; }
+
+		/// <summary>
+		/// 实际办理意见
+		/// </summary>
+		public string? FileOpinion { get; set; }
+
+		/// <summary>
+		/// 回访部门信息
+		/// </summary>
+		public string? VisitOrg { get; set; }
+
+		/// <summary>
+		/// 回访内容
+		/// </summary>
+		public string? VisitContent { get; set; }
+
+		/// <summary>
+		/// 交办时间(中心交部门办理时间)
+		/// </summary>
+		public string? CenterToOrgTime { get; set; }
+
+		/// <summary>
+		/// 超期时间(期满时间)
+		/// </summary>
+		public string? ExpiredTime { get; set; }
+
+		/// <summary>
+		///  交办人
+		/// </summary>
+		public string? CenterToOrgHandlerName { get; set; }
+
+
+		/// <summary>
+		/// 受理人名称
+		/// </summary>
+		public string? AcceptorName { get; set; }
+
+
+		/// <summary>
+		/// 质检分数
+		/// </summary>
+		public int? Grade { get; set; }
+
+		/// <summary>
+		/// 质检评价
+		/// </summary>
+		public string? QualityContent { get; set; }
+
+
+		/// <summary>
+		/// 质检人
+		/// </summary>
+		public string? UserName { get; set; }
+
+		/// <summary>
+		/// 质检时间
+		/// </summary>
+		public string? QualityTime { get; set; }
+	}
+}

+ 75 - 1
src/Hotline.Share/Dtos/Snapshot/InviteCodeDto.cs

@@ -77,7 +77,7 @@ public class GetInviteCodeStatisticInDto
     /// 统计开始时间
     /// </summary>
     [Required]
-    public DateTime BeginTime { get; set; }
+    public DateTime StartTime { get; set; }
 
     /// <summary>
     /// 统计结束书剑
@@ -85,3 +85,77 @@ public class GetInviteCodeStatisticInDto
     [Required]
     public DateTime EndTime { get; set; }
 }
+
+public record GetInviteCodeStatisticDetailInDto : PagedRequest
+{
+    /// <summary>
+    /// Id
+    /// </summary>
+    [Required]
+    public string OrgId { get; set; }
+
+    /// <summary>
+    /// 统计开始时间
+    /// </summary>
+    [Required]
+    public DateTime StartTime { get; set; }
+
+    /// <summary>
+    /// 统计结束时间
+    /// </summary>
+    [Required]
+    public DateTime EndTime { get; set; }
+
+    /// <summary>
+    /// 姓名
+    /// </summary>
+    public string? Name { get; set; }
+
+    /// <summary>
+    /// 电话
+    /// </summary>
+    public string? PhoneNumber { get; set; }
+
+    /// <summary>
+    /// 邀请码
+    /// </summary>
+    public string? InviteCode { get; set; }
+}
+
+public class InviteCodeStatisticDetailOutDto
+{
+    /// <summary>
+    /// Id
+    /// </summary>
+    public string Id { get; set; }
+
+    /// <summary>
+    /// 姓名
+    /// </summary>
+    public string Name { get; set; }
+
+    /// <summary>
+    /// 电话
+    /// </summary>
+    public string PhoneNumber { get; set; }
+
+    /// <summary>
+    /// 微信OpenId
+    /// </summary>
+    public string OpenId { get; set; }
+
+    /// <summary>
+    /// 邀请时间
+    /// </summary>
+    public DateTime CreationTime { get; set; }
+
+    /// <summary>
+    /// 邀请码
+    /// </summary>
+    public string InviteCode { get; set; }
+
+    /// <summary>
+    /// 部门
+    /// </summary>
+    public string OrgName { get; set; }
+}

+ 29 - 0
src/Hotline.Share/Dtos/Snapshot/OrderDeclareDto.cs

@@ -0,0 +1,29 @@
+using Hotline.Share.Enums.Settings;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Xml.Linq;
+
+namespace Hotline.Share.Dtos.Snapshot;
+public class OrderDeclareItemsOutDto
+{
+    public string AreaCode { get; set; }
+    public string AreaName { get; set; }
+    public string EventType { get; set; }
+    public string WorkAddress { get; set; }
+    public DateTime CreatedTime { get; set; }
+    public string WorkType { get; set; }
+    public int Source { get; set; }
+    public DateTime WorkTimeStart { get; set; }
+    public DateTime WorkTimeStop { get; set; }
+    public string OnsiteSituateDescription { get; set; }
+    public string Name { get; set; }
+    public string Phone { get; set; }
+    public string EventId { get; set; }
+    public string PlaceType { get; set; }
+}
+
+public class OrderDeclareItemsInDto
+{ }

+ 22 - 22
src/Hotline.Share/Dtos/Snapshot/OrderDto.cs

@@ -125,12 +125,12 @@ public class AddSnapshotOrderInDto : Position
     /// <summary>
     /// 作业时间
     /// </summary>
-    public DateTime? StartWorkTime { get; set; }
+    public string? StartWorkTime { get; set; }
 
     /// <summary>
     /// 作业结束时间
     /// </summary>
-    public DateTime? EndWorkTime { get; set; }
+    public string? EndWorkTime { get; set; }
 
     /// <summary>
     /// 姓名
@@ -173,7 +173,7 @@ public class AddSnapshotOrderInDto : Position
                     $"场所名称:{this.WorkplaceName}\r\n" + // 多业态混合经营场所 - 存储多种功能的劳动密集型企业
                     $"作业区域: {this.County + this.Town}\r\n" + // 自流井区舒坪街道
                     $"作业类型: {this.JobType}\r\n" + // 电焊
-                    $"作业时间:{this.StartWorkTime.Value.ToString("yyyy-MM-dd hh:mm")}~{this.EndWorkTime.Value.ToString("yyyy-MM-dd hh:mm")}\r\n" +
+                    $"作业时间:{this.StartWorkTime}~{this.EndWorkTime}\r\n" +
                     $"作业地点: {this.Town}({this.County})({this.Street})"; // 舒平(自流井区)(油库)"
         }
         return $"{Description}";
@@ -642,7 +642,7 @@ public class GetRedPackAuditSMSTemplateInDto
 
     }
 
-    public GetRedPackAuditSMSTemplateInDto(string id, ERedPackAuditStatus status)
+    public GetRedPackAuditSMSTemplateInDto(string id, ESnapshotSMSStatus status)
     {
         OrderId = id;
         Status = status;
@@ -656,7 +656,7 @@ public class GetRedPackAuditSMSTemplateInDto
     /// <summary>
     /// 状态
     /// </summary>
-    public ERedPackAuditStatus Status { get; set; }
+    public ESnapshotSMSStatus Status { get; set; }
 }
 
 public class SnapshotOrderAuditDetailOutDto
@@ -981,7 +981,7 @@ public class GuiderReplyItemsOutDto
     public string? NetworkENumber { get; set; }
 }
 
-public record SignedOrderSnapshotItemsInDto : PagedRequest
+public record LabeledOrderSnapshotItemsInDto : PagedRequest
 {
     /// <summary>
     /// 受理编号
@@ -1020,7 +1020,7 @@ public record SignedOrderSnapshotItemsInDto : PagedRequest
 }
 
 
-public class SignedOrderSnapshotItemsOutDto
+public class LabeledOrderSnapshotItemsOutDto
 {
     /// <summary>
     /// Id
@@ -1080,7 +1080,7 @@ public class SignedOrderSnapshotItemsOutDto
     /// <summary>
     /// 标注人
     /// </summary>
-    public string? SignUserName { get; set; }
+    public string? LabelUserName { get; set; }
 
     /// <summary>
     /// 标注类型
@@ -1090,10 +1090,10 @@ public class SignedOrderSnapshotItemsOutDto
     /// <summary>
     /// 标记时间
     /// </summary>
-    public DateTime? SignTime { get; set; }
+    public DateTime? LabelTime { get; set; }
 }
 
-public record SignOrderSnapshotItemsInDto : PagedRequest
+public record LabelOrderSnapshotItemsInDto : PagedRequest
 {
     /// <summary>
     /// No
@@ -1108,13 +1108,13 @@ public record SignOrderSnapshotItemsInDto : PagedRequest
     /// <summary>
     /// 状态;
     /// 0: 全部;
-    /// 1: 待标;
-    /// 2: 已标;
+    /// 1: 待标;
+    /// 2: 已标;
     /// </summary>
     public int Status { get; set; }
 }
 
-public class SignOrderSnapshotItemsOutDto
+public class LabelOrderSnapshotItemsOutDto
 {
     /// <summary>
     /// Id
@@ -1176,12 +1176,12 @@ public class SignOrderSnapshotItemsOutDto
     /// <summary>
     /// 标注人
     /// </summary>
-    public string? SignUserName { get; set; }
+    public string? LabelUserName { get; set; }
 
     /// <summary>
     /// 标记时间
     /// </summary>
-    public DateTime? SignTime { get; set; }
+    public DateTime? LabelTime { get; set; }
 }
 
 public class GetOrderSnapshotPublishItemsOutDto
@@ -1502,7 +1502,7 @@ public class OrderSignBathInDto
     public string Remark { get; set; }
 }
 
-public record SignOrderSnapshotLogItemsInDto : PagedRequest
+public record LabelOrderSnapshotLogItemsInDto : PagedRequest
 {
     /// <summary>
     /// 受理编号
@@ -1527,7 +1527,7 @@ public record SignOrderSnapshotLogItemsInDto : PagedRequest
     /// <summary>
     /// 标注人
     /// </summary>
-    public string? SignName { get; set; }
+    public string? LabelUserName { get; set; }
 
     /// <summary>
     /// 标注类型
@@ -1545,7 +1545,7 @@ public record SignOrderSnapshotLogItemsInDto : PagedRequest
     public DateTime? EndSignTime { get; set; }
 }
 
-public class SignOrderSnapshotLogItemsOutDto
+public class LabelOrderSnapshotLogItemsOutDto
 {
     /// <summary>
     /// Id
@@ -1605,7 +1605,7 @@ public class SignOrderSnapshotLogItemsOutDto
     /// <summary>
     /// 标注人
     /// </summary>
-    public string SignName { get; set; }
+    public string LabelUserName { get; set; }
 
     /// <summary>
     /// 标注类型
@@ -1615,7 +1615,7 @@ public class SignOrderSnapshotLogItemsOutDto
     /// <summary>
     /// 标注时间
     /// </summary>
-    public DateTime SignTime { get; set; }
+    public DateTime LabelTime { get; set; }
 }
 
 public record OrderSnapshotItemsInDto : PagedRequest
@@ -1942,7 +1942,7 @@ public class NameCodeDto
     public string Code { get; set; }
 }
 
-public class UpdateSignOrderSnapshotInDto
+public class UpdateLabelOrderSnapshotInDto
 {
     /// <summary>
     /// Id
@@ -1955,7 +1955,7 @@ public class UpdateSignOrderSnapshotInDto
     public IList<Kv>? SnapshotLabels { get; set; }
 }
 
-public class SignOrderSnapshotDetailOutDto
+public class LabelOrderSnapshotDetailOutDto
 {
     /// <summary>
     /// Id

+ 55 - 0
src/Hotline.Share/Dtos/Transfer.cs

@@ -0,0 +1,55 @@
+using Hotline.Share.Enums.Quality;
+using Lucene.Net.Search;
+using Newtonsoft.Json.Linq;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hotline.Share.Dtos
+{
+	/// <summary>
+	/// 转写
+	/// </summary>
+	public class Transfer
+	{
+		public Transfer()
+		{
+		}
+
+		public Transfer(EQualityTransferType type, string content,string reviseContent, int sort,string prohibited)
+		{
+			Type  = type;
+			Content = content;
+			ReviseContent = reviseContent;
+			Sort = sort;
+			Prohibited = prohibited;
+		}
+
+		/// <summary>
+		/// 转写类型
+		/// </summary>
+		public EQualityTransferType Type { get; set; }
+
+		/// <summary>
+		/// 转写内容
+		/// </summary>
+		public string? Content { get; set; }
+
+		/// <summary>
+		/// 转写修改后内容
+		/// </summary>
+		public string? ReviseContent { get; set; }
+
+		/// <summary>
+		/// 排序
+		/// </summary>
+		public int Sort { get; set; }
+
+		/// <summary>
+		/// 违禁词
+		/// </summary>
+		public string? Prohibited { get; set; }
+	}
+}

+ 42 - 0
src/Hotline.Share/Enums/Quality/EQuality.cs

@@ -42,4 +42,46 @@ namespace Hotline.Share.Enums.Quality
 		[Description("回访待质检")]
 		Visit = 3
 	}
+
+	public enum EQualityTransferState
+	{
+		/// <summary>
+		/// 未开始
+		/// </summary>
+		[Description("未开始")]
+		NotStarted = 0,
+
+		/// <summary>
+		/// 转写中
+		/// </summary>
+		[Description("转写中")]
+		Translating = 1,
+
+		/// <summary>
+		/// 转写成功
+		/// </summary>
+		[Description("转写成功")]
+		Succeed = 2,
+
+		/// <summary>
+		/// 转写失败
+		/// </summary>
+		[Description("转写失败")]
+		Lose = 3,
+	}
+
+	public enum EQualityTransferType {
+
+		/// <summary>
+		/// 坐席
+		/// </summary>
+		[Description("坐席")]
+		Seats = 0,
+
+		/// <summary>
+		/// 市民
+		/// </summary>
+		[Description("市民")]
+		Citizen = 1,
+	}
 }

+ 5 - 0
src/Hotline.Share/Requests/DepartmentKeyWordRequest.cs

@@ -31,5 +31,10 @@
         /// 来电/信人身份0:全部 ,1:市民,2:企业
         /// </summary>
         public int? TypeId { get; set; }
+
+        /// <summary>
+        /// 1:受理时间 2:办结时间  默认:2
+        /// </summary>
+        public int TimeType { get; set; } = 2;
     }
 }

+ 21 - 1
src/Hotline.Share/Requests/PagedKeywordRequest.cs

@@ -346,7 +346,7 @@ public record QueryOrderDelayDataDetailRequest : ReportRequiredPagedRequest
     public string? OrgCode { get; set; }
 
     /// <summary>
-    /// 查询类型  1已同意次数 2 未同意次数 3  审批中次数 4 小计
+    /// 查询类型  1已同意次数 2 未同意次数 3  审批中次数 4 小计 5:撤销
     /// </summary>
     public int? Type { get; set; }
 
@@ -1392,6 +1392,26 @@ public record SeatSatisfactionOrderVisitRequest : PagedKeywordRequest
     public string? No {  get; set; }
 }
 
+public record QualityWorkAnalysisRequest :PagedKeywordRequest
+{
+
+	/// <summary>
+	/// 最大质检分数
+	/// </summary>
+	public int? MaxGrade { get; set; }
+
+    /// <summary>
+    ///// 最小质检分数
+    /// </summary>
+    public int? MinGrade { get; set; }
+
+    /// <summary>
+    /// 受理人
+    /// </summary>
+    public string? AcceptorId { get; set; }
+
+}
+
 #region 智能回访
 
 public class QueryAiVisitStatisticsRequest

+ 2 - 0
src/Hotline/Ai/Quality/IAiQualityService.cs

@@ -15,5 +15,7 @@ namespace Hotline.Ai.Quality
 	{  
         //TrCallRecord? call,
 		Task CreateAiOrderQualityTask(string id, string audioFile,string fromNo,DateTime? callStartTime, string viteRecordPrefix, string ywlxString, CancellationToken cancellationToken);
+
+		Task<string> CreateAiOrderQualityTask(string filename, CancellationToken cancellationToken);
 	}
 }

+ 2 - 1
src/Hotline/Configurations/AppConfiguration.cs

@@ -41,7 +41,8 @@ namespace Hotline.Configurations
 
     public class ZiGongConfiguration : DefaultAppScopeConfiguration
     {
-    }
+		public AiQualityConfiguration AiQuality { get; set; }
+	}
 
     public class LuZhouConfiguration : DefaultAppScopeConfiguration
     {

+ 2 - 2
src/Hotline/FlowEngine/Workflows/WorkflowDomainService.cs

@@ -2945,7 +2945,7 @@ namespace Hotline.FlowEngine.Workflows
             var updateTraces = new List<WorkflowTrace>();
 
             //update uncomplete traces
-            var uncompleteTraces = workflow.Traces.Where(d => d.Status != EWorkflowStepStatus.Handled).ToList();
+            var uncompleteTraces = workflow.Traces.Where(d => d.Status != EWorkflowStepStatus.Handled  && d.TraceStyle == ETraceStyle.Flow).ToList();
             if (uncompleteTraces.Any())
             {
                 foreach (var trace in uncompleteTraces)
@@ -2962,7 +2962,7 @@ namespace Hotline.FlowEngine.Workflows
             }
             else
             {
-                var endTrace = workflow.Traces.Where(d => d.StepType == EStepType.End).MaxBy(d => d.CreationTime);
+                var endTrace = workflow.Traces.Where(d => d.StepType == EStepType.End && d.TraceStyle == ETraceStyle.Flow).MaxBy(d => d.CreationTime);
                 if (endTrace is not null)
                 {
                     endTrace.Opinion += ("\r\n" + dto.Opinion);

+ 6 - 6
src/Hotline/KnowledgeBase/KnowledgeImportTemplate.cs

@@ -13,11 +13,11 @@ namespace Hotline.KnowledgeBase
         [ExcelColumnName("一级分类")]
         public string? KnowledgeTypeLevelOne { get; set; }
 
-        [ExcelColumnName("二级分类")]
-        public string? KnowledgeTypeLevelTwo { get; set; }
+        //[ExcelColumnName("二级分类")]
+        //public string? KnowledgeTypeLevelTwo { get; set; }
 
-        [ExcelColumnName("三级分类")]
-        public string? KnowledgeTypeLevelThree { get; set; }
+        //[ExcelColumnName("三级分类")]
+        //public string? KnowledgeTypeLevelThree { get; set; }
 
         [ExcelColumnName("失效时间")]
         public DateTime? ExpiredTime { get; set; }
@@ -28,8 +28,8 @@ namespace Hotline.KnowledgeBase
         [ExcelColumnName("是否公开(是/否)")]
         public string IsPublic { get; set; }
 
-        [ExcelColumnName("热点分类")]
-        public string? HotspotName { get; set; }
+        //[ExcelColumnName("热点分类")]
+        //public string? HotspotName { get; set; }
 
         /// <summary>
         /// 标题

+ 6 - 0
src/Hotline/Planlibrary/PlanList.cs

@@ -60,6 +60,12 @@ public class PlanList : CreationEntity
     [SugarColumn(ColumnDescription = "热点")]
     public string? HotspotId { get; set; }
 
+    /// <summary>
+    /// 外部数据(为前端提供级联功能)
+    /// </summary>
+    [SugarColumn(IsNullable = true, ColumnDescription = "外部数据(为前端提供级联功能)")]
+    public string? HotspotExternal { get; set; }
+
     /// <summary>
     /// 热点
     /// </summary>

+ 18 - 0
src/Hotline/Quality/Quality.cs

@@ -1,4 +1,5 @@
 using Hotline.Orders;
+using Hotline.Share.Dtos;
 using Hotline.Share.Enums.Quality;
 using SqlSugar;
 using System;
@@ -12,6 +13,9 @@ using XF.Domain.Repository;
 namespace Hotline.Quality
 {
 	[Description("质检")]
+	[SugarIndex("index_quality_creationtime", nameof(Quality.CreationTime), OrderByType.Desc)]
+	[SugarIndex("index_quality_orderId", nameof(OrderScreen.OrderId), OrderByType.Asc)]
+	[SugarIndex("index_quality_visitId", nameof(OrderScreen.VisitId), OrderByType.Asc)]
 	public class Quality : FullStateEntity
 	{
 		/// <summary>
@@ -90,5 +94,19 @@ namespace Hotline.Quality
 		/// 智能质检
 		/// </summary>
 		public bool? AiQuality { get; set; } = false;
+
+		/// <summary>
+		/// 转写状态
+		/// </summary>
+		[SugarColumn(ColumnDescription = "转写状态", DefaultValue = "0")]
+		public EQualityTransferState? TransferState { get; set; }
+
+		/// <summary>
+		/// 转写内容
+		/// </summary>
+		[SugarColumn(ColumnDataType = "json", IsJson = true, IsNullable = true)]
+		public List<Transfer>? Transfer { get; set; }
+
 	}
+
 }

+ 40 - 0
src/Hotline/Quality/QualityTransferRecords.cs

@@ -0,0 +1,40 @@
+using Hotline.Share.Enums.Quality;
+using SqlSugar;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using XF.Domain.Repository;
+
+namespace Hotline.Quality
+{
+	[Description("录音文件质检转写")]
+	public class QualityTransferRecords : FullStateEntity
+	{
+
+		/// <summary>
+		/// 质检ID
+		/// </summary>
+		public string QualityId { get; set; }
+
+
+		/// <summary>
+		/// 是否完成
+		/// </summary>
+		public bool? IsFinished { get; set; }
+
+		/// <summary>
+		/// 转写时间
+		/// </summary>
+		public DateTime? TransferTime { get; set; }
+
+
+		/// <summary>
+		/// 转写状态
+		/// </summary>
+		[SugarColumn(ColumnDescription = "转写状态", DefaultValue = "0")]
+		public EQualityTransferState? TransferState { get; set; }
+	}
+}

+ 1 - 1
src/Hotline/Settings/TimeLimitDomain/ExpireTimeHandler.cs

@@ -134,7 +134,7 @@ public class ExpireTimeHandler : IExpireTimeHandler, IScopeDependency
                         //最后一天
                         if (beginTime.Date == endTime.Date)
                         {
-                            if (endTime > WorkEndTime)
+                            if (endTime > DateTime.Parse(endTime.ToShortDateString() + " " + workTime[1] + ":00"))
                             {
                                 if (i != 1)
                                 {

+ 6 - 0
src/Hotline/Snapshot/InviteCode.cs

@@ -46,4 +46,10 @@ public class InviteCode : CreationSoftDeleteEntity
     /// </summary>
     [SugarColumn(ColumnDescription = "邀请码Url")]
     public string? QRCodeUrl { get; set; }
+
+    /// <summary>
+    /// 上传附件Id
+    /// </summary>
+    [SugarColumn(ColumnDescription ="上传附件Id")]
+    public string? Additions { get; set; }
 }

+ 22 - 0
src/Hotline/Snapshot/OrderSnapshot.cs

@@ -77,6 +77,8 @@ public class OrderSnapshot : CreationSoftDeleteEntity
     [SugarColumn(ColumnDescription = "作业时间")]
     public DateTime? EndWorkTime { get; set; }
 
+    #region 标记工单是否安全生产字段
+
     /// <summary>
     /// 部门标记是否安全生产
     /// </summary>
@@ -106,6 +108,7 @@ public class OrderSnapshot : CreationSoftDeleteEntity
     /// </summary>
     [SugarColumn(ColumnDescription = "标记备注")]
     public string? SignRemark { get; set; }
+    #endregion
 
     /// <summary>
     /// 部门是否存在安全隐患
@@ -155,6 +158,24 @@ public class OrderSnapshot : CreationSoftDeleteEntity
     [SugarColumn(ColumnDescription = "工单标签")]
     public string? LabelName { get; set; }
 
+    /// <summary>
+    /// 标签用户
+    /// </summary>
+    [SugarColumn(ColumnDescription = "标签用户")]
+    public string? LabelUserName { get; set; }
+
+    /// <summary>
+    /// 标签用户Id
+    /// </summary>
+    [SugarColumn(ColumnDescription = "标签用户Id")]
+    public string? LabelUserId { get; set; }
+
+    /// <summary>
+    /// 标签时间
+    /// </summary>
+    [SugarColumn(ColumnDescription = "标签时间")]
+    public DateTime? LabelTime { get; set; }
+
     /// <summary>
     /// 工单办理:
     /// 是否申请追加奖励
@@ -303,4 +324,5 @@ public class OrderSnapshot : CreationSoftDeleteEntity
     /// </summary>
     [SugarColumn(ColumnDescription = "购气单位/个人名称")]
     public string? CompanyName { get; set; }
+
 }

+ 36 - 36
src/Hotline/Snapshot/SnapshotMessageTemplate.cs

@@ -6,44 +6,44 @@ using Hotline.Share.Enums.Snapshot;
 
 namespace Hotline.Snapshot;
 
-[Description("随手拍短信模板设置")]
-public class SnapshotMessageTemplate : CreationSoftDeleteEntity
-{
-    /// <summary>
-    /// 短信模板代码
-    /// 表<inheritdoc cref="MessageTemplate"/>中的Code字段
-    /// </summary>
-    [SugarColumn(ColumnDescription = "MessageTemplateCode")]
-    public string MessageTemplateCode { get; set; }
+//[Description("随手拍短信模板设置")]
+//public class SnapshotMessageTemplate : CreationSoftDeleteEntity
+//{
+//    /// <summary>
+//    /// 短信模板代码
+//    /// 表<inheritdoc cref="MessageTemplate"/>中的Code字段
+//    /// </summary>
+//    [SugarColumn(ColumnDescription = "MessageTemplateCode")]
+//    public string MessageTemplateCode { get; set; }
 
-    /// <summary>
-    /// 行业页面Id
-    /// </summary>
-    [SugarColumn(ColumnDescription = "行业页面Id")]
-    public string IndustrPageId { get; set; }
+//    /// <summary>
+//    /// 行业页面Id
+//    /// </summary>
+//    [SugarColumn(ColumnDescription = "行业页面Id")]
+//    public string IndustrPageId { get; set; }
 
-    /// <summary>
-    /// 审批类型
-    /// </summary>
-    [SugarColumn(ColumnDescription = "审批类型")]
-    public ESnapshopApproveType ApproveType { get; set; }
+//    /// <summary>
+//    /// 审批类型
+//    /// </summary>
+//    [SugarColumn(ColumnDescription = "审批类型")]
+//    public ESnapshopApproveType ApproveType { get; set; }
 
-    /// <summary>
-    /// 是否启用
-    /// </summary>
-    [SugarColumn(ColumnDescription = "是否启用")]
-    public bool IsEnable { get; set; }
+//    /// <summary>
+//    /// 是否启用
+//    /// </summary>
+//    [SugarColumn(ColumnDescription = "是否启用")]
+//    public bool IsEnable { get; set; }
 
-    /// <summary>
-    /// 是否公用
-    /// 公用后不受行业页面限制
-    /// </summary>
-    [SugarColumn(ColumnDescription = "是否公用")]
-    public bool IsPublic { get; set; }
+//    /// <summary>
+//    /// 是否公用
+//    /// 公用后不受行业页面限制
+//    /// </summary>
+//    [SugarColumn(ColumnDescription = "是否公用")]
+//    public bool IsPublic { get; set; }
 
-    /// <summary>
-    /// 排序
-    /// </summary>
-    [SugarColumn(ColumnDescription = "排序")]
-    public int DisplayOrder { get; set; }
-}
+//    /// <summary>
+//    /// 排序
+//    /// </summary>
+//    [SugarColumn(ColumnDescription = "排序")]
+//    public int DisplayOrder { get; set; }
+//}

+ 1 - 1
src/Hotline/Snapshot/SnapshotSMSTemplate.cs

@@ -26,7 +26,7 @@ public class SnapshotSMSTemplate : FullStateEntity
     /// 审核状态
     /// </summary>
     [SugarColumn(ColumnDescription = "审核状态")]
-    public ERedPackAuditStatus Status { get; set; }
+    public ESnapshotSMSStatus Status { get; set; }
 
     /// <summary>
     /// 是否启用

+ 31 - 0
src/Hotline/dataview.md

@@ -518,3 +518,34 @@ group by "OrderId") and "VisitState" not in(50,30)) and "VisitTarget"=20
 
 
 
+### 部门受理类型统计周期
+select 
+部门名称,部门类别,
+总件数,总时长, CAST(总时长/总件数/3600 as NUMERIC(10,2)) as 总平均,
+咨询件数,咨询时长,CASE 咨询件数 WHEN 0 THEN	0	ELSE CAST(咨询时长/咨询件数/3600 as NUMERIC(10,2)) END as 咨询平均,
+建议件数,建议时长,CASE 建议件数 WHEN 0 THEN 0 ELSE CAST(建议时长/建议件数/3600 as NUMERIC(10,2)) END as 建议平均,
+求助件数,求助时长,CASE 求助件数 WHEN 0 THEN 0 ELSE CAST(求助时长/求助件数/3600 as NUMERIC(10,2)) END as 求助平均,
+表扬件数,表扬时长,CASE 表扬件数 WHEN 0 THEN 0 ELSE CAST(表扬时长/表扬件数/3600 as NUMERIC(10,2)) END as 表扬平均,
+举报件数,举报时长,CASE 举报件数 WHEN 0 THEN 0 ELSE CAST(举报时长/举报件数/3600 as NUMERIC(10,2)) END as 举报平均,
+投诉件数,投诉时长,CASE 投诉件数 WHEN 0 THEN 0 ELSE CAST(投诉时长/投诉件数/3600 as NUMERIC(10,2)) END as 投诉平均,
+其他件数,其他时长,CASE 其他件数 WHEN 0 THEN 0 ELSE CAST(其他时长/其他件数/3600 as NUMERIC(10,2)) END as 其他平均 from (
+select "OrgName" as 部门名称,"OrgType" as  部门类别,("ZxAllCount" + "JyAllCount" + "QzAllCount" + "ByAllCount" + "JbAllCount" + "TsAllCount" + "QtAllCount")as 总件数,
+("ZxAllTimes" + "JyAllTimes" + "QzAllTimes" + "ByAllTimes" + "JbAllTimes" + "TsAllTimes" + "QtAllTimes") as 总时长,"ZxAllCount" as 咨询件数,"ZxAllTimes" as 咨询时长,"JyAllCount" as 建议件数,
+"JyAllTimes" as 建议时长,"QzAllCount" as 求助件数,"QzAllTimes" as 求助时长,"ByAllCount" as 表扬件数,"ByAllTimes" as 表扬时长,"JbAllCount" as 举报件数,"JbAllTimes" as 举报时长,"TsAllCount" as 投诉件数,
+"TsAllTimes" as 投诉时长,"QtAllCount" as 其他件数,"QtAllTimes" as 其他时长  from (
+SELECT * FROM  (SELECT  ( CASE  WHEN ( "it"."OrgCode" = N'001' ) THEN N'热线中心'  ELSE "o"."Name" END ) AS "OrgName" , "it"."OrgCode" AS "OrgCode" , ( CASE  WHEN ( "o"."OrgType" = 2 ) THEN N'区县部门'  ELSE N'市直部门' END ) AS "OrgType" ,
+SUM(( CASE  WHEN ( "it"."AcceptTypeCode" = N'10' ) THEN 1  ELSE 0 END )) AS "ZxAllCount" , SUM(( CASE  WHEN (( "it"."AcceptTypeCode" = N'10' ) AND ( "it"."AllTime" IS NOT NULL )) THEN "it"."AllTime"  ELSE 0 END )) AS "ZxAllTimes" ,
+N'10' AS "ZxAcceptanceTypeCode" , SUM(( CASE  WHEN ( "it"."AcceptTypeCode" = N'15' ) THEN 1  ELSE 0 END )) AS "JyAllCount" ,
+SUM(( CASE  WHEN (( "it"."AcceptTypeCode" = N'15' ) AND ( "it"."AllTime" IS NOT NULL )) THEN "it"."AllTime"  ELSE 0 END )) AS "JyAllTimes" , N'15' AS "JyAcceptanceTypeCode" , 
+SUM(( CASE  WHEN ( "it"."AcceptTypeCode" = N'20' ) THEN 1  ELSE 0 END )) AS "QzAllCount" , SUM(( CASE  WHEN (( "it"."AcceptTypeCode" = N'20' ) AND ( "it"."AllTime" IS NOT NULL )) THEN "it"."AllTime"  ELSE 0 END )) AS "QzAllTimes" ,
+N'20' AS "QzAcceptanceTypeCode" , SUM(( CASE  WHEN ( "it"."AcceptTypeCode" = N'25' ) THEN 1  ELSE 0 END )) AS "ByAllCount" , SUM(( CASE  WHEN (( "it"."AcceptTypeCode" = N'25' ) AND ( "it"."AllTime" IS NOT NULL )) THEN "it"."AllTime"  ELSE 0 END ))
+AS "ByAllTimes" , N'25' AS "ByAcceptanceTypeCode" , SUM(( CASE  WHEN ( "it"."AcceptTypeCode" = N'30' ) THEN 1  ELSE 0 END )) AS "JbAllCount" , SUM(( CASE  WHEN (( "it"."AcceptTypeCode" = N'30' ) AND 
+( "it"."AllTime" IS NOT NULL )) THEN "it"."AllTime"  ELSE 0 END )) AS "JbAllTimes" , N'30' AS "JbAcceptanceTypeCode" , SUM(( CASE  WHEN ( "it"."AcceptTypeCode" = N'35' ) THEN 1  ELSE 0 END )) AS "TsAllCount" , 
+SUM(( CASE  WHEN (( "it"."AcceptTypeCode" = N'35' ) AND ( "it"."AllTime" IS NOT NULL )) THEN "it"."AllTime"  ELSE 0 END )) AS "TsAllTimes" , N'35' AS "TsAcceptanceTypeCode" , 
+SUM(( CASE  WHEN ( "it"."AcceptTypeCode" = N'40' ) THEN 1  ELSE 0 END )) AS "QtAllCount" , SUM(( CASE  WHEN (( "it"."AcceptTypeCode" = N'40' ) AND ( "it"."AllTime" IS NOT NULL )) THEN "it"."AllTime"  ELSE 0 END )) AS "QtAllTimes" ,
+N'40' AS "QtAcceptanceTypeCode" , SUM(( CASE  WHEN ( "it"."AcceptTypeCode" = N'1' ) THEN 1  ELSE 0 END )) AS "YjAllCount" , 
+SUM(( CASE  WHEN (( "it"."AcceptTypeCode" = N'1' ) AND ( "it"."AllTime" IS NOT NULL )) THEN "it"."AllTime"  ELSE 0 END )) AS "YjAllTimes" , N'1' AS "YjAcceptanceTypeCode"  FROM  
+(SELECT * FROM  (SELECT  ( CASE  WHEN ( true = true ) THEN SUBSTRING("ActualHandleOrgCode",1 + 0,6)  ELSE SUBSTRING("ActualHandleOrgCode",1 + 0,(LENGTH(N'001')+ 3 )) END ) AS "OrgCode" , "AcceptTypeCode" AS "AcceptTypeCode" ,
+( CASE  WHEN ( "FileOrgIsCenter" = true ) THEN 10  ELSE 20 END ) AS "ProcessType" , ( CASE  WHEN ( "FileOrgIsCenter" = true ) THEN "CreationTimeHandleDurationWorkday"  ELSE "CenterToOrgHandleDurationWorkday" END ) AS "AllTime" ,
+"FileOrgIsCenter" AS "FileOrgIsCenter"  FROM "order"  WHERE ((( "FiledTime" >= '2024-12-01 00:00:00.000' ) AND ( "FiledTime" <= '2024-12-31 23:59:59.000' )) AND ( "Status" >= 300 ))  AND ( "IsDeleted" = false ) ) MergeTable ) "it"
+Left JOIN "system_organize" "o" ON ( "it"."OrgCode" = "o"."Id" )   WHERE ( "o"."IsDeleted" = false )GROUP BY "it"."OrgCode","o"."Name","o"."OrgType"  )))