Browse Source

oldhotline

Dun.Jason 6 tháng trước cách đây
mục cha
commit
55033a119c

+ 2 - 0
src/Hotline.Api/Controllers/CallController.cs

@@ -174,6 +174,8 @@ namespace Hotline.Api.Controllers
 				var Id = call.TransliterationId;
 				var Id = call.TransliterationId;
 				var setting = _systemSettingCacheManager.GetSetting(SettingConstants.ViteRecordPrefix);
 				var setting = _systemSettingCacheManager.GetSetting(SettingConstants.ViteRecordPrefix);
 
 
+
+
 				var handler = new AiQualityHandler()
 				var handler = new AiQualityHandler()
 				{
 				{
 					Id = Id,
 					Id = Id,

+ 368 - 0
src/Hotline.Api/Controllers/OldHotlineController.cs

@@ -0,0 +1,368 @@
+using Hotline.Api.Filter;
+using Hotline.Application.Identity;
+using Hotline.Application.Systems;
+using Hotline.Application.Tools;
+using Hotline.Caching.Interfaces;
+using Hotline.Caching.Services;
+using Hotline.FlowEngine.Workflows;
+using Hotline.Orders;
+using Hotline.Share.Dtos.Identity;
+using Hotline.Share.Dtos.Order;
+using Hotline.Share.Enums.FlowEngine;
+using Hotline.Share.Enums.Order;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using NPOI.POIFS.Macros;
+using SqlSugar;
+using XF.Domain.Authentications;
+using XF.Domain.Entities;
+using XF.Domain.Exceptions;
+using XF.Domain.Repository;
+
+namespace Hotline.Api.Controllers
+{
+    public class OldHotlineController: BaseController
+    {
+        private readonly IIdentityAppService _identityAppService;
+        private readonly ISystemLogApplication _iSystemLogApplication;
+        private readonly IOrderRepository _orderRepository;
+        private readonly IOrderVisitRepository _orderVisitRepository;
+        private readonly IOrderDelayRepository _orderDelayRepository;
+        private readonly IOrderScreenRepository _orderScreenRepository;
+        private readonly IRepository<OrderSendBackAudit> _orderSendBackAuditRepository;
+        private readonly IRepository<OrderVisitDetail> _orderVisitedDetailRepository;
+
+        public OldHotlineController(IIdentityAppService identityAppService, ISystemLogApplication iSystemLogApplication, IOrderRepository orderRepository, IOrderVisitRepository orderVisitRepository, IOrderDelayRepository orderDelayRepository, IOrderScreenRepository orderScreenRepository, IRepository<OrderSendBackAudit> orderSendBackAuditRepository, IRepository<OrderVisitDetail> orderVisitedDetailRepository)
+        {
+            _identityAppService = identityAppService;
+            _iSystemLogApplication = iSystemLogApplication;
+            _orderRepository = orderRepository;
+            _orderVisitRepository = orderVisitRepository;
+            _orderDelayRepository = orderDelayRepository;
+            _orderScreenRepository = orderScreenRepository;
+            _orderSendBackAuditRepository = orderSendBackAuditRepository;
+            _orderVisitedDetailRepository = orderVisitedDetailRepository;
+        }
+
+        /// <summary>
+        /// 老系统登录新系统
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [AllowAnonymous]
+        [HttpPost("loing-oldtonew")]
+        [LogFilter("",false)]
+        public async Task<string> HotlineLoginOldToNew([FromBody] HotlineLoginOldToNewDto dto)
+        {
+            try
+            {
+                dto.UserName = RSA.RSADecrypt(dto.UserName, RSA_Create.RSA_PRIVATE_KEY, "PEM");
+            }
+            catch 
+            {
+                throw UserFriendlyException.SameMessage("帐号解密失败");
+            }
+            var res = await _identityAppService.OldToNewLoginAsync(dto, HttpContext.RequestAborted);
+            await _iSystemLogApplication.AddLog("账号登录", res, dto, HttpContext, dto.UserName);
+            return res;
+        }
+
+        /// <summary>
+        /// 是否存在可办信息
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [AllowAnonymous]
+        [HttpGet("ishas_canhandler")]
+        public async Task<bool> IsHasCanHandler([FromBody] HotlineLoginOldToNewDto dto)
+        {
+            try
+            {
+                dto.UserName = RSA.RSADecrypt(dto.UserName, RSA_Create.RSA_PRIVATE_KEY, "PEM");
+            }
+            catch
+            {
+                throw UserFriendlyException.SameMessage("帐号解密失败");
+            }
+
+            var isHas = false;
+
+            var (isAdmin, isCenter, user) = await _identityAppService.IsCheckAdmin(dto.UserName);
+            List<string> Roles = user.Roles.Select(x => x.Name).ToList();
+            if (isCenter)
+            {
+                #region 待办
+                //待办
+                var waitedDataList = await _orderRepository
+                    .Queryable(hasHandled: false, isAdmin: isAdmin)
+                    .Where(d => SqlFunc.Subqueryable<WorkflowStep>()
+                        .Where(step => step.ExternalId == d.Id && step.Status != EWorkflowStepStatus.Handled &&
+                                       ((step.FlowAssignType == EFlowAssignType.User && !string.IsNullOrEmpty(step.HandlerId) && step.HandlerId == user.Id) ||
+                                        (step.FlowAssignType == EFlowAssignType.Org && !string.IsNullOrEmpty(step.HandlerOrgId) && step.HandlerOrgId == user.OrgId) ||
+                                        (step.FlowAssignType == EFlowAssignType.Role && !string.IsNullOrEmpty(step.RoleId) && Roles.Contains(step.RoleId))))
+                        .Any())
+                    .Includes(d => d.OrderSpecials)
+                    .Where(d => d.Status != EOrderStatus.WaitForAccept && d.Status != EOrderStatus.BackToUnAccept && d.Status != EOrderStatus.SpecialToUnAccept && d.Status != EOrderStatus.HandOverToUnAccept)
+                    .Where(d => d.Source < ESource.MLSQ || d.Source > ESource.WZSC)
+                    .Where(d => d.Status != EOrderStatus.BackToProvince && d.Status < EOrderStatus.Filed)
+                    .Where(d => d.OrderSpecials.Any() == false || d.OrderSpecials.Any(s => s.State > 0))
+                    .OrderByDescending(d => new { d.IsUrgent, d.StartTime })
+                    .Select(d => new HomeOrderDto
+                    {
+                        No = d.No,
+                        OrderId = d.Id,
+                        Title = d.Title,
+                        AcceptType = d.AcceptType,
+                        HotspotName = d.HotspotName,
+                        Type = "Waited",
+                        Time = d.ExpiredTime,
+                        Status = d.Status,
+                        ActualHandleTime = d.ActualHandleTime,
+                        CounterSignType = d.CounterSignType
+                    })
+                    .ToListAsync();
+                isHas = waitedDataList.Any();
+
+                //待办是否有数据
+                if (isHas)
+                {
+                    return isHas;
+                }
+                #endregion
+
+                #region 已超期
+                // 已超期
+                isHas = waitedDataList.Any(x => (x.Time < DateTime.Now && x.Status < EOrderStatus.Filed) ||
+                                                                      (x.Time < x.ActualHandleTime && x.Status >= EOrderStatus.Filed));
+                //超期是否有待办
+                if (isHas)
+                {
+                    return isHas;
+                }
+                #endregion
+
+                #region 回访待办
+                //回访待办
+                isHas = await _orderVisitRepository.Queryable()
+                    .AnyAsync(d => (d.VisitState == EVisitState.WaitForVisit || d.VisitState == EVisitState.NoSatisfiedWaitForVisit) && d.EmployeeId == user.Id);
+
+                //回访待办是否有数据
+                if (isHas)
+                {
+                    return isHas;
+                }
+                #endregion
+
+                #region 会签待办
+                //会签待办
+                isHas = waitedDataList.Any(x => x.CounterSignType == ECounterSignType.Center || x.CounterSignType == ECounterSignType.Department);
+
+                //会签待办是否有数据
+                if (isHas)
+                {
+                    return isHas;
+                }
+                #endregion
+
+                #region 延期待审批
+                //延期待审批
+                isHas = await _orderDelayRepository.Queryable(canView: true, isAdmin: isAdmin)
+                    .AnyAsync(d => d.DelayState == EDelayState.Examining);
+
+                //延期待审批是否有数据
+                if (isHas)
+                {
+                    return isHas;
+                }
+
+                #endregion
+
+                #region 部门即将超期
+                //部门即将超期
+                isHas = await _orderRepository.Queryable(canView: !isCenter)
+                .AnyAsync(d => SqlFunc.Subqueryable<WorkflowStep>()
+                        .Where(step => step.ExternalId == d.Id && step.Status != EWorkflowStepStatus.Handled &&
+                                       ((step.FlowAssignType == EFlowAssignType.User && !string.IsNullOrEmpty(step.HandlerId) && step.HandlerId == user.Id) ||
+                                        (step.FlowAssignType == EFlowAssignType.Org && !string.IsNullOrEmpty(step.HandlerOrgId) && step.HandlerOrgId == user.OrgId) ||
+                                        (step.FlowAssignType == EFlowAssignType.Role && !string.IsNullOrEmpty(step.RoleId) && Roles.Contains(step.RoleId))))
+                        .Any()
+                    && d.Status < EOrderStatus.Filed && DateTime.Now > d.NearlyExpiredTime && DateTime.Now < d.ExpiredTime);
+
+                //部门即将超期是否有数据
+                if (isHas)
+                {
+                    return isHas;
+                }
+                #endregion
+
+                #region 甄别待审批
+                //甄别待审批
+                isHas = await _orderScreenRepository.Queryable(hasHandled: !true, isAdmin: isAdmin)
+                    .Includes(d => d.Order)
+                    .Includes(d => d.ScreenDetails.Where(sd => sd.AuditUserId == user.Id).OrderByDescending(sd => sd.AuditTime).Take(1).ToList())
+                    .AnyAsync(d => (d.Status == EScreenStatus.Apply || d.Status == EScreenStatus.Approval || (d.Status == EScreenStatus.SendBack && d.SendBackApply == false)));
+
+                //甄别待审批是否有数据
+                if (isHas)
+                {
+                    return isHas;
+                }
+                #endregion
+
+                #region 退回待审批
+                //退回待审批
+                isHas = await _orderSendBackAuditRepository.Queryable()
+                    .Where(d => d.State == ESendBackAuditState.Apply)
+                    .WhereIF(Roles.Contains("role_sysadmin") == false, x => x.SendBackOrgId == user.Id) // 123 系统管理员;
+                    .AnyAsync();
+
+                if (isHas)
+                {
+                    return isHas;
+                }
+                #endregion
+            }
+            else
+            {
+                #region 待办
+                //待办
+                var waitedDataList = await _orderRepository
+                    .Queryable(hasHandled: false, isAdmin: isAdmin)
+                    .Where(d => SqlFunc.Subqueryable<WorkflowStep>()
+                        .Where(step => step.ExternalId == d.Id && step.Status != EWorkflowStepStatus.Handled &&
+                                       ((step.FlowAssignType == EFlowAssignType.User && !string.IsNullOrEmpty(step.HandlerId) && step.HandlerId == user.Id) ||
+                                        (step.FlowAssignType == EFlowAssignType.Org && !string.IsNullOrEmpty(step.HandlerOrgId) && step.HandlerOrgId == user.OrgId) ||
+                                        (step.FlowAssignType == EFlowAssignType.Role && !string.IsNullOrEmpty(step.RoleId) && Roles.Contains(step.RoleId))))
+                        .Any())
+                    .Includes(d => d.OrderSpecials)
+                    .Where(d => d.Status != EOrderStatus.WaitForAccept && d.Status != EOrderStatus.BackToUnAccept && d.Status != EOrderStatus.SpecialToUnAccept && d.Status != EOrderStatus.HandOverToUnAccept)
+                    .Where(d => d.Source < ESource.MLSQ || d.Source > ESource.WZSC)
+                    .Where(d => d.Status != EOrderStatus.BackToProvince && d.Status < EOrderStatus.Filed)
+                    .Where(d => d.OrderSpecials.Any() == false || d.OrderSpecials.Any(s => s.State > 0))
+                    .OrderByDescending(d => new { d.IsUrgent, d.StartTime })
+                    .Select(d => new HomeOrderDto
+                    {
+                        No = d.No,
+                        OrderId = d.Id,
+                        Title = d.Title,
+                        AcceptType = d.AcceptType,
+                        HotspotName = d.HotspotName,
+                        Type = "Waited",
+                        Time = d.ExpiredTime,
+                        Status = d.Status,
+                        ActualHandleTime = d.ActualHandleTime,
+                        CounterSignType = d.CounterSignType
+                    })
+                    .ToListAsync();
+                isHas = waitedDataList.Any();
+                if (isHas)
+                {
+                    return isHas;
+                }
+                #endregion
+
+                #region 已超期
+                //已超期
+                isHas = waitedDataList.Any(x => (x.Time < DateTime.Now && x.Status < EOrderStatus.Filed) ||
+                                                                      (x.Time < x.ActualHandleTime && x.Status >= EOrderStatus.Filed));
+                //超期是否有待办
+                if (isHas)
+                {
+                    return isHas;
+                }
+                #endregion
+
+                #region 会签待办
+                //会签待办
+                isHas = waitedDataList.Any(x => x.CounterSignType == ECounterSignType.Center || x.CounterSignType == ECounterSignType.Department);
+
+                if (isHas)
+                {
+                    return isHas;
+                }
+                #endregion
+
+                #region 甄别待审批
+                //甄别待审批
+                isHas = await _orderScreenRepository.Queryable(hasHandled: !true, isAdmin: isAdmin)
+                    .Includes(d => d.Order)
+                    .Includes(d => d.ScreenDetails.Where(sd => sd.AuditUserId == user.Id).OrderByDescending(sd => sd.AuditTime).Take(1).ToList())
+                    .AnyAsync(d => (d.Status == EScreenStatus.Apply || d.Status == EScreenStatus.Approval || (d.Status == EScreenStatus.SendBack && d.SendBackApply == false)));
+
+                if (isHas)
+                {
+                    return isHas;
+                }
+
+                #endregion
+
+                #region 延期待审批
+                //延期待审批
+                isHas = await _orderDelayRepository.Queryable(canView: true, isAdmin: isAdmin)
+                    .AnyAsync(d => d.DelayState == EDelayState.Examining);
+
+                if (isHas)
+                {
+                    return isHas;
+                }
+                #endregion
+
+                #region 甄别待申请
+                //甄别待申请
+                isHas = await _orderVisitedDetailRepository.Queryable(false, true)
+                .Includes(x => x.OrderVisit)
+                .Includes(x => x.OrderVisit, y => y.Order)
+                .Includes(x => x.OrderVisit, y => y.Employee)
+                .Includes(x => x.OrderScreens)
+                .Where(x => x.OrderScreens.Any(s => s.Status == EScreenStatus.SendBack && s.SendBackApply == true) || x.OrderScreens.Any(s => (s.Status != EScreenStatus.SendBack && s.SendBackApply != true)) == false)
+                .Where(x => x.OrderVisit.VisitState == EVisitState.Visited && x.OrderVisit.IsCanHandle)
+                .Where(x => x.VisitTarget == EVisitTarget.Org && x.VisitOrgCode == user.OrgId && (
+                    SqlFunc.JsonField(x.OrgProcessingResults, "Key") == "1" ||
+                    SqlFunc.JsonField(x.OrgProcessingResults, "Key") == "2" ||
+                    SqlFunc.JsonField(x.OrgHandledAttitude, "Key") == "1" ||
+                    SqlFunc.JsonField(x.OrgHandledAttitude, "Key") == "2"
+                ))
+               .AnyAsync();
+
+                if (isHas)
+                {
+                    return isHas;
+                }
+                #endregion
+
+                #region 退回待审批
+                //退回待审批
+                isHas = await _orderSendBackAuditRepository.Queryable()
+                    .Where(d => d.State == ESendBackAuditState.Apply)
+                    .WhereIF(Roles.Contains("role_sysadmin") == false, x => x.SendBackOrgId == user.OrgId) // 123 系统管理员;
+                    .AnyAsync();
+                if (isHas)
+                {
+                    return isHas;
+                }
+
+                #endregion
+
+                #region 部门即将超期
+                //部门即将超期
+                isHas = await _orderRepository.Queryable(canView: !isCenter)
+                .Includes(d => d.OrderDelays)
+                .Where(d => SqlFunc.Subqueryable<WorkflowStep>()
+                        .Where(step => step.ExternalId == d.Id && step.Status != EWorkflowStepStatus.Handled &&
+                        ((step.FlowAssignType == EFlowAssignType.User && !string.IsNullOrEmpty(step.HandlerId) && step.HandlerId == user.Id) ||
+                        (step.FlowAssignType == EFlowAssignType.Org && !string.IsNullOrEmpty(step.HandlerOrgId) && step.HandlerOrgId == user.OrgId) ||
+                                        (step.FlowAssignType == EFlowAssignType.Role && !string.IsNullOrEmpty(step.RoleId) && Roles.Contains(step.RoleId))))
+                        .Any())
+                    .Where(d => d.Status < EOrderStatus.Filed && DateTime.Now > d.NearlyExpiredTime && DateTime.Now < d.ExpiredTime)
+                    .AnyAsync();
+                if (isHas)
+                {
+                    return isHas;
+                }
+
+                #endregion
+            }
+            return isHas;
+        }
+    }
+}

+ 6 - 0
src/Hotline.Application/Identity/IIdentityAppService.cs

@@ -3,12 +3,18 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
 using System.Text;
 using System.Text;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
+using Hotline.Settings;
 using Hotline.Share.Dtos.Identity;
 using Hotline.Share.Dtos.Identity;
+using Hotline.Users;
 
 
 namespace Hotline.Application.Identity
 namespace Hotline.Application.Identity
 {
 {
     public interface IIdentityAppService
     public interface IIdentityAppService
     {
     {
         Task<string> LoginAsync(LoginDto dto, CancellationToken cancellationToken);
         Task<string> LoginAsync(LoginDto dto, CancellationToken cancellationToken);
+
+        Task<string> OldToNewLoginAsync(HotlineLoginOldToNewDto dto, CancellationToken cancellationToken);
+
+        Task<(bool,bool,User)> IsCheckAdmin(string userName);
     }
     }
 }
 }

+ 106 - 0
src/Hotline.Application/Identity/IdentityAppService.cs

@@ -5,6 +5,7 @@ using Hotline.Identity.Accounts;
 using Hotline.Orders;
 using Hotline.Orders;
 using Hotline.Push;
 using Hotline.Push;
 using Hotline.Schedulings;
 using Hotline.Schedulings;
+using Hotline.SeedData;
 using Hotline.Settings;
 using Hotline.Settings;
 using Hotline.Share.Dtos.FlowEngine;
 using Hotline.Share.Dtos.FlowEngine;
 using Hotline.Share.Dtos.Identity;
 using Hotline.Share.Dtos.Identity;
@@ -63,6 +64,111 @@ public class IdentityAppService : IIdentityAppService, IScopeDependency
 
 
 	}
 	}
 
 
+    public async Task<string> OldToNewLoginAsync(HotlineLoginOldToNewDto dto, CancellationToken cancellationToken)
+    {
+        var account = await _accountRepository.GetExtAsync(
+           d => d.UserName == dto.UserName,
+           d => d.Includes(x => x.Roles));
+
+        if (account is null)
+            throw UserFriendlyException.SameMessage("未找到帐号,请联系管理员查看");
+
+        if (account.Status != EAccountStatus.Normal)
+            throw UserFriendlyException.SameMessage("帐号已被注销,请联系管理员查看");
+
+        if (account.LockoutEnabled && account.LockoutEnd >= DateTime.Now)
+            throw UserFriendlyException.SameMessage("账号被锁定!请联系管理员查看");
+
+        var userInfo = await _userRepository.GetAsync(p => p.Id == account.Id, cancellationToken);
+
+        //限制系统类型账户频繁获取token的行为
+        //todo
+
+        if (account.LockoutEnd.HasValue || account.AccessFailedCount > 0)
+        {
+            account.LockoutEnd = null;
+            account.AccessFailedCount = 0;
+            await _accountRepository.UpdateAsync(account, cancellationToken);
+        }
+
+        var user = await _userRepository.Queryable()
+            .Includes(d => d.Organization)
+            .FirstAsync(d => d.Id == account.Id);
+
+        //平均派单
+        var averageSendOrder = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.AverageSendOrder).SettingValue[0]);
+        if (averageSendOrder)
+        {
+            await AverageOrderScheduling(account.Id, cancellationToken);
+        }
+        var jwtOptions = _identityOptionsAccessor.Value.Jwt;
+        var claims = new List<Claim>
+        {
+            //new(JwtClaimTypes.Id, account.Id),
+            new(JwtClaimTypes.Subject, account.Id),
+            new(JwtClaimTypes.PhoneNumber, account.PhoneNo ?? string.Empty),
+            new(AppClaimTypes.UserDisplayName, account.Name),
+            new(JwtClaimTypes.Scope, jwtOptions.Scope),
+            new(AppClaimTypes.UserPasswordChanged, account.PasswordChanged.ToString()),
+            new(AppClaimTypes.StaffNo, user.StaffNo ?? string.Empty),
+        };
+        if (!string.IsNullOrEmpty(user.OrgId) && user.Organization is not null)
+        {
+            claims.AddRange(
+                new List<Claim>
+                {
+                    new(AppClaimTypes.DepartmentId, user.OrgId ?? string.Empty),
+                    new(AppClaimTypes.DepartmentIsCenter, user.Organization?.IsCenter.ToString()),
+                    new(AppClaimTypes.DepartmentName, user.Organization?.Name ?? string.Empty),
+                    new(AppClaimTypes.DepartmentAreaCode, user.Organization?.AreaCode ?? string.Empty),
+                    new(AppClaimTypes.DepartmentAreaName, user.Organization?.AreaName ?? string.Empty),
+                    new(AppClaimTypes.DepartmentLevel, user.Organization?.Level.ToString() ?? string.Empty),
+                    new(AppClaimTypes.AreaId, user.OrgId?.GetHigherOrgId() ?? string.Empty),
+                }
+            );
+        }
+        claims.AddRange(account.Roles.Select(d => new Claim(JwtClaimTypes.Role, d.Name)));
+        var audience = new AudienceTicket(account.Id);
+        var expiredSeconds = jwtOptions.Expired <= 0 ? 3600 : jwtOptions.Expired;
+        await _cacheAudience.SetAsync(audience.Id, audience, TimeSpan.FromSeconds(expiredSeconds), cancellationToken);
+        var token = _jwtSecurity.EncodeJwtToken(claims, audience.Ticket);
+        return token;
+    }
+
+    public async Task<(bool,bool, User)> IsCheckAdmin(string userName)
+    {
+        var isAdmin = false;
+        var isCenter = false;
+        var account = await _accountRepository.GetExtAsync(
+           d => d.UserName == userName,
+           d => d.Includes(x => x.Roles)
+           );
+
+        if (account is null)
+            throw UserFriendlyException.SameMessage("未找到帐号,请联系管理员查看");
+
+        if (account.Status != EAccountStatus.Normal)
+            throw UserFriendlyException.SameMessage("帐号已被注销,请联系管理员查看");
+
+        if (account.LockoutEnabled && account.LockoutEnd >= DateTime.Now)
+            throw UserFriendlyException.SameMessage("账号被锁定!请联系管理员查看");
+
+        var user = await _userRepository.Queryable()
+            .Includes(d => d.Organization)
+            .Includes(d => d.Roles)
+            .FirstAsync(d => d.Id == account.Id);
+
+        var systemAdministrator = _systemSettingCacheManager.GetSetting(SettingConstants.SystemAdministrator)?.SettingValue[0];
+        if (!string.IsNullOrEmpty(systemAdministrator) && (user.Roles.Any(x=>x.Name.Contains(systemAdministrator)) || user.Roles.Any(x=>x.Name.Contains(RoleSeedData.AdminRole))))
+            isAdmin = true;
+        else
+            isAdmin = false;
+
+        isCenter = user.Organization.IsCenter;
+
+        return (isAdmin, isCenter,user);
+    }
+
     public async Task<string> LoginAsync(LoginDto dto, CancellationToken cancellationToken)
     public async Task<string> LoginAsync(LoginDto dto, CancellationToken cancellationToken)
     {
     {
         var account = await _accountRepository.GetExtAsync(
         var account = await _accountRepository.GetExtAsync(

+ 144 - 0
src/Hotline.Application/Tools/RSA.cs

@@ -0,0 +1,144 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Security.Cryptography;
+using System.Text;
+using System.Threading.Tasks;
+using XF.Domain.Exceptions;
+
+namespace Hotline.Application.Tools
+{
+    /// <summary>
+    /// 加密、解密
+    /// </summary>
+    public class RSA
+    {
+        /// <summary>
+        /// RSA加密
+        /// </summary>
+        /// <param name="Data">原文</param>
+        /// <param name="PublicKeyString">公钥</param>
+        /// <param name="KeyType">密钥类型XML/PEM</param>
+        /// <returns></returns>
+        public static string RSAEncrypt(string Data, string PublicKeyString, string KeyType)
+        {
+            byte[] data = Encoding.GetEncoding("UTF-8").GetBytes(Data);
+            RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
+            switch (KeyType)
+            {
+                case "XML":
+                    rsa.FromXmlString(PublicKeyString);
+                    break;
+                case "PEM":
+                    rsa = RSA_PEM.FromPEM(PublicKeyString);
+                    break;
+                default:
+                    throw UserFriendlyException.SameMessage("不支持的密钥类型");
+            }
+            //加密块最大长度限制,如果加密数据的长度超过 秘钥长度/8-11,会引发长度不正确的异常,所以进行数据的分块加密
+            int MaxBlockSize = rsa.KeySize / 8 - 11;
+            //正常长度
+            if (data.Length <= MaxBlockSize)
+            {
+                byte[] hashvalueEcy = rsa.Encrypt(data, false); //加密
+                return System.Convert.ToBase64String(hashvalueEcy);
+            }
+            //长度超过正常值
+            else
+            {
+                using (MemoryStream PlaiStream = new MemoryStream(data))
+                using (MemoryStream CrypStream = new MemoryStream())
+                {
+                    Byte[] Buffer = new Byte[MaxBlockSize];
+                    int BlockSize = PlaiStream.Read(Buffer, 0, MaxBlockSize);
+                    while (BlockSize > 0)
+                    {
+                        Byte[] ToEncrypt = new Byte[BlockSize];
+                        Array.Copy(Buffer, 0, ToEncrypt, 0, BlockSize);
+
+                        Byte[] Cryptograph = rsa.Encrypt(ToEncrypt, false);
+                        CrypStream.Write(Cryptograph, 0, Cryptograph.Length);
+                        BlockSize = PlaiStream.Read(Buffer, 0, MaxBlockSize);
+                    }
+                    return System.Convert.ToBase64String(CrypStream.ToArray(), Base64FormattingOptions.None);
+                }
+            }
+        }
+
+        /// <summary>
+        /// RSA解密
+        /// </summary>
+        /// <param name="Data">密文</param>
+        /// <param name="PrivateKeyString">私钥</param>
+        /// <param name="KeyType">密钥类型XML/PEM</param>
+        /// <returns></returns>
+        public static string RSADecrypt(string Data, string PrivateKeyString, string KeyType)
+        {
+            // 把+号,替换回来
+            Data = Data.Replace("%2B", "+");
+
+            // 补全密文
+            Data = CorrectionCiphertext(Data);
+
+            RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
+            switch (KeyType)
+            {
+                case "XML":
+                    rsa.FromXmlString(PrivateKeyString);
+                    break;
+                case "PEM":
+                    rsa = RSA_PEM.FromPEM(PrivateKeyString);
+                    break;
+                default:
+                    throw UserFriendlyException.SameMessage("不支持的密钥类型");
+            }
+            int MaxBlockSize = rsa.KeySize / 8;    //解密块最大长度限制
+            //正常解密
+            if (Data.Length <= MaxBlockSize)
+            {
+                byte[] hashvalueDcy = rsa.Decrypt(System.Convert.FromBase64String(Data), false);//解密
+                return Encoding.GetEncoding("UTF-8").GetString(hashvalueDcy);
+            }
+            //分段解密
+            else
+            {
+                using (MemoryStream CrypStream = new MemoryStream(System.Convert.FromBase64String(Data)))
+                using (MemoryStream PlaiStream = new MemoryStream())
+                {
+                    Byte[] Buffer = new Byte[MaxBlockSize];
+                    int BlockSize = CrypStream.Read(Buffer, 0, MaxBlockSize);
+
+                    while (BlockSize > 0)
+                    {
+                        Byte[] ToDecrypt = new Byte[BlockSize];
+                        Array.Copy(Buffer, 0, ToDecrypt, 0, BlockSize);
+
+                        Byte[] Plaintext = rsa.Decrypt(ToDecrypt, false);
+                        PlaiStream.Write(Plaintext, 0, Plaintext.Length);
+                        BlockSize = CrypStream.Read(Buffer, 0, MaxBlockSize);
+                    }
+                    string output = Encoding.GetEncoding("UTF-8").GetString(PlaiStream.ToArray());
+                    return output;
+                }
+            }
+        }
+
+        /// <summary>
+        /// 补全密文
+        /// </summary>
+        /// <param name="strCiphertext">密文</param>
+        /// <param name="keySize">秘钥长度</param>
+        /// <returns>补全后的密文</returns>
+        private static string CorrectionCiphertext(string strCiphertext, int keySize = 1024)
+        {
+            int ciphertextLength = keySize / 8;
+            byte[] data = Convert.FromBase64String(strCiphertext);
+            var newData = new List<byte>(data);
+            while (newData.Count < ciphertextLength)
+            {
+                newData.Insert(0, 0x00);
+            }
+            return Convert.ToBase64String(newData.ToArray());
+        }
+    }
+}

+ 71 - 0
src/Hotline.Application/Tools/RSA_Create.cs

@@ -0,0 +1,71 @@
+
+using System.Security.Cryptography;
+
+namespace Hotline.Application.Tools
+{
+    /// <summary>
+    /// 公钥、私钥创建
+    /// </summary>
+    public class RSA_Create
+    {
+        /// <summary>
+        /// 登陆公钥
+        /// </summary>
+        public const string RSA_PUBLIC_KEY = "-----BEGIN PUBLIC KEY-----MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDGC7ZYMOLX8+Ze4AA0VrMcy/SbMhSGuTfOk5AWWekdSOZfb/OZ8G+oup5b/FCAxDmRolK0cK6nGgQ6QglMBB1qrHmfvyLR6Ock1MhUT8++T2lODRgM3xEtdJnFvcKIyte5eiSfJMby/7avHX0cho4OupRo1WK9veADyX45zmaMpQIDAQAB-----END PUBLIC KEY-----";
+
+        /// <summary>
+        /// 登陆私钥
+        /// </summary>
+        public const string RSA_PRIVATE_KEY = "-----BEGIN PRIVATE KEY-----MIICXAIBAAKBgQDGC7ZYMOLX8+Ze4AA0VrMcy/SbMhSGuTfOk5AWWekdSOZfb/OZ8G+oup5b/FCAxDmRolK0cK6nGgQ6QglMBB1qrHmfvyLR6Ock1MhUT8++T2lODRgM3xEtdJnFvcKIyte5eiSfJMby/7avHX0cho4OupRo1WK9veADyX45zmaMpQIDAQABAoGAELhaa7Ws4SiRUX895S1Qr+S0QQoIW5t1j7Y7mvd6S+Okt3f/LAb1llwzFHcH4OcwKRB4RQ2iYS88HiMHH+jVBs9uKtaVUGoTyly1TSBgNg7mtSh2JrGWPMAmVYYJKrIiwqrZh0783fQ0oYjSHoOGFaevVSiWdTgFCXkyJzXACKECQQDxaP8yO/YJ0k3oyYso0rzqrkt6NC17wl34VqGQ95QkJl40h2f0CXh458wPp1BgBQz9gcykri0V0MsdNK6a8TStAkEA0gPMbIHbNJ8FcbmToqWKBGlkDWSg/BZk9Xrov/wyc3QDtHZ7aV0m0x9/X0hXVzpAKiBnuehBVM/PxeQDaMs+2QJAT/hmQW/Xi11dbbYGIVneNyfR3QOX5Z13n6KjFvDM5ETJhEEu0Iqqm/K9iJn2aDiEGx56ejswuD9Yaa5gAPfSaQJBAIKuMmnic9BOk0isvR0b/MG6ArQCxdmW5cOKc4HjIFGPAy8r+qcZe6srkkpr7U0txfdwqpqX1u87UIJG24g90CECQBZTY2pGT6NVKFJ0cK7Qjwg1ekYMMPFGHjcf0jwI6nwZav7t46qSvSLSAXFtFsY7zTo3komvifKz8noLnA2qpBQ=-----END PRIVATE KEY-----";
+
+        /// <summary>
+        /// 取得私钥和公钥 XML 格式,返回数组第一个是私钥,第二个是公钥.
+        /// </summary>
+        /// <param name="size">密钥长度,默认1024,可以为2048</param>
+        /// <returns></returns>
+        public static string[] CreateXmlKey(int size = 1024)
+        {
+            //密钥格式要生成pkcs#1格式的  而不是pkcs#8格式的
+            RSACryptoServiceProvider sp = new RSACryptoServiceProvider(size);
+            string privateKey = sp.ToXmlString(true);//private key
+            string publicKey = sp.ToXmlString(false);//public  key
+            return new string[] { privateKey, publicKey };
+        }
+
+        /// <summary>
+        /// 取得私钥和公钥 CspBlob 格式,返回数组第一个是私钥,第二个是公钥.
+        /// </summary>
+        /// <param name="size"></param>
+        /// <returns></returns>
+        public static string[] CreateCspBlobKey(int size = 1024)
+        {
+            //密钥格式要生成pkcs#1格式的  而不是pkcs#8格式的
+            RSACryptoServiceProvider sp = new RSACryptoServiceProvider(size);
+            string privateKey = System.Convert.ToBase64String(sp.ExportCspBlob(true));//private key
+            string publicKey = System.Convert.ToBase64String(sp.ExportCspBlob(false));//public  key 
+
+            return new string[] { privateKey, publicKey };
+        }
+        /// <summary>
+        /// 导出PEM PKCS#1格式密钥对,返回数组第一个是私钥,第二个是公钥.
+        /// </summary>
+        public static string[] CreateKey_PEM_PKCS1(int size = 1024)
+        {
+            RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(size);
+            string privateKey = RSA_PEM.ToPEM(rsa, false, false);
+            string publicKey = RSA_PEM.ToPEM(rsa, true, false);
+            return new string[] { privateKey, publicKey };
+        }
+
+        /// <summary>
+        /// 导出PEM PKCS#8格式密钥对,返回数组第一个是私钥,第二个是公钥.
+        /// </summary>
+        public static string[] CreateKey_PEM_PKCS8(int size = 1024, bool convertToPublic = false)
+        {
+            RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(size);
+            string privateKey = RSA_PEM.ToPEM(rsa, false, true);
+            string publicKey = RSA_PEM.ToPEM(rsa, true, true);
+            return new string[] { privateKey, publicKey };
+        }
+    }
+}

+ 318 - 0
src/Hotline.Application/Tools/RSA_PEM.cs

@@ -0,0 +1,318 @@
+using System.Security.Cryptography;
+using System.Text.RegularExpressions;
+
+
+namespace Hotline.Application.Tools
+{
+    public class RSA_PEM
+    {
+        public static RSACryptoServiceProvider FromPEM(string pem)
+        {
+            var rsaParams = new CspParameters();
+            rsaParams.Flags = CspProviderFlags.UseMachineKeyStore;
+            var rsa = new RSACryptoServiceProvider(rsaParams);
+
+            var param = new RSAParameters();
+
+            var base64 = _PEMCode.Replace(pem, "");
+            var data = RSA_Unit.Base64DecodeBytes(base64);
+            if (data == null)
+            {
+                throw new Exception("PEM内容无效");
+            }
+            var idx = 0;
+
+            //读取长度
+            Func<byte, int> readLen = (first) =>
+            {
+                if (data[idx] == first)
+                {
+                    idx++;
+                    if (data[idx] == 0x81)
+                    {
+                        idx++;
+                        return data[idx++];
+                    }
+                    else if (data[idx] == 0x82)
+                    {
+                        idx++;
+                        return (((int)data[idx++]) << 8) + data[idx++];
+                    }
+                    else if (data[idx] < 0x80)
+                    {
+                        return data[idx++];
+                    }
+                }
+                throw new Exception("PEM未能提取到数据");
+            };
+            //读取块数据
+            Func<byte[]> readBlock = () =>
+            {
+                var len = readLen(0x02);
+                if (data[idx] == 0x00)
+                {
+                    idx++;
+                    len--;
+                }
+                var val = data.sub(idx, len);
+                idx += len;
+                return val;
+            };
+            //比较data从idx位置开始是否是byts内容
+            Func<byte[], bool> eq = (byts) =>
+            {
+                for (var i = 0; i < byts.Length; i++, idx++)
+                {
+                    if (idx >= data.Length)
+                    {
+                        return false;
+                    }
+                    if (byts[i] != data[idx])
+                    {
+                        return false;
+                    }
+                }
+                return true;
+            };
+
+
+
+
+            if (pem.Contains("PUBLIC KEY"))
+            {
+                /****使用公钥****/
+                //读取数据总长度
+                readLen(0x30);
+                if (!eq(_SeqOID))
+                {
+                    throw new Exception("PEM未知格式");
+                }
+                //读取1长度
+                readLen(0x03);
+                idx++;//跳过0x00
+                      //读取2长度
+                readLen(0x30);
+
+                //Modulus
+                param.Modulus = readBlock();
+
+                //Exponent
+                param.Exponent = readBlock();
+            }
+            else if (pem.Contains("PRIVATE KEY"))
+            {
+                /****使用私钥****/
+                //读取数据总长度
+                readLen(0x30);
+
+                //读取版本号
+                if (!eq(_Ver))
+                {
+                    throw new Exception("PEM未知版本");
+                }
+
+                //检测PKCS8
+                var idx2 = idx;
+                if (eq(_SeqOID))
+                {
+                    //读取1长度
+                    readLen(0x04);
+                    //读取2长度
+                    readLen(0x30);
+
+                    //读取版本号
+                    if (!eq(_Ver))
+                    {
+                        throw new Exception("PEM版本无效");
+                    }
+                }
+                else
+                {
+                    idx = idx2;
+                }
+
+                //读取数据
+                param.Modulus = readBlock();
+                param.Exponent = readBlock();
+                param.D = readBlock();
+                param.P = readBlock();
+                param.Q = readBlock();
+                param.DP = readBlock();
+                param.DQ = readBlock();
+                param.InverseQ = readBlock();
+            }
+            else
+            {
+                throw new Exception("pem需要BEGIN END标头");
+            }
+
+            rsa.ImportParameters(param);
+            return rsa;
+        }
+        static private Regex _PEMCode = new Regex(@"--+.+?--+|\s+");
+        static private byte[] _SeqOID = new byte[] { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 };
+        static private byte[] _Ver = new byte[] { 0x02, 0x01, 0x00 };
+
+
+        /// <summary>
+        /// 将RSA中的密钥对转换成PEM格式,usePKCS8=false时返回PKCS#1格式,否则返回PKCS#8格式,如果convertToPublic含私钥的RSA将只返回公钥,仅含公钥的RSA不受影响
+        /// </summary>
+        public static string ToPEM(RSACryptoServiceProvider rsa, bool convertToPublic, bool usePKCS8)
+        {
+            //https://www.jianshu.com/p/25803dd9527d
+            //https://www.cnblogs.com/ylz8401/p/8443819.html
+            //https://blog.csdn.net/jiayanhui2877/article/details/47187077
+            //https://blog.csdn.net/xuanshao_/article/details/51679824
+            //https://blog.csdn.net/xuanshao_/article/details/51672547
+
+            var ms = new MemoryStream();
+            //写入一个长度字节码
+            Action<int> writeLenByte = (len) =>
+            {
+                if (len < 0x80)
+                {
+                    ms.WriteByte((byte)len);
+                }
+                else if (len <= 0xff)
+                {
+                    ms.WriteByte(0x81);
+                    ms.WriteByte((byte)len);
+                }
+                else
+                {
+                    ms.WriteByte(0x82);
+                    ms.WriteByte((byte)(len >> 8 & 0xff));
+                    ms.WriteByte((byte)(len & 0xff));
+                }
+            };
+            //写入一块数据
+            Action<byte[]> writeBlock = (byts) =>
+            {
+                var addZero = (byts[0] >> 4) >= 0x8;
+                ms.WriteByte(0x02);
+                var len = byts.Length + (addZero ? 1 : 0);
+                writeLenByte(len);
+
+                if (addZero)
+                {
+                    ms.WriteByte(0x00);
+                }
+                ms.Write(byts, 0, byts.Length);
+            };
+            //根据后续内容长度写入长度数据
+            Func<int, byte[], byte[]> writeLen = (index, byts) =>
+            {
+                var len = byts.Length - index;
+
+                ms.SetLength(0);
+                ms.Write(byts, 0, index);
+                writeLenByte(len);
+                ms.Write(byts, index, len);
+
+                return ms.ToArray();
+            };
+
+
+            if (rsa.PublicOnly || convertToPublic)
+            {
+                /****生成公钥****/
+                var param = rsa.ExportParameters(false);
+
+
+                //写入总字节数,不含本段长度,额外需要24字节的头,后续计算好填入
+                ms.WriteByte(0x30);
+                var index1 = (int)ms.Length;
+
+                //固定内容
+                // encoded OID sequence for PKCS #1 rsaEncryption szOID_RSA_RSA = "1.2.840.113549.1.1.1"
+                ms.writeAll(_SeqOID);
+
+                //从0x00开始的后续长度
+                ms.WriteByte(0x03);
+                var index2 = (int)ms.Length;
+                ms.WriteByte(0x00);
+
+                //后续内容长度
+                ms.WriteByte(0x30);
+                var index3 = (int)ms.Length;
+
+                //写入Modulus
+                writeBlock(param.Modulus);
+
+                //写入Exponent
+                writeBlock(param.Exponent);
+
+
+                //计算空缺的长度
+                var byts = ms.ToArray();
+
+                byts = writeLen(index3, byts);
+                byts = writeLen(index2, byts);
+                byts = writeLen(index1, byts);
+
+
+                return "-----BEGIN PUBLIC KEY-----\n" + RSA_Unit.TextBreak(RSA_Unit.Base64EncodeBytes(byts), 64) + "\n-----END PUBLIC KEY-----";
+            }
+            else
+            {
+                /****生成私钥****/
+                var param = rsa.ExportParameters(true);
+
+                //写入总字节数,后续写入
+                ms.WriteByte(0x30);
+                int index1 = (int)ms.Length;
+
+                //写入版本号
+                ms.writeAll(_Ver);
+
+                //PKCS8 多一段数据
+                int index2 = -1, index3 = -1;
+                if (usePKCS8)
+                {
+                    //固定内容
+                    ms.writeAll(_SeqOID);
+
+                    //后续内容长度
+                    ms.WriteByte(0x04);
+                    index2 = (int)ms.Length;
+
+                    //后续内容长度
+                    ms.WriteByte(0x30);
+                    index3 = (int)ms.Length;
+
+                    //写入版本号
+                    ms.writeAll(_Ver);
+                }
+
+                //写入数据
+                writeBlock(param.Modulus);
+                writeBlock(param.Exponent);
+                writeBlock(param.D);
+                writeBlock(param.P);
+                writeBlock(param.Q);
+                writeBlock(param.DP);
+                writeBlock(param.DQ);
+                writeBlock(param.InverseQ);
+
+
+                //计算空缺的长度
+                var byts = ms.ToArray();
+
+                if (index2 != -1)
+                {
+                    byts = writeLen(index3, byts);
+                    byts = writeLen(index2, byts);
+                }
+                byts = writeLen(index1, byts);
+
+
+                var flag = " PRIVATE KEY";
+                if (!usePKCS8)
+                {
+                    flag = " RSA" + flag;
+                }
+                return "-----BEGIN" + flag + "-----\n" + RSA_Unit.TextBreak(RSA_Unit.Base64EncodeBytes(byts), 64) + "\n-----END" + flag + "-----";
+            }
+        }
+    }
+}

+ 74 - 0
src/Hotline.Application/Tools/RSA_Unit.cs

@@ -0,0 +1,74 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hotline.Application.Tools
+{
+    public class RSA_Unit
+    {
+        public static string Base64EncodeBytes(byte[] byts)
+        {
+            return System.Convert.ToBase64String(byts);
+        }
+        public static byte[] Base64DecodeBytes(string str)
+        {
+            try
+            {
+                return System.Convert.FromBase64String(str);
+            }
+            catch
+            {
+                return null;
+            }
+        }
+        /// <summary>
+        /// 把字符串按每行多少个字断行
+        /// </summary>
+        public static string TextBreak(string text, int line)
+        {
+            var idx = 0;
+            var len = text.Length;
+            var str = new StringBuilder();
+            while (idx < len)
+            {
+                if (idx > 0)
+                {
+                    str.Append('\n');
+                }
+                if (idx + line >= len)
+                {
+                    str.Append(text.Substring(idx));
+                }
+                else
+                {
+                    str.Append(text.Substring(idx, line));
+                }
+                idx += line;
+            }
+            return str.ToString();
+        }
+
+    }
+
+    public static class Extensions
+    {
+        /// <summary>
+        /// 从数组start开始到指定长度复制一份
+        /// </summary>
+        public static T[] sub<T>(this T[] arr, int start, int count)
+        {
+            T[] val = new T[count];
+            for (var i = 0; i < count; i++)
+            {
+                val[i] = arr[start + i];
+            }
+            return val;
+        }
+        static public void writeAll(this Stream stream, byte[] byts)
+        {
+            stream.Write(byts, 0, byts.Length);
+        }
+    }
+}

+ 5 - 0
src/Hotline.Share/Dtos/Identity/LoginDto.cs

@@ -13,6 +13,11 @@ namespace Hotline.Share.Dtos.Identity
         public string? MsgCode { get; set; }
         public string? MsgCode { get; set; }
     }
     }
 
 
+    public class HotlineLoginOldToNewDto
+    {
+        public string UserName { get; set; }
+    }
+
     public class LoginPageInfoDto
     public class LoginPageInfoDto
     {
     {
 
 

+ 2 - 0
src/Hotline/Orders/OrderDomainService.cs

@@ -205,6 +205,8 @@ public class OrderDomainService : IOrderDomainService, IScopeDependency
             return false;
             return false;
     }
     }
 
 
+   
+
     #region 平均派单
     #region 平均派单
     /// <summary>
     /// <summary>
     /// 平均派单
     /// 平均派单