xf vor 1 Jahr
Ursprung
Commit
25381e0e41

+ 0 - 4
.editorconfig

@@ -1,4 +0,0 @@
-[*.cs]
-
-# CS8625: 无法将 null 字面量转换为非 null 的引用类型。
-dotnet_diagnostic.CS8625.severity = none

+ 0 - 5
Hotline.sln

@@ -41,11 +41,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wex.Sdk", "src\Wex.Sdk\Wex.
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hotline.Wex", "src\Hotline.Wex\Hotline.Wex.csproj", "{40B6FBEC-0524-430C-8B28-22229681D0F8}"
 EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{5A1F7142-179E-47F7-A7C2-F73C6C3E68C4}"
-	ProjectSection(SolutionItems) = preProject
-		.editorconfig = .editorconfig
-	EndProjectSection
-EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU

+ 66 - 3
src/Hotline.Api/Controllers/OrgController.cs

@@ -14,19 +14,22 @@ namespace Hotline.Api.Controllers
     /// <summary>
     /// 组织架构相关接口
     /// </summary>
-    public class OrgController: BaseController
+    public class OrgController : BaseController
     {
         private readonly ISystemOrganizeRepository _systemOrganizeRepository;
         private readonly ISystemDomainService _systemDomainService;
+        private readonly ISystemAreaDomainService _areaDomainService;
         private readonly IMapper _mapper;
 
         public OrgController(
             ISystemOrganizeRepository systemOrganizeRepository,
             ISystemDomainService systemDomainService,
+            ISystemAreaDomainService areaDomainService,
             IMapper mapper)
         {
             _systemOrganizeRepository = systemOrganizeRepository;
             _systemDomainService = systemDomainService;
+            _areaDomainService = areaDomainService;
             _mapper = mapper;
         }
 
@@ -49,6 +52,7 @@ namespace Hotline.Api.Controllers
         /// <returns></returns>
         [Permission(EPermission.AddOrg)]
         [HttpPost("add-org")]
+        [Obsolete]
         public async Task AddOrg([FromBody] AddOrgDto dto)
         {
             var org = _mapper.Map<SystemOrganize>(dto);
@@ -60,7 +64,28 @@ namespace Hotline.Api.Controllers
 
             //处理编码
             org.OrgCode = await _systemDomainService.GenerateNewOrgCodeAsync(dto.ParentId, HttpContext.RequestAborted);
-            
+
+            org.InitOrgLevel();
+            await _systemOrganizeRepository.AddAsync(org, HttpContext.RequestAborted);
+        }
+
+        /// <summary>
+        /// 新增组织架构
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [Permission(EPermission.AddOrg)]
+        [HttpPost]
+        public async Task Add([FromBody] AddOrgDto dto)
+        {
+            var exists = await _systemOrganizeRepository.AnyAsync(
+                d => d.OrgName == dto.OrgName || d.OrgShortName == dto.OrgShortName, HttpContext.RequestAborted);
+            if (exists)
+                throw UserFriendlyException.SameMessage("名称或简称已存在");
+
+            var org = _mapper.Map<SystemOrganize>(dto);
+
+            org.OrgCode = await _systemDomainService.GenerateNewOrgCodeAsync(dto.ParentId, HttpContext.RequestAborted);
             org.InitOrgLevel();
             await _systemOrganizeRepository.AddAsync(org, HttpContext.RequestAborted);
         }
@@ -72,7 +97,8 @@ namespace Hotline.Api.Controllers
         /// <returns></returns>
         [Permission(EPermission.UpdateOrg)]
         [HttpPost("update-org")]
-        public async Task UpdateOrg([FromBody]UpdateOrgDto dto)
+        [Obsolete]
+        public async Task UpdateOrg([FromBody] UpdateOrgDto dto)
         {
             var org = await _systemOrganizeRepository.GetAsync(dto.Id, HttpContext.RequestAborted);
             if (org is null)
@@ -89,6 +115,30 @@ namespace Hotline.Api.Controllers
             await _systemOrganizeRepository.UpdateAsync(org, HttpContext.RequestAborted);
         }
 
+        /// <summary>
+        /// 修改组织架构(不支持修改上级)
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [Permission(EPermission.UpdateOrg)]
+        [HttpPut]
+        public async Task Update([FromBody] UpdateOrgDto dto)
+        {
+            var org = await _systemOrganizeRepository.GetAsync(dto.Id, HttpContext.RequestAborted);
+            if (org is null)
+                throw UserFriendlyException.SameMessage("无效组织架构");
+            if (org.Id == dto.ParentId)
+                throw UserFriendlyException.SameMessage("父级不能为自己");
+
+            var exists = await _systemOrganizeRepository.AnyAsync(
+                d => d.OrgName == dto.OrgName || d.OrgShortName == dto.OrgShortName, HttpContext.RequestAborted);
+            if (exists)
+                throw UserFriendlyException.SameMessage("名称或简称已存在");
+
+            _mapper.Map(dto, org);
+            await _systemOrganizeRepository.UpdateAsync(org, HttpContext.RequestAborted);
+        }
+
         /// <summary>
         /// 获取组织
         /// </summary>
@@ -120,6 +170,7 @@ namespace Hotline.Api.Controllers
         /// </summary>
         /// <returns></returns>
         [HttpGet("base-data-add")]
+        [Obsolete]
         public async Task<object> BaseDataAdd()
         {
             var rep = new
@@ -128,5 +179,17 @@ namespace Hotline.Api.Controllers
             };
             return rep;
         }
+
+        /// <summary>
+        /// 部门页面基础数据
+        /// </summary>
+        /// <returns></returns>
+        [HttpGet("base-data")]
+        public async Task<dynamic> BaseData() =>
+            new
+            {
+                OrgTypeOptions = EnumExts.GetDescriptions<EOrgType>(),
+                Areas = await _areaDomainService.GetAreaTree()
+            };
     }
 }

+ 23 - 0
src/Hotline.Application.Contracts/Validators/Orgs/AddOrgDtoValidator.cs

@@ -0,0 +1,23 @@
+using FluentValidation;
+using Hotline.Share.Dtos.Org;
+
+namespace Hotline.Application.Contracts.Validators.Orgs;
+
+public class AddOrgDtoValidator : AbstractValidator<AddOrgDto>
+{
+    public AddOrgDtoValidator()
+    {
+        RuleFor(d => d.OrgName).NotEmpty();
+        RuleFor(d => d.OrgShortName).NotEmpty();
+        RuleFor(d => d.AreaCode).NotEmpty();
+        RuleFor(d => d.AreaName).NotEmpty();
+    }
+}
+
+public class UpdateOrgDtoValidator : AbstractValidator<UpdateOrgDto>
+{
+    public UpdateOrgDtoValidator()
+    {
+        Include(new AddOrgDtoValidator());
+    }
+}

+ 2 - 2
src/Hotline.Application/FlowEngine/WorkflowApplication.cs

@@ -234,7 +234,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
             case EHandlerType.OrgLevel:
                 //当前操作人所属部门的下级部门并且属于配置orgLevel的部门
                 var levels = stepDefine.HandlerClassifies.Select(d => d.Id).Select(d => int.Parse(d));
-                var levelOneOrg = _sessionContext.RequiredOrgCode.GetOrgUpper();
+                var levelOneOrg = _sessionContext.RequiredOrgCode.GetUpperOrgCode();
                 var orgs1 = await _organizeRepository.QueryAsync(d =>
                     d.IsEnable && d.OrgCode.StartsWith(levelOneOrg) &&
                     levels.Contains(d.OrgLevel));
@@ -243,7 +243,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
             case EHandlerType.OrgType:
                 var types = stepDefine.HandlerClassifies.Select(d => d.Id)
                     .Select(d => Enum.Parse<EOrgType>(d));
-                var levelOneOrg1 = _sessionContext.RequiredOrgCode.GetOrgUpper();
+                var levelOneOrg1 = _sessionContext.RequiredOrgCode.GetUpperOrgCode();
                 var org2 = await _organizeRepository.QueryAsync(d =>
                     d.IsEnable && d.OrgCode.StartsWith(levelOneOrg1) &&
                     types.Contains(d.OrgType));

+ 1 - 1
src/Hotline.Application/Handlers/FlowEngine/EndWorkflowHandler.cs

@@ -69,7 +69,7 @@ public class EndWorkflowHandler : INotificationHandler<EndWorkflowNotify>
                 break;
 
             case WorkflowModuleConsts.OrderManage:
-                var levelOneOrg = workflow.ActualHandleOrgCode.GetOrgUpper();
+                var levelOneOrg = workflow.ActualHandleOrgCode.GetUpperOrgCode();
                 var org = await _orgRepository.GetAsync(d => d.OrgCode == levelOneOrg, cancellationToken);
                 if (org is null)
                     throw new UserFriendlyException($"无效部门编码, levelOneOrg: {levelOneOrg}", "无效部门编码");

+ 17 - 1
src/Hotline.Application/Mappers/MapperConfigs.cs

@@ -6,10 +6,12 @@ using Hotline.Identity.Roles;
 using Hotline.KnowledgeBase;
 using Hotline.Orders;
 using Hotline.Push.FWMessage;
+using Hotline.Settings;
 using Hotline.Share.Dtos.CallCenter;
 using Hotline.Share.Dtos.FlowEngine;
 using Hotline.Share.Dtos.Knowledge;
 using Hotline.Share.Dtos.Order;
+using Hotline.Share.Dtos.Org;
 using Hotline.Share.Dtos.Push.FWMessage;
 using Hotline.Share.Dtos.Roles;
 using Hotline.Share.Dtos.Users;
@@ -19,7 +21,7 @@ using XF.Domain.Entities;
 
 namespace Hotline.Application.Mappers
 {
-    public class MapperConfigs : IRegister
+    public partial class MapperConfigs : IRegister
     {
         public void Register(TypeAdapterConfig config)
         {
@@ -27,6 +29,8 @@ namespace Hotline.Application.Mappers
                 .Ignore(d => d.Expired)
                 .AfterMapping((s, t) => t.InitExpired());
 
+            #region identity
+
             config.ForType<AddUserDto, User>()
                 .Map(d => d.Name, x => x.Name ?? x.UserName);
 
@@ -43,6 +47,8 @@ namespace Hotline.Application.Mappers
                 .Map(d => d.AccountIds, x => x.Accounts.Select(d => d.Id))
                 .Map(d => d.State, x => x.IsDeleted ? "已删除" : "正常");
 
+            #endregion
+
             #region workflow
 
             config.ForType<UpdateDefinitionDto, Definition>()
@@ -225,6 +231,16 @@ namespace Hotline.Application.Mappers
                .Map(d => d.SendOrganize, x => x.SystemOrganize.OrgName)
                .Map(d => d.OrderId, x => x.Order.Id)
                .Map(d => d.OrderNo, x => x.Order.No);
+
+            #region org
+
+            config.ForType<UpdateOrgDto, SystemOrganize>()
+                .Ignore(d => d.Id)
+                .Ignore(d => d.ParentId)
+                .Ignore(d => d.ParentName);
+
+            #endregion
         }
     }
+
 }

+ 19 - 11
src/Hotline.Repository.SqlSugar/Extensions/SqlSugarStartupExtensions.cs

@@ -246,8 +246,14 @@ namespace Hotline.Repository.SqlSugar.Extensions
                 //inset生效
                 if (entityInfo.PropertyName == "CreationTime" && entityInfo.OperationType == DataFilterType.InsertByObject)
                 {
-                    entityInfo.SetValue(DateTime.Now);//修改CreateTime字段
-                                                      //entityInfo有字段所有参数
+                    if (oldValue is DateTime createTime)
+                    {
+                        if (createTime == DateTime.MinValue)
+                        {
+                            entityInfo.SetValue(DateTime.Now);//修改CreateTime字段
+                                                              //entityInfo有字段所有参数
+                        }
+                    }
                 }
                 //update生效        
                 else if (entityInfo.PropertyName == "LastModificationTime" && entityInfo.OperationType == DataFilterType.UpdateByObject)
@@ -263,16 +269,18 @@ namespace Hotline.Repository.SqlSugar.Extensions
 
                 else if (entityInfo.EntityColumnInfo.IsPrimarykey
                          && entityInfo.EntityColumnInfo.PropertyName.ToLower() == "id"
-                         && entityInfo.OperationType == DataFilterType.InsertByObject) //通过主键保证只进一次事件
+                         && entityInfo.OperationType == DataFilterType.InsertByObject
+                         && oldValue is null) //通过主键保证只进一次事件
                 {
-                    var propertyId = entityInfo.EntityValue.GetType().GetProperty("Id");
-                    if (propertyId is not null)
-                    {
-                        var idValue = propertyId.GetValue(entityInfo.EntityValue);
-                        if (idValue is null)
-                            //这样每条记录就只执行一次 
-                            entityInfo.SetValue(SequentialStringGenerator.Create());
-                    }
+                    //var propertyId = entityInfo.EntityValue.GetType().GetProperty("Id");
+                    //if (propertyId is not null)
+                    //{
+                    //    var idValue = propertyId.GetValue(entityInfo.EntityValue);
+                    //    if (idValue is null)
+                    //        //这样每条记录就只执行一次 
+                    //        entityInfo.SetValue(SequentialStringGenerator.Create());
+                    //}
+                    entityInfo.SetValue(SequentialStringGenerator.Create());
 
                 }
             };

+ 1 - 0
src/Hotline.Repository.SqlSugar/System/SystemOrganizeRepository.cs

@@ -15,6 +15,7 @@ namespace Hotline.Repository.SqlSugar.System
         public async Task<IReadOnlyList<SystemOrganize>> GetOrgJson()
         {
             var list = await Db.Queryable<SystemOrganize>()
+                .OrderBy(d => d.OrgCode)
                 .ToTreeAsync(x => x.Children, it => it.ParentId, null);
             return list;
         }

+ 2 - 2
src/Hotline.Share/Dtos/Org/OrgDto.cs

@@ -18,12 +18,12 @@ namespace Hotline.Share.Dtos.Org
         /// <summary>
         /// 上级ID
         /// </summary>
-        public string ParentId { get; set; }
+        public string? ParentId { get; set; }
 
         /// <summary>
         /// 上级名称
         /// </summary>
-        public string ParentName { get; set; }
+        public string? ParentName { get; set; }
 
         /// <summary>
         /// 是否启用

+ 45 - 45
src/Hotline/Caching/Services/CallRecordManager.cs

@@ -1,47 +1,47 @@
-using Hotline.CallCenter.Calls;
-using Microsoft.EntityFrameworkCore.Metadata.Internal;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Hosting;
-using Microsoft.Extensions.Logging;
-using SqlSugar;
-
-namespace Hotline.Caching.Services
-{
-    public class CallRecordManager : BackgroundService
-    {
-        private readonly IServiceScopeFactory _serviceScopeFactory;
-
-        public CallRecordManager(IServiceScopeFactory serviceScopeFactory)
-        {
-            _serviceScopeFactory = serviceScopeFactory;
-        }
-
-
-
-        protected override Task ExecuteAsync(CancellationToken stoppingToken)
-        {
-            //20秒扫描一次威尔信数据库
-            //using var sc = _serviceScopeFactory.CreateScope();
-            //var _logger = sc.ServiceProvider.GetService<ILogger<CallRecordManager>>();
-            //var time = TimeSpan.FromSeconds(20);
-            //while(!stoppingToken.IsCancellationRequested)
-            //{
-            //    using var scope = _serviceScopeFactory.CreateScope();
-            //    var _wexCallRecordRepository = scope.ServiceProvider.GetService<IWexCallRecordRepository>();
-            //}
-            throw new NotImplementedException();
-        }
-
-
-        public class WexRecord
-        {
-            [SugarColumn(ColumnName = "CallID")]
-            public string CallId { get; set; }
-
-            [SugarColumn(ColumnName = "CallType")]
-            public string CallType { get; set; }
+//using Hotline.CallCenter.Calls;
+//using Microsoft.EntityFrameworkCore.Metadata.Internal;
+//using Microsoft.Extensions.DependencyInjection;
+//using Microsoft.Extensions.Hosting;
+//using Microsoft.Extensions.Logging;
+//using SqlSugar;
+
+//namespace Hotline.Caching.Services
+//{
+//    public class CallRecordManager : BackgroundService
+//    {
+//        private readonly IServiceScopeFactory _serviceScopeFactory;
+
+//        public CallRecordManager(IServiceScopeFactory serviceScopeFactory)
+//        {
+//            _serviceScopeFactory = serviceScopeFactory;
+//        }
+
+
+
+//        protected override Task ExecuteAsync(CancellationToken stoppingToken)
+//        {
+//            //20秒扫描一次威尔信数据库
+//            //using var sc = _serviceScopeFactory.CreateScope();
+//            //var _logger = sc.ServiceProvider.GetService<ILogger<CallRecordManager>>();
+//            //var time = TimeSpan.FromSeconds(20);
+//            //while(!stoppingToken.IsCancellationRequested)
+//            //{
+//            //    using var scope = _serviceScopeFactory.CreateScope();
+//            //    var _wexCallRecordRepository = scope.ServiceProvider.GetService<IWexCallRecordRepository>();
+//            //}
+//            throw new NotImplementedException();
+//        }
+
+
+//        public class WexRecord
+//        {
+//            [SugarColumn(ColumnName = "CallID")]
+//            public string CallId { get; set; }
+
+//            [SugarColumn(ColumnName = "CallType")]
+//            public string CallType { get; set; }
 
             
-        }
-    }
-}
+//        }
+//    }
+//}

+ 1 - 1
src/Hotline/Settings/ISystemDomainService.cs

@@ -24,7 +24,7 @@ namespace Hotline.Settings
         /// <param name="parentId"></param>
         /// <param name="cancellationToken"></param>
         /// <returns></returns>
-        Task<string> GenerateNewOrgCodeAsync(string parentId, CancellationToken cancellationToken);
+        Task<string> GenerateNewOrgCodeAsync(string? parentId, CancellationToken cancellationToken);
 
         /// <summary>
         /// 部门名称是否重复

+ 43 - 12
src/Hotline/Settings/SystemDomainService.cs

@@ -47,20 +47,51 @@ public class SystemDomainService : ISystemDomainService, IScopeDependency
     /// <param name="parentId"></param>
     /// <param name="cancellationToken"></param>
     /// <returns></returns>
-    public async Task<string> GenerateNewOrgCodeAsync(string parentId, CancellationToken cancellationToken)
+    public async Task<string> GenerateNewOrgCodeAsync(string? parentId, CancellationToken cancellationToken)
     {
-        var org = await _organizeRepository.GetAsync(parentId, cancellationToken);
-        if (org == null)
-            throw new UserFriendlyException("无效上级部门编码");
-        var count = await _organizeRepository.CountAsync(d => d.ParentId == parentId, cancellationToken);
-        var current = (count + 1).ToString("000");
-        var code = $"{org.OrgCode}{current}";
-        var exists = await _organizeRepository.AnyAsync(d => d.OrgCode == code, cancellationToken);
-        while (exists)
+        //部门等级
+        if (string.IsNullOrEmpty(parentId))
         {
-            return await GenerateNewOrgCodeAsync(parentId, cancellationToken);
+            //一级部门
+            var maxCode = await _organizeRepository.Queryable()
+                 .Where(d => d.OrgLevel == 1)
+                 .MaxAsync(d => d.OrgCode, cancellationToken);
+
+            if (!int.TryParse(maxCode.GetLastOrgCode(), out var max))
+                throw new UserFriendlyException("无效部门编码");
+            return (max + 1).ToString("000");
+        }
+        else
+        {
+            var parentOrg = await _organizeRepository.GetAsync(parentId, cancellationToken);
+            if (parentOrg == null)
+                throw new UserFriendlyException("无效上级部门编码");
+
+            var maxCode = await _organizeRepository.Queryable()
+                .Where(d => d.ParentId == parentId)
+                .MaxAsync(d => d.OrgCode, cancellationToken);
+
+            if (string.IsNullOrEmpty(maxCode))
+                return $"{parentOrg.OrgCode}001";
+
+            if (!int.TryParse(maxCode.GetLastOrgCode(), out var max))
+                throw new UserFriendlyException("无效部门编码");
+
+            return $"{parentOrg.OrgCode}{(max + 1):000}";
         }
-        return code;
+
+        //var org = await _organizeRepository.GetAsync(parentId, cancellationToken);
+        //if (org == null)
+        //    throw new UserFriendlyException("无效上级部门编码");
+        //var count = await _organizeRepository.CountAsync(d => d.ParentId == parentId, cancellationToken);
+        //var current = (count + 1).ToString("000");
+        //var code = $"{org.OrgCode}{current}";
+        //var exists = await _organizeRepository.AnyAsync(d => d.OrgCode == code, cancellationToken);
+        //while (exists)
+        //{
+        //    return await GenerateNewOrgCodeAsync(parentId, cancellationToken);
+        //}
+        //return code;
     }
 
     /// <summary>
@@ -68,7 +99,7 @@ public class SystemDomainService : ISystemDomainService, IScopeDependency
     /// </summary>
     /// <param name="orgname"></param>
     /// <returns></returns>
-    public async Task<int> IsNameRepeat(string orgname,CancellationToken cancellationToken)
+    public async Task<int> IsNameRepeat(string orgname, CancellationToken cancellationToken)
     {
         return await _organizeRepository.CountAsync(x => x.OrgName == orgname, cancellationToken);
     }

+ 22 - 11
src/Hotline/Settings/SystemOrganize.cs

@@ -9,7 +9,7 @@ namespace Hotline.Settings;
 
 [SugarIndex("unique_org_code", nameof(SystemOrganize.OrgCode), OrderByType.Desc, true)]
 [Description("组织架构")]
-public class SystemOrganize : CreationEntity
+public class SystemOrganize : CreationSoftDeleteEntity
 {
     /// <summary>
     /// 组织架构名称
@@ -82,32 +82,43 @@ public static class OrgExtensions
     {
         if (orgCode.Length % 3 != 0)
             throw new UserFriendlyException("非法部门Code");
-        return (orgCode.Length / 3) - 1;
+        //return (orgCode.Length / 3) - 1;
+        return orgCode.Length / 3;
     }
 
     public static bool CheckIfOrgLevelIs(this string orgCode, int orgLevel)
     {
-        //中心算做一级部门
-        if (orgLevel == 1 && orgCode == OrgSeedData.CallCenterCode)
-            return true;
+        ////中心算做一级部门
+        //if (orgLevel == 1 && orgCode == OrgSeedData.CallCenterCode)
+        //    return true;
 
         var level = orgCode.CalcOrgLevel();
         return level == orgLevel;
     }
 
     /// <summary>
-    /// 根据orgCode获取该部门所属对应级别的上级部门
+    /// 根据orgCode获取该部门所属对应级别的上级部门code
     /// </summary>
     /// <param name="orgCode"></param>
-    /// <param name="orgLevel"></param>
+    /// <param name="orgLevel">默认一级部门</param>
     /// <returns></returns>
-    public static string GetOrgUpper(this string orgCode, int orgLevel = 1)
+    public static string GetUpperOrgCode(this string orgCode, int orgLevel = 1)
     {
         if (string.IsNullOrEmpty(orgCode))
             throw UserFriendlyException.SameMessage("无效部门编码");
-        //中心算做一级部门
-        if (orgLevel == 1 && orgCode == OrgSeedData.CallCenterCode) return orgCode;
+        ////中心算做一级部门
+        //if (orgLevel == 1 && orgCode == OrgSeedData.CallCenterCode) return orgCode;
 
-        return orgCode.Substring(0, 3 * (orgLevel + 1));
+        return orgCode.Substring(0, 3 * orgLevel);
+    }
+
+    /// <summary>
+    /// 获取最末级部门对应编号(最末3位)
+    /// </summary>
+    public static string GetLastOrgCode(this string orgCode)
+    {
+        if (orgCode.Length < 3)
+            throw new UserFriendlyException("非法部门编码");
+        return orgCode.Substring(orgCode.Length - 3, 3);
     }
 }