فهرست منبع

1. 修复:更新sessioncontext无效的问题 2. 取消ISessionContext中的更新Session方法

xfe 1 ماه پیش
والد
کامیت
a51e58bff0

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

@@ -99,6 +99,7 @@ public class OrderController : BaseController
     private readonly ISystemDicDataCacheManager _sysDicDataCacheManager;
     private readonly ITypedCache<string> _typeCache;
     private readonly ISessionContext _sessionContext;
+    private readonly ISessionContextManager _sessionContextManager;
     private readonly IMapper _mapper;
     private readonly IMediator _mediator;
     private readonly IRepository<OrderPublish> _orderPublishRepository;
@@ -171,6 +172,7 @@ public class OrderController : BaseController
         IDefinitionDomainService definitionDomainService,
         ISystemDicDataCacheManager sysDicDataCacheManager,
         ISessionContext sessionContext,
+        ISessionContextManager sessionContextManager,
         IMapper mapper,
         IMediator mediator,
         IRepository<OrderPublish> orderPublishRepository,
@@ -245,6 +247,7 @@ public class OrderController : BaseController
         _definitionDomainService = definitionDomainService;
         _sysDicDataCacheManager = sysDicDataCacheManager;
         _sessionContext = sessionContext;
+        _sessionContextManager = sessionContextManager;
         _mapper = mapper;
         _mediator = mediator;
         _orderPublishRepository = orderPublishRepository;
@@ -4760,7 +4763,8 @@ public class OrderController : BaseController
                         if (unhandleStep.BusinessType is EBusinessType.Department)
                         {
                             var operater = await _orderApplication.GetHandlerRandomAsync(unhandleStep, cancellationToken);
-                            await _sessionContext.ChangeSessionAsync(operater.Id, cancellationToken);
+                            // await _sessionContext.ChangeSessionAsync(operater.Id, cancellationToken);
+                            await _sessionContextManager.ChangeSessionContextByUserIdAsync(operater.Id, cancellationToken);
                         }
 
                         var (_, _, _, nextSteps) = await _workflowDomainService.NextAsync(nextflowDto,
@@ -4868,7 +4872,8 @@ public class OrderController : BaseController
                 if (currentStep.BusinessType is EBusinessType.Department)
                 {
                     var operater = await _orderApplication.GetHandlerRandomAsync(currentStep, cancellation);
-                    await _sessionContext.ChangeSessionAsync(operater.Id, cancellation);
+                    // await _sessionContext.ChangeSessionAsync(operater.Id, cancellation);
+                    await _sessionContextManager.ChangeSessionContextByUserIdAsync(operater.Id,cancellation);
                 }
                 await HandleNextInMainAndSecondaryAsync(definition, orgs, nextDto, expiredTime, isAutoFillSummaryOpinion, cancellation);
             }

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

@@ -115,7 +115,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
     private readonly IRepository<User> _userRepository;
     private readonly IWorkflowApplication _workflowApplication;
     private readonly ICircularRecordDomainService _circularRecordDomainService;
-    private readonly ISessionContextCreator _sessionContextCreator;
+    private readonly ISessionContextManager _sessionContextManager;
 
     public OrderApplication(
         IOrderDomainService orderDomainService,
@@ -164,7 +164,8 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         IRepository<User> userRepository,
         IWorkflowApplication workflowApplication,
         ICircularRecordDomainService circularRecordDomainService,
-        ISessionContextCreator sessionContextCreator
+        ISessionContextCreator sessionContextCreator,
+        ISessionContextManager sessionContextManager
         )
     {
         _orderDomainService = orderDomainService;
@@ -213,7 +214,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         _userRepository = userRepository;
         _workflowApplication = workflowApplication;
         _circularRecordDomainService = circularRecordDomainService;
-        _sessionContextCreator = sessionContextCreator;
+        _sessionContextManager = sessionContextManager;
     }
 
     /// <summary>
@@ -714,7 +715,8 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         {
             case ESource.ProvinceStraight:
                 //SessionContextCreator.CreateSessionContext(_sessionContextProvider, "province");
-                await _sessionContextCreator.CreateSessionContextAsync("province", cancellationToken);
+                //await _sessionContextCreator.CreateSessionContextAsync("province", cancellationToken);
+                _sessionContextManager.ChangeSessionContext("province");
 
                 return await ReceiveOrderFromProvinceAsync(dto, dto.Files, cancellationToken);
             case ESource.Police110:

+ 58 - 44
src/Hotline.Application/Subscribers/DatasharingSubscriber.cs

@@ -82,7 +82,7 @@ namespace Hotline.Application.Subscribers
         private readonly ICalcExpireTime _expireTime;
         private readonly ISessionContextProvider _sessionContextProvider;
         private readonly ISessionContext _sessionContext;
-        private readonly ISessionContextCreator _sessionContextCreator;
+        private readonly ISessionContextManager _sessionContextManager;
         private readonly ILogger<DataSharingSubscriber> _logger;
 
         public DataSharingSubscriber(
@@ -120,6 +120,7 @@ namespace Hotline.Application.Subscribers
             ISessionContextProvider sessionContextProvider,
             ISessionContext sessionContext,
             ISessionContextCreator sessionContextCreator,
+            ISessionContextManager sessionContextManager,
             ILogger<DataSharingSubscriber> logger)
         {
             _orderSendBackRepository = orderSendBackRepository;
@@ -155,7 +156,7 @@ namespace Hotline.Application.Subscribers
             _expireTime = expireTime;
             _sessionContextProvider = sessionContextProvider;
             _sessionContext = sessionContext;
-            _sessionContextCreator = sessionContextCreator;
+            _sessionContextManager = sessionContextManager;
             _logger = logger;
         }
 
@@ -261,6 +262,10 @@ namespace Hotline.Application.Subscribers
             if (order is null)
                 throw new UserFriendlyException("未查询到工单");
 
+            //SessionContextCreator.CreateSessionContext(_sessionContextProvider, dto.Source);
+            // await _sessionContextCreator.CreateSessionContextAsync(dto.Source, cancellationToken);
+            _sessionContextManager.ChangeSessionContext(dto.Source);
+
             OrderRevoke orderRevoke = new()
             {
                 OrderId = order.Id,
@@ -288,22 +293,22 @@ namespace Hotline.Application.Subscribers
             if (_appOptions.Value.IsZiGong)
             {
                 //expiredTimeConfig = await _expireTime.CalcExpiredTime(DateTime.Now, order.StartTime.Value, EFlowDirection.CenterToCenter, order.Adapt<OrderTimeClacInfo>());
-                expiredTimeConfig = await _expireTime.CalcExpiredTime(DateTime.Now, DateTime.Now, EFlowDirection.CenterToCenter, order.Adapt<OrderTimeClacInfo>());
+                expiredTimeConfig = await _expireTime.CalcExpiredTime(DateTime.Now, DateTime.Now, EFlowDirection.CenterToCenter,
+                    order.Adapt<OrderTimeClacInfo>());
             }
             else
             {
                 //期满时间
                 //expiredTimeConfig = _timeLimitDomainService.CalcExpiredTime(DateTime.Now, EFlowDirection.CenterToOrg, order.AcceptTypeCode);
                 //expiredTimeConfig = await _expireTime.CalcExpiredTime(DateTime.Now, order.CenterToOrgTime.Value, EFlowDirection.CenterToOrg, order.Adapt<OrderTimeClacInfo>());
-                expiredTimeConfig = await _expireTime.CalcExpiredTime(DateTime.Now, DateTime.Now, EFlowDirection.CenterToOrg, order.Adapt<OrderTimeClacInfo>());
+                expiredTimeConfig =
+                    await _expireTime.CalcExpiredTime(DateTime.Now, DateTime.Now, EFlowDirection.CenterToOrg, order.Adapt<OrderTimeClacInfo>());
             }
 
             _mapper.Map(expiredTimeConfig, order);
             await _orderRepository.UpdateAsync(order, cancellationToken);
-            //宜宾需求:特提至中心(优先派单组无派单组节点就特提至坐席),由派单员归档
-            //SessionContextCreator.CreateSessionContext(_sessionContextProvider, dto.Source);
-            await _sessionContextCreator.CreateSessionContextAsync(dto.Source, cancellationToken);
 
+            //宜宾需求:特提至中心(优先派单组无派单组节点就特提至坐席),由派单员归档
             if (string.IsNullOrEmpty(order?.WorkflowId))
             {
                 var startDto = new StartWorkflowDto
@@ -512,20 +517,16 @@ namespace Hotline.Application.Subscribers
         [CapSubscribe(Hotline.Share.Mq.EventNames.SharingOrderScreen)]
         public async Task RecOrderScreenResultAsync(ProvinceSendScreenResultDto dto, CancellationToken cancellationToken)
         {
-            //var order = await _orderRepository.Queryable()
-            //    .Where(x => x.ProvinceNo == dto.ProvinceScreenResult!.CaseSerial)
-            //    .FirstAsync(cancellationToken);
-            //var orderScreen = await _orderScreenRepository.Queryable()
-            //    .Where(x => x.OrderId == order.Id && x.Status == Share.Enums.Order.EScreenStatus.Approval)
-            //    .FirstAsync(cancellationToken);
-
             var orderScreen = await _orderScreenRepository.Queryable()
-                .Where(x => x.Order.ReceiveProvinceNo == dto.ProvinceScreenResult.CaseSerial &&
-                            x.Status == EScreenStatus.Approval)
-                .FirstAsync(cancellationToken);
+                .FirstAsync(x => x.Order.ReceiveProvinceNo == dto.ProvinceScreenResult.CaseSerial &&
+                                 x.Status == EScreenStatus.Approval, cancellationToken);
+            if (orderScreen is null)
+                throw new UserFriendlyException($"收到ds推送:{EventNames.SharingOrderScreen}, 未查询到甄别数据, 省交办编号:{dto.ProvinceScreenResult.CaseSerial}");
 
             //SessionContextCreator.CreateSessionContext(_sessionContextProvider, dto.Source);
-            await _sessionContextCreator.CreateSessionContextAsync(dto.Source, cancellationToken);
+            // await _sessionContextCreator.CreateSessionContextAsync(dto.Source, cancellationToken);
+            _sessionContextManager.ChangeSessionContext(dto.Source);
+
 
             if (int.Parse(dto.ProvinceScreenResult.AuditResult) == 0)
             {
@@ -538,10 +539,12 @@ namespace Hotline.Application.Subscribers
             if (int.Parse(dto.ProvinceScreenResult.AuditResult) > 0)
             {
                 await _workflowApplication.HandleToEndAsync(orderScreen.WorkflowId, dto.ProvinceScreenResult.AuditOpinion, null,
-                 reviewResult: int.Parse(dto.ProvinceScreenResult.AuditResult) == 1
-                    ? Share.Enums.FlowEngine.EReviewResult.Approval
-                    : Share.Enums.FlowEngine.EReviewResult.Failed, cancellationToken: cancellationToken);
-                orderScreen.Status = int.Parse(dto.ProvinceScreenResult.AuditResult) == 1 ? Share.Enums.Order.EScreenStatus.End : Share.Enums.Order.EScreenStatus.Refuse;
+                    reviewResult: int.Parse(dto.ProvinceScreenResult.AuditResult) == 1
+                        ? Share.Enums.FlowEngine.EReviewResult.Approval
+                        : Share.Enums.FlowEngine.EReviewResult.Failed, cancellationToken: cancellationToken);
+                orderScreen.Status = int.Parse(dto.ProvinceScreenResult.AuditResult) == 1
+                    ? Share.Enums.Order.EScreenStatus.End
+                    : Share.Enums.Order.EScreenStatus.Refuse;
                 //if (orderScreen.Status == Share.Enums.Order.EScreenStatus.End)
                 //{
                 //    var visitDetail = await _orderVisitedDetailRepository.GetAsync(orderScreen.VisitDetailId, cancellationToken);
@@ -567,15 +570,20 @@ namespace Hotline.Application.Subscribers
                 {
                     var visitDetails = await _orderVisitedDetailRepository.Queryable().Where(x => x.OrderVisit.OrderId == orderScreen.OrderId &&
                                                                                                   x.VisitTarget == EVisitTarget.Org && (
-                                                                                                      SqlFunc.JsonField(x.OrgProcessingResults, "Key") ==
+                                                                                                      SqlFunc.JsonField(x.OrgProcessingResults,
+                                                                                                          "Key") ==
                                                                                                       "1" ||
-                                                                                                      SqlFunc.JsonField(x.OrgProcessingResults, "Key") ==
+                                                                                                      SqlFunc.JsonField(x.OrgProcessingResults,
+                                                                                                          "Key") ==
                                                                                                       "2" ||
-                                                                                                      SqlFunc.JsonField(x.OrgHandledAttitude, "Key") ==
+                                                                                                      SqlFunc.JsonField(x.OrgHandledAttitude,
+                                                                                                          "Key") ==
                                                                                                       "1" ||
-                                                                                                      SqlFunc.JsonField(x.OrgHandledAttitude, "Key") ==
+                                                                                                      SqlFunc.JsonField(x.OrgHandledAttitude,
+                                                                                                          "Key") ==
                                                                                                       "2"
-                                                                                                  ) && x.Id != orderScreen.VisitDetailId).ToListAsync();
+                                                                                                  ) && x.Id != orderScreen.VisitDetailId)
+                        .ToListAsync();
                     foreach (var visitDetail in visitDetails)
                     {
                         if (visitDetail != null)
@@ -797,6 +805,10 @@ namespace Hotline.Application.Subscribers
             var order = await _orderRepository.GetAsync(x => x.ReceiveProvinceNo == dto.No, cancellationToken);
             if (order != null)
             {
+                //SessionContextCreator.CreateSessionContext(_sessionContextProvider, dto.Source);
+                // await _sessionContextCreator.CreateSessionContextAsync(dto.Source, cancellationToken);
+                _sessionContextManager.ChangeSessionContext(dto.Source);
+
                 //查询延期
                 var orderDelay = await _orderDelayRepository
                     .GetAsync(x => x.OrderId == order.Id && x.DelayState == EDelayState.Examining, cancellationToken);
@@ -811,14 +823,8 @@ namespace Hotline.Application.Subscribers
                         orderDelay.FileJson = await _fileRepository.AddFileAsync(dto.Files, orderDelay.Id, orderDelay.WorkflowId, cancellationToken);
                     await _orderDelayRepository.UpdateAsync(orderDelay, cancellationToken);
 
-                    //var setting = _systemSettingCacheManager.GetSetting(SettingConstants.CityBaseConfiguration)?.SettingValue[0];
-                    //CityBaseConfiguration cityBase = JsonConvert.DeserializeObject<CityBaseConfiguration>(setting);
-
-                    //SessionContextCreator.CreateSessionContext(_sessionContextProvider, dto.Source);
-                    await _sessionContextCreator.CreateSessionContextAsync(dto.Source, cancellationToken);
-
                     await _workflowApplication.HandleToEndAsync(orderDelay.WorkflowId, dto.Opinion, dto.Files,
-                      reviewResult: dto.IsPass ? EReviewResult.Approval : EReviewResult.Failed, cancellationToken: cancellationToken);
+                        reviewResult: dto.IsPass ? EReviewResult.Approval : EReviewResult.Failed, cancellationToken: cancellationToken);
                 }
             }
         }
@@ -826,11 +832,9 @@ namespace Hotline.Application.Subscribers
         /// <summary>
         /// 接收工单处理结果
         /// </summary>
-        [CapSubscribe(EventNames.SharingOrderReultSend)]
+        [CapSubscribe(EventNames.SharingOrderReultSend, Group = "debug")]
         public async Task RecOrderResultAsync(OrderResultDto dto, CancellationToken cancellationToken)
         {
-            //SessionContextCreator.CreateSessionContext(_sessionContextProvider, dto.Source);
-            await _sessionContextCreator.CreateSessionContextAsync(dto.Source, cancellationToken);
             var order = await _orderRepository.Queryable().Includes(x => x.OrderExtension).FirstAsync(x => x.Id == dto.OrderId, cancellationToken);
             if (order is null)
                 throw new UserFriendlyException($"无效工单编号, orderId: {dto.OrderId}");
@@ -846,6 +850,10 @@ namespace Hotline.Application.Subscribers
             //var setting = _systemSettingCacheManager.GetSetting(SettingConstants.CityBaseConfiguration)?.SettingValue[0];
             //CityBaseConfiguration cityBase = JsonConvert.DeserializeObject<CityBaseConfiguration>(setting);
 
+            //SessionContextCreator.CreateSessionContext(_sessionContextProvider, dto.Source);
+            //await _sessionContextCreator.CreateSessionContextAsync(dto.Source, cancellationToken);
+            _sessionContextManager.ChangeSessionContext(dto.Source);
+
             switch (dto.FinishType)
             {
                 case "0":
@@ -855,14 +863,16 @@ namespace Hotline.Application.Subscribers
                     if (_appOptions.Value.IsZiGong)
                     {
                         expiredTimeConfig =
-                            await _expireTime.CalcExpiredTime(DateTime.Now, DateTime.Now, EFlowDirection.CenterToCenter, order.Adapt<OrderTimeClacInfo>());
+                            await _expireTime.CalcExpiredTime(DateTime.Now, DateTime.Now, EFlowDirection.CenterToCenter,
+                                order.Adapt<OrderTimeClacInfo>());
                     }
                     else
                     {
                         //期满时间
                         //expiredTimeConfig = _timeLimitDomainService.CalcExpiredTime(DateTime.Now, EFlowDirection.CenterToOrg, order.AcceptTypeCode);
                         expiredTimeConfig =
-                            await _expireTime.CalcExpiredTime(DateTime.Now, DateTime.Now, EFlowDirection.CenterToOrg, order.Adapt<OrderTimeClacInfo>());
+                            await _expireTime.CalcExpiredTime(DateTime.Now, DateTime.Now, EFlowDirection.CenterToOrg,
+                                order.Adapt<OrderTimeClacInfo>());
                     }
 
                     _mapper.Map(expiredTimeConfig, order);
@@ -878,9 +888,9 @@ namespace Hotline.Application.Subscribers
 
                     var handleMode = order.OrderExtension is null ? EHandleMode.Recall : EHandleMode.ProvinceHandlePrevious;
 
-
                     var (isPaiDan, workflow) = await _workflowDomainService.RecallToCenterFirstToSendAsync(order.WorkflowId, dto.Opinion,
-                        order.Status >= EOrderStatus.Filed, order.ExpiredTime, EHandleMode.ProvinceHandlePrevious, cancellationToken: cancellationToken);
+                        order.Status >= EOrderStatus.Filed, order.ExpiredTime, EHandleMode.ProvinceHandlePrevious,
+                        cancellationToken: cancellationToken);
 
                     order.Status = isPaiDan ? EOrderStatus.Special : EOrderStatus.WaitForAccept;
                     order.CurrentStepName = workflow.CurrentStepName;
@@ -912,13 +922,16 @@ namespace Hotline.Application.Subscribers
                         {
                             order.ActualOpinion += dto.Opinion;
                         }
+
                         order.FileOpinion = order.ActualOpinion;
-                        await _orderRepository.Updateable(order).UpdateColumns(d => d.ActualOpinion).UpdateColumns(d => d.FileOpinion).ExecuteCommandAsync(cancellationToken);
+                        await _orderRepository.Updateable(order).UpdateColumns(d => d.ActualOpinion).UpdateColumns(d => d.FileOpinion)
+                            .ExecuteCommandAsync(cancellationToken);
                         await _workflowDomainService.AppendFileOpinionAsync(order.WorkflowId, dto.Opinion, dto.Files, cancellationToken);
                     }
                     else
                     {
-                        await _workflowApplication.HandleToEndAsync(order.WorkflowId, dto.Opinion, dto.Files, expiredTime: order.ExpiredTime, cancellationToken: cancellationToken);
+                        await _workflowApplication.HandleToEndAsync(order.WorkflowId, dto.Opinion, dto.Files, expiredTime: order.ExpiredTime,
+                            cancellationToken: cancellationToken);
                     }
 
                     break;
@@ -1300,7 +1313,8 @@ namespace Hotline.Application.Subscribers
                         orderData.SourceChannelCode = "QT";
 
                         var orderDto = _mapper.Map<OrderDto>(orderData);
-                        await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineOrderExpiredTimeUpdate, orderDto, cancellationToken: cancellationToken);
+                        await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineOrderExpiredTimeUpdate, orderDto,
+                            cancellationToken: cancellationToken);
                     }
                 }
             }

+ 112 - 0
src/Hotline/Authentications/SessionContextManager.cs

@@ -0,0 +1,112 @@
+using System.Security.Claims;
+using Hotline.Caching.Interfaces;
+using Hotline.Configurations;
+using Hotline.Identity.Accounts;
+using Hotline.Settings;
+using IdentityModel;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.DependencyInjection;
+using Newtonsoft.Json;
+using XF.Domain.Authentications;
+using XF.Domain.Dependency;
+using XF.Domain.Exceptions;
+using XF.Domain.Repository;
+
+namespace Hotline.Authentications;
+
+public interface ISessionContextManager
+{
+    Task ChangeSessionContextByUserIdAsync(string userId, CancellationToken cancellation);
+
+    /// <summary>
+    /// 依据source创建对应Session
+    /// </summary>
+    void ChangeSessionContext(string source);
+}
+
+public class SessionContextManager : ISessionContextManager, IScopeDependency
+{
+    private readonly IServiceProvider _serviceProvider;
+    private readonly ISystemSettingCacheManager _systemSettingCacheManager;
+
+    public SessionContextManager(
+        IServiceProvider serviceProvider,
+        ISystemSettingCacheManager systemSettingCacheManager)
+    {
+        _serviceProvider = serviceProvider;
+        _systemSettingCacheManager = systemSettingCacheManager;
+    }
+
+    public async Task ChangeSessionContextByUserIdAsync(string userId, CancellationToken cancellation)
+    {
+        var httpContextAccessor = _serviceProvider.GetRequiredService<IHttpContextAccessor>();
+        if (httpContextAccessor.HttpContext == null)
+            throw new UserFriendlyException("current httpContext is null");
+        var accountRepository = _serviceProvider.GetService<IRepository<Account>>();
+
+        var account = await accountRepository.Queryable()
+            .Includes(d => d.User, x => x.Organization)
+            .Includes(d => d.Roles)
+            .FirstAsync(d => d.Id == userId, cancellation);
+
+        List<Claim> userClaims =
+        [
+            new(JwtClaimTypes.Subject, account.Id),
+            new(JwtClaimTypes.PhoneNumber, account.PhoneNo ?? string.Empty),
+            new(ClaimTypes.NameIdentifier, account.Id),
+            new(AppClaimTypes.UserDisplayName, account.Name),
+            new(AppClaimTypes.DepartmentId, account.User?.OrgId ?? string.Empty),
+            new(AppClaimTypes.DepartmentIsCenter, account.User?.Organization?.IsCenter.ToString() ?? string.Empty),
+            new(AppClaimTypes.DepartmentName, account.User?.Organization?.Name ?? string.Empty),
+            new(AppClaimTypes.DepartmentAreaCode, account.User?.Organization?.AreaCode ?? string.Empty),
+            new(AppClaimTypes.DepartmentAreaName, account.User?.Organization?.AreaName ?? string.Empty),
+            new(AppClaimTypes.DepartmentLevel, account.User?.Organization?.Level.ToString() ?? string.Empty),
+            new(AppClaimTypes.AreaId, account.User?.OrgId?.GetHigherOrgId() ?? string.Empty),
+        ];
+        userClaims.AddRange(account.Roles.Select(d => new Claim(JwtClaimTypes.Role, d.Name)));
+        httpContextAccessor.HttpContext.User = new ClaimsPrincipal(new ClaimsIdentity(userClaims));
+    }
+
+    /// <summary>
+    /// 依据source创建对应Session
+    /// </summary>
+    public void ChangeSessionContext(string source)
+    {
+        var setting = _systemSettingCacheManager.GetSetting(SettingConstants.CityBaseConfiguration)?.SettingValue[0];
+        var cityBase = JsonConvert.DeserializeObject<CityBaseConfiguration>(setting);
+        DefaultCityBaseConfiguration config = source switch
+        {
+            "province" => cityBase.CityProvince,
+            "110" => cityBase.PublicSecurity,
+            "yb-enterprise" => cityBase.CityEnterprise,
+            "zzpt" => cityBase.ComprehensiveTreatment,
+            _ => throw new ArgumentOutOfRangeException(nameof(source), source, null)
+        };
+
+        ChangeSession(config.UserId, config.UserName, config.OrgId, config.OrgName, config.OrgId.CalcOrgLevel());
+    }
+
+    #region private
+
+    private void ChangeSession(string userId, string username, string orgId, string orgname, int orgLevel)
+    {
+        var httpContextAccessor = _serviceProvider.GetRequiredService<IHttpContextAccessor>();
+        if (httpContextAccessor.HttpContext == null)
+            throw new UserFriendlyException("current httpContext is null");
+
+        List<Claim> userClaims =
+        [
+            new(JwtClaimTypes.Subject, userId),
+            new(ClaimTypes.NameIdentifier, userId),
+            new(AppClaimTypes.UserDisplayName, username),
+            new(AppClaimTypes.DepartmentId, orgId ?? string.Empty),
+            new(AppClaimTypes.DepartmentIsCenter, orgId?.IsCenter().ToString() ?? string.Empty),
+            new(AppClaimTypes.DepartmentName, orgname ?? string.Empty),
+            new(AppClaimTypes.DepartmentLevel, orgLevel.ToString() ?? string.Empty),
+            new(AppClaimTypes.AreaId, orgId?.GetHigherOrgId() ?? string.Empty),
+        ];
+        httpContextAccessor.HttpContext.User = new ClaimsPrincipal(new ClaimsIdentity(userClaims));
+    }
+
+    #endregion
+}

+ 74 - 17
src/XF.Domain/Authentications/DefaultSessionContext.cs

@@ -17,12 +17,17 @@ namespace XF.Domain.Authentications
         {
             _contextAccessor = httpContextAccessor;
             _sessionContextProvider = serviceProvider.GetService<ISessionContextProvider>();
+            _contextAccessor.HttpContext ??= new DefaultHttpContext();
         }
 
         /// <summary>
         /// Id of current tenant or null for host
         /// </summary>
-        public string? UserId { get => _contextAccessor.HttpContext?.User.FindFirstValue(ClaimTypes.NameIdentifier); init { } }
+        public string? UserId
+        {
+            get => _contextAccessor.HttpContext?.User.FindFirstValue(ClaimTypes.NameIdentifier);
+            init { }
+        }
 
         /// <summary>
         /// Id of current user or throw Exception for guest
@@ -30,9 +35,17 @@ namespace XF.Domain.Authentications
         /// <exception cref="AuthenticationException"></exception>
         public string RequiredUserId => UserId ?? throw new ArgumentNullException();
 
-        public string? UserName { get => _contextAccessor.HttpContext?.User.FindFirstValue(AppClaimTypes.UserDisplayName); init { } }
+        public string? UserName
+        {
+            get => _contextAccessor.HttpContext?.User.FindFirstValue(AppClaimTypes.UserDisplayName);
+            init { }
+        }
 
-        public string? Phone { get => _contextAccessor.HttpContext?.User.FindFirstValue(JwtClaimTypes.PhoneNumber); init { } }
+        public string? Phone
+        {
+            get => _contextAccessor.HttpContext?.User.FindFirstValue(JwtClaimTypes.PhoneNumber);
+            init { }
+        }
 
         /// <summary>
         /// Roles
@@ -40,36 +53,83 @@ namespace XF.Domain.Authentications
         /// 在 JwtBearerHandler 处理 JWT 令牌时,它会使用 Microsoft.AspNetCore.Authentication.JwtBearer 自动转换 Claim 名称。
         /// </summary>
         // public string[] Roles { get => _contextAccessor.HttpContext?.User.Claims.Where(d => d.Type == JwtClaimTypes.Role).Select(d => d.Value).ToArray(); init { } }
-        public string[] Roles { get => _contextAccessor.HttpContext?.User.Claims.Where(d => d.Type == ClaimTypes.Role).Select(d => d.Value).ToArray(); init { } }
+        public string[] Roles
+        {
+            get => _contextAccessor.HttpContext?.User.Claims.Where(d => d.Type == ClaimTypes.Role).Select(d => d.Value).ToArray();
+            init { }
+        }
+
+        public string? OrgId
+        {
+            get => _contextAccessor.HttpContext?.User.FindFirstValue(AppClaimTypes.DepartmentId);
+            init { }
+        }
+
+        public string? OrgName
+        {
+            get => _contextAccessor.HttpContext?.User.FindFirstValue(AppClaimTypes.DepartmentName);
+            init { }
+        }
+
+        public int OrgLevel
+        {
+            get => _contextAccessor.HttpContext?.User.FindIntValue(AppClaimTypes.DepartmentLevel) ?? 0;
+            init { }
+        }
 
-        public string? OrgId { get => _contextAccessor.HttpContext?.User.FindFirstValue(AppClaimTypes.DepartmentId); init { } }
-        public string? OrgName { get => _contextAccessor.HttpContext?.User.FindFirstValue(AppClaimTypes.DepartmentName); init { } }
+        public string? OrgAreaCode
+        {
+            get => _contextAccessor.HttpContext?.User.FindFirstValue(AppClaimTypes.DepartmentAreaCode);
+            init { }
+        }
 
-        public int OrgLevel { get => _contextAccessor.HttpContext?.User.FindIntValue(AppClaimTypes.DepartmentLevel) ?? 0; init { } }
-        public string? OrgAreaCode { get => _contextAccessor.HttpContext?.User.FindFirstValue(AppClaimTypes.DepartmentAreaCode); init { } }
-        public bool OrgIsCenter { get => _contextAccessor.HttpContext?.User.FindBoolValue(AppClaimTypes.DepartmentIsCenter) ?? false; init { } }
+        public bool OrgIsCenter
+        {
+            get => _contextAccessor.HttpContext?.User.FindBoolValue(AppClaimTypes.DepartmentIsCenter) ?? false;
+            init { }
+        }
 
         /// <summary>
         /// 部门行政区划名称
         /// </summary>
-        public string? OrgAreaName { get => _contextAccessor.HttpContext?.User.FindFirstValue(AppClaimTypes.DepartmentAreaName); init { } }
+        public string? OrgAreaName
+        {
+            get => _contextAccessor.HttpContext?.User.FindFirstValue(AppClaimTypes.DepartmentAreaName);
+            init { }
+        }
 
-        public string? AreaId { get => _contextAccessor.HttpContext?.User.FindFirstValue(AppClaimTypes.AreaId); init { } }
+        public string? AreaId
+        {
+            get => _contextAccessor.HttpContext?.User.FindFirstValue(AppClaimTypes.AreaId);
+            init { }
+        }
 
         public string RequiredOrgId => OrgId ?? throw new ArgumentNullException();
 
-        public string? ClientId { get => _contextAccessor.HttpContext?.User.FindFirstValue(JwtClaimTypes.ClientId); init { } }
+        public string? ClientId
+        {
+            get => _contextAccessor.HttpContext?.User.FindFirstValue(JwtClaimTypes.ClientId);
+            init { }
+        }
 
         /// <summary>
         /// 工号
         /// </summary>
-        public string? StaffNo { get => _contextAccessor.HttpContext?.User.FindFirstValue(AppClaimTypes.StaffNo); init { } }
+        public string? StaffNo
+        {
+            get => _contextAccessor.HttpContext?.User.FindFirstValue(AppClaimTypes.StaffNo);
+            init { }
+        }
 
         /// <summary>
         /// 第三方平台用户唯一标识
         /// 例如: 微信的OpenId
         /// </summary>
-        public string? OpenId { get => _contextAccessor.HttpContext?.User.FindFirstValue(AppClaimTypes.OpenId); init { } }
+        public string? OpenId
+        {
+            get => _contextAccessor.HttpContext?.User.FindFirstValue(AppClaimTypes.OpenId);
+            init { }
+        }
 
         public void ChangeSession(string id)
         {
@@ -78,9 +138,6 @@ namespace XF.Domain.Authentications
 
         public async Task ChangeSessionAsync(string userId, CancellationToken cancellation) =>
             await _sessionContextProvider.ChangeSessionByUserIdAsync(userId, cancellation);
-
-        public void ChangeSession(string userId, string username, string orgId, string orgname, int orgLevel) => 
-            _sessionContextProvider.ChangeSession(userId, username, orgId, orgname, orgLevel);
     }
 
     public static class ClaimsPrincipalExtensions

+ 1 - 2
src/XF.Domain/Authentications/ISessionContext.cs

@@ -54,7 +54,6 @@ public interface ISessionContext
 
     void ChangeSession(string id);
 
-    Task ChangeSessionAsync(string userId, CancellationToken cancellation);
+    // Task ChangeSessionAsync(string userId, CancellationToken cancellation);
 
-    void ChangeSession(string userId, string username, string orgId, string orgname, int orgLevel);
 }