<|start_filename|>src/EnterpriseContactDomain/Dtos/EmployeeCacheOutput.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Text;

namespace EnterpriseContact
{
    public class EmployeeCacheOutput
    {
        public Guid Id { get; set; }
        public string Name { get; set; }
        public string Number { get; set; }
        public Guid? UserId { get; set; }
        public string Mobile { get; set; }
        public int Gender { get; set; }
        //public string Avatar { get; set; }
    }
}


<|start_filename|>src/EnterpriseContactService/Consumers/MdmDataConsumer.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using AutoMapper;
using Base.Mdm.Org.MsgContracts;
using EFCore.BulkExtensions;
using EnterpriseContact.Entities;
using EnterpriseContactService.Entities;
using MassTransit;
using Microsoft.EntityFrameworkCore;

namespace EnterpriseContact.Consumers
{
    //TODO 在更新数据之后，需要发送事件，让其他地方处理诸如清除缓存等操作
    public class MdmDataConsumer : IConsumer<FullOrgData>
    {
        protected readonly IMapper _mapper;
        protected readonly DbContextOptions<ServiceDbContext> _dbOptions;

        public MdmDataConsumer(DbContextOptions<ServiceDbContext> dbOptions, IMapper mapper)
        {
            _dbOptions = dbOptions;
            _mapper = mapper;
        }

        public async Task Consume(ConsumeContext<FullOrgData> context)
        {
            //sort: department,position,employee
            var message = context.Message;
            if (message.Contacts == null ||
                message.Contacts.Count == 0 ||
                message.OrgUnits == null ||
                message.OrgUnits.Count == 0)
            {
                return;
            }

            if (!await this.EnsureHistoryChangedAsync(message.DataVersion, message.OrgUnits.Count, message.Contacts.Count))
            {
                return;
            }
            var departments = this._mapper.Map<List<Department>>(message.OrgUnits);
            var positions = departments.SelectMany(u => u.Positions).ToList();
            var employees = this._mapper.Map<List<Employee>>(message.Contacts);
            var employeePositions = employees.SelectMany(u => u.Positions).ToList();
            employeePositions = this.RemoveRepeatAndNull(employeePositions);
            using (var db = new ServiceDbContext(_dbOptions))
            {
                try
                {
                    await this.FillInManualDataAsync(db, departments, positions, employees, employeePositions);
                    await db.BulkInsertOrUpdateOrDeleteAsync(departments);
                    await db.BulkInsertOrUpdateOrDeleteAsync(positions);
                    await db.BulkInsertOrUpdateOrDeleteAsync(employees);
                    await db.BulkInsertOrUpdateOrDeleteAsync(employeePositions);
                    await db.SaveChangesAsync();

                    await db.MdmDataHistories.AddAsync(new MdmDataHistory()
                    {
                        HistoryVersion = message.DataVersion,
                        SyncTime = DateTimeOffset.Now,
                        Description = $"Departments:{message.OrgUnits.Count},Employeees:{message.Contacts.Count}"
                    });
                    await db.SaveChangesAsync();
                }
                catch (Exception ex)
                {
                    ServiceEventSource.Current.Message(ex.ToString());
                }

            }
        }

        private async Task FillInManualDataAsync(ServiceDbContext db, List<Department> departments, List<Position> positions, List<Employee> employees, List<EmployeePosition> employeePositions)
        {
            var manualDepartments = await db.Departments.Where(u => u.DataSourceType == DataSourceType.Manual).ToListAsync();
            var manualPositions = await db.Positions.Where(u => u.DataSourceType == DataSourceType.Manual).ToListAsync();
            var manualEmployees = await db.Employees.Where(u => u.DataSourceType == DataSourceType.Manual).ToListAsync();
            var manualEmployeePositions = await db.EmployeePositions.Where(u => u.DataSourceType == DataSourceType.Manual).ToListAsync();
            departments.AddRange(manualDepartments);
            positions.AddRange(manualPositions);
            employees.AddRange(manualEmployees);
            employeePositions.AddRange(manualEmployeePositions);

        }

        private List<EmployeePosition> RemoveRepeatAndNull(List<EmployeePosition> employeePositions)
        {
            var dic = new Dictionary<string, EmployeePosition>();
            foreach (var employeePosition in employeePositions)
            {
                if (employeePosition.PositionId == default) continue;
                var key = employeePosition.EmployeeId.ToString() + employeePosition.PositionId.ToString();
                if (!dic.ContainsKey(key))
                {
                    dic.Add(key, employeePosition);
                }
            }
            return dic.Select(u => u.Value).ToList();
        }

        public async Task<bool> EnsureHistoryChangedAsync(string currentVersion, int departmentCount, int employeeCount)
        {
            using (var db = new ServiceDbContext(this._dbOptions))
            {
                var latestHistory = await db.MdmDataHistories.OrderByDescending(u => u.SyncTime).FirstOrDefaultAsync();
                if (latestHistory == null || latestHistory.HistoryVersion != currentVersion)
                {
                    return true;
                }
                return false;
            }
        }
    }
}


<|start_filename|>test/ConfigMgmtUnitTest/Controllers/WorkbenchControllerInitDataTest.cs<|end_filename|>
﻿using Common.Services;
using ConfigMgmt;
using ConfigMgmt.Controllers;
using ConfigMgmt.Entities;
using FluentAssertions;
using Microsoft.AspNetCore.Mvc;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using NSubstitute;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnitTestCommon;

namespace ConfigMgmtUnitTest.Controllers
{
    [TestClass]
    public class WorkbenchControllerInitDataTest : ControllerTestBase
    {
        [TestMethod]
        public async Task TestAddAppEntrancesAsync()
        {
            var model = new List<AppEntrance>();

            var workbenchAppService = Substitute.For<IWorkbenchAppService>();
            workbenchAppService.AddOrUpdateEntrancesAsync(Arg.Is<AddEntrancesInput>(
                o => o.ClientId == Client.DefaultId && o.AppEntrances == model
                ))
                .Returns(Task.FromResult(true));

            var target = new WorkbenchController(
                workbenchAppService,
                Substitute.For<IClientAppService>(),
                Substitute.For<IBizSystemAppService>(),
                Substitute.For<IBadgeApiClient>(),
                Substitute.For<IMobileCodeSender>(),
                _ => Substitute.For<IUserSettingAppService>());
            target.ControllerContext = CreateMockContext();

            var result = await target.AddAppEntrancesAsync(model);
            result.Should().BeOfType<OkObjectResult>();
            var realResult = result.As<OkObjectResult>();
            realResult.Value.Should().Be(true);
        }

        [TestMethod]
        public async Task TestRemoveAppEntranceAsync()
        {
            var workbenchAppService = Substitute.For<IWorkbenchAppService>();
            workbenchAppService.RemoveEntranceAsync(Client.DefaultId, Arg.Any<Guid>())
                .Returns(Task.FromResult(true));

            var target = new WorkbenchController(
                workbenchAppService,
                Substitute.For<IClientAppService>(),
                Substitute.For<IBizSystemAppService>(),
                Substitute.For<IBadgeApiClient>(),
                Substitute.For<IMobileCodeSender>(),
                _ => Substitute.For<IUserSettingAppService>());
            target.ControllerContext = CreateMockContext();

            var result = await target.RemoveAppEntranceAsync(Guid.NewGuid());
            result.Should().BeOfType<OkObjectResult>();
            var realResult = result.As<OkObjectResult>();
            realResult.Value.Should().Be(true);
        }

        [TestMethod]
        public async Task TestUpdateAppEntranceAsync()
        {
            var model = new AppEntrance() { Id = Guid.NewGuid() };

            var workbenchAppService = Substitute.For<IWorkbenchAppService>();
            workbenchAppService.AddOrUpdateEntrancesAsync(Arg.Is<AddEntrancesInput>(
              o => o.ClientId == Client.DefaultId && o.AppEntrances[0] == model
              ))
              .Returns(Task.FromResult(true));

            var target = new WorkbenchController(
                workbenchAppService,
                Substitute.For<IClientAppService>(),
                Substitute.For<IBizSystemAppService>(),
                Substitute.For<IBadgeApiClient>(),
                Substitute.For<IMobileCodeSender>(),
                _ => Substitute.For<IUserSettingAppService>());
            target.ControllerContext = CreateMockContext();

            var result = await target.UpdateAppEntranceAsync(model);
            result.Should().BeOfType<OkObjectResult>();
            var realResult = result.As<OkObjectResult>();
            realResult.Value.Should().Be(true);
        }

        [TestMethod]
        public async Task TestGetAppEntrancesAsync()
        {
            var workbenchAppService = Substitute.For<IWorkbenchAppService>();
            workbenchAppService.GetAppEntrancesRawByClientIdAsync(Client.DefaultId)
              .Returns(Task.FromResult(new List<AppEntrance>(){
                  new AppEntrance { Id = new Guid("0459738c0233483cabb53295e3bae783") }
              }
              ));

            var target = new WorkbenchController(
                workbenchAppService,
                Substitute.For<IClientAppService>(),
                Substitute.For<IBizSystemAppService>(),
                Substitute.For<IBadgeApiClient>(),
                Substitute.For<IMobileCodeSender>(),
                _ => Substitute.For<IUserSettingAppService>());
            target.ControllerContext = CreateMockContext();

            var result = await target.GetAppEntrancesAsync();
            var lst = result.Value.As<List<AppEntrance>>();
            lst.Should().NotBeNullOrEmpty();
            lst[0].Id.Should().Be(new Guid("0459738c0233483cabb53295e3bae783"));
        }

        [TestMethod]
        public async Task TestAddOrUpdateTodoCenterBizSystemAsync()
        {
            var todoCenterAppService = Substitute.For<IBizSystemAppService>();
            todoCenterAppService.AddOrUpdateForTodoCenterAsync(Arg.Any<TodoCenterBizSystem>())
                .Returns(Task.FromResult(true));

            var target = new WorkbenchController(
                Substitute.For<IWorkbenchAppService>(),
                Substitute.For<IClientAppService>(),
                todoCenterAppService,
                Substitute.For<IBadgeApiClient>(),
                Substitute.For<IMobileCodeSender>(),
                _ => Substitute.For<IUserSettingAppService>());
            target.ControllerContext = CreateMockContext();

            var result = await target.AddOrUpdateTodoCenterBizSystemAsync(new TodoCenterBizSystem
            {
                Id = "aaa"
            });
            result.Should().BeOfType<OkObjectResult>();
            var realResult = result.As<OkObjectResult>();
            realResult.Value.Should().Be(true);
        }

        [TestMethod]
        public async Task TestRemoveTodoCenterBizSystemAsync()
        {
            var todoCenterAppService = Substitute.For<IBizSystemAppService>();
            todoCenterAppService.RemoveForTodoCenterAsync(Arg.Any<string>())
                .Returns(Task.FromResult(true));

            var target = new WorkbenchController(
                Substitute.For<IWorkbenchAppService>(),
                Substitute.For<IClientAppService>(),
                todoCenterAppService,
                Substitute.For<IBadgeApiClient>(),
                Substitute.For<IMobileCodeSender>(),
                _ => Substitute.For<IUserSettingAppService>());
            target.ControllerContext = CreateMockContext();

            var result = await target.RemoveTodoCenterBizSystemAsync("aaa");
            result.Should().BeOfType<OkObjectResult>();
            var realResult = result.As<OkObjectResult>();
            realResult.Value.Should().Be(true);
        }

        [TestMethod]
        public async Task TestGetTodoCenterBizSystemsAsync()
        {
            var model = new List<TodoCenterBizSystem>
            {
               new TodoCenterBizSystem
               {
                   Id="aaa"
               }
            };
            var todoCenterAppService = Substitute.For<IBizSystemAppService>();
            todoCenterAppService.GetAllForTodoCenterAsync()
                .Returns(Task.FromResult(model));

            var target = new WorkbenchController(
                Substitute.For<IWorkbenchAppService>(),
                Substitute.For<IClientAppService>(),
                todoCenterAppService,
                Substitute.For<IBadgeApiClient>(),
                Substitute.For<IMobileCodeSender>(),
                _ => Substitute.For<IUserSettingAppService>());
            target.ControllerContext = CreateMockContext();

            var result = await target.GetTodoCenterBizSystemsAsync();
            var lst = result.Value.As<List<TodoCenterBizSystem>>();
            lst.Should().NotBeNullOrEmpty();
            lst[0].Id.Should().Be("aaa");
        }
    }
}


<|start_filename|>src/UserConfigStateService/UserFavoriteAppService.cs<|end_filename|>
﻿using ConfigMgmt;
using Microsoft.ServiceFabric.Data;
using Microsoft.ServiceFabric.Data.Collections;
using ServiceFabricContrib;
using System;
using System.Collections.Generic;
using System.Fabric;
using System.Linq;
using System.Threading.Tasks;

namespace UserConfigStateService
{
    public class UserFavoriteAppService : StatefulRemotingService, IUserFavoriteAppService
    {
        private const string DictionaryName = "EmployeeFavs";

        public UserFavoriteAppService(StatefulServiceContext serviceContext, IReliableStateManager stateManager)
            : base(serviceContext, stateManager)
        {

        }

        public async Task<bool> AddEmployeeAsync(Guid userId, Guid employeeId)
        {
            var dictionary = await StateManager.GetOrAddAsync<IReliableDictionary<Guid, HashSet<Guid>>>(DictionaryName);
            using (var tx = StateManager.CreateTransaction())
            {
                var wrap = await dictionary.TryGetValueAsync(tx, userId);
                if (wrap.HasValue)
                {
                    var oldValue = new HashSet<Guid>(wrap.Value);
                    var result = wrap.Value.Add(employeeId);
                    if (result)
                    {
                        await dictionary.TryUpdateAsync(tx, userId, wrap.Value, oldValue);
                        await tx.CommitAsync();
                    }
                    return true;
                }
                else
                {
                    await dictionary.AddAsync(tx, userId, new HashSet<Guid>(new[] { employeeId }));
                    await tx.CommitAsync();
                    return true;
                }
            }
        }

        public async Task<bool> RemoveEmployeeAsync(Guid userId, Guid employeeId)
        {
            var dictionary = await StateManager.GetOrAddAsync<IReliableDictionary<Guid, HashSet<Guid>>>(DictionaryName);
            using (var tx = StateManager.CreateTransaction())
            {
                if (await dictionary.ContainsKeyAsync(tx, userId))
                {
                    var wrap = await dictionary.TryGetValueAsync(tx, userId);
                    if (wrap.HasValue)
                    {
                        var oldValue = new HashSet<Guid>(wrap.Value);
                        var employeeIds = wrap.Value;
                        if (!employeeIds.Contains(employeeId)) return true;
                        employeeIds.Remove(employeeId);
                        if (employeeIds.Count == 0)
                        {
                            var tryRemoveResult = await dictionary.TryRemoveAsync(tx, userId);
                            await tx.CommitAsync();
                            return true;
                        }
                        await dictionary.SetAsync(tx, userId, employeeIds);
                        await tx.CommitAsync();
                        return true;
                    }
                    return true;
                }
                return true;
            }
        }

        public async Task<Guid[]> GetEmployeesAsync(Guid userId)
        {
            var dictionary = await StateManager.GetOrAddAsync<IReliableDictionary<Guid, HashSet<Guid>>>(DictionaryName);
            using (var tx = StateManager.CreateTransaction())
            {
                var wrap = await dictionary.TryGetValueAsync(tx, userId);
                if (wrap.HasValue)
                {
                    return wrap.Value.ToArray();
                }
                return new Guid[0];
            }
        }

        public async Task<bool> IsFavoritedAsync(Guid userId, Guid employeeId)
        {
            var favs = await GetEmployeesAsync(userId);
            return favs.Contains(employeeId);
        }
    }
}


<|start_filename|>src/ApiCommon/Services/MobileCodeSender.cs<|end_filename|>
﻿using AppsOnSF.Common.BaseServices;
using Base.Csi.Sms.MsgContracts;
using MassTransit;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;

namespace Common.Services
{
    public class MobileCodeSender : IMobileCodeSender
    {
        private const string SimpleKeyValueServiceContainerName = "Eap.MobileCode";

        private readonly IBusControl _bus;
        private readonly ISimpleKeyValueService _simpleKeyValueService;

        public MobileCodeSender(IBusControl bus, ISimpleKeyValueService simpleKeyValueService)
        {
            _bus = bus;
            _simpleKeyValueService = simpleKeyValueService;
        }

        public async Task<bool> CheckAsync(string phoneNumber, string code)
        {
            var original = await _simpleKeyValueService.CheckAndGet(
                   SimpleKeyValueServiceContainerName, phoneNumber, TimeSpan.FromMinutes(5));
            return original == code;
        }

        public async Task<string> SendAsync(string phoneNumber)
        {
            var code = (new Random().Next(1000, 9999)).ToString();

            await _simpleKeyValueService.AddOrUpdate(
                     SimpleKeyValueServiceContainerName, phoneNumber, code);

            await _bus.Send<SendMobileCodeCommand>(new
            {
                PhoneNumbers = new string[] { phoneNumber },
                Code = code,
            });

            return code;
        }
    }
}


<|start_filename|>src/TodoCenterProxyApi/Controllers/BadgeController.cs<|end_filename|>
﻿using System;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ApiCommon.Extensions;
using AppComponent.ViewModels;
using Common.Extensions;
using TodoCenterProxyApi.Services;
using TodoCenterProxyApi.Extensions;

namespace TodoCenterProxyApi.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class BadgeController : ControllerBase
    {
        private readonly IEnumerable<ITodoService> _services;

        public BadgeController(IEnumerable<ITodoService> services)
        {
            _services = services;
        }

        // GET api/values
        [HttpGet]
        public async Task<ActionResult<int>> GetAsync()
        {
            var taskes = new List<Task<int>>();
            foreach (var service in _services)
            {
                taskes.Add(this.GetUnReadCountAsync(service));
            }
            await Task.WhenAll(taskes);
            var unread_count = taskes.Sum(u => u.Result);
            return unread_count;
        }

        private async Task<int> GetUnReadCountAsync(ITodoService todoService)
        {
            try
            {
                var doneList = await todoService.GetUnReadCountAsync(this.HttpContext.DecodeUserClaimsFromRequestHeaders());
                return doneList;
            }
            catch (Exception e)
            {
                ServiceEventSource.Current.Message("Get UnReadCount Faild,Reason:{0}", e.ToString());
                return 0;
            }
        }
    }
}


<|start_filename|>src/EnterpriseContactService/Entities/Position.cs<|end_filename|>
﻿using EnterpriseContactService.Entities;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EnterpriseContact.Entities
{
    public class Position
    {
        public Guid Id { get; set; }

        [Required]
        [StringLength(128)]
        public string Name { get; set; }

        public Guid DepartmentId { get; set; }

        public DataSourceType DataSourceType { get; set; }

        public virtual Department Department { get; set; }
    }
}


<|start_filename|>src/NotifyGatewayWeb/EventBus/BusHostedService.cs<|end_filename|>
﻿using MassTransit;
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace NotifyGatewayWeb.EventBus
{
    public class BusHostedService : IHostedService, IDisposable
    {
        private readonly IEnumerable<IBusControl> _buses;

        public BusHostedService(IEnumerable<IBusControl> buses)
        {
            _buses = buses;
        }

        public void Dispose()
        {
            if (_buses != null)
            {
                _buses.ForEach(o => o.Stop());
            }
        }

        public Task StartAsync(CancellationToken cancellationToken)
        {
            if (_buses != null)
            {
                var tasks = _buses.Select(o =>
                {
                    ServiceEventSource.Current.Message("EventBus Started: " + o.Address);
                    return o.StartAsync();
                });
                return Task.WhenAll(tasks);
            }
            return Task.CompletedTask;
        }

        public Task StopAsync(CancellationToken cancellationToken)
        {
            if (_buses != null)
            {
                var tasks = _buses.Select(o =>
                {
                    ServiceEventSource.Current.Message("EventBus Stoped: " + o.Address);
                    return o.StopAsync();
                });
                return Task.WhenAll(tasks);
            }
            return Task.CompletedTask;
        }
    }
}


<|start_filename|>test/GroupFileServiceTest/Services/ControlAppServiceTest.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using GroupFile;
using GroupFile.Dtos;
using GroupFile.Entities;
using GroupFile.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace GroupFileServiceTest.Services
{
    [TestClass]
    public class ControlAppServiceTest : TheGroupFileAppServiceTestBase
    {
        private const string Store_Md5 = "xxxxxxmd5xxxx";
        private const string Store_Location = "http://github.com";
        private const string File_Name = "学习强国";

        private readonly Guid File_Id = new Guid("02BA9FC9-FAF9-4944-B77F-A0FB69E613B0");
        private readonly Guid File_GroupId = new Guid("02BA9FC9-FAF9-4944-B77F-A0FB69E613B1");
        private readonly Guid Uploader_Id = new Guid("12BA9FC9-FAF9-4944-B77F-A0FB69E613B1");



        // delete
        //[TestMethod]
        //public async Task CheckFileExistAsync_Test()
        //{
        //    //Arrage:
        //    var service = await ArrageAsync();

        //    //Act:
        //    var b = await service.CheckFileExistAsync(Store_Md5);

        //    //Assert:
        //    Assert.IsTrue(b);
        //}

        /// <summary>
        /// 获取下载地址
        /// </summary>
        /// <returns></returns>
        //[TestMethod]
        //public async Task DownloadFileAsync_Test()
        //{
        //    //Arrage:
        //    var service = await ArrageAsync();

        //    //Act:
        //    var _location = await service.DownloadFileAsync(File_Id);
        //    Assert.AreEqual(Store_Location, _location);
        //}

        //[TestMethod]
        //public async Task GetFileItemAsync_Test()
        //{
        //    //Arrage:
        //    var service = await this.ArrageAsync();

        //    //Act:
        //    var fileItem = await service.GetFileItemAsync(File_Id);

        //    Assert.IsNotNull(fileItem);
        //    Assert.AreEqual(File_Name, fileItem.Name);
        //}

        //[TestMethod]
        //public async Task GetFileItemsAsync_Test()
        //{
        //    //Arrage:
        //    var service = await this.ArrageAsync();

        //    //Act:
        //    var files = await service.GetFileItemsAsync(File_GroupId, 1);

        //    Assert.IsNotNull(files);
        //    Assert.IsTrue(files.Count == 1);
        //    Assert.AreEqual(File_Name, files.First().Name);
        //}


        //[TestMethod]
        //public async Task SaveFileInfoAsync_Test()
        //{
        //    //Arrage:
        //    var (_, dbOptions) = OpenDb();
        //    var mapper = CreateMapper();
        //    var service = new ControlAppService(statelessServiceContext, dbOptions, mapper);

        //    var input = new FileInfoInput()
        //    {
        //        GroupId = File_GroupId,
        //        Location = Store_Location,
        //        MD5 = Store_Md5,
        //        Name = File_Name,
        //        Size = 40,
        //        UploaderId = Guid.NewGuid(),
        //    };
        //    await service.SaveFileInfoAsync(input);
        //}

        //delete
        //[TestMethod]
        //public async Task SecUploadFileAsync_Test()
        //{
        //    //Arrage:
        //    var services = await ArrageAsync();

        //    //Act:
        //    var fileItem = await services.UploadFileAsync(new UploadInput()
        //    {
        //        FileName = "NewSec",
        //        GroupId = File_GroupId,
        //        StoreId = Store_Md5
        //    }, Uploader_Id);


        //    Assert.IsNotNull(fileItem);
        //    Assert.AreEqual(Uploader_Id, fileItem.UploaderId);
        //    Assert.AreEqual("NewSec", fileItem.Name);
        //}



        //private async Task<ControlAppService> ArrageAsync()
        //{
        //    var (_, dbOptions) = OpenDb();
        //    var mapper = CreateMapper();
        //    var fileStore = new FileStore()
        //    {
        //        Id = Store_Md5,
        //        Items = new List<FileItem>()
        //        {
        //            new FileItem()
        //            {
        //                DownloadAmount = 0,
        //                GroupId = File_GroupId,
        //                Id = File_Id,
        //                Name = File_Name,
        //                StoreId = Store_Md5,
        //                UpdatedOn = DateTimeOffset.Now,
        //                UploaderId = Guid.NewGuid()
        //            }
        //        },
        //        Location = Store_Location,
        //        Size = 10
        //    };
        //    var service = new ControlAppService(statelessServiceContext, dbOptions, mapper);
        //    using (var db = new ServiceDbContext(dbOptions))
        //    {
        //        var s = db.FileStores.ToList();
        //        db.FileStores.Add(fileStore);
        //        await db.SaveChangesAsync();
        //    }

        //    return service;
        //}

        //[TestInitialize]
        //public void Cleanup()
        //{
        //    var (_, dbOptions) = OpenDb();
        //    using (var db = new ServiceDbContext(dbOptions))
        //    {
        //        var stores = db.FileStores.Include(u => u.Items).ToList();
        //        db.FileStores.RemoveRange(stores);
        //        db.SaveChanges();
        //    }

        //}
    }
}


<|start_filename|>src/AppComponentApi/ApiClients/TodoApiClient.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using ApiCommon.Extensions;
using Common.Services;
using AppComponent.ViewModels;
using ConfigMgmt;
using ConfigMgmt.Entities;
using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;
using Serilog;

namespace AppComponent
{
    public class TodoApiClient : ITodoApiClient
    {
        private readonly HttpClient _httpClient;
        private readonly IIdentityService _identityService;
        private readonly IHttpContextAccessor _httpContextAccessor;
        private readonly ILogger _logger;

        public TodoApiClient(HttpClient httpClient, IIdentityService identityService, IHttpContextAccessor httpContextAccessor)
        {
            _httpClient = httpClient;
            _identityService = identityService;
            _httpContextAccessor = httpContextAccessor;
            _logger = Log.ForContext<TodoApiClient>();
        }

        public async Task<List<TodoListVM>> GetDoneListAsync(ComponentConfig config)
        {
            return await this.GetAsync(config, ComponentDataSourceKeys.TodoDoneList);
        }

        /// <summary>
        /// get pending task 
        /// </summary>
        /// <param name="config"></param>
        /// <returns></returns>
        public async Task<List<TodoListVM>> GetPendingListAsync(ComponentConfig config)
        {
            return await this.GetAsync(config, ComponentDataSourceKeys.TodoPendingList);
        }

        private async Task<List<TodoListVM>> GetAsync(ComponentConfig config, string componentDataSouceKey)
        {
            var datasource = config.DataSources.Find(u => u.Key == componentDataSouceKey);
            if (datasource == null || string.IsNullOrEmpty(datasource.Value))
            {
                _logger.Warning(
                    "{@ComponentConfig} has not config PendingList|DoneList key Or DataSource Url has not Config",
                    config);

                return new List<TodoListVM>();
            }

            if (config.AuthType == ComponentDataSourceAuthType.ClientCredential)
            {
                var access_token =
                    await _identityService.GetClientCredentialAccessTokenAsync(config.AuthUsername, config.AuthPassword, "<PASSWORD>");
                _httpClient.SetBearerToken(access_token);
            }
            _httpClient.AddUserClaimsHeader(_httpContextAccessor.HttpContext);

            var response = await _httpClient.GetStringAsync(datasource.Value);
            var vm = JsonConvert.DeserializeObject<List<TodoListVM>>(response);
            return vm;
        }
    }
}


<|start_filename|>test/ConfigMgmtUnitTest/TheAppServiceTestBase.cs<|end_filename|>
﻿using AutoMapper;
using ConfigMgmt;
using UnitTestCommon;

namespace ConfigMgmtUnitTest
{
    public class TheAppServiceTestBase : AppServiceTestBase
    {
        protected static IMapper CreateMapper()
        {
            var config = new MapperConfiguration(cfg =>
            {
                cfg.AddProfile<MappingProfile>();
            });
            return config.CreateMapper();
        }
    }
}


<|start_filename|>src/EnterpriseContactApi/ViewModels/GroupEditVm.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Text;

namespace EnterpriseContact.ViewModels
{
    public class GroupEditVm
    {
        /// <summary>
        /// 在Update的时候如果为null，即跳过此属性的更新
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// 在Update的时候如果为null，即跳过此属性的更新
        /// </summary>
        public string Remark { get; set; }

        /// <summary>
        /// 要添加成员的员工Id的集合，在Create的时候设置这个集合
        /// </summary>
        public HashSet<Guid> AddingMemberIds { get; set; }

        /// <summary>
        /// 要添加成员的员工名称集合，和 <see cref="AddingMemberIds" /> 顺序保持一致
        /// </summary>
        public List<string> AddingMemberNames { get; set; }

        /// <summary>
        /// 要删除成员的员工Id的集合
        /// </summary>
        public HashSet<Guid> RemovingMemberIds { get; set; }

        /// <summary>
        ///  要删除成员的员工名称集合，和 <see cref="RemovingMemberIds"/> 顺序保持一致
        /// </summary>
        public List<string> RemovingMemberNames { get; set; }
    }
}


<|start_filename|>test/ConfigMgmtUnitTest/AppServices/WorkbenchAppServiceTest.cs<|end_filename|>
﻿using ConfigMgmt;
using ConfigMgmt.Entities;
using FluentAssertions;
using Microsoft.ServiceFabric.Data.Collections;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using NSubstitute;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using SystemConfigStateService;

namespace ConfigMgmtUnitTest.AppServices
{
    [TestClass]
    public class WorkbenchAppServiceTest : TheAppServiceTestBase
    {
        [TestMethod]
        public async Task AddOrUpdateEntrancesAsync()
        {
            var dictClients = await stateManager.GetOrAddAsync<IReliableDictionary2<string, Client>>(Service.DictionaryName_Client);
            var client = Client.Default();

            using (var tx = stateManager.CreateTransaction())
            {
                await dictClients.AddAsync(tx, client.Id, client);
                await tx.CommitAsync();
            }

            var target = new WorkbenchAppService(statefulServiceContext, stateManager, CreateMapper());
            var result = await target.AddOrUpdateEntrancesAsync(new ConfigMgmt.AddEntrancesInput
            {
                ClientId = client.Id,
                AppEntrances = new List<AppEntrance>
                {
                    new AppEntrance
                    {
                        Id=Guid.NewGuid(),
                        ClientId=client.Id,
                    },
                    new AppEntrance
                    {
                        Id=Guid.NewGuid(),
                        ClientId=client.Id,
                    }
                }
            });

            result.Should().BeTrue();
            Assert.AreEqual(2, client.AppEntranceIds.Count);

        }

        [TestMethod]
        public async Task GetAppEntrancesAsync()
        {
            var dictClients = await stateManager.GetOrAddAsync<IReliableDictionary2<string, Client>>(Service.DictionaryName_Client);
            var dictAppEntrances = await stateManager.GetOrAddAsync<IReliableDictionary2<Guid, AppEntrance>>(Service.DictionaryName_AppEntrance);
            var client = Client.Default();
            var entrances = new List<AppEntrance>();
            for (int i = 0; i < 3; i++)
            {
                entrances.Add(new AppEntrance
                {
                    Id = Guid.NewGuid(),
                    ClientId = client.Id,
                    Uris = new Dictionary<ClientPlatform, string>
                    {
                        [ClientPlatform.Android] = "http://android" + i
                    }
                });
            }
            for (int i = 0; i < 6; i++)
            {
                entrances.Add(new AppEntrance
                {
                    Id = Guid.NewGuid(),
                    ClientId = client.Id,
                    Uris = new Dictionary<ClientPlatform, string>
                    {
                        [ClientPlatform.Desktop] = "http://desktop" + i
                    }
                });
            }
            for (int i = 0; i < 9; i++)
            {
                entrances.Add(new AppEntrance
                {
                    Id = Guid.NewGuid(),
                    ClientId = client.Id,
                    Uris = new Dictionary<ClientPlatform, string>
                    {
                        [ClientPlatform.iOS] = "http://ios" + i
                    }
                });
            }
            for (int i = 0; i < 10; i++)
            {
                entrances.Add(new AppEntrance
                {
                    Id = Guid.NewGuid(),
                    ClientId = client.Id,
                    Uris = new Dictionary<ClientPlatform, string>
                    {
                        [ClientPlatform.Android] = "http://android_desktop_ios_" + i,
                        [ClientPlatform.Desktop] = "http://android_desktop_ios_" + i,
                        [ClientPlatform.iOS] = "http://android_desktop_ios_" + i
                    }
                });
            }

            using (var tx = stateManager.CreateTransaction())
            {
                foreach (var item in entrances)
                {
                    await dictAppEntrances.AddAsync(tx, item.Id, item);
                    client.AppEntranceIds.Add(item.Id);
                }
                client.WorkbenchConfigTimestamp = DateTimeOffset.UtcNow;
                await dictClients.AddAsync(tx, client.Id, client);
                await tx.CommitAsync();
            }

            var appService = new WorkbenchAppService(statefulServiceContext, stateManager, CreateMapper());
            var result = await appService.GetAppEntrancesAsync(new GetAppEntrancesInput
            {
                ClientId = client.Id,
                ClientPlatform = ClientPlatform.Android
            });
            Assert.AreEqual(13, result.Count);
            result = await appService.GetAppEntrancesAsync(new GetAppEntrancesInput
            {
                ClientId = client.Id,
                ClientPlatform = ClientPlatform.Desktop
            });
            Assert.AreEqual(16, result.Count);
            result = await appService.GetAppEntrancesAsync(new GetAppEntrancesInput
            {
                ClientId = client.Id,
                ClientPlatform = ClientPlatform.iOS
            });
            Assert.AreEqual(19, result.Count);
        }

        [TestMethod]
        public async Task GetAppEntrancesAsync_WhenRoleCheck()
        {
            var dictClients = await stateManager.GetOrAddAsync<IReliableDictionary2<string, Client>>(Service.DictionaryName_Client);
            var dictAppEntrances = await stateManager.GetOrAddAsync<IReliableDictionary2<Guid, AppEntrance>>(Service.DictionaryName_AppEntrance);
            var client = Client.Default();
            var entrances = new List<AppEntrance>();
            for (int i = 0; i < 3; i++)
            {
                entrances.Add(new AppEntrance
                {
                    Id = Guid.NewGuid(),
                    ClientId = client.Id,
                    Uris = new Dictionary<ClientPlatform, string>
                    {
                        [ClientPlatform.Android] = "http://android_desktop_ios_" + i,
                        [ClientPlatform.Desktop] = "http://android_desktop_ios_" + i,
                        [ClientPlatform.iOS] = "http://android_desktop_ios_" + i
                    }
                });
            }
            for (int i = 0; i < 6; i++)
            {
                entrances.Add(new AppEntrance
                {
                    Id = Guid.NewGuid(),
                    ClientId = client.Id,
                    Uris = new Dictionary<ClientPlatform, string>
                    {
                        [ClientPlatform.Android] = "http://android_desktop_ios_" + i,
                        [ClientPlatform.Desktop] = "http://android_desktop_ios_" + i,
                        [ClientPlatform.iOS] = "http://android_desktop_ios_" + i
                    },
                    NeedRoles = new[] { "ABC" }
                });
            }
            for (int i = 0; i < 9; i++)
            {
                entrances.Add(new AppEntrance
                {
                    Id = Guid.NewGuid(),
                    ClientId = client.Id,
                    Uris = new Dictionary<ClientPlatform, string>
                    {
                        [ClientPlatform.Android] = "http://android_desktop_ios_" + i,
                        [ClientPlatform.Desktop] = "http://android_desktop_ios_" + i,
                        [ClientPlatform.iOS] = "http://android_desktop_ios_" + i
                    },
                    NeedRoles = new[] { "ABC", "EFG" }
                });
            }

            using (var tx = stateManager.CreateTransaction())
            {
                foreach (var item in entrances)
                {
                    await dictAppEntrances.AddAsync(tx, item.Id, item);
                    client.AppEntranceIds.Add(item.Id);
                }
                client.WorkbenchConfigTimestamp = DateTimeOffset.UtcNow;
                await dictClients.AddAsync(tx, client.Id, client);
                await tx.CommitAsync();
            }

            var appService = new WorkbenchAppService(statefulServiceContext, stateManager, CreateMapper());
            //everyone
            var result = await appService.GetAppEntrancesAsync(new GetAppEntrancesInput
            {
                ClientId = client.Id,
                ClientPlatform = ClientPlatform.Android
            });
            Assert.AreEqual(3, result.Count);
            //everyone without right needroles
            result = await appService.GetAppEntrancesAsync(new GetAppEntrancesInput
            {
                ClientId = client.Id,
                ClientPlatform = ClientPlatform.Desktop,
                UserRoles = new List<string> { "EFG" }
            });
            Assert.AreEqual(3, result.Count);
            //someone with ABC
            result = await appService.GetAppEntrancesAsync(new GetAppEntrancesInput
            {
                ClientId = client.Id,
                ClientPlatform = ClientPlatform.Desktop,
                UserRoles = new List<string> { "ABC" }
            });
            Assert.AreEqual(9, result.Count);
            //someone with ABC and EFG
            result = await appService.GetAppEntrancesAsync(new GetAppEntrancesInput
            {
                ClientId = client.Id,
                ClientPlatform = ClientPlatform.iOS,
                UserRoles = new List<string> { "ABC", "EFG" }
            });
            Assert.AreEqual(18, result.Count);
        }

        [TestMethod]
        public async Task GetComponentConfigAsync_ReturnNull()
        {
            var dictClients = await stateManager.GetOrAddAsync<IReliableDictionary2<string, Client>>(Service.DictionaryName_Client);
            var dictAppEntrances = await stateManager.GetOrAddAsync<IReliableDictionary2<Guid, AppEntrance>>(Service.DictionaryName_AppEntrance);
            var client = Client.Default();
            var entrances = new List<AppEntrance>();
            for (int i = 0; i < 3; i++)
            {
                entrances.Add(new AppEntrance
                {
                    Id = Guid.NewGuid(),
                    ClientId = client.Id,
                    Uris = new Dictionary<ClientPlatform, string>
                    {
                        [ClientPlatform.Android] = "http://android" + i
                    }
                });
            }
            using (var tx = stateManager.CreateTransaction())
            {
                foreach (var item in entrances)
                {
                    await dictAppEntrances.AddAsync(tx, item.Id, item);
                    client.AppEntranceIds.Add(item.Id);
                }
                client.WorkbenchConfigTimestamp = DateTimeOffset.UtcNow;
                await dictClients.AddAsync(tx, client.Id, client);
                await tx.CommitAsync();
            }

            var appService = new WorkbenchAppService(statefulServiceContext, stateManager, CreateMapper());
            var result = await appService.GetComponentConfigAsync(Guid.NewGuid());
            Assert.IsNull(result);
            result = await appService.GetComponentConfigAsync(entrances[0].Id);
            Assert.IsNull(result);
        }

        [TestMethod]
        public async Task GetComponentConfigAsync()
        {
            var dictClients = await stateManager.GetOrAddAsync<IReliableDictionary2<string, Client>>(Service.DictionaryName_Client);
            var dictAppEntrances = await stateManager.GetOrAddAsync<IReliableDictionary2<Guid, AppEntrance>>(Service.DictionaryName_AppEntrance);
            var client = Client.Default();
            var entrances = new List<AppEntrance>();
            for (int i = 0; i < 3; i++)
            {
                entrances.Add(new AppEntrance
                {
                    Id = Guid.NewGuid(),
                    ClientId = client.Id,
                    Uris = new Dictionary<ClientPlatform, string>
                    {
                        [ClientPlatform.Android] = "http://android" + i
                    },
                    ComponentConfig = new ComponentConfig
                    {
                        AuthType = ComponentDataSourceAuthType.None
                    }
                });
            }
            using (var tx = stateManager.CreateTransaction())
            {
                foreach (var item in entrances)
                {
                    await dictAppEntrances.AddAsync(tx, item.Id, item);
                    client.AppEntranceIds.Add(item.Id);
                }
                client.WorkbenchConfigTimestamp = DateTimeOffset.UtcNow;
                await dictClients.AddAsync(tx, client.Id, client);
                await tx.CommitAsync();
            }

            var appService = new WorkbenchAppService(statefulServiceContext, stateManager, CreateMapper());
            var result = await appService.GetComponentConfigAsync(entrances[0].Id);
            Assert.IsNotNull(result);
            Assert.AreEqual(ComponentDataSourceAuthType.None, result.AuthType);
        }
    }
}


<|start_filename|>src/GroupFileService/ServiceDbContext.cs<|end_filename|>
﻿using GroupFile.Entities;
using Microsoft.EntityFrameworkCore;
using System;
using System.Configuration;

namespace GroupFile.Models
{
    public class ServiceDbContext : DbContext
    {
        public ServiceDbContext(DbContextOptions<ServiceDbContext> options):base(options)
        {
        }
        

        public DbSet<FileItem> FileItems { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<FileItem>()
                .HasIndex(o => o.GroupId);
        }
    }
}


<|start_filename|>src/AttachmentApi/Services/MinioAttachmentStoreService.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Attachment.Entities;
using Attachment.ViewModels;
using AppsOnSF.Common.Options;
using Microsoft.Extensions.Options;
using Minio;
using Serilog;

namespace Attachment.Services
{
    public class MinioAttachmentStoreService : IAttachmentStoreService
    {
        private readonly MinioOption _minioOption;
        private readonly MinioClient _client;
        private readonly ILogger _logger;

        public MinioAttachmentStoreService(IOptions<MinioOption> minioOptionAccessor)
        {
            _minioOption = minioOptionAccessor.Value;
            _client = new MinioClient(_minioOption.Endpoint, _minioOption.AccessKey, _minioOption.SecretKey);
            _logger = Log.ForContext<MinioAttachmentStoreService>();
        }

        public async Task FillMemoryStreamAsync(AttachmentItem dto, MemoryStream ms)
        {
            await _client.GetObjectAsync(_minioOption.BucketName, dto.Location,
                stream => stream.CopyTo(ms));
        }

        public async Task<string> StoreFileAsync(UploadVm model, DateTimeOffset now)
        {
            var fileName = model.File.FileName;
            var folder = $"attachments/{now.Year}/{now.Month}/{now.Day}/";
            var saveFileName = model.MD5 + Path.GetExtension(fileName);
            //attachments/2018/12/28/{id}.ext
            var objectName = folder + saveFileName;

            bool found = await _client.BucketExistsAsync(_minioOption.BucketName);
            if (!found)
            {
                await _client.MakeBucketAsync(_minioOption.BucketName);
            }
            using (var stream = model.File.OpenReadStream())
            {
                await _client.PutObjectAsync(_minioOption.BucketName, objectName, stream, model.File.Length);
            }
            return objectName;
        }

        public async Task TryDeleteFileAsync(string objectName)
        {
            try
            {
                await _client.RemoveObjectAsync(_minioOption.BucketName, objectName);
            }
            catch (Exception e)
            {
                _logger.Error(e, e.Message);
            }
        }
    }
}


<|start_filename|>src/InstantMessageApi/ViewModels/CreateP2PConversationVm.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Text;

namespace InstantMessage.ViewModels
{
    public class CreateP2PConversationVm
    {
        /// <summary>
        /// 发起方的Id(单点登录返回的Sub)
        /// </summary>
        public Guid SenderId { get; set; }
        /// <summary>
        /// 接收方的Id(单点登录返回的Sub)
        /// </summary>
        public Guid RecieverId { get; set; }
    }
}


<|start_filename|>test/ConversationServiceTest/AppServices/ConversationMsgAppServiceTest.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ConversationMsgStateService;
using InstantMessage;
using InstantMessage.Entities;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Notification;
using ServiceFabricContrib;

namespace ConversationServiceTest.AppServices
{
    [TestClass]
    public class ConversationMsgAppServiceTest : TheAppServiceTestBase
    {
        [TestMethod]
        public async Task SendMessageAsync_Test()
        {
            //Arrage:
            await InitDictDataAsync();

            //Act:
            var service = new ConversationMsgAppService(statefulServiceContext, stateManager);
            var msg = DefaultConversationMsg.DeepCopy();
            await service.SendMessageAsync(msg);

            //Assert:
            using (var tx = stateManager.CreateTransaction())
            {
                var msgValue = await MessageProccessQueue.TryDequeueAsync(tx);
                var msg2 = msgValue.Value;
                Assert.IsTrue(msgValue.HasValue);
                Assert.IsTrue(msg2.Content == msg.Content);
            }
        }

        [TestMethod]
        public async Task GetMessagesAsync_Test()
        {
            //Arrage:
            await InitDictDataAsync();

            var service = new ConversationMsgAppService(statefulServiceContext, stateManager);
            var msg = DefaultConversationMsg.DeepCopy();

            //Act:
            var gettedMessages = await service.GetMessagesAsync(new List<MessageNotifyDto>()
            {
                new MessageNotifyDto()
                {
                    LatestMsgId = Guid.Empty,
                    Target = NotifyTargetType.Conversation,
                    TargetId = msg.ConversationId.ToString(),
                    TargetCategory = (int) ConversationType.P2P,
                }
            });

            //Assert:
            //PS:P2PMessageIndexDictName 类型错误
            Assert.AreEqual(2, gettedMessages.Count);
            var targetMsg = gettedMessages.Find(o => o.Id == msg.Id);
            Assert.IsNotNull(targetMsg);
            Assert.AreEqual(msg.Content, targetMsg.Content);
            Assert.AreEqual(msg.SenderId, targetMsg.SenderId);
            Assert.AreEqual(msg.Type, targetMsg.Type);
        }

        [TestMethod]
        public async Task GetOldMessagesAsync_Test()
        {
            //Arrage:
            await InitDictDataAsync();

            var service = new ConversationMsgAppService(statefulServiceContext, stateManager);
            var msg2 = DefaultConversationMsg2.DeepCopy();
            var msg1 = DefaultConversationMsg.DeepCopy();

            //Act:
            var oldMeesage = await service.GetOldMessagesAsync(new GetOldMessagesInput()
            {
                Id = msg2.ConversationId,
                OldestMsgId = msg2.Id,
            });

            //Assert:
            Assert.AreEqual(1, oldMeesage.Count);
        }

        /// <summary>
        /// 归档群组消息
        /// </summary>
        /// <returns></returns>
        //[TestMethod]
        //public async Task ArchiveGroupMessagesAsync_Test()
        //{
        //    //Arrage:
        //    await InitDicAsync();
        //    var service = new ConversationMsgAppService(statefulServiceContext, stateManager);

        //    //Act:群组消息会话id
        //    var conversationId = Guid.NewGuid();
        //    await service.ArchiveGroupMessagesAsync(conversationId);

        //    //Assert:
        //    using (var tx = stateManager.CreateTransaction())
        //    {
        //        var value = await pendingArchiveQueue.TryDequeueAsync(tx);
        //        Assert.IsTrue(value.HasValue);
        //        Assert.AreEqual(conversationId, value.Value);
        //    }
        //}
    }
}


<|start_filename|>src/EnterpriseContactApi/ViewModels/GroupMemberListVm.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Text;

namespace EnterpriseContact.ViewModels
{

    public class GroupMemberListVm
    {
        public Guid EmployeeId { get; set; }

        public string EmployeeName { get; set; }

        public int Gender { get; set; }

        public bool IsOwner { get; set; }

        /// <summary>
        /// 员工所属主部门的名称，及其上级名称，Controller可以选择填充几级
        /// </summary>
        public List<string> DepartmentNames { get; set; }
    }
}


<|start_filename|>src/NotifyDomain/MsgContracts/SendMessageNotify.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Notification
{
    public class SendMessageNotify
    {
        public Guid UserId { get; set; }

        public List<MessageNotifyDto> Notifies { get; set; }
    }
}


<|start_filename|>src/AppComponentApi/ViewModels/TodoListVM.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Text;
using ConfigMgmt;

namespace AppComponent.ViewModels
{
    public class TodoListVM
    {
        public int Id { get; set; }

        /// <summary>
        /// 源系统
        /// </summary>
        public TODOSYSTEM OrginSystem { get; set; }

        /// <summary>
        /// 模块名称
        /// </summary>
        public string Module { get; set; }

        /// <summary>
        /// 用户标识（不限于单点登录UserId,IdCardNo)
        /// </summary>
        public string UserIdentity { get; set; }

        /// <summary>
        /// 这个作为用户在SSO里面的Id
        /// </summary>
        public string UserId { get; set; }

        /// <summary>
        /// 代办类型
        /// </summary>
        public string TodoType { get; set; }

        /// <summary>
        /// 代办标题
        /// </summary>
        public string Title { get; set; }

        /// <summary>
        /// 描述信息
        /// </summary>
        public string Description { get; set; }

        /// <summary>
        /// 代办所处的状态
        /// </summary>
        public string Status { get; set; }

        /// <summary>
        /// 紧急程度
        /// </summary>
        public string Urgent { get; set; }

        /// <summary>
        /// 移动平台接收的时间点
        /// </summary>
        public DateTimeOffset WriteTime { get; set; }

        public AppEntryAuthType AuthType { get; set; }

        /// <summary>
        /// 应用Id 下载使用
        /// </summary>
        public string AppId { get; set; } = "";

        public Dictionary<ClientPlatform, string> OpenUrls { get; set; }
    }

    public enum TODOSYSTEM
    {

        OA = 1,

        PROJECT_MANAGE_SYSTEM = 2,

        KINGDEE_SYSTEM = 3
    }
}


<|start_filename|>src/EnterpriseContactDomain/Services/IEmployeeCacheService.cs<|end_filename|>
﻿using EnterpriseContact;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;

namespace EnterpriseContact.Services
{
    public interface IEmployeeCacheService
    {
        Task<List<EmployeeCacheOutput>> ByNumberAsync(params string[] keys);

        Task<List<EmployeeCacheOutput>> ByUserIdAsync(params Guid[] keys);

        Task<List<EmployeeCacheOutput>> ByIdAsync(params Guid[] keys);

        /// <summary>
        /// 清除映射数据的缓存，在EC同步到MDM的数据之后，需要通过事件消费者来调用此方法
        /// </summary>
        void ClearCache();
    }
}


<|start_filename|>src/TodoCenterProxyApi/Services/ITodoService.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using AppComponent.ViewModels;
using ConfigMgmt;
using ConfigMgmt.Entities;

namespace TodoCenterProxyApi.Services
{
    public interface ITodoService
    {
        /// <summary>
        /// 获取代办数量
        /// </summary>
        /// <param name="userIdentities"></param>
        /// <returns></returns>
        Task<IList<TodoListVM>> GetAsync(Dictionary<string, string> userIdentities);

        /// <summary>
        /// 获取待办的数量
        /// </summary>
        /// <param name="userIdentities"></param>
        /// <returns></returns>
        Task<int> GetUnReadCountAsync(Dictionary<string, string> userIdentities);

        /// <summary>
        /// 获取已完成代办信息
        /// </summary>
        /// <param name="userIdentities"></param>
        /// <returns></returns>
        Task<IList<TodoListVM>> GetCompleteAsync(Dictionary<string, string> userIdentities);
    }

    public abstract class TodoServiceBase
    {
        protected readonly IBizSystemAppService _bizSystemAppService;

        protected TodoServiceBase(IBizSystemAppService bizSystemAppService)
        {
            _bizSystemAppService = bizSystemAppService;
        }

        protected async Task<TodoCenterBizSystem> EnsureBizSystemConfigAsync(string bizSystemId)
        {
            var configs = await this._bizSystemAppService.GetAllForTodoCenterAsync();
            var config = configs.FirstOrDefault(u => u.Id == bizSystemId);
            if (config != null)
                return config;
            throw new Exception("requird TodoCenterApp Config:" + bizSystemId);
        }

        protected void EnsureUserIdentity(Dictionary<string, string> userIdentities, ref string idcardno)
        {
            if (userIdentities == null || userIdentities.Count == 0)
                throw new Exception("required userIdentityies!");
            idcardno = userIdentities.ContainsKey("idcard_number") ? userIdentities["idcard_number"] : "";
            if (string.IsNullOrEmpty(idcardno))
                throw new Exception("required user idcardno!");
        }
    }
}


<|start_filename|>src/EnterpriseContactApi/Controllers/DepartmentController.cs<|end_filename|>
﻿using Common;
using AutoMapper;
using EnterpriseContact.ViewModels;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
using System.Collections.Generic;
using System.Threading.Tasks;
using Serilog;

namespace EnterpriseContact.Controllers
{
    [Route("api/v1/department")]
    public class DepartmentController : TheBaseController
    {
        private readonly ILogger _logger;

        public DepartmentController(IMemoryCache cache, IMapper mapper, IDepartmentAppService departmentAppService, IPositionAppService positionAppService) 
            : base(cache, mapper, departmentAppService, positionAppService)
        {
            _logger = Log.ForContext<DepartmentController>();
        }

        [HttpGet("search")]
        public async Task<ActionResult<List<DepartmentSearchListVm>>> Search(string keyword)
        {
            try
            {
                await EnsureDepartmentListCacheAsync();

                var dto = await _departmentAppService.SearchByKeywordAsync(keyword);
                var data = new List<DepartmentSearchListVm>();
                foreach (var item in dto)
                {
                    var vm = _mapper.Map<DepartmentListOutput, DepartmentSearchListVm>(item, (opts) =>
                    {
                        opts.AfterMap((s, d) =>
                        {
                            if (s.ParentId.HasValue && _departmentListCache.ContainsKey(s.ParentId.Value))
                                d.ParentName = _departmentListCache[s.ParentId.Value].Name;
                        });
                    });
                    data.Add(vm);
                }

                return data;
            }
            catch (System.Exception ex)
            {
                return BadRequest(LogError(_logger, ex));
            }
        }
    }
}


<|start_filename|>src/TodoCenterProxyApi/Extensions/UriBulderExtension.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;

namespace TodoCenterProxyApi.Extensions
{
    public static class UriBulderExtension
    {
        public static Uri BuildQueryString(this UriBuilder builder, NameValueCollection query)
        {
            var collection = HttpUtility.ParseQueryString(string.Empty);
            foreach (var key in query.Cast<string>().Where(key => !string.IsNullOrEmpty(query[key])))
            {
                collection[key] = query[key];
            }
            builder.Query = collection.ToString();
            return builder.Uri;
        }
    }
}


<|start_filename|>src/InstantMessageDomain/Entities/ConversationMsg.cs<|end_filename|>
﻿using ServiceFabricContrib;
using System;
using System.Runtime.Serialization;

namespace InstantMessage.Entities
{
    [Serializable]
    [DataContract]
    public struct ConversationMsg
    {
        [DataMember]
        public Guid Id { get; set; }

        [DataMember]
        public Guid ConversationId { get; set; }

        [DataMember]
        public Guid SenderId { get; set; }

        [DataMember]
        public DateTimeOffset Time { get; set; }

        [DataMember]
        public string Content { get; set; }

        [DataMember]
        public ConversationMsgType Type { get; set; }

        public static ConversationMsg Create(Guid conversationId, Guid senderId, string content, ConversationMsgType type)
        {
            var msg = new ConversationMsg
            {
                Content = content,
                ConversationId = conversationId,
                Id = Guid.NewGuid(),
                SenderId = senderId,
                Time = DateTimeOffset.UtcNow,
                Type = type
            };
            return msg;
        }
    }
}

<|start_filename|>src/AppComponentApi/TheStartup.cs<|end_filename|>
﻿using Common;
using Microsoft.Extensions.DependencyInjection;
using System;

namespace AppComponent
{
    public static class TheStartup
    {
        public static void ConfigureServices(IServiceCollection services)
        {
            services.AddHttpClient<ITodoApiClient, TodoApiClient>();
        }
    }
}


<|start_filename|>src/SystemConfigStateService/WorkbenchAppService.cs<|end_filename|>
﻿using AutoMapper;
using ConfigMgmt;
using ConfigMgmt.Entities;
using Microsoft.ServiceFabric.Data;
using Microsoft.ServiceFabric.Data.Collections;
using ServiceFabricContrib;
using System;
using System.Collections.Generic;
using System.Fabric;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace SystemConfigStateService
{
    public class WorkbenchAppService : StatefulRemotingService, IWorkbenchAppService
    {
        private readonly IMapper _mapper;

        public WorkbenchAppService(StatefulServiceContext serviceContext, IReliableStateManager stateManager
            , IMapper mapper)
         : base(serviceContext, stateManager)
        {
            _mapper = mapper;
        }

        public async Task<bool> AddOrUpdateEntrancesAsync(AddEntrancesInput input)
        {
            if (input == null)
                throw new ArgumentNullException(nameof(input));

            if (string.IsNullOrEmpty(input.ClientId))
                throw new ArgumentNullException(nameof(input.ClientId));

            if (input.AppEntrances == null || input.AppEntrances.Count == 0)
                throw new ArgumentNullException(nameof(input.AppEntrances));

            foreach (var item in input.AppEntrances)
            {
                if (item.ClientId != input.ClientId)
                    throw new ArgumentException("所有AppEntrance的ClientId必须等于clientId", nameof(input.AppEntrances));
            }

            var dictClients = await StateManager.GetOrAddAsync<IReliableDictionary2<string, Client>>(Service.DictionaryName_Client);
            var dictAppEntrances = await StateManager.GetOrAddAsync<IReliableDictionary2<Guid, AppEntrance>>(Service.DictionaryName_AppEntrance);
            using (var tx = StateManager.CreateTransaction())
            {
                var clientWrap = await dictClients.TryGetValueAsync(tx, input.ClientId);
                if (!clientWrap.HasValue) return false;
                var client = clientWrap.Value;
                foreach (var item in input.AppEntrances)
                {
                    await dictAppEntrances.AddOrUpdateAsync(tx, item.Id, item, (k, o) => item);
                    if (!client.AppEntranceIds.Contains(item.Id))
                        client.AppEntranceIds.Add(item.Id);
                }
                await dictClients.TryUpdateAsync(tx, input.ClientId, client, clientWrap.Value);

                await tx.CommitAsync();
                return true;
            }
        }

        public async Task<List<AppEntranceDto>> GetAppEntrancesAsync(GetAppEntrancesInput input)
        {
            if (input == null)
                throw new ArgumentNullException(nameof(input));

            if (string.IsNullOrEmpty(input.ClientId))
                throw new ArgumentNullException(nameof(input.ClientId));

            var dictClients = await StateManager.GetOrAddAsync<IReliableDictionary2<string, Client>>(Service.DictionaryName_Client);
            var dictAppEntrances = await StateManager.GetOrAddAsync<IReliableDictionary2<Guid, AppEntrance>>(Service.DictionaryName_AppEntrance);
            var token = CancellationToken.None;
            var result = new List<AppEntranceDto>();
            using (var tx = StateManager.CreateTransaction())
            {
                var clientWrap = await dictClients.TryGetValueAsync(tx, input.ClientId);
                if (!clientWrap.HasValue)
                    throw new ArgumentOutOfRangeException("没有clientId的数据", nameof(input.ClientId));

                var client = clientWrap.Value;
                var enumerator = (await dictAppEntrances.CreateEnumerableAsync(tx,
                          o => client.AppEntranceIds.Contains(o), EnumerationMode.Unordered)).GetAsyncEnumerator();
                while (await enumerator.MoveNextAsync(token))
                {
                    var item = enumerator.Current.Value;
                    if (item.NeedRoles?.Length > 0)
                    {
                        //without any roles then continue
                        if (input.UserRoles == null || input.UserRoles.Count == 0) continue;
                        var hasNeedRole = true;
                        foreach (var role in item.NeedRoles)
                        {
                            //without this role then break
                            hasNeedRole = input.UserRoles.Contains(role);
                            if (!hasNeedRole) break;
                        }
                        if (!hasNeedRole) continue;
                    }
                    var dto = CheckPlatformThenAdd(input.ClientPlatform, result, item);
                }
            }
            return result;
        }

        private AppEntranceDto CheckPlatformThenAdd(ClientPlatform clientPlatform, List<AppEntranceDto> result, AppEntrance item)
        {
            if(item.IsFolder)
            {
                var dto = _mapper.Map<AppEntranceDto>(item);
                result.Add(dto);
                return dto;
            }
            if (item.Uris?.ContainsKey(clientPlatform) == true)
            {
                var dto = _mapper.Map<AppEntranceDto>(item);
                dto.Uri = item.Uris[clientPlatform];
                result.Add(dto);
                return dto;
            }
            return null;
        }

        public async Task<ComponentConfig> GetComponentConfigAsync(Guid entryId)
        {
            var dictAppEntrances = await StateManager.GetOrAddAsync<IReliableDictionary2<Guid, AppEntrance>>(Service.DictionaryName_AppEntrance);
            using (var tx = StateManager.CreateTransaction())
            {
                var itemWrap = await dictAppEntrances.TryGetValueAsync(tx, entryId);
                if (!itemWrap.HasValue) return null;
                return itemWrap.Value.ComponentConfig;
            }
        }

        public async Task<bool> RemoveEntranceAsync(string clientId, Guid entryId)
        {
            if (clientId == null)
                throw new ArgumentNullException(nameof(clientId));
            if (entryId == null)
                throw new ArgumentNullException(nameof(entryId));
            var dictClients = await StateManager.GetOrAddAsync<IReliableDictionary2<string, Client>>(Service.DictionaryName_Client);
            var dictAppEntrances = await StateManager.GetOrAddAsync<IReliableDictionary2<Guid, AppEntrance>>(Service.DictionaryName_AppEntrance);
            using (var tx = StateManager.CreateTransaction())
            {
                var clientWrap = await dictClients.TryGetValueAsync(tx, clientId);
                if (!clientWrap.HasValue) return false;
                var client = clientWrap.Value;
                if (client.AppEntranceIds.Contains(entryId))
                {
                    await dictAppEntrances.TryRemoveAsync(tx, entryId);
                    client.AppEntranceIds.Remove(entryId);
                }
                await dictClients.TryUpdateAsync(tx, clientId, client, clientWrap.Value);
                await tx.CommitAsync();
                return true;
            }
        }

        public async Task<List<AppEntrance>> GetAppEntrancesRawByClientIdAsync(string clientId)
        {
            if (string.IsNullOrEmpty(clientId))
                throw new ArgumentException("不能为空", nameof(clientId));

            var dictClients = await StateManager.GetOrAddAsync<IReliableDictionary2<string, Client>>(Service.DictionaryName_Client);
            var dictAppEntrances = await StateManager.GetOrAddAsync<IReliableDictionary2<Guid, AppEntrance>>(Service.DictionaryName_AppEntrance);
            var token = CancellationToken.None;
            using (var tx = StateManager.CreateTransaction())
            {
                var clientWrap = await dictClients.TryGetValueAsync(tx, clientId);
                if (!clientWrap.HasValue)
                    throw new ArgumentOutOfRangeException("没有clientId的数据", clientId);
                var client = clientWrap.Value;
                var lst = await dictAppEntrances.GetAllByIdsAsync(tx, client.AppEntranceIds, token);
                return lst.OrderBy(o => o.Order).ToList();
            }
        }
    }
}


<|start_filename|>test/EnterpriseContactUnitTest/Controllers/DepartmentControllerTest.cs<|end_filename|>
﻿using EnterpriseContact;
using EnterpriseContact.Controllers;
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using NSubstitute;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace EnterpriseContactUnitTest.Controllers
{
    [TestClass]
    public class DepartmentControllerTest : TheControllerTestBase
    {
        [TestMethod]
        public async Task TestSearch()
        {
            var dep0 = new DepartmentListOutput
            {
                Id = Guid.NewGuid(),
                Name = "sclq",
            };
            var dep1 = new DepartmentListOutput
            {
                Id = Guid.NewGuid(),
                Name = "sclq-jt",
                ParentId = dep0.Id,
            };
            var dep2 = new DepartmentListOutput
            {
                Id = Guid.NewGuid(),
                Name = "sclq-gs",
                ParentId = dep0.Id,
            };
            var dep3 = new DepartmentListOutput
            {
                Id = Guid.NewGuid(),
                Name = "sclq-jt-jg",
                ParentId = dep1.Id,
            };
            var departments = new List<DepartmentListOutput>(new[] { dep0, dep1, dep2, dep3 });

            var departmentAppService = Substitute.For<IDepartmentAppService>();
            departmentAppService.GetAllListAsync()
                .Returns(Task.FromResult(departments));
            var searchResult = departments.FindAll(o => o.Name.Contains("jt"));
            departmentAppService.SearchByKeywordAsync("jt")
                .Returns(Task.FromResult(searchResult));


            var target = new DepartmentController(
                CreateMemoryCache(),
                CreateMapper(),
                departmentAppService,
                Substitute.For<IPositionAppService>());

            var result = await target.Search("jt");

            var data = result.Value;
            data.Count.Should().Be(2);
            data[0].Id.Should().Be(dep1.Id);
            data[1].Id.Should().Be(dep3.Id);
        }
    }
}


<|start_filename|>src/ApiCommon/Extensions/ExceptionMiddlewareExtension.cs<|end_filename|>
﻿using Microsoft.AspNetCore.Builder;
using Serilog;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Http;
using System.Net;
using Common.ResponseModels;
using IdentityModel.Client;

namespace Common.Extensions
{
    public static class ExceptionMiddlewareExtension
    {
        public static void ConfigureExceptionHandler(this IApplicationBuilder app)
        {
            app.UseExceptionHandler(appError =>
            {
                appError.Run(async context =>
                {
                    context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
                    context.Response.ContentType = "application/json";

                    var contextFeature = context.Features.Get<IExceptionHandlerFeature>();
                    if (contextFeature != null)
                    {
                        Log.Error(contextFeature.Error,"Server Error. \r\n" +
                            "RequestPath: {RequestPath}. \r\n" +
                            "RequestQuery: {@RequestQuery}. ",
                            context.Request.Path.Value,
                            context.Request.Query);

                        await context.Response.WriteAsync(new ErrorDetails()
                        {
                            StatusCode = context.Response.StatusCode,
                            Message = contextFeature.Error.Message
                        }.ToString());
                    }
                });
            });
        }
    }
}


<|start_filename|>test/ConfigMgmtUnitTest/AppServices/UserFavoriteAppServiceTest.cs<|end_filename|>
﻿using Microsoft.VisualStudio.TestTools.UnitTesting;
using ServiceFabric.Mocks;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UserConfigStateService;

namespace ConfigMgmtUnitTest.AppServices
{
    [TestClass]
    public class UserFavoriteAppServiceTest:TheAppServiceTestBase
    {
        [TestMethod]
        public async Task TestAddEmployeeAsync_Correct()
        {
            var target = new UserFavoriteAppService(statefulServiceContext, stateManager);
            var userId = Guid.NewGuid();
            var empId = Guid.NewGuid();
            var result = await target.AddEmployeeAsync(userId, empId);
            var empIdFonud = (await target.GetEmployeesAsync(userId)).FirstOrDefault();
            Assert.AreEqual(empIdFonud, empId);
        }

        [TestMethod]
        public async Task TestAddEmployeeAsync_SameUsers()
        {
            var target = new UserFavoriteAppService(statefulServiceContext, stateManager);
            var userId = Guid.NewGuid();
            var empId = Guid.NewGuid();
            var result1 = await target.AddEmployeeAsync(userId, empId);
            var result2 = await target.AddEmployeeAsync(userId, empId);
            var addResult = await target.GetEmployeesAsync(userId);

            Assert.AreEqual(true, result1);
            Assert.AreEqual(true, result2);
            Assert.AreEqual(true, addResult != null);
            Assert.AreEqual(1, addResult.Count());
        }

        [TestMethod]
        public async Task TestAddEmployeeAsync_NotSameUsers()
        {
            var target = new UserFavoriteAppService(statefulServiceContext, stateManager);
            var userId = Guid.NewGuid();
            var empIds = new Guid[] { Guid.NewGuid(),Guid.NewGuid() };
            var results = new List<bool>();
            foreach (var empId in empIds)
            {
                results.Add(await target.AddEmployeeAsync(userId, empId));
            }
            var addResult = await target.GetEmployeesAsync(userId);
            Assert.AreEqual(true, !results.Contains(false));
            Assert.AreEqual(true, addResult != null);
            Assert.AreEqual(empIds.Length, addResult.Count());
        }

        [TestMethod]
        public async Task TestRemoveEmployeeAsync_Correct()
        {
            var target = new UserFavoriteAppService(statefulServiceContext, stateManager);
            var userId = Guid.NewGuid();
            var empId = Guid.NewGuid();
            await target.AddEmployeeAsync(userId, empId);
            var beforRemoveResult = await target.GetEmployeesAsync(userId);
            await target.RemoveEmployeeAsync(userId, empId);
            var afterRemoveResult = await target.GetEmployeesAsync(userId);

            Assert.AreEqual(true, beforRemoveResult.Any());
            Assert.AreEqual(true, !afterRemoveResult.Any());
        }

        [TestMethod]
        public async Task TestRemoveEmployeeAsync_NotHasInFavorites()
        {
            var target = new UserFavoriteAppService(statefulServiceContext, stateManager);
            var userId = Guid.NewGuid();
            var result = await target.RemoveEmployeeAsync(userId, Guid.NewGuid());
            Assert.AreEqual(true, result);
        }

        [TestMethod]
        public async Task TestRemoveEmployeeAsync_Batch()
        {
            var target = new UserFavoriteAppService(statefulServiceContext, stateManager);
            var userId = Guid.NewGuid();
            var empIds = new Guid[] { Guid.NewGuid(), Guid.NewGuid() };
            var results = new List<bool>();
            foreach (var empId in empIds)
            {
                results.Add(await target.AddEmployeeAsync(userId, empId));
            }

            var beforRemoveResult = await target.GetEmployeesAsync(userId);
            foreach (var empId in empIds)
            {
                await target.RemoveEmployeeAsync(userId, empId);
            }

            var afterRemoveResult = await target.GetEmployeesAsync(userId);

            Assert.AreEqual(true, beforRemoveResult.Any());
            Assert.AreEqual(true, !afterRemoveResult.Any());
        }
    }
}


<|start_filename|>src/ConfigMgmtDomain/RemotingClient.cs<|end_filename|>
﻿using Microsoft.ServiceFabric.Services.Remoting.Client;
using Microsoft.ServiceFabric.Services.Remoting.V2.FabricTransport.Client;
using ServiceFabricContrib;
using System;
using System.Collections.Generic;
using System.Fabric;
using System.Text;
using static Common.Constants;

namespace ConfigMgmt
{
    public static class RemotingClient
    {
        private static ServiceProxyFactory proxyFactory = new ServiceProxyFactory((c) => new FabricTransportServiceRemotingClientFactory());

        private static FabricClient fabricClient = new FabricClient();

        public static IClientAppService CreateClientAppService()
        {
            var builder = new ServiceUriBuilder(AppName_ConfigMgmt, ServiceName_SystemConfigStateService);
            return proxyFactory.CreateServiceProxy<IClientAppService>(
                builder.ToUri(), listenerName: ListenerName_ClientAppService);
        }

        public static IBizSystemAppService CreateBizSystemAppService()
        {
            var builder = new ServiceUriBuilder(AppName_ConfigMgmt, ServiceName_SystemConfigStateService);
            return proxyFactory.CreateServiceProxy<IBizSystemAppService>(
                builder.ToUri(), listenerName: ListenerName_TodoCenterAppService);
        }

        public static IWorkbenchAppService CreateWorkbenchAppService()
        {
            var builder = new ServiceUriBuilder(AppName_ConfigMgmt, ServiceName_SystemConfigStateService);
            return proxyFactory.CreateServiceProxy<IWorkbenchAppService>(
                builder.ToUri(), listenerName: ListenerName_WorkbenchAppService);
        }

        public static IUserFavoriteAppService CreateUserFavoriteAppService(Guid userId)
        {
            var builder = new ServiceUriBuilder(AppName_ConfigMgmt, ServiceName_UserConfigStateService);
            return proxyFactory.CreateServiceProxy<IUserFavoriteAppService>(
                builder.ToUri(), new ItemId(userId).GetPartitionKey(), 
                listenerName: ListenerName_UserFavoriteAppService);
        }

        public static IUserSettingAppService CreateUserSettingAppService(Guid userId)
        {
            var builder = new ServiceUriBuilder(AppName_ConfigMgmt, ServiceName_UserConfigStateService);
            return proxyFactory.CreateServiceProxy<IUserSettingAppService>(
                builder.ToUri(), new ItemId(userId).GetPartitionKey(), 
                listenerName: ListenerName_UserSettingAppService);
        }
    }
}


<|start_filename|>src/NotifyDomain/Dtos/MessageNotifyDto.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Threading.Tasks;

namespace Notification
{
    [DataContract]
    public class MessageNotifyDto : NotifyDto
    {
        [DataMember]
        public Guid LatestMsgId { get; set; }
    }
}


<|start_filename|>src/ConversationMsgStateService/ConversationMsgQueueProcessor.cs<|end_filename|>
﻿using InstantMessage;
using InstantMessage.Entities;
using Microsoft.ServiceFabric.Data;
using Microsoft.ServiceFabric.Data.Collections;
using Notification;
using Serilog;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace ConversationMsgStateService
{
    public static class ConversationMsgQueueProcessor
    {
        public static bool IsInUnitTest = false;

        public static async Task ProcessQueueAsync(IReliableStateManager stateManager,
            IConversationCtrlAppService conversationCtrlAppService,
            Func<Guid, INotifySessionActor> notifySessionActorFactory,
            CancellationToken cancellationToken)
        {
            var queueMessageProcess = await Service.GetMessageProcessQueue(stateManager);

            while (!cancellationToken.IsCancellationRequested)
            {
                using (var tx = stateManager.CreateTransaction())
                {
                    var msgWrap = await queueMessageProcess.TryDequeueAsync(tx);
                    if (msgWrap.HasValue)
                    {
                        var item = msgWrap.Value;

                        await SaveMsgAsync(stateManager, tx, item);

                        await PushNotifyAsync(conversationCtrlAppService, notifySessionActorFactory, item);
                    }
                    else
                    {
                        await tx.CommitAsync();
                        await Task.Delay(100);
                    }
                }
            }
        }

        private static async Task SaveMsgAsync(IReliableStateManager stateManager,
            ITransaction tx, ConversationMsg item)
        {
            try
            {
                var listDict = await Service.GetMessageListDictByIdAsync(stateManager, item.Id);
                await listDict.AddAsync(tx, item.Id, item);

                var indexDict = await Service.GetMessageIndexDictByIdAsync(stateManager, item.ConversationId);
                var lstMessageIndex = await indexDict.GetOrAddAsync(tx, item.ConversationId, new List<Guid>());
                lstMessageIndex.Add(item.Id);
                await indexDict.SetAsync(tx, item.ConversationId, lstMessageIndex);

                await tx.CommitAsync();
            }
            catch (Exception ex)
            {
                Log.Error("ConversationMsgQueueProcessor SaveMsgAsync Error: " +
                    $"ItemId: {item.Id}; ConversationId: {item.ConversationId}; SenderId: {item.SenderId}; Message: {ex.Message}");
                if (IsInUnitTest) throw;
            }
        }

        private static async Task PushNotifyAsync(
            IConversationCtrlAppService conversationCtrlAppService,
            Func<Guid, INotifySessionActor> notifySessionActorFactory,
            ConversationMsg item)
        {
            try
            {
                var entityConversationWrap = await conversationCtrlAppService.GetByIdAsync(item.ConversationId);
                if (entityConversationWrap.HasValue)
                {
                    var entityConversation = entityConversationWrap.Value;

                    var tasks = new List<Task>();
                    foreach (var userId in entityConversation.Participants)
                    {
                        var actorSession = notifySessionActorFactory(userId);
                        tasks.Add(actorSession.PushMsgNotifyAsync(new MessageNotifyDto
                        {
                            Target = NotifyTargetType.Conversation,
                            TargetId = item.ConversationId.ToString(),
                            TargetCategory = (int)entityConversation.Type,
                            LatestMsgId = item.Id,
                        }));
                    }
                    await Task.WhenAll(tasks);
                }
            }
            catch (Exception ex)
            {
                //如果PushMsgNotifyAsync过程出错，也实现保存消息的过程，只是会出现接收方接收不到消息
                Log.Error("ConversationMsgQueueProcessor PushMsgNotifyAsync Message: " +
                    $"ItemId is {item.Id}, ConversationId is {item.ConversationId}, SenderId is {item.SenderId}, Message is {ex.Message}");
                if (IsInUnitTest) throw;
            }
        }
    }
}


<|start_filename|>src/AttachmentDomain/Enums.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Attachment
{
    /// <summary>
    /// 附件文件上传的状态，如果为Uploaded，即无需重复上传
    /// </summary>
    public enum UploadStatus
    {
        /// <summary>
        /// 不存在
        /// </summary>
        None = 0,
        /// <summary>
        /// 添加成功了Attachment的数据项，等待上传文件
        /// </summary>
        Waiting = 1,
        /// <summary>
        /// 正在上传文件
        /// </summary>
        Uploading = 2,
        /// <summary>
        /// 上传文件成功
        /// </summary>
        Uploaded = 3,
        /// <summary>
        /// 附件被业务模块所使用，比如Conversation或者GroupFile
        /// </summary>
        InUsed = 4
    }
}


<|start_filename|>src/InstantMessageDomain/Entities/Conversation.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Text;

namespace InstantMessage.Entities
{
    [Serializable]
    [DataContract]
    public struct Conversation
    {
        [DataMember]
        public Guid Id { get; set; }

        /// <summary>
        /// 此属性仅用于P2P
        /// </summary>
        [DataMember]
        public string Key { get; set; }

        [DataMember]
        public ConversationType Type { get; set; }

        [DataMember]
        public List<Guid> Participants { get; set; }

        [DataMember]
        public DateTimeOffset Updated { get; set; }

        [DataMember]
        public bool IsDeleted { get; set; }
    }
}


<|start_filename|>src/EnterpriseContactDomain/RemotingClient.cs<|end_filename|>
﻿using Microsoft.ServiceFabric.Services.Remoting.Client;
using Microsoft.ServiceFabric.Services.Remoting.V2.FabricTransport.Client;
using ServiceFabricContrib;
using System;
using System.Collections.Generic;
using System.Fabric;
using System.Text;
using static Common.Constants;

namespace EnterpriseContact
{
    public static class RemotingClient
    {
        private static ServiceProxyFactory proxyFactory = new ServiceProxyFactory((c) => new FabricTransportServiceRemotingClientFactory());

        private static FabricClient fabricClient = new FabricClient();

        public static IDepartmentAppService CreateDepartmentAppService()
        {
            var builder = new ServiceUriBuilder(AppName_EnterpriseContact, ServiceName_EnterpriseContactService);
            return proxyFactory.CreateServiceProxy<IDepartmentAppService>(
                builder.ToUri(), listenerName:ListenerName_DepartmentAppService);
        }

        public static IEmployeeAppService CreateEmployeeAppService()
        {
            var builder = new ServiceUriBuilder(AppName_EnterpriseContact, ServiceName_EnterpriseContactService);
            return proxyFactory.CreateServiceProxy<IEmployeeAppService>(
                builder.ToUri(), listenerName: ListenerName_EmployeeAppService);
        }

        public static IPositionAppService CreatePositionAppService()
        {
            var builder = new ServiceUriBuilder(AppName_EnterpriseContact, ServiceName_EnterpriseContactService);
            return proxyFactory.CreateServiceProxy<IPositionAppService>(
                builder.ToUri(), listenerName: ListenerName_PositionAppService);
        }

        public static IGroupAppService CreateGroupAppService()
        {
            var builder = new ServiceUriBuilder(AppName_EnterpriseContact, ServiceName_EnterpriseContactService);
            return proxyFactory.CreateServiceProxy<IGroupAppService>(
                builder.ToUri(), listenerName: ListenerName_GroupAppService);
        }
    }
}


<|start_filename|>src/InstantMessageDomain/Enums.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Text;

namespace InstantMessage
{
    [DataContract]
    public enum ConversationType
    {
        [EnumMember]
        P2P = 1,
        //Group = 2,
        [EnumMember(Value = "Group")]
        CustomGroup = 2,
        [EnumMember]
        DepartmentGroup = 3
    }

    public enum ConversationMsgType
    {
        Text = 1,
        Image = 2,
        File = 3,
        Voice = 4,
        Video = 5,
        Location = 6
    }
}


<|start_filename|>src/EnterpriseContactApi/ViewModels/EmployeeDetailVm.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Text;

namespace EnterpriseContact.ViewModels
{
    public class EmployeeDetailVm
    {
        /// <summary>
        /// 员工id
        /// </summary>
        public Guid Id { get; set; }

        public string Number { get; set; }

        /// <summary>
        /// 员工姓名
        /// </summary>
        public string Name { get; set; }

        public Guid? UserId { get; set; }
      
        /// <summary>
        /// 员工的的账号名称
        /// </summary>
        public string UserName { get; set; }

        /// <summary>
        /// 手机号
        /// </summary>
        public string Mobile { get; set; }

        /// <summary>
        /// 性别
        /// </summary>
        public int Gender { get; set; }

        /// <summary>
        /// 部门id
        /// </summary>
        //public Guid DepartmentId { get; set; }

        /// <summary>
        /// 部门
        /// </summary>
        public List<string> DepartmentNames { get; set; }

        /// <summary>
        /// 职位
        /// </summary>
        public string PositionName { get; set; }

        /// <summary>
        /// 父级部门Id
        /// </summary>
        //public Guid ParentDepartmentId { get; set; }

        /// <summary>
        /// 父级部门名称
        /// </summary>
        //public string ParentDepartmentName { get; set; }

        /// <summary>
        /// 邮箱
        /// </summary>
        //public string Mail { get; set; }

        /// <summary>
        /// 是否在同一个白名单组当中，可以发送P2P消息
        /// </summary>
        public bool SameWhiteListGroup { get; set; }

        /// <summary>
        /// 此员工是否已经收藏
        /// </summary>
        public bool IsFavorited { get; set; }

        //public List<DepartmentListVm> DepartmentHierarchy { get; set; }

        public List<ParttimeJobListVm> ParttimeJobs { get; set; }
    }
}


<|start_filename|>src/EnterpriseContactApi/ViewModels/OrgVm.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Text;

namespace EnterpriseContact.ViewModels
{
    public class OrgVm
    {
        public List<DepartmentListVm> Departments { get; set; }

        public List<EmployeeListVm> Employees { get; set; }
    }
}


<|start_filename|>src/GroupFileService/ControlAppService.cs<|end_filename|>
﻿using AutoMapper;
using GroupFile.Dtos;
using GroupFile.Entities;
using GroupFile.Models;
using Microsoft.EntityFrameworkCore;
using ServiceFabricContrib;
using System;
using System.Collections.Generic;
using System.Fabric;
using System.Linq;
using System.Threading.Tasks;

namespace GroupFile
{
    public class ControlAppService : StatelessRemotingService, IControlAppService
    {
        private readonly DbContextOptions<ServiceDbContext> _dbContextOptions;
        private readonly IMapper _mapper;

        public ControlAppService(StatelessServiceContext serviceContext, DbContextOptions<ServiceDbContext> dbContextOptions, IMapper mapper) : base(serviceContext)
        {
            _mapper = mapper;
            _dbContextOptions = dbContextOptions;
        }

        public async Task DeleteFileItemAsync(Guid fileId)
        {
            using (var db = new ServiceDbContext(_dbContextOptions))
            {
                try
                {
                    var result = db.FileItems.FirstOrDefault(fi => fi.Id.Equals(fileId));
                    if (result != null)
                    {
                        db.FileItems.Remove(result);
                        await db.SaveChangesAsync();
                    }
                }
                catch (Exception e)
                {
                    ServiceEventSource.Current.Message("DeleteFileItemAsync try delete file:{0},but do not found!,exception:{1}", fileId, e.ToString());
                }
            }
        }

        public async Task<string> DownloadFileAsync(Guid fileId)
        {
            using (var db = new ServiceDbContext(_dbContextOptions))
            {
                try
                {
                    var result = db.FileItems.FirstOrDefault(fi => fi.Id.Equals(fileId));
                    if (result != null)
                    {
                        result.DownloadAmount++;
                        await db.SaveChangesAsync();
                        return result.StoreId;
                    }
                }
                catch (Exception e)
                {
                    ServiceEventSource.Current.Message("DownloadFileAsync try download file:{0},but do not found!,exception:{1}", fileId, e.ToString());
                }
                return null;
            }
        }

        public Task<FileItemDto> GetFileItemAsync(Guid fileId)
        {
            using (var db = new ServiceDbContext(_dbContextOptions))
            {
                var data = db.FileItems.FirstOrDefault(fi => fi.Id.Equals(fileId));
                return Task.FromResult(_mapper.Map<FileItemDto>(data));
            }
        }

        public Task<List<FileItemDto>> GetFileItemsAsync(Guid groupId, int page, int pageCount = 20)
        {
            if (page <= 0) throw new ArgumentOutOfRangeException("page", "page must > 0");
            using (var db = new ServiceDbContext(_dbContextOptions))
            {
                var skip = (page - 1) * pageCount;
                var datas = db.FileItems.Where((item) => item.GroupId.Equals(groupId)).OrderByDescending(f => f.UpdatedOn).Skip(skip).Take(pageCount).ToList();
                var result = new List<FileItemDto>();
                if (datas != null)
                {
                    foreach (var item in datas)
                    {
                        var data = _mapper.Map<FileItemDto>(item);
                        result.Add(data);
                    }
                }
                return Task.FromResult(result);
            }
        }

        public async Task<FileItemDto> UploadFileAsync(UploadInput param, Guid uploaderId)
        {
            using (var db = new ServiceDbContext(_dbContextOptions))
            {
                var fileItem = new FileItem
                {
                    Id = Guid.NewGuid(),
                    DownloadAmount = 0,
                    GroupId = param.GroupId,
                    Name = param.FileName,
                    StoreId = param.StoreId,
                    UpdatedOn = DateTimeOffset.UtcNow,
                    UploaderId = uploaderId,
                };
                await db.FileItems.AddAsync(fileItem);
                await db.SaveChangesAsync();
                return _mapper.Map<FileItemDto>(fileItem);
            }
        }
    }

}


<|start_filename|>src/EnterpriseContactApi/Controllers/EmployeeController.cs<|end_filename|>
﻿using Common;
using AutoMapper;
using ConfigMgmt;
using EnterpriseContact.ViewModels;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Common.Services;
using Serilog;

namespace EnterpriseContact.Controllers
{
    [Route("api/v1/employee")]
    public class EmployeeController : TheBaseController
    {
        private readonly IEmployeeAppService _employeeAppService;
        private readonly IGroupAppService _groupAppService;
        private readonly Func<Guid, IUserFavoriteAppService> _userFavoriteAppServiceFactory;
        private readonly Func<Guid, IUserSettingAppService> _userSettingAppServiceFactory;
        private readonly ILogger _logger;

        public EmployeeController(IMemoryCache cache, IMapper mapper,
            IDepartmentAppService departmentAppService, IPositionAppService positionAppService,
            IEmployeeAppService employeeAppService,
            IGroupAppService groupAppService,
            Func<Guid, IUserFavoriteAppService> userFavoriteAppServiceFactory,
            Func<Guid, IUserSettingAppService> userSettingAppServiceFactory
            )
            : base(cache, mapper, departmentAppService, positionAppService)
        {
            _employeeAppService = employeeAppService;
            _groupAppService = groupAppService;
            _userFavoriteAppServiceFactory = userFavoriteAppServiceFactory;
            _userSettingAppServiceFactory = userSettingAppServiceFactory;
            _logger = Log.ForContext<EmployeeController>();
        }

        [HttpGet("my")]
        public async Task<ActionResult<List<EmployeeListVm>>> GetMy()
        {
            try
            {
                var userId = GetUserId();

                await EnsureDepartmentListCacheAsync();
                await EnsurePositionListCacheAsync();

                var favs = await _userFavoriteAppServiceFactory(userId).GetEmployeesAsync(userId);
                var dto = await _employeeAppService.GetListByIdsAsync(favs);
                return GenerateEmployeeList(dto);
            }
            catch (Exception ex)
            {
                return BadRequest(LogError(_logger, ex));
            }
        }

        [HttpPost("fav/{employeeId}")]
        public async Task<ActionResult<ResponseData>> AddFavorite(Guid employeeId)
        {
            try
            {
                var userId = GetUserId();

                if (await _userFavoriteAppServiceFactory(userId).AddEmployeeAsync(userId, employeeId))
                    return BuildSuccess();
                return BuildFaild();
            }
            catch (Exception ex)
            {
                return BadRequest(LogError(_logger, ex));
            }
        }

        [HttpDelete("fav/{employeeId}")]
        public async Task<ActionResult<ResponseData>> RemoveFavorite(Guid employeeId)
        {
            try
            {
                var userId = GetUserId();

                if (await _userFavoriteAppServiceFactory(userId).RemoveEmployeeAsync(userId, employeeId))
                    return BuildSuccess();
                return BuildFaild();
            }
            catch (Exception ex)
            {
                return BadRequest(LogError(_logger, ex));
            }
        }

        [HttpGet("search")]
        public async Task<ActionResult<List<EmployeeListVm>>> Search(string keyword)
        {
            try
            {
                await EnsureDepartmentListCacheAsync();
                await EnsurePositionListCacheAsync();

                var dto = await _employeeAppService.SearchByKeywordAsync(keyword);
                return GenerateEmployeeList(dto);
            }
            catch (Exception ex)
            {
                return BadRequest(LogError(_logger, ex));
            }
        }

        private ActionResult<List<EmployeeListVm>> GenerateEmployeeList(List<EmployeeListOutput> dto)
        {
            var data = new List<EmployeeListVm>();
            foreach (var item in dto)
            {
                var vm = _mapper.Map<EmployeeListOutput, EmployeeListVm>(item, (opts) =>
                {
                    opts.AfterMap((EmployeeListOutput s, EmployeeListVm d) =>
                    {
                        if (_positionListCache.ContainsKey(s.PrimaryPositionId))
                            d.PositionName = _positionListCache[s.PrimaryPositionId].Name;
                        SetDepartmentNames(s, d);
                        d.IsPrimary = true;
                    });
                });
                data.Add(vm);
            }

            return data;
        }

        //最多设置两级部门名称
        private void SetDepartmentNames(EmployeeListOutput s, EmployeeListVm d)
        {
            d.DepartmentNames = new List<string>();
            if (_departmentListCache.ContainsKey(s.PrimaryDepartmentId))
            {
                var item = _departmentListCache[s.PrimaryDepartmentId];
                d.DepartmentNames.Add(item.Name);
                if (item.ParentId.HasValue && _departmentListCache.ContainsKey(item.ParentId.Value))
                {
                    d.DepartmentNames.Add(_departmentListCache[item.ParentId.Value].Name);
                }
            }
        }

        private async Task<ActionResult<EmployeeDetailVm>> ReturnByOutputAsync(Guid id, Func<Guid, Task<EmployeeOutput>> getOutput)
        {
            var dto = await getOutput(id);
            if (dto == null) return NotFound(id);

            await EnsureDepartmentListCacheAsync();
            await EnsurePositionListCacheAsync();

            var vm = _mapper.Map<EmployeeOutput, EmployeeDetailVm>(dto, (opts) =>
            {
                opts.AfterMap((EmployeeOutput s, EmployeeDetailVm d) =>
                {
                    if (_positionListCache.ContainsKey(s.PrimaryPositionId))
                        d.PositionName = _positionListCache[s.PrimaryPositionId].Name;
                    SetDepartmentNames(s, d);
                    SetParttimeJobs(s, d);
                });
            });

            var userId = GetUserId();
            var employeeId = GetEmployeeId();

            var task1 = _groupAppService.CheckSameWhiteListGroupAsync(employeeId, id);
            var task2 = _userFavoriteAppServiceFactory(userId).IsFavoritedAsync(userId, id);
            var task3 = dto.UserId.HasValue
                ? _userSettingAppServiceFactory(userId).GetInfoVisibilityAsync(dto.UserId.Value)
                : Task.FromResult(new InfoVisibility());
            await Task.WhenAll(task1, task2, task3);

            vm.SameWhiteListGroup = task1.Result;
            vm.IsFavorited = task2.Result;

            if (!task3.Result.Mobile)
                vm.Mobile = "***";

            return vm;
        }

        [HttpGet("{id}")]
        [ProducesResponseType(StatusCodes.Status200OK)]
        [ProducesResponseType(StatusCodes.Status404NotFound)]
        public async Task<ActionResult<EmployeeDetailVm>> GetById(Guid id)
        {
            try
            {
                return await ReturnByOutputAsync(id, (o) => _employeeAppService.GetByIdAsync(o));
            }
            catch (Exception ex)
            {
                return BadRequest(LogError(_logger, ex));
            }
        }

        [HttpGet("userId/{id}")]
        [ProducesResponseType(StatusCodes.Status200OK)]
        [ProducesResponseType(StatusCodes.Status404NotFound)]
        public async Task<ActionResult<EmployeeDetailVm>> GetByUserId(Guid id)
        {
            try
            {
                return await ReturnByOutputAsync(id, (o) => _employeeAppService.GetByUserIdAsync(o));
            }
            catch (Exception ex)
            {
                return BadRequest(LogError(_logger, ex));
            }
        }

        private void SetParttimeJobs(EmployeeOutput s, EmployeeDetailVm d)
        {
            d.ParttimeJobs = new List<ParttimeJobListVm>();
            foreach (var posId in s.ParttimePositionIds)
            {
                if (_positionListCache.ContainsKey(posId))
                {
                    var pos = _positionListCache[posId];
                    var vm = new ParttimeJobListVm();
                    vm.PositionName = pos.Name;
                    vm.DepartmentNames = new List<string>();
                    //最多设置两级部门名称
                    if (_departmentListCache.ContainsKey(pos.DepartmentId))
                    {
                        var dep = _departmentListCache[pos.DepartmentId];
                        vm.DepartmentNames.Add(dep.Name);
                        if (dep.ParentId.HasValue && _departmentListCache.ContainsKey(dep.ParentId.Value))
                        {
                            vm.DepartmentNames.Add(_departmentListCache[dep.ParentId.Value].Name);
                        }
                    }
                    d.ParttimeJobs.Add(vm);
                }
            }
        }

        //返回完整层级
        private void SetDepartmentNames(EmployeeOutput s, EmployeeDetailVm d)
        {
            d.DepartmentNames = new List<string>();
            if (_departmentListCache.ContainsKey(s.PrimaryDepartmentId))
            {
                var item = _departmentListCache[s.PrimaryDepartmentId];
                d.DepartmentNames.Add(item.Name);
                while (item.ParentId.HasValue && _departmentListCache.ContainsKey(item.ParentId.Value))
                {
                    var sub = _departmentListCache[item.ParentId.Value];
                    d.DepartmentNames.Add(sub.Name);
                    item = sub;
                }
            }
        }

        [HttpGet("checkSameWhiteList")]
        public async Task<ActionResult<bool>> CheckSameWhiteListGroup(Guid employeeId)
        {
            try
            {
                return await _groupAppService.CheckSameWhiteListGroupAsync(GetEmployeeId(), employeeId);
            }
            catch (Exception ex)
            {
                return BadRequest(LogError(_logger, ex));
            }
        }
    }
}


<|start_filename|>src/ConversationCtrlStateService/EventBus/GroupDeletedConsumer.cs<|end_filename|>
﻿using ConversationCtrlStateService.DomainServices;
using EnterpriseContact.MsgConstracts;
using MassTransit;
using Microsoft.ServiceFabric.Data;
using Serilog;
using ServiceFabricContrib;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;

namespace ConversationCtrlStateService.EventBus
{
    public class GroupDeletedConsumer : IConsumer<GroupAdded>
    {
        private readonly IReliableStateManager _stateManager;
        private readonly ILogger _logger;
        private readonly ConversationManager _manager;

        public GroupDeletedConsumer(IReliableStateManager stateManager)
        {
            _stateManager = stateManager;
            _logger = Log.ForContext<GroupDeletedConsumer>();
            _manager = new ConversationManager(stateManager);
        }

        public async Task Consume(ConsumeContext<GroupAdded> context)
        {
            try
            {
                await _manager.DeleteAsync(context.Message.Id);
            }
            catch (Exception ex)
            {
                _logger.Error(ex, ex.Message);
                throw;
            }
        }
    }
}


<|start_filename|>src/NotifyDomain/Dtos/EventNotifyDto.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Runtime.Serialization;

namespace Notification
{
    [DataContract]
    public class EventNotifyDto : NotifyDto
    {

        /// <summary>
        /// 事件提醒标题（可选）
        /// </summary>
        [DataMember]
        public string Title { get; set; }

        /// <summary>
        /// 事件提醒内容文本
        /// </summary>
        [DataMember]
        public string Text { get; set; }

        /// <summary>
        /// 事件提醒的内容参数
        /// </summary>
        [DataMember]
        public Dictionary<string, string> Parameters { get; set; }

        [DataMember]
        public DateTimeOffset Created { get; set; }
    }


}


<|start_filename|>src/EnterpriseContactService/BaseAppService.cs<|end_filename|>
﻿using AutoMapper;
using Microsoft.EntityFrameworkCore;
using ServiceFabricContrib;
using System.Fabric;

namespace EnterpriseContact
{
    public abstract class BaseAppService : StatelessRemotingService
    {
        protected readonly IMapper _mapper;
        protected readonly DbContextOptions<ServiceDbContext> _dbOptions;

        public BaseAppService(StatelessServiceContext serviceContext,
            DbContextOptions<ServiceDbContext> dbOptions,
            IMapper mapper)
            : base(serviceContext)
        {
            _mapper = mapper;
            _dbOptions = dbOptions;
        }
    }
}


<|start_filename|>src/AttachmentApi/TheStartup.cs<|end_filename|>
﻿using Attachment.Services;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Text;

namespace Attachment
{
    public static class TheStartup
    {
        public static void ConfigureServices(IServiceCollection services)
        {
            services.AddSingleton<IAttachmentStoreService, MinioAttachmentStoreService>();
            services.AddTransient<Func<string, IAttachmentAppService>>(provider => RemotingClient.CreateAttachmentAppService);
        }
    }
}


<|start_filename|>src/TodoCenterProxyApi/Extensions/HttpClientExtension.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using TodoCenterProxyApi.Extensions.Internal;

namespace TodoCenterProxyApi.Extensions
{
    public static class HttpClientExtension
    {
        public static async Task<HttpResponseMessage> PostJsonRpcAsync<T>(this HttpClient client, string requestUri, string methodName, T argument = null)
        where T : class
        {
            var rpcRequest = new JsonRpcRequest<T>()
            {
                JsonRpc = "2.0",
                Method = methodName,
                Params = argument,
                Id = 1
            };
            var httpRequestContent = new StringContent(JsonConvert.SerializeObject(rpcRequest), Encoding.UTF8, "application/json");
            var response = await client.PostAsync(requestUri, httpRequestContent);
            return response;
        }
    }
}


<|start_filename|>src/NotifyDomain/AppServices/INotifySessionActor.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.ServiceFabric.Actors;
using Microsoft.ServiceFabric.Actors.Remoting.FabricTransport;
using Microsoft.ServiceFabric.Services.Remoting;

namespace Notification
{
    /// <summary>
    /// 代表用户Session的Actor
    /// </summary>
    public interface INotifySessionActor : IActor
    {
        Task PushMsgNotifyAsync(MessageNotifyDto dto);

        Task PushMsgNotifiesAsync(List<MessageNotifyDto> dtos);

        Task PushEventNotifyAsync(EventNotifyDto dto);

        Task PushEventNotifiesAsync(List<EventNotifyDto> dtos);

        Task AddConnectionIdAsync(string id);

        Task RemoveConnectionIdAsync(string id);
    }
}


<|start_filename|>test/ConversationServiceTest/AppServices/ConversationCtrlStateServiceTest.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ConversationCtrlStateService;
using EnterpriseContact;
using FluentAssertions;
using InstantMessage;
using InstantMessage.Entities;
using Microsoft.ServiceFabric.Data.Collections;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using Notification;
using NSubstitute;
using ServiceFabricContrib;

namespace ConversationServiceTest.AppServices
{
    [TestClass]
    public class ConversationCtrlStateServiceTest : TheAppServiceTestBase
    {
        [TestMethod]
        public async Task GetByIdAsync_Test()
        {
            //Arrage:
            await InitDictDataAsync();

            //Act:
            var service = new ConversationCtrlAppService(statefulServiceContext, stateManager, Substitute.For<IEmployeeAppService>());
            var conversation = await service.GetByIdAsync(DefaultConversation.Id);

            //Assert:
            Assert.IsTrue(conversation.HasValue);
            Assert.IsFalse(conversation.Value.IsDeleted);
            Assert.AreEqual(2, conversation.Value.Participants.Count);
            Assert.AreEqual(ConversationType.P2P, conversation.Value.Type);
            Assert.AreEqual(DefaultConversation.Key, conversation.Value.Key);

        }

        [TestMethod]
        public async Task AddP2PConversationAsync_Test()
        {
            //Arrage:
            await InitDictDataAsync();

            var senderId = Guid.NewGuid();
            var reciverId = Guid.NewGuid();

            //Act:
            var service = new ConversationCtrlAppService(statefulServiceContext, stateManager, Substitute.For<IEmployeeAppService>());
            await service.GetOrAddP2PAsync(senderId, reciverId);
            var key = ConversationCtrlAppService.GenerateConversationKey(senderId, reciverId);

            //Assert:
            Assert.IsNotNull(key);
            using (var tx = stateManager.CreateTransaction())
            {
                var keyToId = await dictKeyToId.TryGetValueAsync(tx, key);
                var idToEntity = await dictIdToEntity.TryGetValueAsync(tx, keyToId.Value);
                var conversation = idToEntity.Value;

                Assert.IsNotNull(keyToId);
                Assert.IsTrue(idToEntity.HasValue);
                Assert.IsFalse(conversation.IsDeleted);

                Assert.IsTrue(conversation.Type == ConversationType.P2P);
                Assert.AreEqual(2, conversation.Participants.Count);
                Assert.IsTrue(conversation.Participants.Contains(senderId));
                Assert.IsTrue(conversation.Participants.Contains(reciverId));
            }

        }


        [TestMethod]
        public async Task DeleteAsync_Test()
        {
            //Arrage:
            await InitDictDataAsync();

            //Act:
            var service = new ConversationCtrlAppService(statefulServiceContext, stateManager, Substitute.For<IEmployeeAppService>());
            await service.DeleteAsync(DefaultConversation.Id);
            await service.DeleteAsync(Guid.NewGuid());

            //Assert:
            using (var tx = stateManager.CreateTransaction())
            {
                var idToEntity = await dictIdToEntity.TryGetValueAsync(tx, DefaultConversation.Id);
                var keyToId = await dictKeyToId.TryGetValueAsync(tx, DefaultConversation.Key);
                Assert.IsTrue(idToEntity.HasValue);
                Assert.IsTrue(idToEntity.Value.IsDeleted);
                Assert.IsFalse(keyToId.HasValue);
            }
        }

        [TestMethod]
        public async Task AddDepAsync()
        {
            //Arrage:
            await InitDictDataAsync();

            var depId = Guid.NewGuid();
            var userId1 = Guid.NewGuid();
            var userId2 = Guid.NewGuid();

            var employeeAppService = Substitute.For<IEmployeeAppService>();
            employeeAppService.GetUserIdsByDepartmentIdAsync(depId)
                .Returns(new List<Guid> { userId1, userId2 });

            //Act:
            var service = new ConversationCtrlAppService(statefulServiceContext, stateManager, employeeAppService);
            var result = await service.AddDepAsync(new AddDepConversationInput { UserId = userId1, DepartmentId = depId });

            //Assert:
            result.IsSuccess.Should().BeTrue();
            await employeeAppService.Received(1).GetUserIdsByDepartmentIdAsync(depId);

            //Fail(1)
            employeeAppService.GetUserIdsByDepartmentIdAsync(depId)
               .Returns(new List<Guid> { Guid.NewGuid(), userId2 });

            result = await service.AddDepAsync(new AddDepConversationInput { UserId = userId1, DepartmentId = depId });
            result.FailedCode.Should().Be(1);


            //Fail(2)
            employeeAppService.GetUserIdsByDepartmentIdAsync(depId)
               .Returns(new List<Guid> { userId1 });

            result = await service.AddDepAsync(new AddDepConversationInput { UserId = userId1, DepartmentId = depId });
            result.FailedCode.Should().Be(2);
        }
    }
}


<|start_filename|>emulator/ClientEmulator/Services/MessageProccessor.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace ClientEmulator.Services
{
    public class MessageProccessor
    {
        private readonly RichTextBox _textBox;

        public MessageProccessor(RichTextBox textBox)
        {
            _textBox = textBox;
        }

        public void AddMessage(string message)
        {
            var text = this._textBox.Text;
            text += "Send:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " " + message + Environment.NewLine;
            this._textBox.Text = text;
        }

        public void RecivedMessage(string message)
        {
            var text = this._textBox.Text;
            text += "Recived:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " " + message + Environment.NewLine;
            this._textBox.Text = text;
        }


    }
}


<|start_filename|>src/GroupFileDomain/IControlAppService.cs<|end_filename|>
﻿using GroupFile.Dtos;
using Microsoft.ServiceFabric.Services.Remoting;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace GroupFile
{
    public interface IControlAppService : IService
    {
        /// <summary>
        /// 群组分页获取文件列表
        /// </summary>
        /// <param name="groupId"></param>
        /// <param name="page"></param>
        /// <param name="pageCount"></param>
        /// <returns></returns>
        Task<List<FileItemDto>> GetFileItemsAsync(Guid groupId, int page, int pageCount);

        /// <summary>
        /// 获取md5值 即 AttachmentItem id 并增加下载量
        /// </summary>
        /// <param name="fileId"></param>
        /// <returns></returns>
        Task<string> DownloadFileAsync(Guid fileId);

        /// <summary>
        /// 秒传文件
        /// </summary>
        /// <param name="param"></param>
        /// <param name="uploaderId"></param>
        /// <returns></returns>
        Task<FileItemDto> UploadFileAsync(UploadInput param, Guid uploaderId);


        /// <summary>
        /// 查询文件信息
        /// </summary>
        /// <param name="fileId"></param>
        /// <returns></returns>
        Task<FileItemDto> GetFileItemAsync(Guid fileId);

        /// <summary>
        /// 删除文件
        /// </summary>
        /// <param name="fileId"></param>
        /// <returns></returns>
        Task DeleteFileItemAsync(Guid fileId);

    }
}


<|start_filename|>src/EnterpriseContactApi/TheStartup.cs<|end_filename|>
﻿using EnterpriseContact.Services;
using Microsoft.Extensions.DependencyInjection;
using System;

namespace EnterpriseContact
{
    public static class TheStartup
    {
        public static void ConfigureServices(IServiceCollection services)
        {
            services.AddSingleton(RemotingClient.CreateDepartmentAppService());
            services.AddSingleton(RemotingClient.CreatePositionAppService());
            services.AddSingleton(RemotingClient.CreateEmployeeAppService());
            services.AddSingleton(RemotingClient.CreateGroupAppService());

            services.AddScoped<IEmployeeCacheService, EmployeeCacheService>();
        }
    }
}


<|start_filename|>src/AttachmentStateService/AttachmentAppService.cs<|end_filename|>
﻿using Attachment;
using Attachment.Entities;
using Microsoft.ServiceFabric.Data;
using Microsoft.ServiceFabric.Data.Collections;
using ServiceFabricContrib;
using System;
using System.Collections.Generic;
using System.Fabric;
using System.Threading.Tasks;

namespace AttachmentStateService
{
    public class AttachmentAppService : StatefulRemotingService, IAttachmentAppService
    {
        private const string DictName_List = "AttachmentItemList";
        private const string DictName_Index_ContextId = "AttachmentItemList/index/ContextId";

        private async Task<(
            IReliableDictionary2<string, AttachmentItem>,
            IReliableDictionary2<Guid, HashSet<string>>
            )> GetDicts()
        {
            var dictList = await StateManager.GetOrAddAsync<IReliableDictionary2<string, AttachmentItem>>(DictName_List);
            var dictIndexContextId = await StateManager.GetOrAddAsync<IReliableDictionary2<Guid, HashSet<string>>>(DictName_Index_ContextId);

            return (dictList, dictIndexContextId);
        }

        public AttachmentAppService(StatefulServiceContext serviceContext,
            IReliableStateManager stateManager) : base(serviceContext, stateManager)
        {
        }

        public async Task AddOrUpdateAsync(AttachmentItem item)
        {
            if (item == null)
                throw new ArgumentNullException(nameof(item));

            if (string.IsNullOrEmpty(item.Id))
                throw new ArgumentNullException(nameof(item.Id));

            if (item.Size < 0)
                throw new ArgumentNullException(nameof(item.Size));

            if (string.IsNullOrEmpty(item.Location))
                throw new ArgumentNullException(nameof(item.Location));

            var (dictList, dictIndexContextId) = await GetDicts();
            using (var tx = StateManager.CreateTransaction())
            {
                await dictList.SetAsync(tx, item.Id, item);

                HashSet<string> contextMapping;
                var contextMappingWrap = await dictIndexContextId.TryGetValueAsync(tx, item.ContextId);
                if (contextMappingWrap.HasValue)
                    contextMapping = contextMappingWrap.Value;
                else
                    contextMapping = new HashSet<string>();
                if (contextMapping.Add(item.Id))
                    await dictIndexContextId.SetAsync(tx, item.ContextId, contextMapping);

                await tx.CommitAsync();
            }
        }

        public async Task<AttachmentItem> GetByIdAsync(string id)
        {
            if (string.IsNullOrEmpty(id))
                throw new ArgumentException("IsNullOrEmpty", nameof(id));

            var (dictList, dictIndexContextId) = await GetDicts();
            using (var tx = StateManager.CreateTransaction())
            {
                var wrap = await dictList.TryGetValueAsync(tx, id);
                if (wrap.HasValue) return wrap.Value;
                return null;
            }
        }

        public async Task<RemotingResult> RemoveByContextIdAsync(Guid contextId)
        {
            var (dictList, dictIndexContextId) = await GetDicts();
            using (var tx = StateManager.CreateTransaction())
            {
                var indexWrap = await dictIndexContextId.TryGetValueAsync(tx, contextId);
                if (!indexWrap.HasValue) return RemotingResult.Fail(1);
                foreach (var item in indexWrap.Value)
                {
                    await dictList.TryRemoveAsync(tx, item);
                }
                await dictIndexContextId.TryRemoveAsync(tx, contextId);

                await tx.CommitAsync();
                return RemotingResult.Success();
            }
        }

        public async Task<RemotingResult> UpdateStatusByIdAsync(string id, UploadStatus status)
        {
            if (string.IsNullOrEmpty(id))
                throw new ArgumentException("IsNullOrEmpty", nameof(id));

            var (dictList, dictIndexContextId) = await GetDicts();
            using (var tx = StateManager.CreateTransaction())
            {
                var wrap = await dictList.TryGetValueAsync(tx, id);
                if (!wrap.HasValue) return RemotingResult.Fail(1);
                if (status < wrap.Value.Status) return RemotingResult.Fail(2);
                var entity = wrap.Value.DeepCopy();
                entity.Status = status;
                if (status == UploadStatus.Uploaded)
                    entity.Uploaded = DateTimeOffset.UtcNow;
                await dictList.TryUpdateAsync(tx, id, entity, wrap.Value);
                await tx.CommitAsync();
                return RemotingResult.Success();
            }
        }
    }
}


<|start_filename|>src/EnterpriseContactDomain/AppServices/IEmployeeAppService.cs<|end_filename|>
﻿using Microsoft.ServiceFabric.Services.Remoting;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;

namespace EnterpriseContact
{
    public interface IEmployeeAppService : IService
    {
        Task<List<EmployeeListOutput>> GetRootListAsync();
        Task<List<EmployeeListOutput>> GetListByDepartmentIdAsync(Guid departmentId);
        Task<List<EmployeeListOutput>> SearchByKeywordAsync(string keyword);
        Task<EmployeeOutput> GetByIdAsync(Guid id);
        Task<List<EmployeeListOutput>> GetListByIdsAsync(Guid[] ids);
        /// <summary>
        /// 此方法仅供 EmployeeCacheService 使用
        /// </summary>
        /// <returns></returns>
        Task<List<EmployeeCacheOutput>> GetCacheDataAsync();
        Task<EmployeeOutput> GetByUserIdAsync(Guid userId);
        Task<List<Guid>> GetUserIdsByDepartmentIdAsync(Guid departmentId);
    }
}


<|start_filename|>test/AttachmentUnitTest/Controllers/AttachmentControllerTest.cs<|end_filename|>
﻿using Attachment;
using Attachment.Controllers;
using Attachment.Entities;
using Attachment.Services;
using Attachment.ViewModels;
using FluentAssertions;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using NSubstitute;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using UnitTestCommon;

namespace AttachmentUnitTest.Controllers
{
    [TestClass]
    public class AttachmentControllerTest : ControllerTestBase
    {
        [TestMethod]
        public async Task TestGetStatusAsync()
        {
            IAttachmentAppService factory(string id)
            {
                var appService = Substitute.For<IAttachmentAppService>();
                appService.GetByIdAsync(id)
                    .ReturnsForAnyArgs(new AttachmentItem
                    {
                        Id = id,
                        Status = UploadStatus.Uploaded
                    });
                return appService;
            };
            var target = new AttachmentController(factory, Substitute.For<IAttachmentStoreService>(),
                Substitute.For<IMemoryCache>());
            var result = await target.GetStatusAsync(Guid.NewGuid().ToString("N"));
            var data = result.Value;
            data.Should().Be(UploadStatus.Uploaded);
        }

        [TestMethod]
        public async Task TestUploadFileAsync_Uploaded()
        {
            IAttachmentAppService factory(string id)
            {
                var appService = Substitute.For<IAttachmentAppService>();
                appService.GetByIdAsync(id)
                    .ReturnsForAnyArgs(new AttachmentItem
                    {
                        Id = id,
                        Status = UploadStatus.Uploaded
                    });
                return appService;
            };
            var storeService = Substitute.For<IAttachmentStoreService>();
            var formFile = Substitute.For<IFormFile>();
            var target = new AttachmentController(factory, storeService,
                Substitute.For<IMemoryCache>());
            var vm = new UploadVm
            {
                ContextId = Guid.Empty,
                File = formFile,
                MD5 = Guid.NewGuid().ToString("N")
            };
            var result = await target.UploadFileAsync(vm);
            var responseData = result.Value;
            responseData.Data.Should().Be(UploadStatus.Uploaded);
            await storeService.DidNotReceiveWithAnyArgs().StoreFileAsync(vm, DateTimeOffset.UtcNow);
        }

        [TestMethod]
        public async Task TestUploadFileAsync_Stored()
        {
            var formFile = Substitute.For<IFormFile>();
            var vm = new UploadVm
            {
                ContextId = Guid.Empty,
                File = formFile,
                MD5 = Guid.NewGuid().ToString("N")
            };

            var appService = Substitute.For<IAttachmentAppService>();
            appService.AddOrUpdateAsync(Arg.Any<AttachmentItem>())
                .Returns(Task.CompletedTask);
            IAttachmentAppService factory(string id) => appService;

            var storeService = Substitute.For<IAttachmentStoreService>();
            var now = DateTimeOffset.UtcNow;
            storeService.StoreFileAsync(vm, Arg.Any<DateTimeOffset>())
                .Returns("abc.ext");

            var target = new AttachmentController(factory, storeService,
                Substitute.For<IMemoryCache>());
            target.ControllerContext = CreateMockContext();

            var result = await target.UploadFileAsync(vm);
            var responseData = result.Value;
            Assert.AreEqual(UploadStatus.Uploaded, responseData.Data);

            await appService.Received().AddOrUpdateAsync(Arg.Is<AttachmentItem>(o =>
                o.Id == vm.MD5 && o.Location == "abc.ext"
                ));
        }

        [TestMethod]
        public async Task TestDownloadFileAsync()
        {
            var md5 = Guid.NewGuid().ToString("N");
            var formFile = Substitute.For<IFormFile>();

            var appService = Substitute.For<IAttachmentAppService>();
            IAttachmentAppService factory(string id) => appService;

            var storeService = Substitute.For<IAttachmentStoreService>();

            var target = new AttachmentController(factory, storeService,
                Substitute.For<IMemoryCache>());
            target.ControllerContext = CreateMockContext();

            var result = await target.DownloadFileAsync(md5, null);
            result.Should().BeOfType<NotFoundResult>();

            appService.GetByIdAsync(md5)
                .Returns(new AttachmentItem
                {
                    Id = md5,
                    Status = UploadStatus.Uploading,
                    Location = null
                });
            result = await target.DownloadFileAsync(md5, null);
            result.Should().BeOfType<NotFoundResult>();

            appService.GetByIdAsync(md5)
                .Returns(new AttachmentItem
                {
                    Id = md5,
                    Status = UploadStatus.Uploaded,
                    Location = null
                });
            result = await target.DownloadFileAsync(md5, null);
            result.Should().BeOfType<NotFoundResult>();

            var dto = new AttachmentItem
            {
                Id = md5,
                Status = UploadStatus.Uploaded,
                Location = "abc.ext"
            };
            appService.GetByIdAsync(md5)
              .Returns(dto);
            storeService.FillMemoryStreamAsync(dto, Arg.Any<MemoryStream>())
                .Returns(Task.CompletedTask);
            result = await target.DownloadFileAsync(md5, null);
            result.Should().BeOfType<FileStreamResult>();

            await storeService.Received().FillMemoryStreamAsync(dto, Arg.Any<MemoryStream>());
        }

        //TODO FIX TestDownloadThumbnailsAsync 
        //[TestMethod]
        public async Task TestDownloadThumbnailsAsync()
        {
            var md5 = Guid.NewGuid().ToString("N");
            var formFile = Substitute.For<IFormFile>();

            var appService = Substitute.For<IAttachmentAppService>();
            IAttachmentAppService factory(string id) => appService;

            var cacheDict = new Dictionary<string, byte[]>();
            var storeService = Substitute.For<IAttachmentStoreService>();
            var cache = Substitute.For<IMemoryCache>();
            cache.Set(Arg.Any<string>(), Arg.Any<byte[]>(), Arg.Any<TimeSpan>())
                .Returns(x =>
                {
                    return x[1] as byte[];
                });
            cache.TryGetValue(Arg.Any<string>(), out Arg.Any<byte[]>())
                .Returns(x =>
                {
                    if (cacheDict.ContainsKey(x[0].ToString()))
                    {
                        x[1] = cacheDict[x[0].ToString()];
                        return true;
                    }
                    return false;
                });

            var target = new AttachmentController(factory, storeService, cache);
            target.ControllerContext = CreateMockContext();

            var result = await target.DownloadThumbnailsAsync(md5, null);
            result.Should().BeOfType<NotFoundResult>();

            appService.GetByIdAsync(md5)
                .Returns(new AttachmentItem
                {
                    Id = md5,
                    Status = UploadStatus.Uploading,
                    Location = null
                });
            result = await target.DownloadFileAsync(md5, null);
            result.Should().BeOfType<NotFoundResult>();

            appService.GetByIdAsync(md5)
                .Returns(new AttachmentItem
                {
                    Id = md5,
                    Status = UploadStatus.Uploaded,
                    Location = null
                });
            result = await target.DownloadFileAsync(md5, null);
            result.Should().BeOfType<NotFoundResult>();

            var dto = new AttachmentItem
            {
                Id = md5,
                Status = UploadStatus.Uploaded,
                Location = "abc.jpg"
            };
            appService.GetByIdAsync(md5)
              .Returns(dto);
            storeService.FillMemoryStreamAsync(dto, Arg.Any<MemoryStream>())
                .Returns(Task.CompletedTask);
            result = await target.DownloadFileAsync(md5, null);
            result.Should().BeOfType<FileStreamResult>();

            await storeService.Received().FillMemoryStreamAsync(dto, Arg.Any<MemoryStream>());
        }
    }
}


<|start_filename|>emulator/ClientEmulator/MainForm.designer.cs<|end_filename|>
﻿namespace ClientEmulator
{
    partial class MainForm
    {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.groupBox1 = new System.Windows.Forms.GroupBox();
            this.button1 = new System.Windows.Forms.Button();
            this.txtBaseAddress = new System.Windows.Forms.TextBox();
            this.label10 = new System.Windows.Forms.Label();
            this.txtAccessToken = new System.Windows.Forms.TextBox();
            this.label6 = new System.Windows.Forms.Label();
            this.btnLogin = new System.Windows.Forms.Button();
            this.txtPassword = new <PASSWORD>();
            this.label2 = new System.Windows.Forms.Label();
            this.txtAuthority = new System.Windows.Forms.TextBox();
            this.label3 = new System.Windows.Forms.Label();
            this.txtUsername = new System.Windows.Forms.TextBox();
            this.label1 = new System.Windows.Forms.Label();
            this.tabControl1 = new System.Windows.Forms.TabControl();
            this.tabPage1 = new System.Windows.Forms.TabPage();
            this.groupBox3 = new System.Windows.Forms.GroupBox();
            this.btnSendNotifyMsg = new System.Windows.Forms.Button();
            this.txtNotifyMsgUserIds = new System.Windows.Forms.TextBox();
            this.label17 = new System.Windows.Forms.Label();
            this.txtNotifyMsgContent = new System.Windows.Forms.TextBox();
            this.label16 = new System.Windows.Forms.Label();
            this.txtNotifyMsgCategory = new System.Windows.Forms.TextBox();
            this.label15 = new System.Windows.Forms.Label();
            this.txtNotifyBizSystemId = new System.Windows.Forms.TextBox();
            this.label14 = new System.Windows.Forms.Label();
            this.groupBox2 = new System.Windows.Forms.GroupBox();
            this.btnConnectRabbitMq = new System.Windows.Forms.Button();
            this.txtRabbitMqPassword = new System.Windows.Forms.TextBox();
            this.label13 = new System.Windows.Forms.Label();
            this.txtRabbitMqUsername = new System.Windows.Forms.TextBox();
            this.label12 = new System.Windows.Forms.Label();
            this.txtRabbitMqAddress = new System.Windows.Forms.TextBox();
            this.label11 = new System.Windows.Forms.Label();
            this.tabPage2 = new System.Windows.Forms.TabPage();
            this.btnTempToken = new System.Windows.Forms.Button();
            this.txtTempToken = new System.Windows.Forms.TextBox();
            this.label5 = new System.Windows.Forms.Label();
            this.tabPage3 = new System.Windows.Forms.TabPage();
            this.textBaseAddress = new System.Windows.Forms.TextBox();
            this.buttonDownload = new System.Windows.Forms.Button();
            this.TextFileId = new System.Windows.Forms.TextBox();
            this.TextDownloadMsg = new System.Windows.Forms.Label();
            this.TextUploadMsg = new System.Windows.Forms.Label();
            this.buttonUpload = new System.Windows.Forms.Button();
            this.buttonChoose = new System.Windows.Forms.Button();
            this.textGroupId = new System.Windows.Forms.TextBox();
            this.textLocation = new System.Windows.Forms.Label();
            this.label7 = new System.Windows.Forms.Label();
            this.label8 = new System.Windows.Forms.Label();
            this.label9 = new System.Windows.Forms.Label();
            this.label4 = new System.Windows.Forms.Label();
            this.tabPage4 = new System.Windows.Forms.TabPage();
            this.btnAttachment_UploadFile = new System.Windows.Forms.Button();
            this.btnAttachment_SelectFile = new System.Windows.Forms.Button();
            this.txtAttachment_FilePath = new System.Windows.Forms.TextBox();
            this.txtAttachment_ContextId = new System.Windows.Forms.TextBox();
            this.lblAttachment_ContextId = new System.Windows.Forms.Label();
            this.lblAttachment_FilePath = new System.Windows.Forms.Label();
            this.groupBox1.SuspendLayout();
            this.tabControl1.SuspendLayout();
            this.tabPage1.SuspendLayout();
            this.groupBox3.SuspendLayout();
            this.groupBox2.SuspendLayout();
            this.tabPage2.SuspendLayout();
            this.tabPage3.SuspendLayout();
            this.tabPage4.SuspendLayout();
            this.SuspendLayout();
            // 
            // groupBox1
            // 
            this.groupBox1.Controls.Add(this.button1);
            this.groupBox1.Controls.Add(this.txtBaseAddress);
            this.groupBox1.Controls.Add(this.label10);
            this.groupBox1.Controls.Add(this.txtAccessToken);
            this.groupBox1.Controls.Add(this.label6);
            this.groupBox1.Controls.Add(this.btnLogin);
            this.groupBox1.Controls.Add(this.txtPassword);
            this.groupBox1.Controls.Add(this.label2);
            this.groupBox1.Controls.Add(this.txtAuthority);
            this.groupBox1.Controls.Add(this.label3);
            this.groupBox1.Controls.Add(this.txtUsername);
            this.groupBox1.Controls.Add(this.label1);
            this.groupBox1.Dock = System.Windows.Forms.DockStyle.Top;
            this.groupBox1.Location = new System.Drawing.Point(0, 0);
            this.groupBox1.Name = "groupBox1";
            this.groupBox1.Size = new System.Drawing.Size(800, 128);
            this.groupBox1.TabIndex = 0;
            this.groupBox1.TabStop = false;
            this.groupBox1.Text = "登录";
            // 
            // button1
            // 
            this.button1.Location = new System.Drawing.Point(710, 22);
            this.button1.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
            this.button1.Name = "button1";
            this.button1.Size = new System.Drawing.Size(68, 23);
            this.button1.TabIndex = 10;
            this.button1.Text = "IMChat";
            this.button1.UseVisualStyleBackColor = true;
            this.button1.Click += new System.EventHandler(this.button1_Click);
            // 
            // txtBaseAddress
            // 
            this.txtBaseAddress.Location = new System.Drawing.Point(109, 83);
            this.txtBaseAddress.Margin = new System.Windows.Forms.Padding(2);
            this.txtBaseAddress.MinimumSize = new System.Drawing.Size(302, 4);
            this.txtBaseAddress.Name = "txtBaseAddress";
            this.txtBaseAddress.Size = new System.Drawing.Size(670, 20);
            this.txtBaseAddress.TabIndex = 9;
            this.txtBaseAddress.Text = "http://localhost:10121/api/v1";
            // 
            // label10
            // 
            this.label10.AutoSize = true;
            this.label10.Location = new System.Drawing.Point(36, 86);
            this.label10.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
            this.label10.Name = "label10";
            this.label10.Size = new System.Drawing.Size(69, 13);
            this.label10.TabIndex = 8;
            this.label10.Text = "BaseAddress";
            // 
            // txtAccessToken
            // 
            this.txtAccessToken.Location = new System.Drawing.Point(109, 58);
            this.txtAccessToken.Name = "txtAccessToken";
            this.txtAccessToken.ReadOnly = true;
            this.txtAccessToken.Size = new System.Drawing.Size(670, 20);
            this.txtAccessToken.TabIndex = 4;
            // 
            // label6
            // 
            this.label6.AutoSize = true;
            this.label6.Location = new System.Drawing.Point(17, 58);
            this.label6.Name = "label6";
            this.label6.Size = new System.Drawing.Size(79, 13);
            this.label6.TabIndex = 3;
            this.label6.Text = "AccessToken：";
            // 
            // btnLogin
            // 
            this.btnLogin.Location = new System.Drawing.Point(630, 22);
            this.btnLogin.Name = "btnLogin";
            this.btnLogin.Size = new System.Drawing.Size(75, 23);
            this.btnLogin.TabIndex = 2;
            this.btnLogin.Text = "登录";
            this.btnLogin.UseVisualStyleBackColor = true;
            this.btnLogin.Click += new System.EventHandler(this.btnLogin_ClickAsync);
            // 
            // txtPassword
            // 
            this.txtPassword.Location = new System.Drawing.Point(512, 23);
            this.txtPassword.Name = "txtPassword";
            this.txtPassword.PasswordChar = '*';
            this.txtPassword.Size = new System.Drawing.Size(100, 20);
            this.txtPassword.TabIndex = 1;
            this.txtPassword.Text = "<PASSWORD>";
            // 
            // label2
            // 
            this.label2.AutoSize = true;
            this.label2.Location = new System.Drawing.Point(477, 25);
            this.label2.Name = "label2";
            this.label2.Size = new System.Drawing.Size(31, 13);
            this.label2.TabIndex = 0;
            this.label2.Text = "密码";
            // 
            // txtAuthority
            // 
            this.txtAuthority.Location = new System.Drawing.Point(63, 22);
            this.txtAuthority.Name = "txtAuthority";
            this.txtAuthority.Size = new System.Drawing.Size(234, 20);
            this.txtAuthority.TabIndex = 1;
            this.txtAuthority.Text = "http://login.test.scrbg.com";
            // 
            // label3
            // 
            this.label3.AutoSize = true;
            this.label3.Location = new System.Drawing.Point(14, 25);
            this.label3.Name = "label3";
            this.label3.Size = new System.Drawing.Size(31, 13);
            this.label3.TabIndex = 0;
            this.label3.Text = "地址";
            // 
            // txtUsername
            // 
            this.txtUsername.Location = new System.Drawing.Point(356, 23);
            this.txtUsername.Name = "txtUsername";
            this.txtUsername.Size = new System.Drawing.Size(100, 20);
            this.txtUsername.TabIndex = 1;
            this.txtUsername.Text = "user1";
            // 
            // label1
            // 
            this.label1.AutoSize = true;
            this.label1.Location = new System.Drawing.Point(312, 25);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(43, 13);
            this.label1.TabIndex = 0;
            this.label1.Text = "用户名";
            // 
            // tabControl1
            // 
            this.tabControl1.Controls.Add(this.tabPage1);
            this.tabControl1.Controls.Add(this.tabPage2);
            this.tabControl1.Controls.Add(this.tabPage3);
            this.tabControl1.Controls.Add(this.tabPage4);
            this.tabControl1.Dock = System.Windows.Forms.DockStyle.Fill;
            this.tabControl1.Location = new System.Drawing.Point(0, 128);
            this.tabControl1.Name = "tabControl1";
            this.tabControl1.SelectedIndex = 0;
            this.tabControl1.Size = new System.Drawing.Size(800, 322);
            this.tabControl1.TabIndex = 1;
            // 
            // tabPage1
            // 
            this.tabPage1.Controls.Add(this.groupBox3);
            this.tabPage1.Controls.Add(this.groupBox2);
            this.tabPage1.Location = new System.Drawing.Point(4, 22);
            this.tabPage1.Name = "tabPage1";
            this.tabPage1.Padding = new System.Windows.Forms.Padding(3);
            this.tabPage1.Size = new System.Drawing.Size(792, 296);
            this.tabPage1.TabIndex = 0;
            this.tabPage1.Text = "发送业务消息";
            this.tabPage1.UseVisualStyleBackColor = true;
            // 
            // groupBox3
            // 
            this.groupBox3.Controls.Add(this.btnSendNotifyMsg);
            this.groupBox3.Controls.Add(this.txtNotifyMsgUserIds);
            this.groupBox3.Controls.Add(this.label17);
            this.groupBox3.Controls.Add(this.txtNotifyMsgContent);
            this.groupBox3.Controls.Add(this.label16);
            this.groupBox3.Controls.Add(this.txtNotifyMsgCategory);
            this.groupBox3.Controls.Add(this.label15);
            this.groupBox3.Controls.Add(this.txtNotifyBizSystemId);
            this.groupBox3.Controls.Add(this.label14);
            this.groupBox3.Location = new System.Drawing.Point(390, 18);
            this.groupBox3.Name = "groupBox3";
            this.groupBox3.Size = new System.Drawing.Size(365, 125);
            this.groupBox3.TabIndex = 3;
            this.groupBox3.TabStop = false;
            this.groupBox3.Text = "BizSystemNotifyMsg";
            // 
            // btnSendNotifyMsg
            // 
            this.btnSendNotifyMsg.Location = new System.Drawing.Point(212, 17);
            this.btnSendNotifyMsg.Name = "btnSendNotifyMsg";
            this.btnSendNotifyMsg.Size = new System.Drawing.Size(147, 23);
            this.btnSendNotifyMsg.TabIndex = 4;
            this.btnSendNotifyMsg.Text = "SendNotifyMsg";
            this.btnSendNotifyMsg.UseVisualStyleBackColor = true;
            this.btnSendNotifyMsg.Click += new System.EventHandler(this.BtnSendNotifyMsg_Click);
            // 
            // txtNotifyMsgUserIds
            // 
            this.txtNotifyMsgUserIds.Location = new System.Drawing.Point(69, 97);
            this.txtNotifyMsgUserIds.Name = "txtNotifyMsgUserIds";
            this.txtNotifyMsgUserIds.Size = new System.Drawing.Size(290, 20);
            this.txtNotifyMsgUserIds.TabIndex = 3;
            this.txtNotifyMsgUserIds.Text = "aef621bb-bf4a-4a20-9b62-a8fd22c4aff1";
            // 
            // label17
            // 
            this.label17.AutoSize = true;
            this.label17.Location = new System.Drawing.Point(8, 100);
            this.label17.Name = "label17";
            this.label17.Size = new System.Drawing.Size(43, 13);
            this.label17.TabIndex = 2;
            this.label17.Text = "UserIds";
            // 
            // txtNotifyMsgContent
            // 
            this.txtNotifyMsgContent.Location = new System.Drawing.Point(69, 71);
            this.txtNotifyMsgContent.Name = "txtNotifyMsgContent";
            this.txtNotifyMsgContent.Size = new System.Drawing.Size(290, 20);
            this.txtNotifyMsgContent.TabIndex = 3;
            this.txtNotifyMsgContent.Text = "you have a todo item";
            // 
            // label16
            // 
            this.label16.AutoSize = true;
            this.label16.Location = new System.Drawing.Point(8, 74);
            this.label16.Name = "label16";
            this.label16.Size = new System.Drawing.Size(44, 13);
            this.label16.TabIndex = 2;
            this.label16.Text = "Content";
            // 
            // txtNotifyMsgCategory
            // 
            this.txtNotifyMsgCategory.Location = new System.Drawing.Point(69, 45);
            this.txtNotifyMsgCategory.Name = "txtNotifyMsgCategory";
            this.txtNotifyMsgCategory.Size = new System.Drawing.Size(137, 20);
            this.txtNotifyMsgCategory.TabIndex = 3;
            this.txtNotifyMsgCategory.Text = "1";
            // 
            // label15
            // 
            this.label15.AutoSize = true;
            this.label15.Location = new System.Drawing.Point(8, 48);
            this.label15.Name = "label15";
            this.label15.Size = new System.Drawing.Size(49, 13);
            this.label15.TabIndex = 2;
            this.label15.Text = "Category";
            // 
            // txtNotifyBizSystemId
            // 
            this.txtNotifyBizSystemId.Location = new System.Drawing.Point(69, 19);
            this.txtNotifyBizSystemId.Name = "txtNotifyBizSystemId";
            this.txtNotifyBizSystemId.Size = new System.Drawing.Size(137, 20);
            this.txtNotifyBizSystemId.TabIndex = 3;
            this.txtNotifyBizSystemId.Text = "oa";
            // 
            // label14
            // 
            this.label14.AutoSize = true;
            this.label14.Location = new System.Drawing.Point(8, 22);
            this.label14.Name = "label14";
            this.label14.Size = new System.Drawing.Size(50, 13);
            this.label14.TabIndex = 2;
            this.label14.Text = "SystemId";
            // 
            // groupBox2
            // 
            this.groupBox2.Controls.Add(this.btnConnectRabbitMq);
            this.groupBox2.Controls.Add(this.txtRabbitMqPassword);
            this.groupBox2.Controls.Add(this.label13);
            this.groupBox2.Controls.Add(this.txtRabbitMqUsername);
            this.groupBox2.Controls.Add(this.label12);
            this.groupBox2.Controls.Add(this.txtRabbitMqAddress);
            this.groupBox2.Controls.Add(this.label11);
            this.groupBox2.Location = new System.Drawing.Point(13, 18);
            this.groupBox2.Name = "groupBox2";
            this.groupBox2.Size = new System.Drawing.Size(358, 125);
            this.groupBox2.TabIndex = 2;
            this.groupBox2.TabStop = false;
            this.groupBox2.Text = "RabbitMQ";
            // 
            // btnConnectRabbitMq
            // 
            this.btnConnectRabbitMq.Location = new System.Drawing.Point(261, 95);
            this.btnConnectRabbitMq.Name = "btnConnectRabbitMq";
            this.btnConnectRabbitMq.Size = new System.Drawing.Size(75, 23);
            this.btnConnectRabbitMq.TabIndex = 2;
            this.btnConnectRabbitMq.Text = "Connect";
            this.btnConnectRabbitMq.UseVisualStyleBackColor = true;
            this.btnConnectRabbitMq.Click += new System.EventHandler(this.BtnConnectRabbitMq_Click);
            // 
            // txtRabbitMqPassword
            // 
            this.txtRabbitMqPassword.Location = new System.Drawing.Point(74, 68);
            this.txtRabbitMqPassword.Name = "txtRabbitMqPassword";
            this.txtRabbitMqPassword.Size = new System.Drawing.Size(263, 20);
            this.txtRabbitMqPassword.TabIndex = 1;
            this.txtRabbitMqPassword.Text = "<PASSWORD>";
            // 
            // label13
            // 
            this.label13.AutoSize = true;
            this.label13.Location = new System.Drawing.Point(13, 71);
            this.label13.Name = "label13";
            this.label13.Size = new System.Drawing.Size(53, 13);
            this.label13.TabIndex = 0;
            this.label13.Text = "Password";
            // 
            // txtRabbitMqUsername
            // 
            this.txtRabbitMqUsername.Location = new System.Drawing.Point(74, 42);
            this.txtRabbitMqUsername.Name = "txtRabbitMqUsername";
            this.txtRabbitMqUsername.Size = new System.Drawing.Size(263, 20);
            this.txtRabbitMqUsername.TabIndex = 1;
            this.txtRabbitMqUsername.Text = "guest";
            // 
            // label12
            // 
            this.label12.AutoSize = true;
            this.label12.Location = new System.Drawing.Point(13, 45);
            this.label12.Name = "label12";
            this.label12.Size = new System.Drawing.Size(55, 13);
            this.label12.TabIndex = 0;
            this.label12.Text = "Username";
            // 
            // txtRabbitMqAddress
            // 
            this.txtRabbitMqAddress.Location = new System.Drawing.Point(74, 16);
            this.txtRabbitMqAddress.Name = "txtRabbitMqAddress";
            this.txtRabbitMqAddress.Size = new System.Drawing.Size(263, 20);
            this.txtRabbitMqAddress.TabIndex = 1;
            this.txtRabbitMqAddress.Text = "rabbitmq://localhost";
            // 
            // label11
            // 
            this.label11.AutoSize = true;
            this.label11.Location = new System.Drawing.Point(13, 19);
            this.label11.Name = "label11";
            this.label11.Size = new System.Drawing.Size(45, 13);
            this.label11.TabIndex = 0;
            this.label11.Text = "Address";
            // 
            // tabPage2
            // 
            this.tabPage2.Controls.Add(this.btnTempToken);
            this.tabPage2.Controls.Add(this.txtTempToken);
            this.tabPage2.Controls.Add(this.label5);
            this.tabPage2.Location = new System.Drawing.Point(4, 22);
            this.tabPage2.Name = "tabPage2";
            this.tabPage2.Padding = new System.Windows.Forms.Padding(3);
            this.tabPage2.Size = new System.Drawing.Size(792, 296);
            this.tabPage2.TabIndex = 1;
            this.tabPage2.Text = "获取临时密码";
            this.tabPage2.UseVisualStyleBackColor = true;
            // 
            // btnTempToken
            // 
            this.btnTempToken.Location = new System.Drawing.Point(405, 15);
            this.btnTempToken.Name = "btnTempToken";
            this.btnTempToken.Size = new System.Drawing.Size(120, 23);
            this.btnTempToken.TabIndex = 7;
            this.btnTempToken.Text = "请求TempToken";
            this.btnTempToken.UseVisualStyleBackColor = true;
            this.btnTempToken.Click += new System.EventHandler(this.btnTempToken_Click);
            // 
            // txtTempToken
            // 
            this.txtTempToken.Location = new System.Drawing.Point(92, 17);
            this.txtTempToken.Name = "txtTempToken";
            this.txtTempToken.ReadOnly = true;
            this.txtTempToken.Size = new System.Drawing.Size(291, 20);
            this.txtTempToken.TabIndex = 6;
            // 
            // label5
            // 
            this.label5.AutoSize = true;
            this.label5.Location = new System.Drawing.Point(19, 20);
            this.label5.Name = "label5";
            this.label5.Size = new System.Drawing.Size(55, 13);
            this.label5.TabIndex = 5;
            this.label5.Text = "临时密码";
            // 
            // tabPage3
            // 
            this.tabPage3.Controls.Add(this.textBaseAddress);
            this.tabPage3.Controls.Add(this.buttonDownload);
            this.tabPage3.Controls.Add(this.TextFileId);
            this.tabPage3.Controls.Add(this.TextDownloadMsg);
            this.tabPage3.Controls.Add(this.TextUploadMsg);
            this.tabPage3.Controls.Add(this.buttonUpload);
            this.tabPage3.Controls.Add(this.buttonChoose);
            this.tabPage3.Controls.Add(this.textGroupId);
            this.tabPage3.Controls.Add(this.textLocation);
            this.tabPage3.Controls.Add(this.label7);
            this.tabPage3.Controls.Add(this.label8);
            this.tabPage3.Controls.Add(this.label9);
            this.tabPage3.Controls.Add(this.label4);
            this.tabPage3.Location = new System.Drawing.Point(4, 22);
            this.tabPage3.Margin = new System.Windows.Forms.Padding(2);
            this.tabPage3.Name = "tabPage3";
            this.tabPage3.Padding = new System.Windows.Forms.Padding(2);
            this.tabPage3.Size = new System.Drawing.Size(792, 296);
            this.tabPage3.TabIndex = 2;
            this.tabPage3.Text = "群文件";
            this.tabPage3.UseVisualStyleBackColor = true;
            this.tabPage3.Click += new System.EventHandler(this.tabPage3_Click);
            // 
            // textBaseAddress
            // 
            this.textBaseAddress.Location = new System.Drawing.Point(74, 12);
            this.textBaseAddress.Margin = new System.Windows.Forms.Padding(2);
            this.textBaseAddress.MinimumSize = new System.Drawing.Size(302, 4);
            this.textBaseAddress.Name = "textBaseAddress";
            this.textBaseAddress.Size = new System.Drawing.Size(302, 20);
            this.textBaseAddress.TabIndex = 8;
            // 
            // buttonDownload
            // 
            this.buttonDownload.Location = new System.Drawing.Point(390, 140);
            this.buttonDownload.Margin = new System.Windows.Forms.Padding(2);
            this.buttonDownload.Name = "buttonDownload";
            this.buttonDownload.Size = new System.Drawing.Size(206, 28);
            this.buttonDownload.TabIndex = 7;
            this.buttonDownload.Text = "Download";
            this.buttonDownload.UseVisualStyleBackColor = true;
            this.buttonDownload.Click += new System.EventHandler(this.buttonDownload_Click);
            // 
            // TextFileId
            // 
            this.TextFileId.Location = new System.Drawing.Point(72, 146);
            this.TextFileId.Margin = new System.Windows.Forms.Padding(2);
            this.TextFileId.MinimumSize = new System.Drawing.Size(302, 4);
            this.TextFileId.Name = "TextFileId";
            this.TextFileId.Size = new System.Drawing.Size(302, 20);
            this.TextFileId.TabIndex = 6;
            // 
            // TextDownloadMsg
            // 
            this.TextDownloadMsg.AutoSize = true;
            this.TextDownloadMsg.Location = new System.Drawing.Point(13, 176);
            this.TextDownloadMsg.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
            this.TextDownloadMsg.MinimumSize = new System.Drawing.Size(400, 31);
            this.TextDownloadMsg.Name = "TextDownloadMsg";
            this.TextDownloadMsg.Size = new System.Drawing.Size(400, 31);
            this.TextDownloadMsg.TabIndex = 5;
            // 
            // TextUploadMsg
            // 
            this.TextUploadMsg.AutoSize = true;
            this.TextUploadMsg.Location = new System.Drawing.Point(13, 90);
            this.TextUploadMsg.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
            this.TextUploadMsg.MinimumSize = new System.Drawing.Size(400, 31);
            this.TextUploadMsg.Name = "TextUploadMsg";
            this.TextUploadMsg.Size = new System.Drawing.Size(400, 31);
            this.TextUploadMsg.TabIndex = 5;
            // 
            // buttonUpload
            // 
            this.buttonUpload.Location = new System.Drawing.Point(498, 58);
            this.buttonUpload.Margin = new System.Windows.Forms.Padding(2);
            this.buttonUpload.Name = "buttonUpload";
            this.buttonUpload.Size = new System.Drawing.Size(97, 28);
            this.buttonUpload.TabIndex = 4;
            this.buttonUpload.Text = "上传";
            this.buttonUpload.UseVisualStyleBackColor = true;
            this.buttonUpload.Click += new System.EventHandler(this.buttonUpload_Click);
            // 
            // buttonChoose
            // 
            this.buttonChoose.Location = new System.Drawing.Point(390, 58);
            this.buttonChoose.Margin = new System.Windows.Forms.Padding(2);
            this.buttonChoose.Name = "buttonChoose";
            this.buttonChoose.Size = new System.Drawing.Size(82, 28);
            this.buttonChoose.TabIndex = 3;
            this.buttonChoose.Text = "选择文件";
            this.buttonChoose.UseVisualStyleBackColor = true;
            this.buttonChoose.Click += new System.EventHandler(this.buttonChoose_Click);
            // 
            // textGroupId
            // 
            this.textGroupId.Location = new System.Drawing.Point(72, 63);
            this.textGroupId.Margin = new System.Windows.Forms.Padding(2);
            this.textGroupId.MinimumSize = new System.Drawing.Size(302, 4);
            this.textGroupId.Name = "textGroupId";
            this.textGroupId.Size = new System.Drawing.Size(302, 20);
            this.textGroupId.TabIndex = 2;
            // 
            // textLocation
            // 
            this.textLocation.AutoSize = true;
            this.textLocation.Location = new System.Drawing.Point(74, 40);
            this.textLocation.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
            this.textLocation.MinimumSize = new System.Drawing.Size(300, 0);
            this.textLocation.Name = "textLocation";
            this.textLocation.Size = new System.Drawing.Size(300, 13);
            this.textLocation.TabIndex = 1;
            // 
            // label7
            // 
            this.label7.AutoSize = true;
            this.label7.Location = new System.Drawing.Point(13, 148);
            this.label7.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
            this.label7.Name = "label7";
            this.label7.Size = new System.Drawing.Size(40, 13);
            this.label7.TabIndex = 0;
            this.label7.Text = "文件Id";
            // 
            // label8
            // 
            this.label8.AutoSize = true;
            this.label8.Location = new System.Drawing.Point(13, 65);
            this.label8.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
            this.label8.Name = "label8";
            this.label8.Size = new System.Drawing.Size(40, 13);
            this.label8.TabIndex = 0;
            this.label8.Text = "群组Id";
            // 
            // label9
            // 
            this.label9.AutoSize = true;
            this.label9.Location = new System.Drawing.Point(13, 12);
            this.label9.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
            this.label9.Name = "label9";
            this.label9.Size = new System.Drawing.Size(37, 13);
            this.label9.TabIndex = 0;
            this.label9.Text = "地址：";
            // 
            // label4
            // 
            this.label4.AutoSize = true;
            this.label4.Location = new System.Drawing.Point(13, 40);
            this.label4.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
            this.label4.Name = "label4";
            this.label4.Size = new System.Drawing.Size(61, 13);
            this.label4.TabIndex = 0;
            this.label4.Text = "文件地址：";
            // 
            // tabPage4
            // 
            this.tabPage4.Controls.Add(this.btnAttachment_UploadFile);
            this.tabPage4.Controls.Add(this.btnAttachment_SelectFile);
            this.tabPage4.Controls.Add(this.txtAttachment_FilePath);
            this.tabPage4.Controls.Add(this.txtAttachment_ContextId);
            this.tabPage4.Controls.Add(this.lblAttachment_ContextId);
            this.tabPage4.Controls.Add(this.lblAttachment_FilePath);
            this.tabPage4.Location = new System.Drawing.Point(4, 22);
            this.tabPage4.Name = "tabPage4";
            this.tabPage4.Padding = new System.Windows.Forms.Padding(3);
            this.tabPage4.Size = new System.Drawing.Size(792, 296);
            this.tabPage4.TabIndex = 3;
            this.tabPage4.Text = "附件";
            this.tabPage4.UseVisualStyleBackColor = true;
            // 
            // btnAttachment_UploadFile
            // 
            this.btnAttachment_UploadFile.Location = new System.Drawing.Point(124, 101);
            this.btnAttachment_UploadFile.Margin = new System.Windows.Forms.Padding(2);
            this.btnAttachment_UploadFile.Name = "btnAttachment_UploadFile";
            this.btnAttachment_UploadFile.Size = new System.Drawing.Size(97, 28);
            this.btnAttachment_UploadFile.TabIndex = 9;
            this.btnAttachment_UploadFile.Text = "上传";
            this.btnAttachment_UploadFile.UseVisualStyleBackColor = true;
            this.btnAttachment_UploadFile.Click += new System.EventHandler(this.BtnAttachment_UploadFile_Click);
            // 
            // btnAttachment_SelectFile
            // 
            this.btnAttachment_SelectFile.Location = new System.Drawing.Point(16, 101);
            this.btnAttachment_SelectFile.Margin = new System.Windows.Forms.Padding(2);
            this.btnAttachment_SelectFile.Name = "btnAttachment_SelectFile";
            this.btnAttachment_SelectFile.Size = new System.Drawing.Size(82, 28);
            this.btnAttachment_SelectFile.TabIndex = 8;
            this.btnAttachment_SelectFile.Text = "选择文件";
            this.btnAttachment_SelectFile.UseVisualStyleBackColor = true;
            this.btnAttachment_SelectFile.Click += new System.EventHandler(this.BtnAttachment_SelectFile_Click);
            // 
            // txtAttachment_FilePath
            // 
            this.txtAttachment_FilePath.Location = new System.Drawing.Point(84, 67);
            this.txtAttachment_FilePath.Margin = new System.Windows.Forms.Padding(2);
            this.txtAttachment_FilePath.MinimumSize = new System.Drawing.Size(302, 4);
            this.txtAttachment_FilePath.Name = "txtAttachment_FilePath";
            this.txtAttachment_FilePath.Size = new System.Drawing.Size(302, 20);
            this.txtAttachment_FilePath.TabIndex = 7;
            // 
            // txtAttachment_ContextId
            // 
            this.txtAttachment_ContextId.Location = new System.Drawing.Point(84, 34);
            this.txtAttachment_ContextId.Margin = new System.Windows.Forms.Padding(2);
            this.txtAttachment_ContextId.MinimumSize = new System.Drawing.Size(302, 4);
            this.txtAttachment_ContextId.Name = "txtAttachment_ContextId";
            this.txtAttachment_ContextId.Size = new System.Drawing.Size(302, 20);
            this.txtAttachment_ContextId.TabIndex = 7;
            // 
            // lblAttachment_ContextId
            // 
            this.lblAttachment_ContextId.AutoSize = true;
            this.lblAttachment_ContextId.Location = new System.Drawing.Point(11, 37);
            this.lblAttachment_ContextId.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
            this.lblAttachment_ContextId.Name = "lblAttachment_ContextId";
            this.lblAttachment_ContextId.Size = new System.Drawing.Size(52, 13);
            this.lblAttachment_ContextId.TabIndex = 5;
            this.lblAttachment_ContextId.Text = "上下文Id";
            // 
            // lblAttachment_FilePath
            // 
            this.lblAttachment_FilePath.AutoSize = true;
            this.lblAttachment_FilePath.Location = new System.Drawing.Point(13, 70);
            this.lblAttachment_FilePath.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
            this.lblAttachment_FilePath.Name = "lblAttachment_FilePath";
            this.lblAttachment_FilePath.Size = new System.Drawing.Size(61, 13);
            this.lblAttachment_FilePath.TabIndex = 6;
            this.lblAttachment_FilePath.Text = "文件地址：";
            // 
            // MainForm
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(800, 450);
            this.Controls.Add(this.tabControl1);
            this.Controls.Add(this.groupBox1);
            this.Name = "MainForm";
            this.Text = "Form1";
            this.groupBox1.ResumeLayout(false);
            this.groupBox1.PerformLayout();
            this.tabControl1.ResumeLayout(false);
            this.tabPage1.ResumeLayout(false);
            this.groupBox3.ResumeLayout(false);
            this.groupBox3.PerformLayout();
            this.groupBox2.ResumeLayout(false);
            this.groupBox2.PerformLayout();
            this.tabPage2.ResumeLayout(false);
            this.tabPage2.PerformLayout();
            this.tabPage3.ResumeLayout(false);
            this.tabPage3.PerformLayout();
            this.tabPage4.ResumeLayout(false);
            this.tabPage4.PerformLayout();
            this.ResumeLayout(false);

        }

        #endregion

        private System.Windows.Forms.GroupBox groupBox1;
        private System.Windows.Forms.Button btnLogin;
        private System.Windows.Forms.TextBox txtPassword;
        private System.Windows.Forms.Label label2;
        private System.Windows.Forms.TextBox txtUsername;
        private System.Windows.Forms.Label label1;
        private System.Windows.Forms.TabControl tabControl1;
        private System.Windows.Forms.TabPage tabPage1;
        private System.Windows.Forms.TabPage tabPage2;
        private System.Windows.Forms.TextBox txtAuthority;
        private System.Windows.Forms.Label label3;
        private System.Windows.Forms.Button btnTempToken;
        private System.Windows.Forms.TextBox txtTempToken;
        private System.Windows.Forms.Label label5;
        private System.Windows.Forms.TextBox txtAccessToken;
        private System.Windows.Forms.Label label6;
        private System.Windows.Forms.TabPage tabPage3;
        private System.Windows.Forms.Label label4;
        private System.Windows.Forms.Label textLocation;
        private System.Windows.Forms.TextBox textGroupId;
        private System.Windows.Forms.Label label8;
        private System.Windows.Forms.Button buttonChoose;
        private System.Windows.Forms.Button buttonUpload;
        private System.Windows.Forms.Label TextUploadMsg;
        private System.Windows.Forms.Label label7;
        private System.Windows.Forms.TextBox TextFileId;
        private System.Windows.Forms.Button buttonDownload;
        private System.Windows.Forms.Label TextDownloadMsg;
        private System.Windows.Forms.Label label9;
        private System.Windows.Forms.TextBox textBaseAddress;
        private System.Windows.Forms.TabPage tabPage4;
        private System.Windows.Forms.Button btnAttachment_UploadFile;
        private System.Windows.Forms.Button btnAttachment_SelectFile;
        private System.Windows.Forms.TextBox txtAttachment_FilePath;
        private System.Windows.Forms.TextBox txtAttachment_ContextId;
        private System.Windows.Forms.Label lblAttachment_ContextId;
        private System.Windows.Forms.Label lblAttachment_FilePath;
        private System.Windows.Forms.TextBox txtBaseAddress;
        private System.Windows.Forms.Label label10;
        private System.Windows.Forms.Button button1;
        private System.Windows.Forms.GroupBox groupBox2;
        private System.Windows.Forms.Button btnConnectRabbitMq;
        private System.Windows.Forms.TextBox txtRabbitMqPassword;
        private System.Windows.Forms.Label label13;
        private System.Windows.Forms.TextBox txtRabbitMqUsername;
        private System.Windows.Forms.Label label12;
        private System.Windows.Forms.TextBox txtRabbitMqAddress;
        private System.Windows.Forms.Label label11;
        private System.Windows.Forms.GroupBox groupBox3;
        private System.Windows.Forms.Button btnSendNotifyMsg;
        private System.Windows.Forms.TextBox txtNotifyMsgUserIds;
        private System.Windows.Forms.Label label17;
        private System.Windows.Forms.TextBox txtNotifyMsgContent;
        private System.Windows.Forms.Label label16;
        private System.Windows.Forms.TextBox txtNotifyMsgCategory;
        private System.Windows.Forms.Label label15;
        private System.Windows.Forms.TextBox txtNotifyBizSystemId;
        private System.Windows.Forms.Label label14;
    }
}



<|start_filename|>src/GroupFileService/Migrations/20190227085008_Init.Designer.cs<|end_filename|>
﻿// <auto-generated />
using System;
using GroupFile.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;

namespace GroupFile.Migrations
{
    [DbContext(typeof(ServiceDbContext))]
    [Migration("20190227085008_Init")]
    partial class Init
    {
        protected override void BuildTargetModel(ModelBuilder modelBuilder)
        {
#pragma warning disable 612, 618
            modelBuilder
                .HasAnnotation("ProductVersion", "2.2.2-servicing-10034")
                .HasAnnotation("Relational:MaxIdentifierLength", 128)
                .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

            modelBuilder.Entity("GroupFile.Entities.FileItem", b =>
                {
                    b.Property<Guid>("Id")
                        .ValueGeneratedOnAdd();

                    b.Property<int>("DownloadAmount");

                    b.Property<Guid>("GroupId");

                    b.Property<string>("Name");

                    b.Property<string>("StoreId");

                    b.Property<DateTimeOffset>("UpdatedOn");

                    b.Property<Guid>("UploaderId");

                    b.HasKey("Id");

                    b.HasIndex("GroupId");

                    b.HasIndex("StoreId");

                    b.ToTable("FileItems");
                });

            modelBuilder.Entity("GroupFile.Entities.FileStore", b =>
                {
                    b.Property<string>("Id")
                        .ValueGeneratedOnAdd()
                        .HasMaxLength(32);

                    b.Property<string>("Location");

                    b.Property<int>("Size");

                    b.HasKey("Id");

                    b.ToTable("FileStores");
                });

            modelBuilder.Entity("GroupFile.Entities.FileItem", b =>
                {
                    b.HasOne("GroupFile.Entities.FileStore", "Store")
                        .WithMany("Items")
                        .HasForeignKey("StoreId");
                });
#pragma warning restore 612, 618
        }
    }
}


<|start_filename|>src/ApiCommon/Extensions/HttpClientExtension.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Common.Extensions;
using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;

namespace ApiCommon.Extensions
{
    public static class HttpClientExtension
    {
        public static string UserClaimsHeaderName = "UserClaims";

        public static void AddUserClaimsHeader(this HttpClient client, HttpContext context)
        {
            if (!context.User.Identity.IsAuthenticated)
                throw new InvalidOperationException("user do not Authenticated!");

            var claimsDic = new Dictionary<string, string>();
            foreach (var userClaim in context.User.Claims)
            {
                if (!claimsDic.ContainsKey(userClaim.Type))
                    claimsDic.Add(userClaim.Type, userClaim.Value);
            }
            var claimsString = JsonConvert.SerializeObject(claimsDic);
            var claimsBase64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(claimsString));
            if (client.DefaultRequestHeaders.Contains(UserClaimsHeaderName))
                client.DefaultRequestHeaders.Remove(UserClaimsHeaderName);
            client.DefaultRequestHeaders.Add(UserClaimsHeaderName, claimsBase64);
        }
    }
}


<|start_filename|>src/TodoCenterProxyApi/Controllers/DoneListController.cs<|end_filename|>
﻿using System;
using AppComponent.ViewModels;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ApiCommon.Extensions;
using Common.Extensions;
using TodoCenterProxyApi.Services;
using TodoCenterProxyApi.Extensions;

namespace TodoCenterProxyApi.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class DoneListController : ControllerBase
    {
        private readonly IEnumerable<ITodoService> _services;

        public DoneListController(IEnumerable<ITodoService> services)
        {
            _services = services;
        }

        // GET api/values
        [HttpGet]
        public async Task<ActionResult<IEnumerable<TodoListVM>>> GetAsync()
        {
            var taskes = new List<Task<IList<TodoListVM>>>();
            foreach (var service in _services)
            {
                taskes.Add(this.GetDoneListAsync(service));
            }
            await Task.WhenAll(taskes);
            var Donelist = taskes.SelectMany(u => u.Result).ToList();
            return Donelist;
        }

        private async Task<IList<TodoListVM>> GetDoneListAsync(ITodoService todoService)
        {
            try
            {
                var doneList = await todoService.GetCompleteAsync(this.HttpContext.DecodeUserClaimsFromRequestHeaders());
                return doneList;
            }
            catch (Exception e)
            {
                ServiceEventSource.Current.Message("Get PendingList Faild,Reason:{0}", e.ToString());
                return new List<TodoListVM>();
            }
        }
    }
}


<|start_filename|>test/ConfigMgmtUnitTest/AppServices/UserSettingAppServiceTest.cs<|end_filename|>
﻿using ConfigMgmt;
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Threading.Tasks;
using UserConfigStateService;

namespace ConfigMgmtUnitTest.AppServices
{
    [TestClass]
    public class UserSettingAppServiceTest : TheAppServiceTestBase
    {
        [TestMethod]
        public async Task SetAndGetInfoVisibilityAsync()
        {
            var target = new UserSettingAppService(statefulServiceContext, stateManager);
            var userId = Guid.NewGuid();
            await target.SetInfoVisibilityAsync(userId, new InfoVisibility
            {
                Mobile = true
            });
            var result = await target.GetInfoVisibilityAsync(userId);
            result.Should().NotBeNull();
            result.Mobile.Should().BeTrue();

            result = await target.GetInfoVisibilityAsync(Guid.NewGuid());
            result.Should().NotBeNull();
            result.Mobile.Should().BeFalse();
        }

        [TestMethod]
        public async Task SetAndGetAppEntranceAuthStateAsync()
        {
            var target = new UserSettingAppService(statefulServiceContext, stateManager);
            var userId = Guid.NewGuid();
            var input = new AppEntranceAuthStateInput
            {
                AppEntranceId = Guid.NewGuid(),
                DeviceCode = Guid.NewGuid().ToString("N")
            };
            var result = await target.GetAppEntranceAuthStateAsync(userId, input.DeviceCode, input.AppEntranceId);
            result.Should().BeTrue();

            await target.SetAppEntranceAuthStateAsync(userId, input);
            result = await target.GetAppEntranceAuthStateAsync(userId, input.DeviceCode, input.AppEntranceId);
            result.Should().BeFalse();

            await target.SetAppEntranceAuthStateAsync(userId, input);
            await target.SetAppEntranceAuthStateAsync(userId, new AppEntranceAuthStateInput
            {
                AppEntranceId = Guid.NewGuid(),
                DeviceCode = input.DeviceCode
            });
            result = await target.GetAppEntranceAuthStateAsync(userId, input.DeviceCode, Guid.NewGuid());
            result.Should().BeTrue();

            await target.SetAppEntranceAuthStateAsync(userId, new AppEntranceAuthStateInput
            {
                AppEntranceId = Guid.NewGuid(),
                DeviceCode = Guid.NewGuid().ToString("N")
            });
            result = await target.GetAppEntranceAuthStateAsync(userId, Guid.NewGuid().ToString("N"), Guid.NewGuid());
            result.Should().BeTrue();
        }
    }
}


<|start_filename|>src/ApiCommon/Services/IdentityService.cs<|end_filename|>
﻿using IdentityModel.Client;
using AppsOnSF.Common.Options;
using Microsoft.Extensions.Options;
using System;
using System.Net.Http;
using System.Threading.Tasks;

namespace Common.Services
{
    public class IdentityService : IIdentityService
    {
        private readonly IdSvrOption _option;
        private readonly HttpClient _httpClient;

        public IdentityService(HttpClient httpClient, IOptions<IdSvrOption> optionAccessor)
        {
            _option = optionAccessor.Value;
            _httpClient = httpClient;
        }

        public async Task<string> GetClientCredentialAccessTokenAsync(string clientId, string clientSecret, string scope)
        {
            var disco = await _httpClient.GetDiscoveryDocumentAsync(new DiscoveryDocumentRequest
            {
                Address = _option.IssuerUri,
                Policy =
                {
                    RequireHttps=_option.RequireHttps
                }
            });
            if (disco.IsError) throw new Exception(disco.Error);

            var tokenResponse = await _httpClient.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
            {
                Address = disco.TokenEndpoint,
                ClientId = clientId,
                ClientSecret = clientSecret,
                Scope = scope
            });
            if (tokenResponse.IsError) throw new Exception(tokenResponse.Error);

            return tokenResponse.AccessToken;
        }
    }
}


<|start_filename|>test/EnterpriseContactUnitTest/Controllers/GroupControllerTest.cs<|end_filename|>
﻿using Common;
using EnterpriseContact;
using EnterpriseContact.Controllers;
using EnterpriseContact.ViewModels;
using FluentAssertions;
using Microsoft.AspNetCore.Mvc;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using NSubstitute;
using ServiceFabricContrib;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace EnterpriseContactUnitTest.Controllers
{
    [TestClass]
    public class GroupControllerTest : TheControllerTestBase
    {
        [TestMethod]
        public async Task TestGetMy()
        {
            var groups = new List<GroupListOutput>
            {
                new GroupListOutput
                {
                    Id = Guid.NewGuid(),
                    Name = "aaa",
                },
                new GroupListOutput
                {
                    Id = Guid.NewGuid(),
                    Name = "bbb",
                },
                new GroupListOutput
                {
                    Id = Guid.NewGuid(),
                    Name = "ccc",
                },
            };

            var groupAppService = Substitute.For<IGroupAppService>();
            groupAppService.GetListByEmployeeIdAsync(User_EmployeeMdmId)
                .Returns(Task.FromResult(groups));

            var target = new GroupController(
                CreateMemoryCache(),
                CreateMapper(),
                Substitute.For<IDepartmentAppService>(),
                Substitute.For<IPositionAppService>(),
                groupAppService);
            target.ControllerContext = CreateMockContext();

            var result = await target.GetMy();
            var data = result.Value;
            data.Count.Should().Be(3);
        }

        [TestMethod]
        public async Task TestCreate()
        {
            var groupAppService = Substitute.For<IGroupAppService>();
            groupAppService.CreateCustomAsync(Arg.Any<GroupInput>())
                .ReturnsForAnyArgs(Guid.NewGuid());

            var target = new GroupController(
                CreateMemoryCache(),
                CreateMapper(),
                Substitute.For<IDepartmentAppService>(),
                Substitute.For<IPositionAppService>(),
                groupAppService);
            target.ControllerContext = CreateMockContext();

            var result = await target.Create(new GroupEditVm
            {
                Name = "aaa",
                AddingMemberIds = new HashSet<Guid> { User_EmployeeMdmId, Guid.NewGuid() },
                AddingMemberNames = new List<string> { User_EmployeeName, "李四" }
            });

            var createdResult = result.Result.As<CreatedAtActionResult>();
            createdResult.Should().NotBeNull();
            var data = createdResult.Value.As<ResponseData<Guid>>();
            data.Should().NotBe(Guid.Empty);

            await groupAppService.ReceivedWithAnyArgs().CreateCustomAsync(
                Arg.Is<GroupInput>(o => o.CurrentUserId == User_Id && o.CurrentEmployeeId == User_EmployeeMdmId));
        }

        [TestMethod]
        public async Task TestGetById()
        {
            var output = new GroupOutput
            {
                Id = Guid.NewGuid(),
                Name = "aaa",
                Members = new List<GroupMemberOutput>
                {
                    new GroupMemberOutput
                    {
                        EmployeeId = Guid.NewGuid(),
                        EmployeeName = "aaa",
                        IsOwner = true,
                        Joined = DateTimeOffset.UtcNow,
                    },
                    new GroupMemberOutput
                    {
                        EmployeeId = Guid.NewGuid(),
                        EmployeeName = "bbb",
                        IsOwner = false,
                        Joined = DateTimeOffset.UtcNow,
                    },
                }
            };
            var groupAppService = Substitute.For<IGroupAppService>();
            groupAppService.GetByIdAsync(output.Id)
                .ReturnsForAnyArgs(output);

            var target = new GroupController(
                CreateMemoryCache(),
                CreateMapper(),
                Substitute.For<IDepartmentAppService>(),
                Substitute.For<IPositionAppService>(),
                groupAppService);
            target.ControllerContext = CreateMockContext();

            var result = await target.GetById(output.Id);
            var data = result.Value;
            data.Should().NotBeNull();
            data.Name.Should().Be("aaa");
            data.Members.Count.Should().Be(2);
            data.Members[0].EmployeeName.Should().Be("aaa");
        }

        [TestMethod]
        public async Task TestUpdate()
        {
            var groupAppService = Substitute.For<IGroupAppService>();

            var target = new GroupController(
                CreateMemoryCache(),
                CreateMapper(),
                Substitute.For<IDepartmentAppService>(),
                Substitute.For<IPositionAppService>(),
                groupAppService);
            target.ControllerContext = CreateMockContext();

            groupAppService.UpdateAsync(Arg.Any<GroupInput>())
                .ReturnsForAnyArgs(RemotingResult.Success());
            var result = await target.Update(Guid.NewGuid(), new GroupEditVm());
            result.Value.Should().NotBeNull();
            result.Value.Status.Should().Be(0);

            groupAppService.UpdateAsync(Arg.Any<GroupInput>())
               .ReturnsForAnyArgs(RemotingResult.Fail());
            result = await target.Update(Guid.NewGuid(), new GroupEditVm());
            result.Value.Status.Should().NotBe(0);

            groupAppService.UpdateAsync(Arg.Any<GroupInput>())
              .ReturnsForAnyArgs(RemotingResult.Fail(FailedCodes.Group_NotCreatedBy));
            result = await target.Update(Guid.NewGuid(), new GroupEditVm());
            result.Value.Status.Should().Be(FailedCodes.Group_NotCreatedBy);
        }

        [TestMethod]
        public async Task TestDelete()
        {
            var groupAppService = Substitute.For<IGroupAppService>();

            var target = new GroupController(
                CreateMemoryCache(),
                CreateMapper(),
                Substitute.For<IDepartmentAppService>(),
                Substitute.For<IPositionAppService>(),
                groupAppService);
            target.ControllerContext = CreateMockContext();

            groupAppService.DeleteAsync(Arg.Any<GroupInput>())
                .ReturnsForAnyArgs(RemotingResult.Success());
            var result = await target.Delete(Guid.NewGuid());
            result.Value.Should().NotBeNull();
            result.Value.Status.Should().Be(0);

            groupAppService.DeleteAsync(Arg.Any<GroupInput>())
               .ReturnsForAnyArgs(RemotingResult.Fail());
            result = await target.Delete(Guid.NewGuid());
            result.Value.Status.Should().NotBe(0);

            groupAppService.DeleteAsync(Arg.Any<GroupInput>())
              .ReturnsForAnyArgs(RemotingResult.Fail(FailedCodes.Group_NotCreatedBy));
            result = await target.Delete(Guid.NewGuid());
            result.Value.Status.Should().Be(FailedCodes.Group_NotCreatedBy);
        }

        [TestMethod]
        public async Task TestQuit()
        {
            var groupAppService = Substitute.For<IGroupAppService>();

            var target = new GroupController(
                CreateMemoryCache(),
                CreateMapper(),
                Substitute.For<IDepartmentAppService>(),
                Substitute.For<IPositionAppService>(),
                groupAppService);
            target.ControllerContext = CreateMockContext();

            groupAppService.QuitAsync(Arg.Any<GroupInput>())
                .ReturnsForAnyArgs(RemotingResult.Success());
            var result = await target.Quit(Guid.NewGuid());
            result.Value.Should().NotBeNull();
            result.Value.Status.Should().Be(0);

            groupAppService.QuitAsync(Arg.Any<GroupInput>())
               .ReturnsForAnyArgs(RemotingResult.Fail());
            result = await target.Quit(Guid.NewGuid());
            result.Value.Status.Should().NotBe(0);
        }
    }
}


<|start_filename|>src/ConfigMgmtDomain/Entities/MsgNotifyBizSystem.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Runtime.Serialization;

namespace ConfigMgmt.Entities
{
    /// <summary>
    /// 可以发送消息提醒的业务系统
    /// </summary>
    [DataContract]
    public class MsgNotifyBizSystem
    {
        [DataMember]
        public string Id { get; set; }

        [DataMember]
        public string Name { get; set; }

        [DataMember]
        public string AppId { get; set; }

        /// <summary>
        /// 发送提醒的方式，可以多个
        /// </summary>
        [DataMember]
        public MsgNotifyApproach[] Approachs { get; set; }

        /// <summary>
        /// 发送提醒的模式，所有方式或者按顺序选择一个
        /// </summary>
        [DataMember]
        public bool UseAllApproachs { get; set; }

        /// <summary>
        /// 不同消息类别所具有的内容Pattern
        /// </summary>
        [DataMember]
        public Dictionary<int, string> PatternOfCategory { get; set; }
    }
}


<|start_filename|>src/InstantMessageApi/ViewModels/CreateDepConversationVm.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Text;

namespace InstantMessage.ViewModels
{
    public class CreateDepConversationVm
    {
        public Guid DepartmentId { get; set; }
    }
}


<|start_filename|>src/GroupFileApi/Params/GetFilesParam.cs<|end_filename|>
﻿using System;

namespace GroupFile.Params
{
    public class GetFilesParam
    {
        /// <summary>
        /// 群组Id
        /// </summary>
        public Guid GroupId { get; set; }
        /// <summary>
        /// 页数
        /// </summary>
        public int Page { get; set; }
        /// <summary>
        /// 分页大小
        /// </summary>
        public int PageSize { get; set; } = 20;
    }
}


<|start_filename|>src/NotifySessionActor/TheActorService.cs<|end_filename|>
﻿using Common;
using MassTransit;
using AppsOnSF.Common.Options;
using Microsoft.Diagnostics.EventFlow;
using Microsoft.ServiceFabric.Actors;
using Microsoft.ServiceFabric.Actors.Runtime;
using ServiceFabricContrib;
using System;
using System.Fabric;
using Notification;
using Notification.MsgContracts;

namespace NotifySessionActor
{
    public class TheActorService : ActorServiceEapBase
    {
        public TheActorService(StatefulServiceContext context,
            ActorTypeInformation actorTypeInfo,
            DiagnosticPipeline diagnosticPipeline,
            IBusControl bus = null,
            Func<ActorService, ActorId, ActorBase> actorFactory = null,
            Func<ActorBase, IActorStateProvider, IActorStateManager> stateManagerFactory = null,
            IActorStateProvider stateProvider = null,
            ActorServiceSettings settings = null)
            : base(context, actorTypeInfo, diagnosticPipeline, actorFactory, stateManagerFactory, stateProvider, settings)
        {
            if (bus == null)
                EventBus = CreateBus();
            else
                EventBus = bus;
        }

        private IBusControl CreateBus()
        {
            var optionRabbitMQ = Context.GetOption<RabbitMQOption>("RabbitMQ_ms");

            var hostAddress = optionRabbitMQ.HostAddress.EndsWith("/") ? optionRabbitMQ.HostAddress : optionRabbitMQ + "/";
            var endpointUri = new Uri(hostAddress + RabbitMqReceiveEndpointNames.NotifyGateway);

            EndpointConvention.Map<SendMessageNotify>(endpointUri);
            EndpointConvention.Map<SendEventNotify>(endpointUri);

            var bus = Bus.Factory.CreateUsingRabbitMq(cfg =>
            {
                cfg.Host(new Uri(optionRabbitMQ.HostAddress), h =>
                {
                    h.Username(optionRabbitMQ.Username);
                    h.Password(<PASSWORD>);
                });

            });

            return bus;
        }

        public IBusControl EventBus { get; }
    }
}


<|start_filename|>emulator/ClientEmulator/LoginControl.Designer.cs<|end_filename|>
﻿namespace ClientEmulator
{
    partial class LoginControl
    {
        /// <summary> 
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary> 
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Component Designer generated code

        /// <summary> 
        /// Required method for Designer support - do not modify 
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.backgroundWorker1 = new System.ComponentModel.BackgroundWorker();
            this.lab_Status = new System.Windows.Forms.Label();
            this.btn_login = new System.Windows.Forms.Button();
            this.txtToken = new System.Windows.Forms.TextBox();
            this.txtPassword = new System.Windows.Forms.TextBox();
            this.txtUserName = new System.Windows.Forms.TextBox();
            this.label4 = new System.Windows.Forms.Label();
            this.label5 = new System.Windows.Forms.Label();
            this.label6 = new System.Windows.Forms.Label();
            this.SuspendLayout();
            // 
            // lab_Status
            // 
            this.lab_Status.AutoSize = true;
            this.lab_Status.Location = new System.Drawing.Point(158, 106);
            this.lab_Status.Name = "lab_Status";
            this.lab_Status.Size = new System.Drawing.Size(47, 15);
            this.lab_Status.TabIndex = 18;
            this.lab_Status.Text = "43432";
            // 
            // btn_login
            // 
            this.btn_login.Location = new System.Drawing.Point(211, 102);
            this.btn_login.Name = "btn_login";
            this.btn_login.Size = new System.Drawing.Size(75, 23);
            this.btn_login.TabIndex = 17;
            this.btn_login.Text = "Login";
            this.btn_login.UseVisualStyleBackColor = true;
            this.btn_login.Click += new System.EventHandler(this.btn_login_Click);
            // 
            // txtToken
            // 
            this.txtToken.Location = new System.Drawing.Point(85, 71);
            this.txtToken.Name = "txtToken";
            this.txtToken.ReadOnly = true;
            this.txtToken.Size = new System.Drawing.Size(201, 25);
            this.txtToken.TabIndex = 16;
            // 
            // txtPassword
            // 
            this.txtPassword.Location = new System.Drawing.Point(85, 40);
            this.txtPassword.Name = "txtPassword";
            this.txtPassword.Size = new System.Drawing.Size(201, 25);
            this.txtPassword.TabIndex = 15;
            // 
            // txtUserName
            // 
            this.txtUserName.Location = new System.Drawing.Point(85, 8);
            this.txtUserName.Name = "txtUserName";
            this.txtUserName.Size = new System.Drawing.Size(201, 25);
            this.txtUserName.TabIndex = 14;
            // 
            // label4
            // 
            this.label4.AutoSize = true;
            this.label4.Location = new System.Drawing.Point(8, 72);
            this.label4.Name = "label4";
            this.label4.Size = new System.Drawing.Size(47, 15);
            this.label4.TabIndex = 13;
            this.label4.Text = "Token";
            // 
            // label5
            // 
            this.label5.AutoSize = true;
            this.label5.Location = new System.Drawing.Point(8, 42);
            this.label5.Name = "label5";
            this.label5.Size = new System.Drawing.Size(71, 15);
            this.label5.TabIndex = 12;
            this.label5.Text = "Password";
            // 
            // label6
            // 
            this.label6.AutoSize = true;
            this.label6.Location = new System.Drawing.Point(8, 11);
            this.label6.Name = "label6";
            this.label6.Size = new System.Drawing.Size(71, 15);
            this.label6.TabIndex = 11;
            this.label6.Text = "UserName";
            // 
            // LoginControl
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 15F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.Controls.Add(this.lab_Status);
            this.Controls.Add(this.btn_login);
            this.Controls.Add(this.txtToken);
            this.Controls.Add(this.txtPassword);
            this.Controls.Add(this.txtUserName);
            this.Controls.Add(this.label4);
            this.Controls.Add(this.label5);
            this.Controls.Add(this.label6);
            this.Name = "LoginControl";
            this.Size = new System.Drawing.Size(296, 132);
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

        private System.ComponentModel.BackgroundWorker backgroundWorker1;
        private System.Windows.Forms.Label lab_Status;
        private System.Windows.Forms.Button btn_login;
        private System.Windows.Forms.TextBox txtToken;
        public System.Windows.Forms.TextBox txtPassword;
        public System.Windows.Forms.TextBox txtUserName;
        private System.Windows.Forms.Label label4;
        private System.Windows.Forms.Label label5;
        private System.Windows.Forms.Label label6;
    }
}


<|start_filename|>src/ServiceCommon/AssemblyInfo.cs<|end_filename|>
﻿using Microsoft.ServiceFabric.Actors.Remoting.FabricTransport;
using Microsoft.ServiceFabric.Services.Remoting;
using Microsoft.ServiceFabric.Services.Remoting.FabricTransport;

[assembly: FabricTransportServiceRemotingProvider(RemotingListenerVersion = RemotingListenerVersion.V2, RemotingClientVersion = RemotingClientVersion.V2)]
[assembly: FabricTransportActorRemotingProvider(RemotingListenerVersion = RemotingListenerVersion.V2, RemotingClientVersion = RemotingClientVersion.V2)]



<|start_filename|>src/InstantMessageApi/Controllers/InstantMessageController.GetNew.cs<|end_filename|>
﻿using Common;
using EnterpriseContact;
using InstantMessage.Entities;
using InstantMessage.ViewModels;
using Microsoft.AspNetCore.Mvc;
using Notification;
using Serilog;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;

namespace InstantMessage.Controllers
{
    public partial class InstantMessageController
    {

        /// <summary>
        /// 拉取新消息
        /// </summary>
        /// <param name="model">拉取消息参数</param>
        /// <returns></returns>
        [HttpPost("message/getNew")]
        public async Task<ActionResult<List<ConversationVm>>> GetNewMessageListAsync(List<MessageNotifyDto> model)
        {
            try
            {
                if (model == null || model.Count == 0)
                {
                    return BadRequest();
                }

                //去重 唯一conversation
                var dictNotifies = new Dictionary<Guid, MessageNotifyDto>();

                foreach (var item in model)
                {
                    var id = item.TargetId.ToGuid();
                    if (!dictNotifies.ContainsKey(id))
                    {
                        dictNotifies.Add(id, item);
                    }
                }

                var convVms = await GenerateConversationVmsAsync(dictNotifies.Keys.ToList());

                var convMsgs = await GetConversationMsgsAsync(dictNotifies.Values.ToList());

                await GenerateMessagesAsync(convVms, convMsgs);

                return convVms;
            }
            catch (Exception ex)
            {
                return BadRequest(LogError(_logger, ex));
            }
        }

        private async Task GenerateMessagesAsync(List<ConversationVm> convVms, List<ConversationMsg> convMsgs)
        {
            var tasks = new List<Task<MessageVm>>();
            convMsgs.ForEach(o => tasks.Add(BuildMessageVmAsync(o)));
            await Task.WhenAll(tasks);

            tasks.ForEach(o =>
            {
                var msgVm = o.Result;
                var convVm = convVms.Find((r) => r.Id == msgVm.ConversationId);
                convVm?.MessageList?.Add(msgVm);
            });
        }

        private async Task<MessageVm> BuildMessageVmAsync(ConversationMsg msg)
        {
            var dtos = await _employeeMappingService.ByUserIdAsync(msg.SenderId);

            var name = (dtos == null || !dtos.Any()) ? "未知用户" : dtos.First().Name;
            var gender = (dtos == null || !dtos.Any()) ? 0 : dtos.First().Gender;
            var message = MessageVm.Create(msg).SetSenderInfo(name, gender);
            return message;
        }

        private async Task<List<ConversationVm>> GenerateConversationVmsAsync(List<Guid> input)
        {
            var currentUserId = GetUserId();

            var result = new List<ConversationVm>();

            var conversations = await _conversationCtrlAppService.GetByIdsAsync(input);

            #region generate p2p vm
            foreach (var c in conversations.Where(x => x.Type == ConversationType.P2P))
            {
                var vm = ConversationVm.Create(c);
                var dest = c.Participants.FirstOrDefault(o => !o.Equals(currentUserId));
                var dtos = await _employeeMappingService.ByUserIdAsync(dest);
                vm.Topic = (dtos == null || !dtos.Any()) ? "未知用户" : dtos.First().Name;
                result.Add(vm);
            }
            #endregion

            #region generate customgroup vm
            var ids = new List<Guid>();
            conversations.Where(x => x.Type == ConversationType.CustomGroup).ForEach(x =>
            {
                var vm = ConversationVm.Create(x);
                vm.Topic = "[未获取到群组名称]";
                result.Add(vm);
                ids.Add(x.Id);
            });
            if (ids.Count > 0)
            {
                var groups = await _groupAppService.GetListByIdsAsync(ids);
                groups.RemoveIfContains(null);
                foreach (var item in groups)
                {
                    var vm = result.Find(x => x.Id == item.Id);
                    vm.Topic = item.Name;
                }
            }
            #endregion

            #region generate departmentgroup vm
            ids = new List<Guid>();
            conversations.Where(x => x.Type == ConversationType.DepartmentGroup).ForEach(x =>
            {
                var vm = ConversationVm.Create(x);
                vm.Topic = "[未获取到群组名称]";
                result.Add(vm);
                ids.Add(x.Id);
            });
            if (ids.Count > 0)
            {
                var departments = await _departmentAppService.GetListByIdsAsync(ids);
                departments.RemoveIfContains(null);
                foreach (var item in departments)
                {
                    var vm = result.Find(x => x.Id == item.Id);
                    vm.Topic = item.Name;
                }
            }
            #endregion

            return result;
        }

        private async Task<List<ConversationMsg>> GetConversationMsgsAsync(List<MessageNotifyDto> input)
        {
            var tasks = new List<Task<List<ConversationMsg>>>();
            foreach (var appService in await _allConversationMsgAppServiceFactory())
            {
                tasks.Add(appService.GetMessagesAsync(input));
            }
            await Task.WhenAll(tasks);
            var result = new List<ConversationMsg>();
            tasks.ForEach(o => result.AddRange(o.Result));
            return result;
        }

    }
}


<|start_filename|>src/InstantMessageDomain/AppServices/IConversationCtrlAppService.cs<|end_filename|>
﻿using InstantMessage.Entities;
using Microsoft.ServiceFabric.Services.Remoting;
using Notification;
using ServiceFabricContrib;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace InstantMessage
{
    public interface IConversationCtrlAppService : IService
    {
        Task<Conversation> GetOrAddP2PAsync(Guid senderId, Guid receiverId);

        Task<Conversation?> GetByIdAsync(Guid id);

        /// <summary>
        /// 标记会话为删除
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        Task DeleteAsync(Guid id);

        /// <summary>
        /// 添加部门群组会话
        /// </summary>
        /// <param name="input"></param>
        /// <returns>FailedCode 1:NotInDepartment 2:OnlyOneMember</returns>
        Task<RemotingResult> AddDepAsync(AddDepConversationInput input);

        /// <summary>
        /// 基于Id集合得到会话
        /// </summary>
        /// <param name="ids"></param>
        /// <returns></returns>
        Task<List<Conversation>> GetByIdsAsync(List<Guid> ids);
    }
}


<|start_filename|>src/TodoCenterProxyApi/Extensions/Internal/JsonRpcRequest.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;

namespace TodoCenterProxyApi.Extensions.Internal
{
    internal class JsonRpcRequest<T> where T : class
    {

        [JsonProperty("jsonrpc")]
        public string JsonRpc { get; set; }

        [JsonProperty("method")]
        public string Method { get; set; }

        [JsonProperty("params")]
        public object Params { get; set; }

        [JsonProperty("id")]
        public int Id { get; set; }
    }
}


<|start_filename|>src/GroupFileApi/TheStartup.cs<|end_filename|>
﻿using Attachment;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace GroupFile
{
    public static class TheStartup
    {
        public static void ConfigureServices(IServiceCollection services)
        {
            services.AddSingleton(RemotingClient.CreateGroupFileControlAppService());
            services.AddTransient<Func<string, IAttachmentAppService>>(provider => Attachment.RemotingClient.CreateAttachmentAppService);
        }
    }
}


<|start_filename|>emulator/ClientEmulator/Config/ClientConfig.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ClientEmulator.Config
{
    public static class ClientConfig
    {
        public static string ClientId { get; set; }

        public static string ClientSercet { get; set; }

        public static string[] Scopes { get; set; }

        static ClientConfig()
        {
            ClientId = "OM_IM_Desktop_001";
            ClientSercet = "OMIMDesktop001";
            //openid profile profile.ext sso.sts eap.api
            Scopes = new[] { "openid", "sso.sts", "mp.apigateway", "profile", "profile.ext", "eap.api" };
        }
    }
}


<|start_filename|>src/AttachmentApi/Services/PhotoResizer.cs<|end_filename|>
﻿using PhotoSauce.MagicScaler;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace Attachment.Services
{
    public static class PhotoResizer
    {
        public const int DefaultThumbnailSize = 150;
        const int Quality = 75;

        public static void Resize(Stream inputStream, Stream outputStream, int oldWidth, int oldHeight, int size = DefaultThumbnailSize)
        {
            var scaled = ScaledSize(oldWidth, oldHeight, size);

            var settings = new ProcessImageSettings()
            {
                Width = scaled.width,
                Height = scaled.height,
                ResizeMode = CropScaleMode.Max,
                SaveFormat = FileFormat.Jpeg,
                JpegQuality = Quality,
                JpegSubsampleMode = ChromaSubsampleMode.Subsample420
            };
            MagicImageProcessor.ProcessImage(inputStream, outputStream, settings);
        }

        static (int width, int height) ScaledSize(int inWidth, int inHeight, int outSize)
        {
            int width, height;
            if (inWidth > inHeight)
            {
                width = outSize;
                height = (int)Math.Round(inHeight * outSize / (double)inWidth);
            }
            else
            {
                width = (int)Math.Round(inWidth * outSize / (double)inHeight);
                height = outSize;
            }

            return (width, height);
        }
    }
}


<|start_filename|>emulator/ClientEmulator/Services/ConversationService.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using ClientEmulator.Config;
using InstantMessage;
using InstantMessage.ViewModels;
using Newtonsoft.Json;
using Notification;

namespace ClientEmulator.Services
{
    public partial class ConversationService
    {
        private readonly HttpClient _httpClient;
        public ConversationService(HttpClient httpClient)
        {
            //httpClient.BaseAddress = new Uri(URLConfig.API_Host);
            _httpClient = httpClient;
        }

        /// <summary>
        /// create a p2p conversation
        /// </summary>
        /// <returns></returns>
        public async Task<Guid> CreateConversationAsync(string senderId, string reciverId, string accessToken)
        {
            //HttpClient must be Mananged,may be overflow!
            this._httpClient.SetBearerToken(accessToken);
            var param = new CreateP2PConversationVm()
            {
                SenderId = new Guid(senderId),
                RecieverId = new Guid(reciverId)
            };

            var response = await this._httpClient.PostAsync(BizUrlConfig.Create_Conversation_POST,
                new StringContent(JsonConvert.SerializeObject(param), Encoding.UTF8, "application/json"));
            var str = await response.Content.ReadAsStringAsync();
            str = str.Replace("\"", "");
            return new Guid(str);
        }

        /// <summary>
        /// send a text message
        /// </summary>
        /// <param name="conversationId"></param>
        /// <param name="senderId"></param>
        /// <param name="text"></param>
        /// <param name="accessToken"></param>
        /// <returns></returns>
        public async Task<SendMessageResult> SendTextAsync(string conversationId, string senderId, string text, string accessToken, ConversationType type)
        {
            this._httpClient.SetBearerToken(accessToken);
            var param = new TextMessageVm()
            {
                Content = text,
                ConversationId = new Guid(conversationId),
                ConversationType = type,
                SenderId = new Guid(senderId),
            };
            var response = await this._httpClient.PostAsync(BizUrlConfig.Send_Text_POST,
                new StringContent(JsonConvert.SerializeObject(param), Encoding.UTF8, "application/json"));
            var response_model = JsonConvert.DeserializeObject<SendMessageResult>(await response.Content.ReadAsStringAsync());
            return response_model;
        }

        public async Task<List<ConversationVm>> GetNewMessageListAsync(List<MessageNotifyDto> parma, string accessToken)
        {
            this._httpClient.SetBearerToken(accessToken);
            var response = await this._httpClient.PostAsync(BizUrlConfig.GetNewMessageList_POST,
                new StringContent(JsonConvert.SerializeObject(parma), Encoding.UTF8, "application/json"));
            var str = await response.Content.ReadAsStringAsync();
            var response_model =
                JsonConvert.DeserializeObject<List<ConversationVm>>(str);
            return response_model;
        }

        public async Task<List<MessageVm>> GetMessageHistryAsync(MessageNotifyDto parma, string accessToken)
        {
            this._httpClient.SetBearerToken(accessToken);
            var response = await this._httpClient.PostAsync(BizUrlConfig.GetMessageHistory_POST,
                new StringContent(JsonConvert.SerializeObject(parma), Encoding.UTF8, "application/json"));
            var str = await response.Content.ReadAsStringAsync();
            var response_model =
                JsonConvert.DeserializeObject<List<MessageVm>>(str);
            return response_model;
        }

        public async Task<string> DeleteP2PConversation(Guid conversationId, string accessToken)
        {
            this._httpClient.SetBearerToken(accessToken);
            var response = await this._httpClient.DeleteAsync(string.Format(BizUrlConfig.DeleteP2pConversation_DELETE, conversationId.ToString()));
            if (response.IsSuccessStatusCode)
                return "OK";
            return "BadRequest";
        }
    }
}


<|start_filename|>src/AttachmentApi/Controllers/AttachmentController.cs<|end_filename|>
﻿using Attachment.Entities;
using Attachment.Services;
using Attachment.ViewModels;
using Common;
using AppsOnSF.Common.Options;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
using Minio;
using Serilog;
using ServiceFabricContrib;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Text;
using System.Threading.Tasks;

namespace Attachment.Controllers
{
    [Route("api/v1/attachment")]
    public class AttachmentController : EapBaseController
    {
        private readonly Func<string, IAttachmentAppService> _attachmentAppServiceFactory;
        private readonly IAttachmentStoreService _attachmentFileStoreService;
        private readonly IMemoryCache _cache;
        private readonly ILogger _logger;

        public AttachmentController(Func<string, IAttachmentAppService> attachmentAppServiceFactory,
            IAttachmentStoreService attachmentFileStoreService,
            IMemoryCache cache)
        {
            _attachmentAppServiceFactory = attachmentAppServiceFactory;
            _attachmentFileStoreService = attachmentFileStoreService;
            _cache = cache;
            _logger = Log.ForContext<AttachmentController>();
        }

        /// <summary>
        /// 判断附件是否存在
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpGet("status/{id}")]
        public async Task<ActionResult<UploadStatus>> GetStatusAsync([Required]string id)
        {
            try
            {
                var appService = _attachmentAppServiceFactory(id);
                var dto = await appService.GetByIdAsync(id);
                if (dto == null) return UploadStatus.None;
                return dto.Status;
            }
            catch (Exception ex)
            {
                return BadRequest(LogError(_logger, ex));
            }
        }

        //[HttpPut("status/{id}")]
        //public async Task<IActionResult> UpdateStatusAsync([Required]string id, UploadStatus status)
        //{
        //    var result=await _attachmentAppService.UpdateStatusByIdAsync(id,status);
        //    if (result.IsSuccess) return Ok();
        //    return BadRequest();
        //}
        [HttpGet("thumbnails/{id}")]
        public async Task<IActionResult> DownloadThumbnailsAsync([Required]string id, string fileName)
        {
            try
            {
                var fileNameWithExt = string.IsNullOrEmpty(fileName) ? id + ".jpg" : fileName;
                if (_cache.TryGetValue(id, out byte[] thumbnailsData))
                    return File(thumbnailsData, "image/jpeg", fileNameWithExt);

                var appService = _attachmentAppServiceFactory(id);
                var dto = await appService.GetByIdAsync(id);
                if (dto == null) return NotFound();
                if (dto.Status < UploadStatus.Uploaded) return NotFound();
                if (string.IsNullOrEmpty(dto.Location)) return NotFound();

                using (var ms = new MemoryStream())
                {
                    await _attachmentFileStoreService.FillMemoryStreamAsync(dto, ms);

                    ms.Position = 0;
                    var thumbnailsImage = Image.FromStream(ms, false, false);
                    var thumbnailsStream = new MemoryStream();
                    ms.Position = 0;
                    PhotoResizer.Resize(ms, thumbnailsStream, thumbnailsImage.Width, thumbnailsImage.Height);
                    thumbnailsData = thumbnailsStream.ToArray();
                    _cache.Set(id, thumbnailsData, TimeSpan.FromMinutes(30));
                    return File(thumbnailsData, "image/jpeg", fileNameWithExt);
                    
                }
            }
            catch (Exception ex)
            {
                return BadRequest(LogError(_logger, ex));
            }
        }

        [HttpGet("{id}")]
        public async Task<IActionResult> DownloadFileAsync([Required]string id, string fileName)
        {
            try
            {
                var appService = _attachmentAppServiceFactory(id);
                var dto = await appService.GetByIdAsync(id);
                if (dto == null) return NotFound();
                if (dto.Status < UploadStatus.Uploaded) return NotFound();
                if (string.IsNullOrEmpty(dto.Location)) return NotFound();

                //ref: https://scottsauber.com/2019/02/25/dynamically-setting-content-type-in-asp-net-core-with-fileextensioncontenttypeprovider/
                var fileProvider = new FileExtensionContentTypeProvider();
                var fileNameWithExt = string.IsNullOrEmpty(fileName) ? Path.GetFileName(dto.Location) : fileName;
                if (!fileProvider.TryGetContentType(fileNameWithExt, out string contentType))
                {
                    contentType = "application/octet-stream";
                }

                var ms = new MemoryStream();
                await _attachmentFileStoreService.FillMemoryStreamAsync(dto, ms);
                ms.Position = 0;
                return File(ms, contentType, fileNameWithExt);
            }
            catch (Exception ex)
            {
                return BadRequest(LogError(_logger, ex));
            }
        }

        //ref: https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-2.2

        [HttpPost]
        public async Task<ActionResult<ResponseData<UploadStatus>>> UploadFileAsync([FromForm]UploadVm model)
        {
            if (model == null)
                throw new ArgumentNullException(nameof(model));

            if (model.File == null || string.IsNullOrEmpty(model.MD5))
                return ResponseData<UploadStatus>.BuildFailedResponse(message: "参数不正确");

            var appService = _attachmentAppServiceFactory(model.MD5);
            var dto = await appService.GetByIdAsync(model.MD5);
            if (dto != null && dto.Status > UploadStatus.Uploading)
                return ResponseData<UploadStatus>.BuildSuccessResponse(dto.Status);

            var now = DateTimeOffset.UtcNow;
            string objectName;
            try
            {
                objectName = await _attachmentFileStoreService.StoreFileAsync(model, now);

                if (dto == null)
                {
                    dto = new AttachmentItem
                    {
                        ContextId = model.ContextId,
                        Id = model.MD5,
                    };
                }
                dto.Location = objectName;
                dto.Size = (int)(model.File.Length / 1000);
                dto.Status = UploadStatus.Uploaded;
                dto.UploadBy = GetUserId();
                dto.Uploaded = now;
                try
                {
                    await appService.AddOrUpdateAsync(dto);
                }
                catch (Exception e)
                {
                    await _attachmentFileStoreService.TryDeleteFileAsync(objectName);
                    return BadRequest(LogError(_logger,e));
                }
                return ResponseData<UploadStatus>.BuildSuccessResponse(dto.Status);
            }
            catch (Exception ex)
            {
                return ResponseData<UploadStatus>.BuildFailedResponse(message: LogError(_logger, ex));
            }
        }
    }
}


<|start_filename|>src/EnterpriseContactDomain/Dtos/DepartmentListOutput.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Text;

namespace EnterpriseContact
{
    [Serializable]
    public class DepartmentListOutput
    {
        public Guid Id { get; set; }

        public Guid? ParentId { get; set; }

        public string Name { get; set; }

        public int? IconId { get; set; }
    }
}


<|start_filename|>emulator/ClientEmulator/Services/Models/UserInfo.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ClientEmulator.Services.Models
{
    public class UserInfo
    {
        /// <summary>
        /// SSO ID,type is guid
        /// </summary>
        public string UserId { get; set; }

        /// <summary>
        /// MDMID,type is guid
        /// </summary>
        public Guid UserMdmId { get; set; }

        public string UserName { get; set; }

        public string Name { get; set; }

        public string AccessToken { get; set; }

        public static List<UserInfo> LoginedUsers { get; set; } = new List<UserInfo>();
    }
}


<|start_filename|>src/NotifyGatewayWeb/Startup.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using AppsOnSF.Common.Options;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Notification;
using NotifyGatewayWeb.Hubs;
using NotifyGatewayWeb.EventBus;
using Microsoft.AspNetCore.DataProtection;
using AppsOnSF.Common.BaseServices;
using Microsoft.IdentityModel.Tokens;
using Common.Extensions;

namespace NotifyGatewayWeb
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddHealthChecks();

            //基础服务
#if !DEBUG
            services.AddDataProtection()
                .SetApplicationName("EAP-NotifyGateway")
                .PersistKeysToServiceFabric();
#endif

            //业务依赖
            services.AddScoped<Func<Guid, INotifySessionActor>>(u => RemotingClient.CreateNotifySessionActor);

            //SignalR
            var redisHosts = Configuration.GetSection("Redis").GetValue<string>("Hosts").Split(' ');
            services.AddSignalR()
#if DEBUG
                ;
#else
                .AddStackExchangeRedis(redisHosts[0]);
#endif

            //Authentication
            var idSvrOption = Configuration.GetSection("IdSvr").Get<IdSvrOption>();
            services.AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            })
            .AddJwtBearer(options =>
            {
                options.Authority = idSvrOption.IssuerUri;
                options.RequireHttpsMetadata = idSvrOption.RequireHttps;

                // Configure JWT Bearer Auth to expect our security key
                options.TokenValidationParameters =
                    new TokenValidationParameters
                    {
                        ValidateAudience = false,
                        ValidateIssuer = false,
                        ValidateActor = false,
                        ValidateLifetime = true,
                        LifetimeValidator = (before, expires, token, param) =>
                        {
                            return expires > DateTime.UtcNow;
                        },
                    };

                options.Events = new JwtBearerEvents
                {
                    OnMessageReceived = context =>
                    {
                        var accessToken = context.Request.Query["access_token"];
                        if (!string.IsNullOrEmpty(accessToken))
                        {
                            // Read the token out of the query string
                            context.Token = accessToken;
                        }
                        return Task.CompletedTask;
                    }
                };
            });

            services.AddEventBus();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            app.UseHealthChecks("/health", new HealthCheckOptions { Predicate = check => check.Tags.Contains("ready") });

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.ConfigureExceptionHandler();

            app.UseAuthentication();
            app.UseSignalR(routes =>
            {
                routes.MapHub<NotifyHub>("/notifyHub");
            });
        }
    }
}


<|start_filename|>src/EnterpriseContactApi/ViewModels/ParttimeJobListVm.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Text;

namespace EnterpriseContact.ViewModels
{
    public class ParttimeJobListVm
    {
        //public Guid DepartmentId { get; set; }

        /// <summary>
        /// 部门名称
        /// </summary>
        public List<string> DepartmentNames { get; set; }

        /// <summary>
        /// 职位名称
        /// </summary>
        public string PositionName { get; set; }
    }
}


<|start_filename|>src/EnterpriseContactApi/ViewModels/GroupDetailVm.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Text;

namespace EnterpriseContact.ViewModels
{
    public class GroupDetailVm : GroupListVm
    {
        public string Remark { get; set; }

        public List<GroupMemberListVm> Members { get; set; }
    }
}


<|start_filename|>emulator/ClientEmulator/Services/ConversationService.Group.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using ClientEmulator.Config;
using ClientEmulator.Services.Models;
using Common;
using EnterpriseContact.ViewModels;
using InstantMessage.ViewModels;
using Newtonsoft.Json;

namespace ClientEmulator.Services
{
    public partial class ConversationService
    {
        /// <summary>
        /// 创建一个群组,返回一个会话ID
        /// </summary>
        /// <returns></returns>
        public async Task<ResponseData<Guid>> CreateGroupAsync(UserInfo owner, UserInfo partner, string name, string remark)
        {
            this._httpClient.SetBearerToken(owner.AccessToken);
            var param = new GroupEditVm()
            {
                Name = name,
                Remark = remark,
                AddingMemberIds = new HashSet<Guid>() { partner.UserMdmId, owner.UserMdmId },
                AddingMemberNames = new List<string>() { partner.Name, owner.Name }
            };

            var response = await this._httpClient.PostAsync(BizUrlConfig.CreateGroup_POST,
                new StringContent(JsonConvert.SerializeObject(param), Encoding.UTF8, "application/json"));
            var str = await response.Content.ReadAsStringAsync();
            var responseModel = JsonConvert.DeserializeObject<ResponseData<Guid>>(str);
            return responseModel;
        }

        public async Task<ResponseData> QuitGroup(UserInfo userinfo, Guid groupId)
        {
            this._httpClient.SetBearerToken(userinfo.AccessToken);
            var response = await this._httpClient.PutAsync(string.Format(BizUrlConfig.QuitGroup_PUT, groupId), null);
            var str = await response.Content.ReadAsStringAsync();
            var model = JsonConvert.DeserializeObject<ResponseData>(str);
            return model;
        }

        public async Task<ResponseData> DeleteGroup(UserInfo userinfo, Guid groupId)
        {
            this._httpClient.SetBearerToken(userinfo.AccessToken);
            var response = await this._httpClient.DeleteAsync(string.Format(BizUrlConfig.DeleteGroup_DELETE, groupId));
            var str = await response.Content.ReadAsStringAsync();
            var model = JsonConvert.DeserializeObject<ResponseData>(str);
            return model;
        }
    }
}


<|start_filename|>src/ConfigMgmtDomain/Dtos/WorkbenchAppServiceInput.cs<|end_filename|>
﻿using ConfigMgmt.Entities;
using System;
using System.Collections.Generic;

namespace ConfigMgmt
{
    public class WorkbenchAppServiceInput
    {
        public string ClientId { get; set; }
    }

    public class GetAppEntrancesInput : WorkbenchAppServiceInput
    {
        public ClientPlatform ClientPlatform { get; set; }

        public List<string> UserRoles { get; set; }

        //public Guid UserId { get; set; }
    }

    public class AddEntrancesInput : WorkbenchAppServiceInput
    {
        public List<AppEntrance> AppEntrances { get; set; }
    }

    public class AppEntranceAuthStateInput
    {
        public Guid AppEntranceId { get; set; }
        public string DeviceCode { get; set; }
    }
}


<|start_filename|>test/EnterpriseContactUnitTest/AppServices/DepartmentAppServiceTest.cs<|end_filename|>
﻿using EnterpriseContact;
using EnterpriseContact.Entities;
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Threading.Tasks;

namespace EnterpriseContactUnitTest.AppServices
{

    [TestClass]
    public class DepartmentAppServiceTest : TheAppServiceTestBase
    {
        [TestMethod]
        public async Task TestGetRootListAsync()
        {
            var (connection, options) = OpenDb();

            try
            {
                using (var db = new ServiceDbContext(options))
                {
                    var dep1 = new Department
                    {
                        Id = Guid.NewGuid(),
                        Number = "1",
                        Name = "sclq"
                    };
                    var dep2 = new Department
                    {
                        Id = Guid.NewGuid(),
                        ParentId = dep1.Id,
                        Number = "2",
                        Name = "gs"
                    };
                    var dep3 = new Department
                    {
                        Id = Guid.NewGuid(),
                        ParentId = dep1.Id,
                        Number = "3",
                        Name = "jt"
                    };
                    await db.Departments.AddRangeAsync(dep1, dep2, dep3);

                    await db.SaveChangesAsync();
                }


                var target = new DepartmentAppService(statelessServiceContext, options, CreateMapper());

                var result = await target.GetRootListAsync();

                result.Count.Should().Be(2);
            }
            finally
            {
                connection.Close();
            }
        }

        [TestMethod]
        public async Task TestGetListByParentIdAsync()
        {
            var (connection, options) = OpenDb();

            try
            {
                var dep1 = new Department
                {
                    Id = Guid.NewGuid(),
                    Number = "1",
                    Name = "sclq"
                };
                var dep2 = new Department
                {
                    Id = Guid.NewGuid(),
                    ParentId = dep1.Id,
                    Number = "2",
                    Name = "gs"
                };
                var dep3 = new Department
                {
                    Id = Guid.NewGuid(),
                    ParentId = dep1.Id,
                    Number = "3",
                    Name = "jt"
                };

                using (var db = new ServiceDbContext(options))
                {
                    await db.Departments.AddRangeAsync(dep1, dep2, dep3);

                    await db.SaveChangesAsync();
                }


                var target = new DepartmentAppService(statelessServiceContext, options, CreateMapper());

                var result = await target.GetListByParentIdAsync(dep1.Id);

                result.Count.Should().Be(2);
            }
            finally
            {
                connection.Close();
            }
        }

        [TestMethod]
        public async Task TestSearchByKeywordAsync()
        {
            var (connection, options) = OpenDb();

            try
            {
                var dep1 = new Department
                {
                    Id = Guid.NewGuid(),
                    Number = "1",
                    Name = "sclq"
                };
                var dep2 = new Department
                {
                    Id = Guid.NewGuid(),
                    ParentId = dep1.Id,
                    Number = "2",
                    Name = "sclqgs"
                };
                var dep3 = new Department
                {
                    Id = Guid.NewGuid(),
                    ParentId = dep1.Id,
                    Number = "3",
                    Name = "sclqjt"
                };

                using (var db = new ServiceDbContext(options))
                {
                    await db.Departments.AddRangeAsync(dep1, dep2, dep3);

                    await db.SaveChangesAsync();
                }


                var target = new DepartmentAppService(statelessServiceContext, options, CreateMapper());

                var result = await target.SearchByKeywordAsync("sclq");
                result.Count.Should().Be(3);

                result = await target.SearchByKeywordAsync("gs");
                result.Count.Should().Be(1);
            }
            finally
            {
                connection.Close();
            }
        }
    }
}


<|start_filename|>emulator/ClientEmulator/ChatBox.Designer.cs<|end_filename|>
﻿using System.Windows.Forms;

namespace ClientEmulator
{
    partial class ChatBox
    {
        /// <summary> 
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary> 
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Component Designer generated code

        /// <summary> 
        /// Required method for Designer support - do not modify 
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.chat_messages = new System.Windows.Forms.RichTextBox();
            this.txt_message = new System.Windows.Forms.TextBox();
            this.btn_sendText = new System.Windows.Forms.Button();
            this.btn_connect = new System.Windows.Forms.Button();
            this.btn_disconnect = new System.Windows.Forms.Button();
            this.btn_creategroup = new System.Windows.Forms.Button();
            this.btn_quitGruop = new System.Windows.Forms.Button();
            this.btn_deleteGroup = new System.Windows.Forms.Button();
            this.btn_CreateConversation = new System.Windows.Forms.Button();
            this.label1 = new System.Windows.Forms.Label();
            this.lab_convarsationType = new System.Windows.Forms.Label();
            this.label2 = new System.Windows.Forms.Label();
            this.lab_conversationId = new System.Windows.Forms.Label();
            this.btn_todo = new System.Windows.Forms.Button();
            this.btn_complete = new System.Windows.Forms.Button();
            this.txt_component = new System.Windows.Forms.TextBox();
            this.label3 = new System.Windows.Forms.Label();
            this.btn_messageHistory = new System.Windows.Forms.Button();
            this.btn_deleteP2pConversation = new System.Windows.Forms.Button();
            this.SuspendLayout();
            // 
            // chat_messages
            // 
            this.chat_messages.Location = new System.Drawing.Point(6, 30);
            this.chat_messages.Name = "chat_messages";
            this.chat_messages.Size = new System.Drawing.Size(1102, 320);
            this.chat_messages.TabIndex = 0;
            this.chat_messages.Text = "";
            // 
            // txt_message
            // 
            this.txt_message.Location = new System.Drawing.Point(8, 355);
            this.txt_message.Name = "txt_message";
            this.txt_message.Size = new System.Drawing.Size(458, 25);
            this.txt_message.TabIndex = 1;
            // 
            // btn_sendText
            // 
            this.btn_sendText.Location = new System.Drawing.Point(473, 356);
            this.btn_sendText.Name = "btn_sendText";
            this.btn_sendText.Size = new System.Drawing.Size(75, 23);
            this.btn_sendText.TabIndex = 2;
            this.btn_sendText.Text = "SendText";
            this.btn_sendText.UseVisualStyleBackColor = true;
            this.btn_sendText.Click += new System.EventHandler(this.button1_Click);
            // 
            // btn_connect
            // 
            this.btn_connect.Location = new System.Drawing.Point(7, 4);
            this.btn_connect.Name = "btn_connect";
            this.btn_connect.Size = new System.Drawing.Size(75, 23);
            this.btn_connect.TabIndex = 3;
            this.btn_connect.Text = "Connect";
            this.btn_connect.UseVisualStyleBackColor = true;
            this.btn_connect.Click += new System.EventHandler(this.btn_connect_Click);
            // 
            // btn_disconnect
            // 
            this.btn_disconnect.Enabled = false;
            this.btn_disconnect.Location = new System.Drawing.Point(88, 4);
            this.btn_disconnect.Name = "btn_disconnect";
            this.btn_disconnect.Size = new System.Drawing.Size(75, 23);
            this.btn_disconnect.TabIndex = 4;
            this.btn_disconnect.Text = "disconnect";
            this.btn_disconnect.UseVisualStyleBackColor = true;
            this.btn_disconnect.Click += new System.EventHandler(this.btn_disconnect_Click);
            // 
            // btn_creategroup
            // 
            this.btn_creategroup.Location = new System.Drawing.Point(786, 354);
            this.btn_creategroup.Name = "btn_creategroup";
            this.btn_creategroup.Size = new System.Drawing.Size(107, 23);
            this.btn_creategroup.TabIndex = 5;
            this.btn_creategroup.Text = "CreateGroup";
            this.btn_creategroup.UseVisualStyleBackColor = true;
            this.btn_creategroup.Click += new System.EventHandler(this.btn_creategroup_Click);
            // 
            // btn_quitGruop
            // 
            this.btn_quitGruop.Location = new System.Drawing.Point(900, 354);
            this.btn_quitGruop.Name = "btn_quitGruop";
            this.btn_quitGruop.Size = new System.Drawing.Size(87, 23);
            this.btn_quitGruop.TabIndex = 6;
            this.btn_quitGruop.Text = "QuitGruop";
            this.btn_quitGruop.UseVisualStyleBackColor = true;
            this.btn_quitGruop.Click += new System.EventHandler(this.btn_quitGruop_Click);
            // 
            // btn_deleteGroup
            // 
            this.btn_deleteGroup.Location = new System.Drawing.Point(994, 354);
            this.btn_deleteGroup.Name = "btn_deleteGroup";
            this.btn_deleteGroup.Size = new System.Drawing.Size(111, 23);
            this.btn_deleteGroup.TabIndex = 7;
            this.btn_deleteGroup.Text = "DeleteGroup";
            this.btn_deleteGroup.UseVisualStyleBackColor = true;
            this.btn_deleteGroup.Click += new System.EventHandler(this.btn_deleteGroup_Click);
            // 
            // btn_CreateConversation
            // 
            this.btn_CreateConversation.Location = new System.Drawing.Point(614, 354);
            this.btn_CreateConversation.Name = "btn_CreateConversation";
            this.btn_CreateConversation.Size = new System.Drawing.Size(166, 23);
            this.btn_CreateConversation.TabIndex = 8;
            this.btn_CreateConversation.Text = "CreateP2PConversation";
            this.btn_CreateConversation.UseVisualStyleBackColor = true;
            this.btn_CreateConversation.Click += new System.EventHandler(this.btn_CreateConversation_Click);
            // 
            // label1
            // 
            this.label1.AutoSize = true;
            this.label1.Location = new System.Drawing.Point(170, 8);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(105, 15);
            this.label1.TabIndex = 9;
            this.label1.Text = "当前会话类型:";
            // 
            // lab_convarsationType
            // 
            this.lab_convarsationType.AutoSize = true;
            this.lab_convarsationType.Location = new System.Drawing.Point(281, 8);
            this.lab_convarsationType.Name = "lab_convarsationType";
            this.lab_convarsationType.Size = new System.Drawing.Size(97, 15);
            this.lab_convarsationType.TabIndex = 10;
            this.lab_convarsationType.Text = "还未创建会话";
            // 
            // label2
            // 
            this.label2.AutoSize = true;
            this.label2.Location = new System.Drawing.Point(421, 8);
            this.label2.Name = "label2";
            this.label2.Size = new System.Drawing.Size(127, 15);
            this.label2.TabIndex = 11;
            this.label2.Text = "ConversationId:";
            // 
            // lab_conversationId
            // 
            this.lab_conversationId.AutoSize = true;
            this.lab_conversationId.Location = new System.Drawing.Point(555, 8);
            this.lab_conversationId.Name = "lab_conversationId";
            this.lab_conversationId.Size = new System.Drawing.Size(0, 15);
            this.lab_conversationId.TabIndex = 12;
            // 
            // btn_todo
            // 
            this.btn_todo.Location = new System.Drawing.Point(614, 401);
            this.btn_todo.Name = "btn_todo";
            this.btn_todo.Size = new System.Drawing.Size(166, 23);
            this.btn_todo.TabIndex = 13;
            this.btn_todo.Text = "PullTodo";
            this.btn_todo.UseVisualStyleBackColor = true;
            this.btn_todo.Click += new System.EventHandler(this.btn_todo_Click);
            // 
            // btn_complete
            // 
            this.btn_complete.Location = new System.Drawing.Point(787, 401);
            this.btn_complete.Name = "btn_complete";
            this.btn_complete.Size = new System.Drawing.Size(106, 23);
            this.btn_complete.TabIndex = 14;
            this.btn_complete.Text = "PULL_DoneTask";
            this.btn_complete.UseVisualStyleBackColor = true;
            this.btn_complete.Click += new System.EventHandler(this.btn_complete_Click);
            // 
            // txt_component
            // 
            this.txt_component.Location = new System.Drawing.Point(109, 382);
            this.txt_component.Name = "txt_component";
            this.txt_component.Size = new System.Drawing.Size(357, 25);
            this.txt_component.TabIndex = 15;
            this.txt_component.Text = "ad8d496c-da1b-4f9b-8a96-d2e0f3a63812";
            // 
            // label3
            // 
            this.label3.AutoSize = true;
            this.label3.Location = new System.Drawing.Point(8, 388);
            this.label3.Name = "label3";
            this.label3.Size = new System.Drawing.Size(95, 15);
            this.label3.TabIndex = 16;
            this.label3.Text = "ComponentId";
            // 
            // btn_messageHistory
            // 
            this.btn_messageHistory.Location = new System.Drawing.Point(614, 377);
            this.btn_messageHistory.Name = "btn_messageHistory";
            this.btn_messageHistory.Size = new System.Drawing.Size(166, 23);
            this.btn_messageHistory.TabIndex = 17;
            this.btn_messageHistory.Text = "GetMessageHistroy";
            this.btn_messageHistory.UseVisualStyleBackColor = true;
            this.btn_messageHistory.Click += new System.EventHandler(this.btn_messageHistory_Click);
            // 
            // btn_deleteP2pConversation
            // 
            this.btn_deleteP2pConversation.Location = new System.Drawing.Point(787, 377);
            this.btn_deleteP2pConversation.Name = "btn_deleteP2pConversation";
            this.btn_deleteP2pConversation.Size = new System.Drawing.Size(200, 23);
            this.btn_deleteP2pConversation.TabIndex = 18;
            this.btn_deleteP2pConversation.Text = "DeleteP2PConversation";
            this.btn_deleteP2pConversation.UseVisualStyleBackColor = true;
            this.btn_deleteP2pConversation.Click += new System.EventHandler(this.btn_deleteP2pConversation_Click);
            // 
            // ChatBox
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 15F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.Controls.Add(this.btn_deleteP2pConversation);
            this.Controls.Add(this.btn_messageHistory);
            this.Controls.Add(this.label3);
            this.Controls.Add(this.txt_component);
            this.Controls.Add(this.btn_complete);
            this.Controls.Add(this.btn_todo);
            this.Controls.Add(this.lab_conversationId);
            this.Controls.Add(this.label2);
            this.Controls.Add(this.lab_convarsationType);
            this.Controls.Add(this.label1);
            this.Controls.Add(this.btn_CreateConversation);
            this.Controls.Add(this.btn_deleteGroup);
            this.Controls.Add(this.btn_quitGruop);
            this.Controls.Add(this.btn_creategroup);
            this.Controls.Add(this.btn_disconnect);
            this.Controls.Add(this.btn_connect);
            this.Controls.Add(this.btn_sendText);
            this.Controls.Add(this.txt_message);
            this.Controls.Add(this.chat_messages);
            this.Name = "ChatBox";
            this.Size = new System.Drawing.Size(1111, 429);
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

        private System.Windows.Forms.RichTextBox chat_messages;
        private System.Windows.Forms.TextBox txt_message;
        private System.Windows.Forms.Button btn_sendText;
        private System.Windows.Forms.Button btn_connect;
        private System.Windows.Forms.Button btn_disconnect;
        private System.Windows.Forms.Button btn_creategroup;
        private System.Windows.Forms.Button btn_quitGruop;
        private System.Windows.Forms.Button btn_deleteGroup;
        private System.Windows.Forms.Button btn_CreateConversation;
        private System.Windows.Forms.Label label1;
        private System.Windows.Forms.Label lab_convarsationType;
        private System.Windows.Forms.Label label2;
        private System.Windows.Forms.Label lab_conversationId;
        private Button btn_todo;
        private Button btn_complete;
        private TextBox txt_component;
        private Label label3;
        private Button btn_messageHistory;
        private Button btn_deleteP2pConversation;
    }
}


<|start_filename|>emulator/ClientEmulator/Config/URLConfig.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ClientEmulator.Config
{
    public static class URLConfig
    {
        public static string Login_Host { get; set; }

        public static string API_Host { get; set; }

        public static string Hub_Host { get; set; }
    }
}


<|start_filename|>src/EnterpriseContactService/Consumers/OrgEventDataConsumer.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using AutoMapper;
using Base.Mdm.Org.MsgContracts;
using EFCore.BulkExtensions;
using EnterpriseContact.Entities;
using EnterpriseContact.MsgConstracts;
using MassTransit;
using Microsoft.EntityFrameworkCore;

namespace EnterpriseContact.Consumers
{
    public class OrgEventDataConsumer : IConsumer<OrgDataEvent>
    {
        protected readonly IMapper _mapper;
        protected readonly DbContextOptions<ServiceDbContext> _dbOptions;
        private readonly IBusControl _busMs;

        public OrgEventDataConsumer(IMapper mapper, DbContextOptions<ServiceDbContext> dbOptions, IBusControl busMs)
        {
            _mapper = mapper;
            _dbOptions = dbOptions;
            _busMs = busMs;
        }

        public async Task Consume(ConsumeContext<OrgDataEvent> context)
        {
            var message = context.Message;
            await this.AddOrgUnits(message.OrgUnitAddeds);
            await this.UpdateOrgUnits(message.OrgUnitUpdateds);
            await this.DeleteOrgUnits(message.OrgUnitDeleteds);
            await this.AddContacts(message.ContactAddeds);
            await this.UpdateContacts(message.ContactUpdateds);
            await this.DeleteContacts(message.ContactDeleteds);

            await PublishDepartmentEvent(message);
        }

        private async Task PublishDepartmentEvent(OrgDataEvent message)
        {
            foreach (var item in message.OrgUnitDeleteds)
            {
                await _busMs.Publish(new DepartmentDeleted { Id = item.OldData.Id });
            }

            var memberUpdatedDepartmentIds = new HashSet<Guid>();
            message.ContactAddeds.ForEach(x =>
                x.NewData.Positions.ForEach(y => memberUpdatedDepartmentIds.Add(y.OrgUnitId)));
            message.ContactUpdateds.ForEach(x =>
            {
                x.OldData.Positions.ForEach(y => memberUpdatedDepartmentIds.Add(y.OrgUnitId));
                x.NewData.Positions.ForEach(y => memberUpdatedDepartmentIds.Add(y.OrgUnitId));
            });
            message.ContactDeleteds.ForEach(x =>
               x.OldData.Positions.ForEach(y => memberUpdatedDepartmentIds.Add(y.OrgUnitId)));

            foreach (var item in memberUpdatedDepartmentIds)
            {
                await _busMs.Publish(new DepartmentMembersUpdated { Id = item });
            }
        }

        private async Task DeleteOrgUnits(List<OrgUnitDeletedMsg> adds)
        {
            var orgUnits = this._mapper.Map<IList<Department>>(adds.Select(u => u.OldData));
            var positons = orgUnits.SelectMany(u => u.Positions).ToList();
            using (var db = new ServiceDbContext(_dbOptions))
            {
                await db.BulkDeleteAsync(orgUnits);
                await db.BulkDeleteAsync(positons);
            }
        }

        private async Task UpdateOrgUnits(List<OrgUnitUpdatedMsg> updates)
        {
            var orgUnits = this._mapper.Map<IList<Department>>(updates.Select(u => u.OldData));
            var positons = orgUnits.SelectMany(u => u.Positions).ToList();
            using (var db = new ServiceDbContext(_dbOptions))
            {
                await db.BulkUpdateAsync(orgUnits);
                await db.BulkUpdateAsync(positons);
            }
        }

        private async Task AddOrgUnits(List<OrgUnitAddedMsg> adds)
        {
            var orgUnits = this._mapper.Map<IList<Department>>(adds.Select(u => u.NewData));
            var positons = orgUnits.SelectMany(u => u.Positions).ToList();
            using (var db = new ServiceDbContext(_dbOptions))
            {
                await db.BulkInsertOrUpdateAsync(orgUnits);
                await db.BulkInsertOrUpdateAsync(positons);
            }
        }

        /// <summary>
        /// 删除联系人,
        /// </summary>
        /// <param name="deletes"></param>
        /// <returns></returns>
        private async Task DeleteContacts(List<ContactDeletedMsg> deletes)
        {
            var employees = this._mapper.Map<IList<Employee>>(deletes.Select(u => u.OldData));
            using (var db = new ServiceDbContext(_dbOptions))
            {
                await db.BulkDeleteAsync(employees);
                await db.SaveChangesAsync();
            }
        }

        /// <summary>
        /// 更新联系人
        /// </summary>
        /// <param name="updates"></param>
        /// <returns></returns>
        private async Task UpdateContacts(List<ContactUpdatedMsg> updates)
        {
            var employees = this._mapper.Map<IList<Employee>>(updates.Select(u => u.NewData));
            using (var db = new ServiceDbContext(_dbOptions))
            {
                await db.BulkUpdateAsync(employees);
                await db.SaveChangesAsync();
            }
        }

        /// <summary>
        /// 添加联系人
        /// </summary>
        /// <param name="adds"></param>
        /// <returns></returns>
        private async Task AddContacts(IList<ContactAddedMsg> adds)
        {
            var employees = this._mapper.Map<IList<Employee>>(adds.Select(u => u.NewData));
            using (var db = new ServiceDbContext(_dbOptions))
            {
                await db.BulkInsertAsync(employees);
                await db.SaveChangesAsync();
            }
        }
    }
}


<|start_filename|>src/GroupFileApi/Params/UploadParam.cs<|end_filename|>
﻿using Microsoft.AspNetCore.Http;
using System;

namespace GroupFile.Params
{
    /// <summary>
    /// 文件上传参数
    /// </summary>
    public class UploadParam
    {
        /// <summary>
        /// 群组id
        /// </summary>
        public Guid GroupId { get; set; }
        /// <summary>
        /// 文件的md5值
        /// </summary>
        public string MD5 { get; set; }
        /// <summary>
        /// 文件
        /// </summary>
        public IFormFile File { get; set; }
    }
}


<|start_filename|>test/ConfigMgmtUnitTest/AppServices/BizSystemAppServiceTest.cs<|end_filename|>
﻿using ConfigMgmt;
using ConfigMgmt.Entities;
using FluentAssertions;
using Microsoft.ServiceFabric.Data.Collections;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Generic;
using System.Threading.Tasks;
using SystemConfigStateService;

namespace ConfigMgmtUnitTest.AppServices
{
    [TestClass]
    public class BizSystemAppServiceTest : TheAppServiceTestBase
    {
        [TestMethod]
        public async Task TestAddOrUpdateForTodoCenterAsync()
        {
            var dictBizSystem = await stateManager.GetOrAddAsync<IReliableDictionary2<string, TodoCenterBizSystem>>(Service.DictionaryName_TodoCenterBizSystem);

            var target = new BizSystemAppService(statefulServiceContext, stateManager);
            var result = await target.AddOrUpdateForTodoCenterAsync(new TodoCenterBizSystem
            {
                Id = "test",
                Name = "测试",
                PendingListUrl = "about:",
                PendingListAmountUrl = "about:",
                DoneListUrl = "about:",
                OpenDetailUris = new Dictionary<ClientPlatform, string>
                {
                    [ClientPlatform.Android] = "about:"
                }
            });
            Assert.IsTrue(result);
        }

        [TestMethod]
        public async Task Test_GetAllForTodoCenterAsync_RemoveForTodoCenterAsync()
        {
            var dictBizSystem = await stateManager.GetOrAddAsync<IReliableDictionary2<string, TodoCenterBizSystem>>(Service.DictionaryName_TodoCenterBizSystem);

            using (var tx = stateManager.CreateTransaction())
            {
                await dictBizSystem.AddAsync(tx, "aaa", new TodoCenterBizSystem
                {
                    Id = "aaa",
                    Name = "aaa"
                });
                await dictBizSystem.AddAsync(tx, "bbb", new TodoCenterBizSystem
                {
                    Id = "bbb",
                    Name = "bbb"
                });
                await tx.CommitAsync();
            }

            var target = new BizSystemAppService(statefulServiceContext, stateManager);
            var result = await target.GetAllForTodoCenterAsync();
            result.Should().NotBeNull();
            result.Count.Should().Be(2);

            var removeResult = await target.RemoveForTodoCenterAsync("bbb");
            removeResult.Should().BeTrue();

            result = await target.GetAllForTodoCenterAsync();
            result.Should().NotBeNull();
            result.Count.Should().Be(1);
        }

        [TestMethod]
        public async Task TestAddOrUpdateForMsgNotifyAsync()
        {
            var dict = await stateManager.GetOrAddAsync<IReliableDictionary2<string, MsgNotifyBizSystem>>(Service.DictionaryName_MsgNotifyBizSystem);

            var target = new BizSystemAppService(statefulServiceContext, stateManager);
            var result = await target.AddOrUpdateForMsgNotifyAsync(new MsgNotifyBizSystem
            {
                Id = "test",
                Name = "测试",
            });
            Assert.IsTrue(result);
        }

        [TestMethod]
        public async Task Test_GetAllForMsgNotifyAsync_RemoveForMsgNotifyAsync()
        {
            var dict = await stateManager.GetOrAddAsync<IReliableDictionary2<string, MsgNotifyBizSystem>>(Service.DictionaryName_MsgNotifyBizSystem);

            using (var tx = stateManager.CreateTransaction())
            {
                await dict.AddAsync(tx, "aaa", new MsgNotifyBizSystem
                {
                    Id = "aaa",
                    Name = "aaa"
                });
                await dict.AddAsync(tx, "bbb", new MsgNotifyBizSystem
                {
                    Id = "bbb",
                    Name = "bbb"
                });
                await tx.CommitAsync();
            }

            var target = new BizSystemAppService(statefulServiceContext, stateManager);
            var result = await target.GetAllForMsgNotifyAsync();
            result.Should().NotBeNull();
            result.Count.Should().Be(2);

            var removeResult = await target.RemoveForMsgNotifyAsync("bbb");
            removeResult.Should().BeTrue();

            result = await target.GetAllForMsgNotifyAsync();
            result.Should().NotBeNull();
            result.Count.Should().Be(1);

            var singleResult = await target.GetByIdForNotifyMsgAsync("aaa");
            singleResult.Should().NotBeNull();
            singleResult.Name.Should().Be("aaa");
        }

    }
}


<|start_filename|>src/NotifyDomain/Constants.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Notification
{
    public static class Constants
    {
    }
}


<|start_filename|>test/NotifyUnitTest/NotifySessionActorTest.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Common;
using MassTransit;
using Microsoft.Diagnostics.EventFlow;
using Microsoft.ServiceFabric.Actors;
using Microsoft.ServiceFabric.Actors.Runtime;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using Notification;
using Notification.MsgContracts;
using NotifySessionActor;
using NSubstitute;
using ServiceFabric.Mocks;
using UnitTestCommon;

namespace NotifySessionActorTest
{
    [TestClass]
    public class NotifySessionActorTest : TheActorTestBase
    {
        [TestMethod]
        public async Task Test_AddConnectionIdAsync_RemoveConnectionIdAsync()
        {
            var userId = Guid.NewGuid();
            var connectionId = Guid.NewGuid().ToString("N");

            var actor = new TheActor(CreateActorService(CreateBus(null)), new ActorId(userId));
            actor.IsInUnitTest = true;

            await actor.AddConnectionIdAsync(connectionId);
            var connectionIds = await actorStateManager.GetStateAsync<List<string>>(TheActor.ConnectionIdListPropertyName);
            Assert.IsTrue(connectionIds.Count == 1);  //添加成功

            await actor.RemoveConnectionIdAsync(Guid.NewGuid().ToString("N"));
            connectionIds = await actorStateManager.GetStateAsync<List<string>>(TheActor.ConnectionIdListPropertyName);
            Assert.IsTrue(connectionIds.Count == 1); //没有被删除掉

            await actor.RemoveConnectionIdAsync(connectionId);
            connectionIds = await actorStateManager.GetStateAsync<List<string>>(TheActor.ConnectionIdListPropertyName);
            Assert.IsTrue(connectionIds == null || connectionIds.Count == 0); //删除掉
        }

        [TestMethod]
        public async Task Test_PushEventNotifyAsync_WhenHaveConnection()
        {
            var userId = Guid.NewGuid();
            var connectionId = Guid.NewGuid().ToString("N");
            var conversationId = Guid.NewGuid();

            void configBus(IInMemoryReceiveEndpointConfigurator e)
            {
                e.Handler<SendEventNotify>(context =>
                {
                    var notify = context.Message;

                    Assert.IsNotNull(notify);
                    Assert.AreEqual(1, notify.Notifies.Count);
                    Assert.AreEqual(conversationId.ToString(), notify.Notifies[0].TargetId);
                    Assert.AreEqual(1, notify.Notifies[0].TargetCategory);
                    Assert.AreEqual("Test", notify.Notifies[0].Text);

                    return Task.CompletedTask;
                });

                MapEndpointConverion(e);
            };

            var actor = new TheActor(CreateActorService(CreateBus(configBus)), new ActorId(userId));
            actor.IsInUnitTest = true;

            await actor.AddConnectionIdAsync(connectionId);
            await actor.PushEventNotifyAsync(new EventNotifyDto
            {
                Target = NotifyTargetType.Conversation,
                TargetId = conversationId.ToString(),
                Text = "Test",
                TargetCategory = 1
            });
            await Task.Delay(500);
        }

        private static void MapEndpointConverion(IInMemoryReceiveEndpointConfigurator e)
        {
            try
            {
                EndpointConvention.Map<SendMessageNotify>(e.InputAddress);
                EndpointConvention.Map<SendEventNotify>(e.InputAddress);
            }
            catch
            {
            }
        }

        [TestMethod]
        public async Task Test_PushMsgNotifyAsync_WhenHaveConnection()
        {
            var userId = Guid.NewGuid();
            var connectionId = Guid.NewGuid().ToString("N");
            var conversationId = Guid.NewGuid();
            var msgId = Guid.NewGuid();

            void configBus(IInMemoryReceiveEndpointConfigurator e)
            {
                e.Handler<SendMessageNotify>(context =>
                {
                    var notify = context.Message;

                    Assert.IsNotNull(notify);
                    Assert.AreEqual(1, notify.Notifies.Count);
                    Assert.AreEqual(conversationId.ToString(), notify.Notifies[0].TargetId);
                    Assert.AreEqual(msgId, notify.Notifies[0].LatestMsgId);
                    Assert.AreEqual(1, notify.Notifies[0].TargetCategory);

                    return Task.CompletedTask;
                });

                MapEndpointConverion(e);
            };

            var actor = new TheActor(CreateActorService(CreateBus(configBus)), new ActorId(userId));
            actor.IsInUnitTest = true;

            await actor.AddConnectionIdAsync(connectionId);
            await actor.PushMsgNotifyAsync(new MessageNotifyDto
            {
                Target = NotifyTargetType.Conversation,
                TargetId = conversationId.ToString(),
                TargetCategory = 1,
                LatestMsgId = msgId,
            });
            await Task.Delay(500);
        }

        [TestMethod]
        public async Task Tesh_PushMsgNotifyAsync_WhenNotHaveConnection()
        {
            var userId = Guid.NewGuid();
            var conversationId = Guid.NewGuid();
            var msgId = Guid.NewGuid();

            void configBus(IInMemoryReceiveEndpointConfigurator e)
            {
                e.Handler<SendMessageNotify>(context =>
                {
                    var notify = context.Message;

                    Assert.Fail();

                    return Task.CompletedTask;
                });

                MapEndpointConverion(e);
            };

            var actor = new TheActor(CreateActorService(CreateBus(configBus)), new ActorId(userId));
            actor.IsInUnitTest = true;

            await actor.PushMsgNotifyAsync(new MessageNotifyDto
            {
                Target = NotifyTargetType.Conversation,
                TargetId = conversationId.ToString(),
                TargetCategory = 1,
                LatestMsgId = msgId,
            });
            var listPushNotify = await actorStateManager.GetStateAsync<List<MessageNotifyDto>>(
                TheActor.MessageListPropertyName);
            Assert.IsTrue(listPushNotify.Count > 0);
            var dto = listPushNotify.Find(o => o.TargetId == conversationId.ToString());
            Assert.IsNotNull(dto);
            Assert.AreEqual(msgId, dto.LatestMsgId);
        }
    }
}


<|start_filename|>src/ConfigMgmtApi/ApiClients/IBadgeApiClient.cs<|end_filename|>
﻿using System.Threading.Tasks;
using ConfigMgmt.Entities;
using Microsoft.AspNetCore.Http;

namespace ConfigMgmt
{
    //TODO 未来重构移动到AppComponentDomain项目中

    public interface IBadgeApiClient
    {
        Task<int?> GetAmountAsync(ComponentConfig config);
    }
}

<|start_filename|>src/ConversationCtrlStateService/EventBus/DepartmentMembersUpdatedConsumer.cs<|end_filename|>
﻿using ConversationCtrlStateService.DomainServices;
using EnterpriseContact;
using EnterpriseContact.MsgConstracts;
using InstantMessage;
using MassTransit;
using Microsoft.ServiceFabric.Data;
using Serilog;
using ServiceFabricContrib;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;

namespace ConversationCtrlStateService.EventBus
{
    public class DepartmentMembersUpdatedConsumer : IConsumer<DepartmentMembersUpdated>
    {
        private readonly IReliableStateManager _stateManager;
        private readonly ConversationManager _manager;
        private readonly ILogger _logger;
        private readonly IEmployeeAppService _employeeAppService;

        public DepartmentMembersUpdatedConsumer(IReliableStateManager stateManager, IEmployeeAppService employeeAppService)
        {
            _stateManager = stateManager;
            _manager = new ConversationManager(stateManager);
            _logger = Log.ForContext<DepartmentMembersUpdatedConsumer>();
            _employeeAppService = employeeAppService;
        }

        public async Task Consume(ConsumeContext<DepartmentMembersUpdated> context)
        {
            try
            {
                var dictList = await Service.GetListDictAsync(_stateManager);
                using (var tx = _stateManager.CreateTransaction())
                {
                    var entityWrap = await dictList.TryGetValueAsync(tx, context.Message.Id);
                    if (entityWrap.HasValue)
                    {
                        var participants = await _employeeAppService.GetUserIdsByDepartmentIdAsync(context.Message.Id);

                        var edited = entityWrap.Value.DeepCopy();
                        edited.Participants = participants;
                        await dictList.TryUpdateAsync(tx, context.Message.Id, edited, entityWrap.Value);
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.Error(ex, ex.Message);
                throw;
            }
        }
    }
}


<|start_filename|>src/ServiceCommon/StatelessServiceEapBase.cs<|end_filename|>
﻿using AppsOnSF.Common.Options;
using Microsoft.Diagnostics.EventFlow;
using Microsoft.ServiceFabric.Services.Runtime;
using Serilog;
using ServiceFabricContrib;
using System;
using System.Collections.Generic;
using System.Fabric;
using System.Text;

namespace Common
{
    public abstract class StatelessServiceEapBase : StatelessService
    {
        public StatelessServiceEapBase(StatelessServiceContext context,
            DiagnosticPipeline diagnosticPipeline)
            : base(context)
        {
            CreateSerilog(diagnosticPipeline);
        }

        private void CreateSerilog(DiagnosticPipeline diagnosticPipeline)
        {
            var option = Context.GetOption<DiagnosticsOption>("Diagnostics");

            Log.Logger = new LoggerConfiguration()
                          .MinimumLevel.Is(option.SerilogEventLevel)
                          .WriteTo.Debug()
                          .WriteTo.EventFlow(diagnosticPipeline)
                          .CreateLogger();
        }
    }
}


<|start_filename|>test/NotifyUnitTest/TheActorTestBase.cs<|end_filename|>
using MassTransit;
using Microsoft.Diagnostics.EventFlow;
using Microsoft.Diagnostics.EventFlow.ServiceFabric;
using Microsoft.ServiceFabric.Actors.Runtime;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using NotifySessionActor;
using System;
using UnitTestCommon;

namespace NotifySessionActorTest
{
    [TestClass]
    public class TheActorTestBase : AppServiceTestBase
    {
        protected TheActorService CreateActorService(IBusControl bus)
        {
            //var actorName = typeof(TheActor).FullName;
            //var assembly = typeof(TheActor).Assembly;
            //var actorType = assembly.GetType(actorName);
            var actorTypeInformation = ActorTypeInformation.Get(typeof(TheActor));
            var service = new TheActorService(
                statefulServiceContext,
                actorTypeInformation,
                null,
                bus,
                null,
                actorStateManagerFactory
                );
            return service;
        }
    }
}


<|start_filename|>src/GroupFileService/Service.cs<|end_filename|>
﻿using AutoMapper;
using GroupFile.Models;
using Microsoft.Diagnostics.EventFlow;
using Microsoft.EntityFrameworkCore;
using Microsoft.ServiceFabric.Services.Communication.Runtime;
using Microsoft.ServiceFabric.Services.Remoting.V2.FabricTransport.Runtime;
using Microsoft.ServiceFabric.Services.Runtime;
using ServiceFabricContrib;
using System.Collections.Generic;
using System.Fabric;
using System.Threading;
using System.Threading.Tasks;
using static Common.Constants;

namespace GroupFile
{
    /// <summary>
    /// An instance of this class is created for each service instance by the Service Fabric runtime.
    /// </summary>
    internal sealed class Service : StatelessService
    {
        private readonly DiagnosticPipeline _diagnosticPipeline;
        private string _dbConnectString;
        private readonly IMapper _mapper;
        private DbContextOptions<ServiceDbContext> _dbOptions;

        public Service(StatelessServiceContext context,
           DiagnosticPipeline diagnosticPipeline)
            : base(context)
        {
            _diagnosticPipeline = diagnosticPipeline;
            var config = new MapperConfiguration(cfg =>
            {
                cfg.AddProfile<MappingProfile>();
            });
            this._mapper = config.CreateMapper();
            this._dbConnectString = Context.GetOption<DBOption>("ConnectionStrings").ServiceDbConnection;
            _dbOptions = CreateDbOptions(_dbConnectString);
        }

        /// <summary>
        /// Optional override to create listeners (e.g., TCP, HTTP) for this service replica to handle client or user requests.
        /// </summary>
        /// <returns>A collection of listeners.</returns>
        protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
        {
            return new[]
            {
                new ServiceInstanceListener(context => new FabricTransportServiceRemotingListener(context,
                    new ControlAppService(context,_dbOptions,this._mapper)),ListenerName_ControlAppService),
            };
        }
        
        private static DbContextOptions<ServiceDbContext> CreateDbOptions(string connectionString)
        {
            var builder = new DbContextOptionsBuilder<ServiceDbContext>()
                   .UseSqlServer(connectionString);
            return builder.Options;
        }

        protected override async Task OnOpenAsync(CancellationToken cancellationToken)
        {
            using (var db = new ServiceDbContext(_dbOptions))
            {
                await db.Database.MigrateAsync();
            }
        }
    }
}


<|start_filename|>src/ServiceCommon/ActorServiceEapBase.cs<|end_filename|>
﻿using MassTransit;
using AppsOnSF.Common.Options;
using Microsoft.Diagnostics.EventFlow;
using Microsoft.ServiceFabric.Actors;
using Microsoft.ServiceFabric.Actors.Runtime;
using Microsoft.ServiceFabric.Services.Remoting;
using Serilog;
using ServiceFabricContrib;
using System;
using System.Collections.Generic;
using System.Fabric;
using System.Text;

namespace Common
{
    public class ActorServiceEapBase : ActorService, IActorService, IService
    {
        public ActorServiceEapBase(StatefulServiceContext context,
            ActorTypeInformation actorTypeInfo,
            DiagnosticPipeline diagnosticPipeline,
            Func<ActorService, ActorId, ActorBase> actorFactory = null,
            Func<ActorBase, IActorStateProvider, IActorStateManager> stateManagerFactory = null,
            IActorStateProvider stateProvider = null, ActorServiceSettings settings = null)
            : base(context, actorTypeInfo, actorFactory, stateManagerFactory, stateProvider, settings)
        {
            CreateSerilog(diagnosticPipeline);
        }

        private void CreateSerilog(DiagnosticPipeline diagnosticPipeline)
        {
            var option = diagnosticPipeline == null
                ? new DiagnosticsOption { SerilogEventLevel = Serilog.Events.LogEventLevel.Debug }
                : Context.GetOption<DiagnosticsOption>("Diagnostics");

            var logConfig = new LoggerConfiguration()
                          .MinimumLevel.Is(option.SerilogEventLevel)
                          .WriteTo.Debug();
            if (diagnosticPipeline != null)
                logConfig.WriteTo.EventFlow(diagnosticPipeline);
            Log.Logger = logConfig.CreateLogger();
        }
    }
}


<|start_filename|>src/EnterpriseContactDomain/Services/EmployeeCacheService.cs<|end_filename|>
﻿using Common;
using EnterpriseContact;
using Microsoft.Extensions.Caching.Memory;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EnterpriseContact.Services
{
    public class EmployeeCacheService : IEmployeeCacheService
    {
        private readonly IMemoryCache _cache;
        private readonly IEmployeeAppService _employeeAppService;

        public EmployeeCacheService(IMemoryCache cache,
            IEmployeeAppService employeeAppService)
        {
            _cache = cache;
            _employeeAppService = employeeAppService;
        }

        class MappingData
        {
            public List<EmployeeCacheOutput> List = new List<EmployeeCacheOutput>();

            public Dictionary<string, EmployeeCacheOutput> ByNumber = new Dictionary<string, EmployeeCacheOutput>();
            public Dictionary<Guid, EmployeeCacheOutput> ByUserId = new Dictionary<Guid, EmployeeCacheOutput>();
            public Dictionary<Guid, EmployeeCacheOutput> ById = new Dictionary<Guid, EmployeeCacheOutput>();

        }

        private async Task<MappingData> EnsureLoadMappingData()
        {
            if (!_cache.TryGetValue(CacheKeys.Common_EmployeeList, out MappingData data))
            {
                data = new MappingData();
                var dto = await _employeeAppService.GetCacheDataAsync();
                if (dto?.Count > 0)
                {
                    data.List = dto;

                    foreach (var item in dto)
                    {
                        if (!string.IsNullOrEmpty(item.Number))
                            data.ByNumber[item.Number] = item;

                        if (item.UserId.HasValue)
                            data.ByUserId[item.UserId.Value] = item;

                        data.ById[item.Id] = item;
                    }
                }

                var cacheEntryOptions = new MemoryCacheEntryOptions()
                   .SetAbsoluteExpiration(CacheKeys.DefaultCacheAbsoluteExpiration);

                _cache.Set(CacheKeys.EnterpriseContact_PositionList, data, cacheEntryOptions);
            }
            return data;
        }

        public async Task<List<EmployeeCacheOutput>> ByUserIdAsync(params Guid[] keys)
        {
            if (keys == null)
                throw new ArgumentNullException(nameof(keys));
            if (keys.Length == 0) return new List<EmployeeCacheOutput>();

            var data = await EnsureLoadMappingData();

            var result = new List<EmployeeCacheOutput>();
            foreach (var key in keys)
            {
                if (data.ByUserId.ContainsKey(key))
                {
                    result.Add(data.ByUserId[key]);
                }
            }

            return result;
        }

        public async Task<List<EmployeeCacheOutput>> ByNumberAsync(params string[] keys)
        {
            if (keys == null)
                throw new ArgumentNullException(nameof(keys));
            if (keys.Length == 0) return new List<EmployeeCacheOutput>();

            var data = await EnsureLoadMappingData();

            var result = new List<EmployeeCacheOutput>();
            foreach (var key in keys)
            {
                if (data.ByNumber.ContainsKey(key))
                {
                    result.Add(data.ByNumber[key]);
                }
            }

            return result;
        }


        public async Task<List<EmployeeCacheOutput>> ByIdAsync(params Guid[] keys)
        {
            if (keys == null)
                throw new ArgumentNullException(nameof(keys));
            if (keys.Length == 0) return new List<EmployeeCacheOutput>();

            var data = await EnsureLoadMappingData();

            var result = new List<EmployeeCacheOutput>();
            foreach (var key in keys)
            {
                if (data.ById.ContainsKey(key))
                {
                    result.Add(data.ById[key]);
                }
            }

            return result;
        }


        public void ClearCache()
        {
            _cache.Remove(CacheKeys.Common_EmployeeList);
        }
    }
}


<|start_filename|>test/EnterpriseContactUnitTest/Controllers/EmployeeControllerTest.cs<|end_filename|>
﻿using ConfigMgmt;
using EnterpriseContact;
using EnterpriseContact.Controllers;
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using NSubstitute;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace EnterpriseContactUnitTest.Controllers
{
    [TestClass]
    public class EmployeeControllerTest : TheControllerTestBase
    {
        [TestMethod]
        public async Task TestSearch()
        {
            var dep0 = new DepartmentListOutput
            {
                Id = Guid.NewGuid(),
                Name = "sclq",
            };
            var dep1 = new DepartmentListOutput
            {
                Id = Guid.NewGuid(),
                Name = "sclq-jt",
                ParentId = dep0.Id,
            };
            var dep2 = new DepartmentListOutput
            {
                Id = Guid.NewGuid(),
                Name = "sclq-gs",
                ParentId = dep0.Id,
            };
            var pos1 = new PositionListOutput
            {
                Id = Guid.NewGuid(),
                Name = "pos1",
                DepartmentId = dep1.Id,
            };
            var pos2 = new PositionListOutput
            {
                Id = Guid.NewGuid(),
                Name = "pos2",
                DepartmentId = dep2.Id,
            };
            var emp0 = new EmployeeListOutput
            {
                Id = Guid.NewGuid(),
                Name = "aaa",
                PrimaryDepartmentId = dep1.Id,
                PrimaryPositionId = pos1.Id,
            };
            var emp1 = new EmployeeListOutput
            {
                Id = Guid.NewGuid(),
                Name = "aabb",
                PrimaryDepartmentId = dep1.Id,
                PrimaryPositionId = pos1.Id,
            };
            var emp2 = new EmployeeListOutput
            {
                Id = Guid.NewGuid(),
                Name = "bbcc",
                PrimaryDepartmentId = dep2.Id,
                PrimaryPositionId = pos2.Id,
            };

            var departmentAppService = Substitute.For<IDepartmentAppService>();
            departmentAppService.GetAllListAsync()
                .Returns(Task.FromResult((new[] { dep0, dep1, dep2 }).ToList()));

            var positionAppService = Substitute.For<IPositionAppService>();
            positionAppService.GetAllListAsync()
                .Returns(Task.FromResult((new[] { pos1, pos2 }).ToList()));

            var employeeAppService = Substitute.For<IEmployeeAppService>();
            employeeAppService.SearchByKeywordAsync("aa")
                .Returns(Task.FromResult((new[] { emp0, emp1 }).ToList()));
            employeeAppService.SearchByKeywordAsync("bb")
                .Returns(Task.FromResult((new[] { emp1, emp2 }).ToList()));

            var target = new EmployeeController(
                CreateMemoryCache(),
                CreateMapper(),
                departmentAppService,
                positionAppService,
                employeeAppService,
                Substitute.For<IGroupAppService>(),
                _ => Substitute.For<IUserFavoriteAppService>(),
                _ => Substitute.For<IUserSettingAppService>());

            var result = await target.Search("aa");
            var data = result.Value;
            data.Count.Should().Be(2);
            data[0].Id.Should().Be(emp0.Id);
            data[0].PositionName.Should().Be(pos1.Name);
            data[0].DepartmentNames.Count.Should().Be(2);
            data[0].DepartmentNames[0].Should().Be(dep1.Name);
            data[0].DepartmentNames[1].Should().Be(dep0.Name);

            result = await target.Search("bb");
            data = result.Value;
            data.Count.Should().Be(2);
            data[1].Id.Should().Be(emp2.Id);
            data[1].PositionName.Should().Be(pos2.Name);
            data[1].DepartmentNames.Count.Should().Be(2);
            data[1].DepartmentNames[0].Should().Be(dep2.Name);
            data[1].DepartmentNames[1].Should().Be(dep0.Name);
        }

        [TestMethod]
        public async Task Test_GetById_GetByUserId()
        {
            var dep0 = new DepartmentListOutput
            {
                Id = Guid.NewGuid(),
                Name = "sclq",
            };
            var dep1 = new DepartmentListOutput
            {
                Id = Guid.NewGuid(),
                Name = "sclq-jt",
                ParentId = dep0.Id,
            };
            var dep2 = new DepartmentListOutput
            {
                Id = Guid.NewGuid(),
                Name = "sclq-gs",
                ParentId = dep0.Id,
            };
            var pos1 = new PositionListOutput
            {
                Id = Guid.NewGuid(),
                Name = "pos1",
                DepartmentId = dep1.Id,
            };
            var pos2 = new PositionListOutput
            {
                Id = Guid.NewGuid(),
                Name = "pos2",
                DepartmentId = dep2.Id,
            };
            var emp0 = new EmployeeOutput
            {
                Id = Guid.NewGuid(),
                UserId = Guid.NewGuid(),
                Name = "aaa",
                PrimaryDepartmentId = dep1.Id,
                PrimaryPositionId = pos1.Id,
                ParttimePositionIds = new List<Guid> { pos2.Id },
            };

            var departmentAppService = Substitute.For<IDepartmentAppService>();
            departmentAppService.GetAllListAsync()
                .Returns(Task.FromResult((new[] { dep0, dep1, dep2 }).ToList()));

            var positionAppService = Substitute.For<IPositionAppService>();
            positionAppService.GetAllListAsync()
                .Returns(Task.FromResult((new[] { pos1, pos2 }).ToList()));

            var employeeAppService = Substitute.For<IEmployeeAppService>();
            employeeAppService.GetByIdAsync(emp0.Id)
                .Returns(Task.FromResult(emp0));
            employeeAppService.GetByUserIdAsync(emp0.UserId.Value)
                .Returns(Task.FromResult(emp0));

            var groupAppService = Substitute.For<IGroupAppService>();
            groupAppService.CheckSameWhiteListGroupAsync(User_EmployeeMdmId, emp0.Id)
                .Returns(Task.FromResult(true));

            var userFavoriteAppService = Substitute.For<IUserFavoriteAppService>();
            userFavoriteAppService.IsFavoritedAsync(User_Id, emp0.Id)
                .Returns(Task.FromResult(true));

            var userSettingAppService = Substitute.For<IUserSettingAppService>();
            userSettingAppService.GetInfoVisibilityAsync(emp0.UserId.Value)
                .Returns(Task.FromResult(new InfoVisibility { Mobile = false }));

            var target = new EmployeeController(
                CreateMemoryCache(),
                CreateMapper(),
                departmentAppService,
                positionAppService,
                employeeAppService,
                groupAppService,
                _ => userFavoriteAppService,
                _ => userSettingAppService
                );
            target.ControllerContext = CreateMockContext();

            var result = await target.GetById(emp0.Id);
            var data = result.Value;
            data.SameWhiteListGroup.Should().BeTrue();
            data.IsFavorited.Should().BeTrue();
            data.Name.Should().Be(emp0.Name);
            data.Mobile.Should().Be("***");
            data.PositionName.Should().Be(pos1.Name);

            data.DepartmentNames.Count.Should().Be(2);
            data.DepartmentNames[0].Should().Be(dep1.Name);

            data.ParttimeJobs.Count.Should().Be(1);
            data.ParttimeJobs[0].PositionName.Should().Be(pos2.Name);
            data.ParttimeJobs[0].DepartmentNames.Count.Should().Be(2);
            data.ParttimeJobs[0].DepartmentNames[0].Should().Be(dep2.Name);

            result = await target.GetByUserId(emp0.UserId.Value);
            data = result.Value;
            data.Should().NotBeNull();
        }

        [TestMethod]
        public async Task TestGetMy()
        {
            var dep0 = new DepartmentListOutput
            {
                Id = Guid.NewGuid(),
                Name = "sclq",
            };
            var dep1 = new DepartmentListOutput
            {
                Id = Guid.NewGuid(),
                Name = "sclq-jt",
                ParentId = dep0.Id,
            };
            var dep2 = new DepartmentListOutput
            {
                Id = Guid.NewGuid(),
                Name = "sclq-gs",
                ParentId = dep0.Id,
            };
            var pos1 = new PositionListOutput
            {
                Id = Guid.NewGuid(),
                Name = "pos1",
                DepartmentId = dep1.Id,
            };
            var pos2 = new PositionListOutput
            {
                Id = Guid.NewGuid(),
                Name = "pos2",
                DepartmentId = dep2.Id,
            };
            var emp0 = new EmployeeListOutput
            {
                Id = Guid.NewGuid(),
                Name = "aaa",
                PrimaryDepartmentId = dep1.Id,
                PrimaryPositionId = pos1.Id,
            };
            var emp1 = new EmployeeListOutput
            {
                Id = Guid.NewGuid(),
                Name = "aabb",
                PrimaryDepartmentId = dep1.Id,
                PrimaryPositionId = pos1.Id,
            };
            var emp2 = new EmployeeListOutput
            {
                Id = Guid.NewGuid(),
                Name = "bbcc",
                PrimaryDepartmentId = dep2.Id,
                PrimaryPositionId = pos2.Id,
            };

            var departmentAppService = Substitute.For<IDepartmentAppService>();
            departmentAppService.GetAllListAsync()
                .Returns(Task.FromResult((new[] { dep0, dep1, dep2 }).ToList()));

            var positionAppService = Substitute.For<IPositionAppService>();
            positionAppService.GetAllListAsync()
                .Returns(Task.FromResult((new[] { pos1, pos2 }).ToList()));

            var employeeIds = new[] { emp0.Id, emp1.Id, emp2.Id };
            var employeeAppService = Substitute.For<IEmployeeAppService>();
            employeeAppService.GetListByIdsAsync(employeeIds)
                .Returns(Task.FromResult((new[] { emp0, emp1, emp2 }).ToList()));

            var userFavoriteAppService = Substitute.For<IUserFavoriteAppService>();
            userFavoriteAppService.GetEmployeesAsync(User_Id)
                .Returns(Task.FromResult(employeeIds));

            var target = new EmployeeController(
                CreateMemoryCache(),
                CreateMapper(),
                departmentAppService,
                positionAppService,
                employeeAppService,
                Substitute.For<IGroupAppService>(),
                _ => userFavoriteAppService,
                _ => Substitute.For<IUserSettingAppService>());
            target.ControllerContext = CreateMockContext();

            var result = await target.GetMy();
            var data = result.Value;
            data.Count.Should().Be(3);
        }

        [TestMethod]
        public async Task TestAddFavorite()
        {
            var userFavoriteAppService = Substitute.For<IUserFavoriteAppService>();
            userFavoriteAppService.AddEmployeeAsync(User_Id, Arg.Any<Guid>())
                .Returns(Task.FromResult(true));

            var target = new EmployeeController(
                CreateMemoryCache(),
                CreateMapper(),
                Substitute.For<IDepartmentAppService>(),
                Substitute.For<IPositionAppService>(),
                Substitute.For<IEmployeeAppService>(),
                Substitute.For<IGroupAppService>(),
                _ => userFavoriteAppService,
                _ => Substitute.For<IUserSettingAppService>());
            target.ControllerContext = CreateMockContext();

            var result = await target.AddFavorite(Guid.NewGuid());
            result.Value.Status.Should().Be(0);
        }

        [TestMethod]
        public async Task TestRemoveFavorite()
        {
            var userFavoriteAppService = Substitute.For<IUserFavoriteAppService>();
            userFavoriteAppService.RemoveEmployeeAsync(User_Id, Arg.Any<Guid>())
                .Returns(Task.FromResult(true));

            var target = new EmployeeController(
                CreateMemoryCache(),
                CreateMapper(),
                Substitute.For<IDepartmentAppService>(),
                Substitute.For<IPositionAppService>(),
                Substitute.For<IEmployeeAppService>(),
                Substitute.For<IGroupAppService>(),
                _ => userFavoriteAppService,
                _ => Substitute.For<IUserSettingAppService>());
            target.ControllerContext = CreateMockContext();

            var result = await target.RemoveFavorite(Guid.NewGuid());
            result.Value.Status.Should().Be(0);
        }

        [TestMethod]
        public async Task TestCheckSameWhiteListGroup()
        {
            var groupAppService = Substitute.For<IGroupAppService>();
            groupAppService.CheckSameWhiteListGroupAsync(User_EmployeeMdmId, Arg.Any<Guid>())
                .Returns(Task.FromResult(true));

            var target = new EmployeeController(
                CreateMemoryCache(),
                CreateMapper(),
                Substitute.For<IDepartmentAppService>(),
                Substitute.For<IPositionAppService>(),
                Substitute.For<IEmployeeAppService>(),
                groupAppService,
                _ => Substitute.For<IUserFavoriteAppService>(),
                _ => Substitute.For<IUserSettingAppService>());
            target.ControllerContext = CreateMockContext();

            var result = await target.CheckSameWhiteListGroup(Guid.NewGuid());
            result.Value.Should().BeTrue();
        }
    }
}


<|start_filename|>src/GroupFileService/Migrations/20190227085008_Init.cs<|end_filename|>
﻿using System;
using Microsoft.EntityFrameworkCore.Migrations;

namespace GroupFile.Migrations
{
    public partial class Init : Migration
    {
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.CreateTable(
                name: "FileStores",
                columns: table => new
                {
                    Id = table.Column<string>(maxLength: 32, nullable: false),
                    Size = table.Column<int>(nullable: false),
                    Location = table.Column<string>(nullable: true)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_FileStores", x => x.Id);
                });

            migrationBuilder.CreateTable(
                name: "FileItems",
                columns: table => new
                {
                    Id = table.Column<Guid>(nullable: false),
                    GroupId = table.Column<Guid>(nullable: false),
                    Name = table.Column<string>(nullable: true),
                    UpdatedOn = table.Column<DateTimeOffset>(nullable: false),
                    UploaderId = table.Column<Guid>(nullable: false),
                    DownloadAmount = table.Column<int>(nullable: false),
                    StoreId = table.Column<string>(nullable: true)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_FileItems", x => x.Id);
                    table.ForeignKey(
                        name: "FK_FileItems_FileStores_StoreId",
                        column: x => x.StoreId,
                        principalTable: "FileStores",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Restrict);
                });

            migrationBuilder.CreateIndex(
                name: "IX_FileItems_GroupId",
                table: "FileItems",
                column: "GroupId");

            migrationBuilder.CreateIndex(
                name: "IX_FileItems_StoreId",
                table: "FileItems",
                column: "StoreId");
        }

        protected override void Down(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.DropTable(
                name: "FileItems");

            migrationBuilder.DropTable(
                name: "FileStores");
        }
    }
}


<|start_filename|>src/ConfigMgmtApi/Controllers/WorkbenchController.InitData.cs<|end_filename|>
﻿using Common.Filters;
using ConfigMgmt.Entities;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace ConfigMgmt.Controllers
{
    public partial class WorkbenchController
    {
        [CheckToken]
        [AllowAnonymous]
        [HttpPost("initdata/AppEntrance")]
        public async Task<IActionResult> AddAppEntrancesAsync(List<AppEntrance> model)
        {
            try
            {
                var client = Client.Default();
                await _clientAppService.AddOrUpdateClientAsync(client);

                var result = await _workbenchAppService.AddOrUpdateEntrancesAsync(new AddEntrancesInput
                {
                    ClientId = client.Id,
                    AppEntrances = model
                });
                return Ok(result);
            }
            catch (Exception ex)
            {
                return BadRequest(ex.Message);
            }
        }

        [CheckToken]
        [AllowAnonymous]
        [HttpDelete("initdata/AppEntrance")]
        public async Task<IActionResult> RemoveAppEntranceAsync(Guid appId, string clientId = Client.DefaultId)
        {
            try
            {
                if (string.IsNullOrEmpty(clientId)) throw new ArgumentNullException(nameof(clientId));
                if (appId == Guid.Empty) throw new ArgumentOutOfRangeException(nameof(appId));

                var result = await _workbenchAppService.RemoveEntranceAsync(clientId, appId);
                return Ok(result);
            }
            catch (Exception ex)
            {
                return BadRequest(ex.Message);
            }
        }

        [CheckToken]
        [AllowAnonymous]
        [HttpPut("initdata/AppEntrance")]
        public async Task<IActionResult> UpdateAppEntranceAsync(AppEntrance model)
        {
            try
            {
                if (model == null) throw new ArgumentNullException(nameof(model));
                if (model.Id == Guid.Empty) throw new ArgumentOutOfRangeException(nameof(model.Id));

                var client = Client.Default();
                var result = await _workbenchAppService.AddOrUpdateEntrancesAsync(new AddEntrancesInput
                {
                    ClientId = client.Id,
                    AppEntrances = new List<AppEntrance> { model }
                });

                return Ok(result);
            }
            catch (Exception ex)
            {
                return BadRequest(ex.Message);
            }
        }

        [CheckToken]
        [AllowAnonymous]
        [HttpGet("initdata/AppEntrance")]
        public async Task<ActionResult<List<AppEntrance>>> GetAppEntrancesAsync(string clientId = Client.DefaultId)
        {
            try
            {
                var result = await _workbenchAppService.GetAppEntrancesRawByClientIdAsync(clientId);
                return result;
            }
            catch (Exception ex)
            {
                return BadRequest(ex.Message);
            }
        }

        [CheckToken]
        [AllowAnonymous]
        [HttpPost("initdata/TodoCenterBizSystem")]
        public async Task<IActionResult> AddOrUpdateTodoCenterBizSystemAsync(TodoCenterBizSystem model)
        {
            try
            {
                if (model == null) throw new ArgumentNullException(nameof(model));
                if (string.IsNullOrEmpty(model.Id)) throw new ArgumentOutOfRangeException(nameof(model.Id));

                return Ok(await _bizSystemAppService.AddOrUpdateForTodoCenterAsync(model));
            }
            catch (Exception ex)
            {
                return BadRequest(ex.Message);
            }
        }

        [CheckToken]
        [AllowAnonymous]
        [HttpDelete("initdata/TodoCenterBizSystem")]
        public async Task<IActionResult> RemoveTodoCenterBizSystemAsync(string id)
        {
            try
            {
                if (string.IsNullOrEmpty(id)) throw new ArgumentNullException(nameof(id));

                return Ok(await _bizSystemAppService.RemoveForTodoCenterAsync(id));
            }
            catch (Exception ex)
            {
                return BadRequest(ex.Message);
            }
        }

        [CheckToken]
        [AllowAnonymous]
        [HttpGet("initdata/TodoCenterBizSystem")]
        public async Task<ActionResult<List<TodoCenterBizSystem>>> GetTodoCenterBizSystemsAsync()
        {
            try
            {
                return await _bizSystemAppService.GetAllForTodoCenterAsync();
            }
            catch (Exception ex)
            {
                return BadRequest(ex.Message);
            }
        }

        [CheckToken]
        [AllowAnonymous]
        [HttpPost("initdata/MsgNotifyBizSystem")]
        public async Task<IActionResult> AddOrUpdateMsgNotifyBizSystemAsync(MsgNotifyBizSystem model)
        {
            try
            {
                if (model == null) throw new ArgumentNullException(nameof(model));
                if (string.IsNullOrEmpty(model.Id)) throw new ArgumentOutOfRangeException(nameof(model.Id));

                return Ok(await _bizSystemAppService.AddOrUpdateForMsgNotifyAsync(model));
            }
            catch (Exception ex)
            {
                return BadRequest(ex.Message);
            }
        }

        [CheckToken]
        [AllowAnonymous]
        [HttpDelete("initdata/MsgNotifyBizSystem")]
        public async Task<IActionResult> RemoveMsgNotifyBizSystemAsync(string id)
        {
            try
            {
                if (string.IsNullOrEmpty(id)) throw new ArgumentNullException(nameof(id));

                return Ok(await _bizSystemAppService.RemoveForMsgNotifyAsync(id));
            }
            catch (Exception ex)
            {
                return BadRequest(ex.Message);
            }
        }

        [CheckToken]
        [AllowAnonymous]
        [HttpGet("initdata/MsgNotifyBizSystem")]
        public async Task<ActionResult<List<MsgNotifyBizSystem>>> GetMsgNotifyBizSystemsAsync()
        {
            try
            {
                return await _bizSystemAppService.GetAllForMsgNotifyAsync();
            }
            catch (Exception ex)
            {
                return BadRequest(ex.Message);
            }
        }
    }
}


<|start_filename|>src/ConfigMgmtDomain/AppServices/IBizSystemAppService.cs<|end_filename|>
﻿using ConfigMgmt.Entities;
using Microsoft.ServiceFabric.Services.Remoting;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;

namespace ConfigMgmt
{
    public interface IBizSystemAppService : IService
    {
        Task<bool> AddOrUpdateForTodoCenterAsync(TodoCenterBizSystem input);
        Task<bool> RemoveForTodoCenterAsync(string id);
        Task<List<TodoCenterBizSystem>> GetAllForTodoCenterAsync();

        Task<bool> AddOrUpdateForMsgNotifyAsync(MsgNotifyBizSystem input);
        Task<bool> RemoveForMsgNotifyAsync(string id);
        Task<List<MsgNotifyBizSystem>> GetAllForMsgNotifyAsync();
        Task<MsgNotifyBizSystem> GetByIdForNotifyMsgAsync(string id);
    }
}


<|start_filename|>src/ServiceCommon/RabbitMqReceiveEndpointNames.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Common
{
    public static class RabbitMqReceiveEndpointNames
    {
        public const string NotifyGateway = "EapSfApp.NotifyGateway";
    }
}


<|start_filename|>src/UserConfigStateService/UserSettingAppService.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Fabric;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ConfigMgmt;
using ConfigMgmt.Entities;
using Microsoft.ServiceFabric.Data;
using Microsoft.ServiceFabric.Data.Collections;
using Nest;
using ServiceFabricContrib;

namespace UserConfigStateService
{
    public class UserSettingAppService : StatefulRemotingService, IUserSettingAppService
    {
        private const string DictionaryName_InfoVisibility = "UserSetttings_InfoVisibility";
        private const string DictionaryName_UserDeviceBinding = "UserSettings/UserDeviceBinding";

        public UserSettingAppService(StatefulServiceContext serviceContext, IReliableStateManager stateManager) : base(serviceContext, stateManager)
        {
        }

        private static readonly TimeSpan AppEntranceAuthStateValidity = TimeSpan.FromDays(1);

        public async Task<bool> GetAppEntranceAuthStateAsync(Guid userId, string deviceCode, Guid appEntranceId)
        {
            var dictionary = await StateManager.GetOrAddAsync<IReliableDictionary<Guid, UserDeviceBinding>>(DictionaryName_UserDeviceBinding);
            using (var tx = StateManager.CreateTransaction())
            {
                var wrap = await dictionary.TryGetValueAsync(tx, userId);
                if (wrap.HasValue)
                {
                    var entity = wrap.Value;
                    return entity.CheckWhetherAuth(deviceCode, appEntranceId, AppEntranceAuthStateValidity);
                }
                else
                    return true;
            }
        }

        public async Task<InfoVisibility> GetInfoVisibilityAsync(Guid userId)
        {
            var dictionary = await StateManager.GetOrAddAsync<IReliableDictionary<Guid, InfoVisibility>>(DictionaryName_InfoVisibility);
            using (var tx = StateManager.CreateTransaction())
            {
                var rDic = await dictionary.TryGetValueAsync(tx, userId);
                if (rDic.HasValue)
                {
                    return rDic.Value;
                }

                return new InfoVisibility();
            }
        }

        public async Task SetAppEntranceAuthStateAsync(Guid userId, AppEntranceAuthStateInput input)
        {
            var dictionary = await StateManager.GetOrAddAsync<IReliableDictionary<Guid, UserDeviceBinding>>(DictionaryName_UserDeviceBinding);
            using (var tx = StateManager.CreateTransaction())
            {
                UserDeviceBinding entity;
                var wrap = await dictionary.TryGetValueAsync(tx, userId);
                if (wrap.HasValue)
                {
                    entity = wrap.Value;
                    if (entity.Id == Guid.Empty) entity.Id = userId;//fix existing data for DataMember missing bug #1657
                    entity.AddOrUpdateLatestAuthed(input.DeviceCode, input.AppEntranceId);
                    await dictionary.SetAsync(tx, userId, entity);
                }
                else
                {
                    entity = UserDeviceBinding.Create(userId, input.DeviceCode, input.AppEntranceId);
                    await dictionary.AddAsync(tx, userId, entity);
                }
                await tx.CommitAsync();
            }
        }

        public async Task SetInfoVisibilityAsync(Guid userId, InfoVisibility value)
        {
            var dictionary = await StateManager.GetOrAddAsync<IReliableDictionary<Guid, InfoVisibility>>(DictionaryName_InfoVisibility);
            using (var tx = StateManager.CreateTransaction())
            {
                await dictionary.SetAsync(tx, userId, value);
                await tx.CommitAsync();
            }
        }
    }
}


<|start_filename|>src/EnterpriseContactApi/Controllers/OrgController.cs<|end_filename|>
﻿using Common;
using AutoMapper;
using EnterpriseContact.ViewModels;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Serilog;

namespace EnterpriseContact.Controllers
{
    [Route("api/v1/org")]
    public class OrgController : TheBaseController
    {
        private readonly IEmployeeAppService _employeeAppService;
        private readonly ILogger _logger;

        public OrgController(IMemoryCache cache, IMapper mapper,
            IDepartmentAppService departmentAppService, IPositionAppService positionAppService,
            IEmployeeAppService employeeAppService) 
            : base(cache, mapper, departmentAppService, positionAppService)
        {
            _employeeAppService = employeeAppService;
            _logger = Log.ForContext<OrgController>();
        }

        [HttpGet("root")]
        public async Task<ActionResult<OrgVm>> GetRoots()
        {
            try
            {
                if (!_cache.TryGetValue(CacheKeys.EnterpriseContact_GetRoots, out OrgVm data))
                {
                    var departments = await _departmentAppService.GetRootListAsync();
                    var employees = await _employeeAppService.GetRootListAsync();
                    data = new OrgVm
                    {
                        Departments = _mapper.Map<List<DepartmentListVm>>(departments),
                        Employees = _mapper.Map<List<EmployeeListVm>>(employees)
                    };

                    var cacheEntryOptions = new MemoryCacheEntryOptions()
                        .SetAbsoluteExpiration(CacheKeys.DefaultCacheAbsoluteExpiration);

                    _cache.Set(CacheKeys.EnterpriseContact_GetRoots, data, cacheEntryOptions);
                }

                return data;
            }
            catch (Exception ex)
            {
                return BadRequest(LogError(_logger, ex));
            }
        }

        [HttpGet("{parentDepartmentId}")]
        public async Task<ActionResult<OrgVm>> GetChildren(Guid parentDepartmentId)
        {
            try
            {
                var departments = await _departmentAppService.GetListByParentIdAsync(parentDepartmentId);
                var employees = await _employeeAppService.GetListByDepartmentIdAsync(parentDepartmentId);
                var data = new OrgVm
                {
                    Departments = _mapper.Map<List<DepartmentListVm>>(departments),
                    Employees = _mapper.Map<List<EmployeeListVm>>(employees)
                };

                return data;
            }
            catch (Exception ex)
            {
                return BadRequest(LogError(_logger, ex));
            }
        }
    }
}


<|start_filename|>src/NotifyGatewayWeb/EventBus/SendMessageNotifyConsumer.cs<|end_filename|>
﻿using NotifyGatewayWeb.Hubs;
using MassTransit;
using Microsoft.AspNetCore.SignalR;
using Notification;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace NotifyGatewayWeb.EventBus
{
    public class SendMessageNotifyConsumer : IConsumer<SendMessageNotify>
    {
        private readonly IHubContext<NotifyHub, INotifyClient> _hubContext;

        public SendMessageNotifyConsumer(IHubContext<NotifyHub, INotifyClient> hubContext)
        {
            _hubContext = hubContext;
        }

        public async Task Consume(ConsumeContext<SendMessageNotify> context)
        {
            try
            {
                await _hubContext.Clients.User(context.Message.UserId.ToString()).ReceiveMessageNotify(context.Message.Notifies);
            }
            catch (Exception ex)
            {
                ServiceEventSource.Current.Message($"SendMessageNotifyConsumer Error: UserId: {context.Message.UserId}, Message: {ex.Message}");
            }
        }
    }
}


<|start_filename|>src/ApiCommon/Services/IMobileCodeSender.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;

namespace Common.Services
{
    public interface IMobileCodeSender
    {
        Task<string> SendAsync(string phoneNumber);

        Task<bool> CheckAsync(string phoneNumber, string code);
    }
}


<|start_filename|>src/EnterpriseContactDomain/Dtos/EmployeePositionOutput.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;

namespace EnterpriseContact
{
    [Serializable]
    public class EmployeePositionOutput
    {
        public Guid PositionId { get; set; }

        /// <summary>
        /// 职位名称
        /// </summary>
        public string PositionName { get; set; }
    }
}


<|start_filename|>test/NotifyUnitTest/EventBus/BizSystemNotifyMsgConsumerTest.cs<|end_filename|>
using Base.Eap.Notify.MsgContracts;
using BizSystemNotifyService.EventBus;
using ConfigMgmt;
using ConfigMgmt.Entities;
using EnterpriseContact;
using EnterpriseContact.Services;
using MassTransit;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Notification;
using NSubstitute;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace NotifyUnitTest
{
    [TestClass]
    public class BizSystemNotifyMsgConsumerTest
    {
        [TestMethod]
        public async Task Consume_UseContent()
        {
            var id1 = Guid.NewGuid();
            var id2 = Guid.NewGuid();

            var bizSystemAppService = Substitute.For<IBizSystemAppService>();
            bizSystemAppService.GetByIdForNotifyMsgAsync("aaa")
                .Returns(new MsgNotifyBizSystem
                {
                    Id = "aaa",
                    Approachs = new MsgNotifyApproach[] { MsgNotifyApproach.APP },
                    Name = "AAA System",
                });

            var notifySessionActor = Substitute.For<INotifySessionActor>();
            var employeeCacheService = Substitute.For<IEmployeeCacheService>();
            var context = Substitute.For<ConsumeContext<BizSystemNotifyMsg>>();
            context.Message.Returns(new BizSystemNotifyMsg
            {
                SystemId = "aaa",
                Category = 1,
                Content = "show me the money",
                ReceiverUserIds = new Guid[] { id1, id2 }
            });

            var target = new BizSystemNotifyMsgConsumer(bizSystemAppService,
               _ => notifySessionActor, employeeCacheService);

            await target.Consume(context);

            await notifySessionActor.Received(2).PushEventNotifyAsync(Arg.Is<EventNotifyDto>(
                x => x.TargetId == "aaa" && x.Title == "AAA System"));
        }

        [TestMethod]
        public async Task Consume_UseParameter()
        {
            var num1 = Guid.NewGuid().ToString("N");
            var num2 = Guid.NewGuid().ToString("N");

            var bizSystemAppService = Substitute.For<IBizSystemAppService>();
            bizSystemAppService.GetByIdForNotifyMsgAsync("aaa")
                .Returns(new MsgNotifyBizSystem
                {
                    Id = "aaa",
                    Approachs = new MsgNotifyApproach[] { MsgNotifyApproach.APP },
                    Name = "AAA System",
                    PatternOfCategory = new Dictionary<int, string> {
                        {1,"$(p1)+$(p2)" }
                    }
                });

            var notifySessionActor = Substitute.For<INotifySessionActor>();

            var employeeCacheService = Substitute.For<IEmployeeCacheService>();
            employeeCacheService.ByNumberAsync(num1, num2)
                .Returns(new List<EmployeeCacheOutput>
                {
                    new EmployeeCacheOutput
                    {
                        Id=Guid.NewGuid(),
                        Number=num1,
                        UserId=Guid.NewGuid(),
                    },
                    new EmployeeCacheOutput
                    {
                        Id=Guid.NewGuid(),
                        Number=num2,
                        UserId=Guid.NewGuid(),
                    }
                });

            var context = Substitute.For<ConsumeContext<BizSystemNotifyMsg>>();
            context.Message.Returns(new BizSystemNotifyMsg
            {
                SystemId = "aaa",
                Category = 1,
                Parameters = new Dictionary<string, string>
                {
                    {"p1","xyz" },
                    {"p2","123" }
                },
                ReceiverNumbers = new string[] { num1, num2 }
            });

            var target = new BizSystemNotifyMsgConsumer(bizSystemAppService,
               _ => notifySessionActor, employeeCacheService);

            await target.Consume(context);

            await notifySessionActor.Received(2).PushEventNotifyAsync(Arg.Is<EventNotifyDto>(
                x => x.TargetId == "aaa" && x.Title == "AAA System" && x.Text == "xyz+123"));
        }
    }
}


<|start_filename|>src/ApiGateway/Startup.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using AutoMapper;
using Common;
using Common.Extensions;
using Common.Services;
using MassTransit;
using AppsOnSF.Common.BaseServices;
using AppsOnSF.Common.Options;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ApplicationParts;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;

namespace ApiGateway
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddHealthChecks();

            //基础服务
#if !DEBUG
            services.AddDataProtection()
                .SetApplicationName("EAP-ApiGateway")
                .PersistKeysToServiceFabric();
#endif
            services.AddMemoryCache();

            services.AddHttpContextAccessor();

            services.Configure<FormOptions>(opt =>
            {
                opt.MultipartBodyLengthLimit = int.MaxValue;
            });

            services.AddHttpClient<IIdentityService, IdentityService>();

            // Register the Swagger services
            services.AddSwaggerDocument(config =>
            {
                config.PostProcess = document =>
                {
                    document.Info.Version = "v1";
                    document.Info.Title = "EAP API";
                    document.Info.Description = "Enterprise Apps Platform API";
                    document.Info.TermsOfService = "None";
                    document.SecurityDefinitions.Add("Bearer", new NSwag.SwaggerSecurityScheme
                    {
                        Description = "JWT Authorization header using the Bearer scheme. Example: \"Bearer {token}\"",
                        Name = "Authorization",
                        Type = NSwag.SwaggerSecuritySchemeType.ApiKey,
                        In = NSwag.SwaggerSecurityApiKeyLocation.Header
                    });
                    document.Security.Add(new NSwag.SwaggerSecurityRequirement { { "Bearer", new string[] { } }, });
                };
            });

            //公共依赖
            services.AddSingleton(CreateMapper());
            services.AddSingleton(RemotingProxyFactory.CreateSimpleKeyValueService());
            var (bus, bus_option) = this.CreateBus("RabbitMQ");
            services.AddCommonServices(bus, bus_option.HostAddress);

            //业务依赖
            AppComponent.TheStartup.ConfigureServices(services);
            Attachment.TheStartup.ConfigureServices(services);
            ConfigMgmt.TheStartup.ConfigureServices(services);
            EnterpriseContact.TheStartup.ConfigureServices(services);
            GroupFile.TheStartup.ConfigureServices(services);
            InstantMessage.TheStartup.ConfigureServices(services);
            NotifyStartup.ConfigureServices(services);
            
            //Application Parts
            var assemblyEC = typeof(EnterpriseContact.TheStartup).GetTypeInfo().Assembly;
            var partEC = new AssemblyPart(assemblyEC);

            //TODO 没有加ConfigMgmtApi，也能用？

            //var assemblyCM=typeof()
            services.AddMvc()
                .ConfigureApplicationPartManager(apm => apm.ApplicationParts.Add(partEC))
                .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

            //Authentication
            var idSvrOption = Configuration.GetSection("IdSvr").Get<IdSvrOption>();
            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
             .AddJwtBearer(options =>
             {
                 options.Authority = idSvrOption.IssuerUri;
                 options.RequireHttpsMetadata = idSvrOption.RequireHttps;
                 options.Audience = "eap.api";

                 options.TokenValidationParameters =
                   new TokenValidationParameters
                   {
                       NameClaimType = "preferred_username",
                       ValidateAudience = false,
                       ValidateIssuer = false,
                       ValidateActor = false,
                       ValidateLifetime = true,
                       LifetimeValidator = (before, expires, token, param) =>
                       {
                           return expires > DateTime.UtcNow;
                       },
                   };
             });

            //相关配置
            services.Configure<IdSvrOption>(Configuration.GetSection("IdSvr"));
            services.Configure<MinioOption>(Configuration.GetSection("Minio"));
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            app.UseHealthChecks("/health", new HealthCheckOptions { Predicate = check => check.Tags.Contains("ready") });

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.ConfigureExceptionHandler();

            // Register the Swagger generator and the Swagger UI middlewares
            app.UseSwagger();
            app.UseSwaggerUi3();

            app.UseAuthentication();
            app.UseMvc();
        }

        private IMapper CreateMapper()
        {
            var config = new MapperConfiguration(cfg =>
            {
                cfg.AddProfile<EnterpriseContact.VmMappingProfile>();
            });
            return config.CreateMapper();
        }

        private (IBusControl, RabbitMQOption) CreateBus(string sectionName)
        {
            var option = Configuration.GetSection(sectionName).Get<RabbitMQOption>();
            var bus = Bus.Factory.CreateUsingRabbitMq(cfg =>
            {
                var host = cfg.Host(new Uri(option.HostAddress), h =>
                {
                    h.Username(option.Username);
                    h.Password(option.Password);
                });
            });
            return (bus, option);
        }
    }
}


<|start_filename|>src/NotifyDomain/Enums.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Notification
{
    public enum NotifyTargetType
    {
        /// <summary>
        /// 针对整个客户端
        /// </summary>
        Client = 1,
        /// <summary>
        /// 针对业务系统
        /// </summary>
        BizSystem = 2,
        /// <summary>
        /// 针对会话
        /// </summary>
        Conversation = 3,
    }
}


<|start_filename|>src/EnterpriseContactService/Entities/Department.cs<|end_filename|>
﻿using EnterpriseContactService.Entities;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace EnterpriseContact.Entities
{
    public class Department
    {
        public Guid Id { get; set; }

        public Guid? ParentId { get; set; }

        public virtual Department Parent { get; set; }

        //[Index]
        [Required]
        [StringLength(128)]
        public string Number { get; set; }

        [Required]
        [StringLength(128)]
        public string Name { get; set; }

        public int TypeId { get; set; }

        public int Sort { get; set; }

        public int? IconId { get; set; }

        public DataSourceType DataSourceType { get; set; }

        public virtual List<Position> Positions { get; set; }
    }
}


<|start_filename|>src/ServiceCommon/CacheKeys.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Common
{
    public static class CacheKeys
    {
        /// <summary>
        /// 15 min
        /// </summary>
        public static readonly TimeSpan DefaultCacheAbsoluteExpiration = TimeSpan.FromMinutes(15);

        public const string EnterpriseContact_GetRoots = "EnterpriseContact.GetRoots";
        public const string EnterpriseContact_DepartmentList = "EnterpriseContact.DepartmentList";
        public const string EnterpriseContact_PositionList = "EnterpriseContact.PositionList";
        public const string Common_EmployeeList = "Common.EmployeeList";
    }
}


<|start_filename|>test/GroupFileServiceTest/TheGroupFileAppServiceTestBase.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using AutoMapper;
using GroupFile.Models;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using UnitTestCommon;

namespace GroupFileServiceTest
{
    public class TheGroupFileAppServiceTestBase : AppServiceTestBase
    {
        protected static IMapper CreateMapper()
        {
            var config = new MapperConfiguration(cfg =>
            {
                cfg.AddProfile<GroupFile.MappingProfile>();
            });
            return config.CreateMapper();
        }

        protected static (SqliteConnection, DbContextOptions<ServiceDbContext>) OpenDb()
        {
            var connection = new SqliteConnection("DataSource=:memory:");
            connection.Open();

            var options = new DbContextOptionsBuilder<ServiceDbContext>()
                //.UseLazyLoadingProxies()
                .EnableDetailedErrors()
                .EnableSensitiveDataLogging()
                .UseLoggerFactory(TestLoggerFactory)
                .UseSqlite(connection)
                .Options;

            // Create the schema in the database
            using (var context = new ServiceDbContext(options))
            {
                context.Database.EnsureCreated();
            }

            return (connection, options);
        }
    }
}


<|start_filename|>emulator/ClientEmulator/TokenResponseExtensions.cs<|end_filename|>
﻿using IdentityModel;
using IdentityModel.Client;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ClientEmulator
{
    public static class TokenResponseExtensions
    {
        public static void Show(this TokenResponse response)
        {
            if (!response.IsError)
            {
                Debug.WriteLine("Token response:");
                Debug.WriteLine(response.Json);

                if (response.AccessToken.Contains("."))
                {
                    Debug.WriteLine("\nAccess Token (decoded):");

                    var parts = response.AccessToken.Split('.');
                    var header = parts[0];
                    var claims = parts[1];

                    Debug.WriteLine(JObject.Parse(Encoding.UTF8.GetString(Base64Url.Decode(header))));
                    Debug.WriteLine(JObject.Parse(Encoding.UTF8.GetString(Base64Url.Decode(claims))));
                }
            }
            else
            {
                if (response.ErrorType == ResponseErrorType.Http)
                {
                    Debug.WriteLine("HTTP error: ");
                    Debug.WriteLine(response.Error);
                    Debug.WriteLine("HTTP status code: ");
                    Debug.WriteLine(response.HttpStatusCode);
                }
                else
                {
                    Debug.WriteLine("Protocol error response:");
                    Debug.WriteLine(response.Raw);
                }
            }
        }
    }
}


<|start_filename|>src/EnterpriseContactService/PositionAppService.cs<|end_filename|>
﻿using AutoMapper;
using Microsoft.EntityFrameworkCore;
using ServiceFabricContrib;
using System;
using System.Collections.Generic;
using System.Fabric;
using System.Threading.Tasks;
using System.Linq;
using AutoMapper.QueryableExtensions;

namespace EnterpriseContact
{
    public class PositionAppService : BaseAppService, IPositionAppService
    {
        public PositionAppService(StatelessServiceContext serviceContext, DbContextOptions<ServiceDbContext> dbOptions, IMapper mapper) 
            : base(serviceContext, dbOptions, mapper)
        {
        }

        public async Task<List<PositionListOutput>> GetAllListAsync()
        {
            using (var db = new ServiceDbContext(_dbOptions))
            {
                var query = from item in db.Positions
                            select item;

                return await query.ProjectTo<PositionListOutput>(_mapper.ConfigurationProvider)
                    .ToListAsync();
            }
        }
    }
}


<|start_filename|>src/Utility/StringExtensions.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;

namespace Common
{
    public static class StringExtensions
    {
        public static string ComputeMd5(this string self)
        {
            using (var hasher = MD5.Create())
            {
                // Convert the input string to a byte array and compute the hash.
                byte[] data = hasher.ComputeHash(Encoding.UTF8.GetBytes(self));

                // Create a new Stringbuilder to collect the bytes
                // and create a string.
                StringBuilder sBuilder = new StringBuilder();

                // Loop through each byte of the hashed data
                // and format each one as a hexadecimal string.
                for (int i = 0; i < data.Length; i++)
                {
                    sBuilder.Append(data[i].ToString("x2"));
                }

                // Return the hexadecimal string.
                return sBuilder.ToString();
            }
        }

        static StringExtensions()
        {
            PartitionKeys = GeneratePartitionKeys();
        }

        private static string[] GeneratePartitionKeys()
        {
            var keys = new List<string>(36);
            for (int i = 48; i < 58; i++)
            {
                keys.Add(((char)i).ToString());
            }
            for (int i = 97; i < 123; i++)
            {
                keys.Add(((char)i).ToString());
            }
            return keys.ToArray();
        }

        public readonly static string[] PartitionKeys;
    }
}


<|start_filename|>src/GroupFileDomain/FileItemDto.cs<|end_filename|>
﻿using System;

namespace GroupFile
{
    public class FileItemDto
    {
        public Guid Id { get; set; }

        public string Name { get; set; }

        /// <summary>
        /// 更新时间
        /// </summary>
        public DateTimeOffset UpdatedOn { get; set; }

        /// <summary>
        /// 上传者的在ECS中的员工Id（也是主数据中的员工Id）
        /// </summary>
        public Guid UploaderId { get; set; }

        public string UploaderName { get; set; }

        public int DownloadAmount { get; set; }

        public string StoreId { get; set; }

        public int Size { get; set; }
    }
}


<|start_filename|>test/EnterpriseContactUnitTest/Consumers/MdmDataaConsumerTest.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Base.Mdm.Org.MsgContracts;
using EnterpriseContact;
using EnterpriseContact.Consumers;
using MassTransit;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace EnterpriseContactUnitTest.Consumers
{
    [TestClass]
    public class MdmDataaConsumerTest : TheAppServiceTestBase
    {
        [TestMethod]
        public async Task Test_Consumer()
        {
            var (_, dbContextOptions) = OpenDb();
            var mapper = CreateMapper();
            MdmDataConsumer comsumer = new MdmDataConsumer(dbContextOptions, mapper);
            bool finished = false;
            var bus = CreateBus(u =>
            {
                //u.Handler<FullOrgData>(async context =>
                //{
                //    await comsumer.Consume(context);
                //    finished = true;
                //});
            });
            bus.Start();
            var orgUnitId = Guid.NewGuid();
            var orgUnitSrcId = "OrgUnitScrId";
            var positionId = Guid.NewGuid();
            var positionSrcId = "PositionSrcId";
            var contactId = Guid.NewGuid();
            var contactSrcId = "ContactSrcId";
            await bus.Publish<FullOrgData>(new
            {
                ModelVersion = "111",
                DataVersion = "222",
                OrgUnits = new List<OrgUnitEntityMsg>()
                {
                    new OrgUnitEntityMsg()
                    {
                        Id = orgUnitId,
                        Name = "内阁",
                        Number = "number",
                        Parents = new List<OrgUnitParentValueMsg>(),//空,表示顶级
                        Sort = 1,
                        SrcId = orgUnitSrcId,
                        Positions = new List<PositionEntityMsg>()
                        {
                            new PositionEntityMsg()
                            {
                                Id = positionId,
                                SrcId = positionSrcId,
                                Name = "内阁主管"
                            }
                        }
                    }
                },
                Contacts = new List<ContactEntityMsg>()
                {
                    new ContactEntityMsg()
                    {
                        ContactId = contactId,
                        ContactSrcId = contactSrcId,
                        Gender = 1,
                        IdCardNo = "510821199408145016",
                        Mobile = "15523318594",
                        Name = "曹操",
                        Number = "111",
                        Positions = new List<ContactPositionValueMsg>()
                        {
                            new ContactPositionValueMsg()
                            {
                                IsPrimary = true,
                                OrgUnitId = orgUnitId,
                                OrgUnitSrcId = orgUnitSrcId,
                                PositionId = positionId,
                                PositionSrcId = positionSrcId,
                            }
                        },
                        RelationTypeId = 1,
                        SrcId = contactSrcId,
                    }
                }
            });
            return; //由于Sqllite 是不支持T-sql中的一些东西，故，这个单元测试需要更改
            while (!finished)
            {
                await Task.Delay(2000);
            }

            using (var db = new ServiceDbContext(dbContextOptions))
            {
                var contactCount = db.Employees.Count();
                var departmentCount = db.Departments.Count();
                Assert.AreEqual(2, contactCount);
                Assert.AreEqual(2, departmentCount);
            }
        }
    }
}


<|start_filename|>src/TodoCenterProxyApi/Extensions/HttpContextExtension.cs<|end_filename|>
﻿using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text;

namespace TodoCenterProxyApi.Extensions
{
    public static class HttpContextExtension
    {
        public static Dictionary<string, string> DecodeUserClaimsFromRequestHeaders(this HttpContext context)
        {
            if (!context.Request.Headers.ContainsKey(ApiCommon.Extensions.HttpClientExtension.UserClaimsHeaderName))
                throw new Exception("request has not userClaims header!");

            var raw_userclaims = context.Request.Headers[ApiCommon.Extensions.HttpClientExtension.UserClaimsHeaderName];
            var claims = Encoding.UTF8.GetString(Convert.FromBase64String(raw_userclaims));
            var claims_dic = JsonConvert.DeserializeObject<Dictionary<string, string>>(claims);
            return claims_dic;
        }
    }
}


<|start_filename|>src/GroupFileService/Entities/FileItem.cs<|end_filename|>
﻿using System;

namespace GroupFile.Entities
{
    public class FileItem
    {
        public Guid Id { get; set; }

        /// <summary>
        /// 文件归属群组Id，来自ECS
        /// </summary>
        //[Index]
        public Guid GroupId { get; set; }

        public string Name { get; set; }

        /// <summary>
        /// 更新时间
        /// </summary>
        public DateTimeOffset UpdatedOn { get; set; }

        /// <summary>
        /// 上传者的在ECS中的员工Id（也是主数据中的员工Id）
        /// </summary>
        public Guid UploaderId { get; set; }

        public int DownloadAmount { get; set; }

        public string StoreId { get; set; }

    }
}


<|start_filename|>src/ServiceCommon/Constants.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Common
{
    public static class Constants
    {
#if DEBUG
        public const string AppName_AppComponent = "Eap";
        public const string AppName_Gateway = "Eap";
        public const string AppName_Attachment = "Eap";
        public const string AppName_ConfigMgmt = "Eap";
        public const string AppName_EnterpriseContact = "Eap";
        public const string AppName_GroupFile = "Eap";
        public const string AppName_InstantMessage = "Eap";
        public const string AppName_Notify = "Eap";
#else
        public const string AppName_AppComponent = "EapAc";
        public const string AppName_Gateway = "EapGateway";
        public const string AppName_Attachment = "EapAttach";
        public const string AppName_ConfigMgmt = "EapCm";
        public const string AppName_EnterpriseContact = "EapEc";
        public const string AppName_GroupFile = "EapGf";
        public const string AppName_InstantMessage = "EapIm";
        public const string AppName_Notify = "EapNotify";
#endif
        public const string ServiceName_AttachmentStateService = "AttachmentStateService";
        public const string ServiceName_UserConfigStateService = "UserConfigStateService";
        public const string ServiceName_SystemConfigStateService = "SystemConfigStateService";
        public const string ServiceName_EnterpriseContactService = "EnterpriseContactService";
        public const string ServiceName_GroupFileService = "GroupFileService";
        public const string ServiceName_ConversationCtrlStateService = "ConversationCtrlStateService";
        public const string ServiceName_ConversationMsgStateService = "ConversationMsgStateService";
        public const string ServiceName_NotifySessionActorService = "NotifySessionActorService";

#region ListenerName_UserConfigStateService
        public const string ListenerName_UserSettingAppService = "UserSettingAppService";
        public const string ListenerName_UserFavoriteAppService = "UserFavoriteAppService";
#endregion

#region ListenerName_SystemConfigStateService
        public const string ListenerName_ClientAppService = "ClientAppService";
        public const string ListenerName_WorkbenchAppService = "WorkbenchAppService";
        public const string ListenerName_TodoCenterAppService = "TodoCenterAppService";
#endregion

#region ListenerName_EnterpriseContactService
        public const string ListenerName_DepartmentAppService = "DepartmentAppService";
        public const string ListenerName_EmployeeAppService = "EmployeeAppService";
        public const string ListenerName_PositionAppService = "PositionAppService";
        public const string ListenerName_GroupAppService = "GroupAppService";
#endregion

#region ListenerName_GroupFileService
        public const string ListenerName_ControlAppService = "ControlAppService";
        #endregion

#if DEBUG
        public const string DnsName_TodoCenterProxyApi = "http://localhost:10123";
#else
        public const string DnsName_TodoCenterProxyApi = "http://todocenterproxyapi.eap:10123";
#endif
        //TODO 待办中心的配置应该加入 http://todocenterproxyapi.eap:10123
    }
}


<|start_filename|>src/ConversationCtrlStateService/EventBus/GroupMembersUpdatedConsumer.cs<|end_filename|>
﻿using ConversationCtrlStateService.DomainServices;
using EnterpriseContact;
using EnterpriseContact.MsgConstracts;
using InstantMessage;
using InstantMessage.Entities;
using MassTransit;
using MassTransit.ConsumeConfigurators;
using Microsoft.ServiceFabric.Data;
using Nest;
using Serilog;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;

namespace ConversationCtrlStateService.EventBus
{
    public class GroupMembersUpdatedConsumer : IConsumer<GroupMembersUpdated>
    {
        private readonly IGroupAppService _groupAppService;
        private readonly IReliableStateManager _stateManager;
        private readonly ConversationManager _manager;
        private readonly ILogger _logger;

        public GroupMembersUpdatedConsumer(IReliableStateManager stateManager, IGroupAppService groupAppService)
        {
            _groupAppService = groupAppService;
            _stateManager = stateManager;
            _manager = new ConversationManager(stateManager);
            _logger = Log.ForContext<GroupMembersUpdatedConsumer>();
        }

        public async Task Consume(ConsumeContext<GroupMembersUpdated> context)
        {
            try
            {
                var participants = await _groupAppService.GetUserIdsByIdAsync(context.Message.Id);
                await _manager.AddOrUpdateAsync(context.Message.Id, participants, ConversationType.CustomGroup);
            }
            catch (Exception ex)
            {
                _logger.Error(ex, ex.Message);
                throw;
            }
        }
    }
}


<|start_filename|>src/ConfigMgmtApi/ViewModels/CheckAuthCodeVm.cs<|end_filename|>
﻿using System;

namespace ConfigMgmt.ViewModels
{
    public class CheckAuthCodeVm
    {
        public Guid AppEntranceId { get; set; }

        public string DeviceCode { get; set; }

        public string MobileCode { get; set; }
    }
}

<|start_filename|>src/NotifyDomain/RemotingClient.cs<|end_filename|>
﻿using Microsoft.ServiceFabric.Actors;
using Microsoft.ServiceFabric.Actors.Client;
using Microsoft.ServiceFabric.Services.Remoting.Client;
using Microsoft.ServiceFabric.Services.Remoting.V2.FabricTransport.Client;
using ServiceFabricContrib;
using System;
using System.Collections.Generic;
using System.Fabric;
using System.Text;
using static Common.Constants;

namespace Notification
{
    public static class RemotingClient
    {
        private static ServiceProxyFactory proxyFactory = new ServiceProxyFactory((c) => new FabricTransportServiceRemotingClientFactory());

        private static FabricClient fabricClient = new FabricClient();

        public static INotifySessionActor CreateNotifySessionActor(Guid userId)
        {
            var builder = new ServiceUriBuilder(AppName_Notify, ServiceName_NotifySessionActorService);
            return ActorProxy.Create<INotifySessionActor>(new ActorId(userId), builder.ToUri());
        }
    }
}


<|start_filename|>src/NotifyGatewayWeb/EventBus/EventBusExtensions.cs<|end_filename|>
﻿using Common;
using MassTransit;
using MassTransit.AspNetCoreIntegration;
using AppsOnSF.Common.Options;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using ServiceFabricContrib;
using System;
using System.Fabric;


namespace NotifyGatewayWeb.EventBus
{
    public static class EventBusExtensions
    {
        public static IServiceCollection AddEventBus(this IServiceCollection services)
        {
            services.AddMassTransit(x =>
            {
                x.AddConsumer<SendEventNotifyConsumer>();
                x.AddConsumer<SendMessageNotifyConsumer>();

                x.AddBus(provider =>
                {
                    return Bus.Factory.CreateUsingRabbitMq(cfg =>
                    {
                        var serviceContext = provider.GetRequiredService<StatelessServiceContext>();
                        var option = serviceContext.GetOption<RabbitMQOption>("RabbitMQ_ms");

                        var host = cfg.Host(new Uri(option.HostAddress), h =>
                        {
                            h.Username(option.Username);
                            h.Password(<PASSWORD>);
                        });

                        cfg.ReceiveEndpoint(host, RabbitMqReceiveEndpointNames.NotifyGateway, e =>
                        {
                            e.ConfigureConsumer<SendEventNotifyConsumer>(provider);
                            e.ConfigureConsumer<SendMessageNotifyConsumer>(provider);
                        });
                    });
                });
            });

            services.AddSingleton<IHostedService>(provider =>
            {
                var buses = provider.GetServices<IBusControl>();
                return new BusHostedService(buses);
            });

            return services;
        }
    }
}


<|start_filename|>src/AttachmentDomain/RemotingClient.cs<|end_filename|>
﻿using Microsoft.ServiceFabric.Services.Client;
using Microsoft.ServiceFabric.Services.Remoting.Client;
using Microsoft.ServiceFabric.Services.Remoting.V2.FabricTransport.Client;
using ServiceFabricContrib;
using System;
using System.Collections.Generic;
using System.Fabric;
using System.Text;
using static Common.Constants;

namespace Attachment
{
    public static class RemotingClient
    {
        private static ServiceProxyFactory proxyFactory = new ServiceProxyFactory((c) => new FabricTransportServiceRemotingClientFactory());

        private static FabricClient fabricClient = new FabricClient();

        public static IAttachmentAppService CreateAttachmentAppService(string itemId)
        {
            var builder = new ServiceUriBuilder(AppName_Attachment, ServiceName_AttachmentStateService);
            return proxyFactory.CreateServiceProxy<IAttachmentAppService>(builder.ToUri(),
                new ServicePartitionKey(HashUtil.getLongHashCode(itemId)));
        }
    }
}


<|start_filename|>test/ConfigMgmtUnitTest/Controllers/WorkbenchControllerTest.cs<|end_filename|>
﻿using Common.Services;
using ConfigMgmt;
using ConfigMgmt.Controllers;
using ConfigMgmt.Entities;
using ConfigMgmt.ViewModels;
using FluentAssertions;
using Microsoft.AspNetCore.Http;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using NSubstitute;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnitTestCommon;

namespace ConfigMgmtUnitTest.Controllers
{
    [TestClass]
    public class WorkbenchControllerTest : ControllerTestBase
    {
        [TestMethod]
        public async Task GetAll()
        {
            var workbenchAppService = Substitute.For<IWorkbenchAppService>();
            workbenchAppService.GetAppEntrancesAsync(Arg.Is<GetAppEntrancesInput>(o =>
                o.ClientId == Client.DefaultId && o.ClientPlatform == ClientPlatform.Android))
            .Returns(Task.FromResult(new List<AppEntranceDto>
            {
                new AppEntranceDto
                {
                    Id=Guid.NewGuid(),
                    AppId="aaa",
                },
                new AppEntranceDto
                {
                    Id=Guid.NewGuid(),
                    AppId="bbb"
                }
            }));

            var target = new WorkbenchController(
                workbenchAppService,
                Substitute.For<IClientAppService>(),
                Substitute.For<IBizSystemAppService>(),
                Substitute.For<IBadgeApiClient>(),
                Substitute.For<IMobileCodeSender>(),
                _ => Substitute.For<IUserSettingAppService>());
            target.ControllerContext = CreateMockContext();

            var result = await target.GetAll(ClientPlatform.Android);
            result.Value.Should().NotBeNull();
            result.Value.Count.Should().Be(2);
        }

        [TestMethod]
        public async Task GetBadgeAmount()
        {
            var config = new ComponentConfig
            {
                AuthType = ComponentDataSourceAuthType.None,
                DataSources = new List<ComponentDataSource>
                {
                    new ComponentDataSource
                    {
                        Key=ComponentDataSourceKeys.BadgeValue
                    }
                }
            };

            var workbenchAppService = Substitute.For<IWorkbenchAppService>();
            workbenchAppService.GetComponentConfigAsync(Arg.Any<Guid>())
                .Returns(Task.FromResult(config));

            var apiClient = Substitute.For<IBadgeApiClient>();
            apiClient.GetAmountAsync(config)
                .Returns(Task.FromResult<int?>(1));

            var target = new WorkbenchController(
                workbenchAppService,
                Substitute.For<IClientAppService>(),
                Substitute.For<IBizSystemAppService>(),
                apiClient,
                Substitute.For<IMobileCodeSender>(),
                _ => Substitute.For<IUserSettingAppService>());
            target.ControllerContext = CreateMockContext();

            var result = await target.GetBadgeAmount(Guid.NewGuid());
            result.Value.Should().Be(1);
        }

        [TestMethod]
        public async Task SendAuthCode()
        {
            var mobileCodeSender = Substitute.For<IMobileCodeSender>();
            mobileCodeSender.SendAsync(Arg.Any<string>())
                .Returns("1234");
            var target = new WorkbenchController(
                Substitute.For<IWorkbenchAppService>(),
                Substitute.For<IClientAppService>(),
                Substitute.For<IBizSystemAppService>(),
                Substitute.For<IBadgeApiClient>(),
                mobileCodeSender,
              _ => Substitute.For<IUserSettingAppService>());
            target.ControllerContext = CreateMockContext();

            var result = await target.SendAuthCode();
            result.Value.Should().NotBeNull();
            result.Value.Data.Should().Contain("5678");
        }

        [TestMethod]
        public async Task CheckAuthCode()
        {
            var deviceCode = Guid.NewGuid().ToString("N");
            var appEntranceId = Guid.NewGuid();

            var mobileCodeSender = Substitute.For<IMobileCodeSender>();
            mobileCodeSender.CheckAsync(Arg.Any<string>(), "1234")
                .Returns(true);

            var userSettingAppService = Substitute.For<IUserSettingAppService>();
            userSettingAppService.SetAppEntranceAuthStateAsync(User_Id, Arg.Any<AppEntranceAuthStateInput>())
                .Returns(Task.CompletedTask);

            var target = new WorkbenchController(
                Substitute.For<IWorkbenchAppService>(),
                Substitute.For<IClientAppService>(),
                Substitute.For<IBizSystemAppService>(),
                Substitute.For<IBadgeApiClient>(),
                mobileCodeSender,
                _ => userSettingAppService);
            target.ControllerContext = CreateMockContext();

            var result = await target.CheckAuthCode(new CheckAuthCodeVm
            {
                AppEntranceId = appEntranceId,
                DeviceCode = deviceCode,
                MobileCode = "1234"
            });

            result.Value.Should().BeTrue();
            await userSettingAppService.Received().SetAppEntranceAuthStateAsync(User_Id,
                Arg.Is<AppEntranceAuthStateInput>(x => x.DeviceCode == deviceCode && x.AppEntranceId == appEntranceId));
        }

        [TestMethod]
        public async Task CheckAuthStatus()
        {
            var deviceCode = Guid.NewGuid().ToString("N");
            var appEntranceId = Guid.NewGuid();

            var userSettingAppService = Substitute.For<IUserSettingAppService>();
            userSettingAppService.GetAppEntranceAuthStateAsync(User_Id, deviceCode, appEntranceId)
                .Returns(true);

            var target = new WorkbenchController(
                Substitute.For<IWorkbenchAppService>(),
                Substitute.For<IClientAppService>(),
                Substitute.For<IBizSystemAppService>(),
                Substitute.For<IBadgeApiClient>(),
                Substitute.For<IMobileCodeSender>(),
                _ => userSettingAppService);
            target.ControllerContext = CreateMockContext();

            var result = await target.CheckAuthStatus(appEntranceId, deviceCode);

            result.Value.Should().BeTrue();
            await userSettingAppService.Received(1).GetAppEntranceAuthStateAsync(User_Id, deviceCode, appEntranceId);
        }
    }
}


<|start_filename|>src/ConfigMgmtDomain/Dtos/AppEntranceDto.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Text;

namespace ConfigMgmt
{
    /// <summary>
    /// 工作台中的应用入口信息
    /// </summary>
    public class AppEntranceDto
    {
        public Guid Id { get; set; }

        public string Title { get; set; }

        public string Icon { get; set; }

        /// <summary>
        /// 关联文件夹Id
        /// </summary>
        public Guid? FolderId { get; set; }

        /// <summary>
        /// 是否是文件夹的配置
        /// </summary>
        public bool IsFolder { get; set; }

        /// <summary>
        /// 图标显示此次比例，1=1x，2=高2x，3=宽2x，4=高宽2x
        /// </summary>
        public int IconDisplaySize { get; set; }

        public int Order { get; set; }

        public AppEntryAuthType AuthType { get; set; }

        public string[] NeedRoles { get; set; }

        /// <summary>
        /// 入口跳转Uri
        /// </summary>
        public string Uri { get; set; }

        /// <summary>
        /// 应用id
        /// </summary>
        public string AppId { get; set; } = "";

        #region component info
        public bool ShowBadge { get; set; }
        #endregion

        public bool NeedAuth { get; set; }
    }
}


<|start_filename|>src/EnterpriseContactService/Migrations/20190306071224_initCreated.cs<|end_filename|>
﻿using System;
using Microsoft.EntityFrameworkCore.Migrations;

namespace EnterpriseContact.Migrations
{
    public partial class initCreated : Migration
    {
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.CreateTable(
                name: "Departments",
                columns: table => new
                {
                    Id = table.Column<Guid>(nullable: false),
                    ParentId = table.Column<Guid>(nullable: true),
                    Number = table.Column<string>(maxLength: 128, nullable: false),
                    Name = table.Column<string>(maxLength: 128, nullable: false),
                    TypeId = table.Column<int>(nullable: false),
                    Sort = table.Column<int>(nullable: false),
                    IconId = table.Column<int>(nullable: true)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Departments", x => x.Id);
                    table.ForeignKey(
                        name: "FK_Departments_Departments_ParentId",
                        column: x => x.ParentId,
                        principalTable: "Departments",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Restrict);
                });

            migrationBuilder.CreateTable(
                name: "Employees",
                columns: table => new
                {
                    Id = table.Column<Guid>(nullable: false),
                    Number = table.Column<string>(maxLength: 64, nullable: false),
                    Name = table.Column<string>(maxLength: 128, nullable: false),
                    UserId = table.Column<Guid>(nullable: true),
                    UserName = table.Column<string>(maxLength: 128, nullable: true),
                    IdCardNo = table.Column<string>(maxLength: 128, nullable: false),
                    Mobile = table.Column<string>(maxLength: 128, nullable: true),
                    Gender = table.Column<int>(nullable: false),
                    Avatar = table.Column<string>(nullable: true),
                    PrimaryDepartmentId = table.Column<Guid>(nullable: false),
                    PrimaryPositionId = table.Column<Guid>(nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Employees", x => x.Id);
                });

            migrationBuilder.CreateTable(
                name: "Groups",
                columns: table => new
                {
                    Id = table.Column<Guid>(nullable: false),
                    Name = table.Column<string>(maxLength: 128, nullable: false),
                    Type = table.Column<int>(nullable: false),
                    Remark = table.Column<string>(nullable: true),
                    IconId = table.Column<int>(nullable: true),
                    Created = table.Column<DateTimeOffset>(nullable: false),
                    Updated = table.Column<DateTimeOffset>(nullable: false),
                    CreatedBy = table.Column<Guid>(nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Groups", x => x.Id);
                });

            migrationBuilder.CreateTable(
                name: "Positions",
                columns: table => new
                {
                    Id = table.Column<Guid>(nullable: false),
                    Name = table.Column<string>(maxLength: 128, nullable: false),
                    DepartmentId = table.Column<Guid>(nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Positions", x => x.Id);
                    table.ForeignKey(
                        name: "FK_Positions_Departments_DepartmentId",
                        column: x => x.DepartmentId,
                        principalTable: "Departments",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                });

            migrationBuilder.CreateTable(
                name: "GroupMembers",
                columns: table => new
                {
                    GroupId = table.Column<Guid>(nullable: false),
                    EmployeeId = table.Column<Guid>(nullable: false),
                    IsOwner = table.Column<bool>(nullable: false),
                    Joined = table.Column<DateTimeOffset>(nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_GroupMembers", x => new { x.GroupId, x.EmployeeId });
                    table.ForeignKey(
                        name: "FK_GroupMembers_Employees_EmployeeId",
                        column: x => x.EmployeeId,
                        principalTable: "Employees",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                    table.ForeignKey(
                        name: "FK_GroupMembers_Groups_GroupId",
                        column: x => x.GroupId,
                        principalTable: "Groups",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                });

            migrationBuilder.CreateTable(
                name: "EmployeePositions",
                columns: table => new
                {
                    EmployeeId = table.Column<Guid>(nullable: false),
                    PositionId = table.Column<Guid>(nullable: false),
                    IsPrimary = table.Column<bool>(nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_EmployeePositions", x => new { x.EmployeeId, x.PositionId });
                    table.ForeignKey(
                        name: "FK_EmployeePositions_Employees_EmployeeId",
                        column: x => x.EmployeeId,
                        principalTable: "Employees",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                    table.ForeignKey(
                        name: "FK_EmployeePositions_Positions_PositionId",
                        column: x => x.PositionId,
                        principalTable: "Positions",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                });

            migrationBuilder.CreateIndex(
                name: "IX_Departments_Number",
                table: "Departments",
                column: "Number");

            migrationBuilder.CreateIndex(
                name: "IX_Departments_ParentId",
                table: "Departments",
                column: "ParentId");

            migrationBuilder.CreateIndex(
                name: "IX_EmployeePositions_PositionId",
                table: "EmployeePositions",
                column: "PositionId");

            migrationBuilder.CreateIndex(
                name: "IX_Employees_Number",
                table: "Employees",
                column: "Number");

            migrationBuilder.CreateIndex(
                name: "IX_Employees_UserId",
                table: "Employees",
                column: "UserId");

            migrationBuilder.CreateIndex(
                name: "IX_GroupMembers_EmployeeId",
                table: "GroupMembers",
                column: "EmployeeId");

            migrationBuilder.CreateIndex(
                name: "IX_Groups_Type",
                table: "Groups",
                column: "Type");

            migrationBuilder.CreateIndex(
                name: "IX_Positions_DepartmentId",
                table: "Positions",
                column: "DepartmentId");
        }

        protected override void Down(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.DropTable(
                name: "EmployeePositions");

            migrationBuilder.DropTable(
                name: "GroupMembers");

            migrationBuilder.DropTable(
                name: "Positions");

            migrationBuilder.DropTable(
                name: "Employees");

            migrationBuilder.DropTable(
                name: "Groups");

            migrationBuilder.DropTable(
                name: "Departments");
        }
    }
}


<|start_filename|>src/EnterpriseContactApi/VmMappingProfile.cs<|end_filename|>
﻿using AutoMapper;
using EnterpriseContact.ViewModels;
using System;

namespace EnterpriseContact
{
    public class VmMappingProfile : Profile
    {
        public VmMappingProfile()
        {
            CreateMap<DepartmentListOutput, DepartmentListVm>();
            CreateMap<DepartmentListOutput, DepartmentSearchListVm>();

            CreateMap<EmployeeListOutput, EmployeeListVm>().
                AfterMap((s, d) =>
                {
                    d.IsPrimary = s.PositionId == s.PrimaryPositionId;
                });
            CreateMap<EmployeeOutput, EmployeeDetailVm>();

            CreateMap<GroupListOutput, GroupListVm>();
            CreateMap<GroupOutput, GroupDetailVm>();
            CreateMap<GroupEditVm, GroupInput>();
            CreateMap<GroupMemberOutput, GroupMemberListVm>();
        }
    }
}


<|start_filename|>src/ApiCommon/Const/StatusConst.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Common
{
    public static class StatusConst
    {
        public const int SucessStatus = 0;
        public const int DefaultFailureStatus = -1;


        //custome statusCode

        /// <summary>
        /// 群组文件中校验md5不存在
        /// </summary>
        public const int FileNotFound = 10;

        /// <summary>
        /// 不在同一个联系人组
        /// </summary>
        public const int NotInSameContactGroup = 0x00A1;


        #region 工作台配置信息错误编码

        /// <summary>
        /// 未找到工作太配置
        /// </summary>
        public const int NotFoundWorkBenchConfig = 0x00B1;

        /// <summary>
        /// 工作台参数配置错误
        /// </summary>
        public const int WorkBenchConfigError = 0x00B2;

        /// <summary>
        /// 工作台内部网络请求错误
        /// </summary>
        public const int WorkBenchInnerError = 0x00B3;

        #endregion
    }
}


<|start_filename|>src/ApiCommon/EapBaseController.cs<|end_filename|>
﻿using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Serilog;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;

namespace Common
{
    [Authorize]
    [ApiController]
    public abstract class EapBaseController : ControllerBase
    {
        protected ResponseData<T> BuildSuccess<T>(T data)
        {
            return ResponseData<T>.BuildSuccessResponse(data);
        }

        protected ResponseData BuildSuccess()
        {
            return ResponseData.BuildSuccessResponse();
        }

        protected ResponseData<T> BuildFaild<T>()
        {
            return ResponseData<T>.BuildFailedResponse(StatusConst.DefaultFailureStatus);
        }

        protected ResponseData<T> BuildFaild<T>(string message)
        {
            return ResponseData<T>.BuildFailedResponse(StatusConst.DefaultFailureStatus, message);
        }

        protected ResponseData<T> BuildFaild<T>(int code)
        {
            return ResponseData<T>.BuildFailedResponse(code);
        }

        protected ResponseData<T> BuildFaild<T>(int code, string message)
        {
            return ResponseData<T>.BuildFailedResponse(code, message);
        }

        protected ResponseData BuildFaild()
        {
            return ResponseData.BuildFailedResponse(StatusConst.DefaultFailureStatus);
        }

        protected ResponseData BuildFaild(string message)
        {
            return ResponseData.BuildFailedResponse(StatusConst.DefaultFailureStatus, message);
        }

        protected ResponseData BuildFaild(int code)
        {
            return ResponseData.BuildFailedResponse(code);
        }

        protected ResponseData BuildFaild(int code, string message)
        {
            return ResponseData.BuildFailedResponse(code, message);
        }

        /// <summary>
        /// 获取当前用户的名称
        /// </summary>
        /// <returns></returns>
        protected string GetUserName()
        {
            if (!User.Identity.IsAuthenticated)
                return string.Empty;
            var claim = User.FindFirst("preferred_username");
            if (claim == null)
                return string.Empty;
            return claim.Value;
        }

        /// <summary>
        /// 获取当前用户的名称
        /// </summary>
        /// <returns></returns>
        protected string GetUserFullName()
        {
            if (!User.Identity.IsAuthenticated)
                return string.Empty;
            var claim = User.FindFirst("name");
            if (claim == null)
                return string.Empty;
            return claim.Value;
        }

        /// <summary>
        /// 获取当前用户的UserId
        /// </summary>
        /// <returns></returns>
        protected Guid GetUserId()
        {
            if (!User.Identity.IsAuthenticated)
                return Guid.Empty;
            //var claim = User.Claims.FirstOrDefault(u => u.Type == "sub");
            var claim = User.FindFirst(ClaimTypes.NameIdentifier);
            if (claim == null || string.IsNullOrEmpty(claim.Value))
                return Guid.Empty;
            return new Guid(claim.Value);
        }

        /// <summary>
        /// 获取当前用户的手机号
        /// </summary>
        /// <returns></returns>
        protected string GetUserMobile()
        {
            if (!User.Identity.IsAuthenticated)
                return string.Empty;
            var claim = User.Claims.FirstOrDefault(u => u.Type == "phone_number");
            if (claim == null)
                return string.Empty;
            return claim.Value;
        }

        /// <summary>
        /// 获取当前用户的员工号
        /// </summary>
        /// <returns></returns>
        protected string GetEmployeeNumber()
        {
            if (!User.Identity.IsAuthenticated)
                return string.Empty;
            var claim = User.FindFirst("employee_number");
            if (claim == null)
                return string.Empty;
            return claim.Value;
        }

        /// <summary>
        /// 获取当前用户的员工号
        /// </summary>
        /// <returns></returns>
        protected Guid GetEmployeeId()
        {
            if (!User.Identity.IsAuthenticated)
                return Guid.Empty;
            var claim = User.FindFirst("employee_mdmid");
            if (claim == null || string.IsNullOrEmpty(claim.Value))
                return Guid.Empty;
            return new Guid(claim.Value);
        }

        /// <summary>
        /// 获取当前用户的身份证号
        /// </summary>
        /// <returns></returns>
        protected string GetIdCardNo()
        {
            if (!User.Identity.IsAuthenticated)
                return string.Empty;
            var claim = User.FindFirst("idcard_number");
            if (claim == null)
                return string.Empty;
            return claim.Value;
        }

        /// <summary>
        /// 获取当前用户的角色数组
        /// </summary>
        /// <returns></returns>
        protected string[] GetUserRoles()
        {
            if (!User.Identity.IsAuthenticated)
                return new string[0];
            var claim = User.FindFirst(ClaimTypes.Role);
            if (claim == null)
                return new string[0];
            return claim.Value.Split(',');
        }

        protected Dictionary<string, string> GetUserContext()
        {
            try
            {
                return new Dictionary<string, string>
                {
                    ["UserName"] = GetUserName(),
                    ["UserFullName"] = GetUserFullName(),
                    ["UserId"] = GetUserId().ToString(),
                    ["UserMobile"] = GetUserMobile(),
                    ["EmployeeNumber"] = GetEmployeeNumber(),
                    ["EmployeeId"] = GetEmployeeId().ToString(),
                    ["IdCardNo"] = GetIdCardNo(),
                    ["UserRoles"] = string.Join(",", GetUserRoles())
                };
            }
            catch
            {
                return new Dictionary<string, string>();
            }
        }

        protected string LogError(ILogger logger, Exception ex)
        {
            logger.Error(ex, "Api Error. \r\n" +
                "UserContext:{@UserContext}. \r\n" +
                "RequestPath: {RequestPath}. \r\n" +
                "RequestQuery: {@RequestQuery}. ",
                GetUserContext(),
                Request.Path.Value,
                Request.Query);

            return ex.Message;
        }
    }
}


<|start_filename|>src/EnterpriseContactService/DepartmentAppService.cs<|end_filename|>
﻿using AutoMapper;
using AutoMapper.QueryableExtensions;
using Microsoft.EntityFrameworkCore;
using Microsoft.ServiceFabric.Data;
using ServiceFabricContrib;
using System;
using System.Collections.Generic;
using System.Fabric;
using System.Linq;
using System.Threading.Tasks;

namespace EnterpriseContact
{
    public class DepartmentAppService : BaseAppService, IDepartmentAppService
    {
        public DepartmentAppService(StatelessServiceContext serviceContext, DbContextOptions<ServiceDbContext> dbOptions, IMapper mapper)
            : base(serviceContext, dbOptions, mapper)
        {
        }

        public async Task<List<DepartmentListOutput>> GetAllListAsync()
        {
            using (var db = new ServiceDbContext(_dbOptions))
            {
                var query = from item in db.Departments
                            select item;

                return await query.ProjectTo<DepartmentListOutput>(_mapper.ConfigurationProvider)
                    .ToListAsync();
            }
        }

        public async Task<GroupOutput> GetDepGroupByIdAsync(Guid id)
        {
            using (var db = new ServiceDbContext(_dbOptions))
            {
                var dto = await db.Departments
                    .Where(x => x.Id == id)
                    .Select(x => new GroupOutput
                    {
                        Created = DateTimeOffset.UtcNow,
                        CreatedBy = Guid.Empty,
                        Id = x.Id,
                        Name = x.Name,
                        Type = GroupType.DepartmentChat,
                        Updated = DateTimeOffset.UtcNow
                    })
                    .FirstOrDefaultAsync();

                if (dto == null) return null;

                var query = from e in db.Employees
                            from ep in e.Positions
                            join pos in db.Positions.Where(o => o.DepartmentId == id)
                                on ep.PositionId equals pos.Id
                            orderby e.Number
                            select new GroupMemberOutput
                            {
                                EmployeeId = e.Id,
                                EmployeeName = e.Name,
                                Gender = e.Gender,
                                IsOwner = false,
                                Joined = DateTimeOffset.UtcNow,
                                PrimaryDepartmentId = e.PrimaryDepartmentId
                            };

                dto.Members = await query.ToListAsync();

                return dto;
            }
        }

        public async Task<List<DepartmentListOutput>> GetListByIdsAsync(List<Guid> ids)
        {
            using (var db = new ServiceDbContext(_dbOptions))
            {
                var query = from item in db.Departments
                            where ids.Contains(item.Id)
                            select item;

                return await query.ProjectTo<DepartmentListOutput>(_mapper.ConfigurationProvider)
                    .ToListAsync();
            }
        }

        public async Task<List<DepartmentListOutput>> GetListByParentIdAsync(Guid id)
        {
            using (var db = new ServiceDbContext(_dbOptions))
            {
                var query = from item in db.Departments
                            where item.ParentId == id
                            orderby item.Sort, item.Name
                            select item;

                return await query.ProjectTo<DepartmentListOutput>(_mapper.ConfigurationProvider)
                    .ToListAsync();
            }
        }

        public async Task<List<DepartmentListOutput>> GetRootListAsync()
        {
            using (var db = new ServiceDbContext(_dbOptions))
            {
                var query = from item in db.Departments
                            where item.ParentId == null
                            join child in db.Departments on item.Id equals child.ParentId
                            orderby child.Sort, child.Name
                            select child;

                var result = await query.ProjectTo<DepartmentListOutput>(_mapper.ConfigurationProvider)
                    .ToListAsync();
                return result;
            }
        }

        public async Task<List<DepartmentListOutput>> SearchByKeywordAsync(string keyword)
        {
            using (var db = new ServiceDbContext(_dbOptions))
            {
                var query = from item in db.Departments
                            where item.Name.Contains(keyword)
                            orderby item.Sort, item.Name
                            select item;

                return await query.ProjectTo<DepartmentListOutput>(_mapper.ConfigurationProvider)
                    .ToListAsync();
            }
        }
    }
}


<|start_filename|>src/ApiCommon/Services/IIdentityService.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Common.Services
{
    public interface IIdentityService
    {
        Task<string> GetClientCredentialAccessTokenAsync(string clientId, string clientSecret, string scope);
    }
}


<|start_filename|>src/InstantMessageDomain/Dtos/GetOldMessagesInput.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Text;

namespace InstantMessage
{
    [Serializable]
    public struct GetOldMessagesInput
    {
        /// <summary>
        /// ConversationId
        /// </summary>
        public Guid Id { get; set; }

        /// <summary>
        /// 客户端本地消息中最老的一条消息的Id
        /// 返回的时候不会包含这条消息
        /// </summary>
        public Guid OldestMsgId { get; set; }
    }
}


<|start_filename|>src/EnterpriseContactService/Entities/GroupMember.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EnterpriseContact.Entities
{
    public class GroupMember
    {
        public Guid GroupId { get; set; }

        public virtual Group Group { get; set; }

        public Guid EmployeeId { get; set; }

        public virtual Employee Employee { get; set; }

        public bool IsOwner { get; set; }

        /// <summary>
        /// 加入时间
        /// </summary>
        public DateTimeOffset Joined { get; set; }
    }
}


<|start_filename|>src/EnterpriseContactApi/Controllers/GroupController.cs<|end_filename|>
﻿using Common;
using AutoMapper;
using EnterpriseContact.ViewModels;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ServiceFabricContrib;
using Serilog;

namespace EnterpriseContact.Controllers
{
    [Route("api/v1/group")]
    public class GroupController : TheBaseController
    {
        private readonly IGroupAppService _groupAppService;
        private readonly ILogger _logger;

        public GroupController(IMemoryCache cache, IMapper mapper,
            IDepartmentAppService departmentAppService, IPositionAppService positionAppService,
            IGroupAppService groupAppService)
            : base(cache, mapper, departmentAppService, positionAppService)
        {
            _groupAppService = groupAppService;
            _logger = Log.ForContext<GroupController>();
        }

        [HttpGet("my")]
        public async Task<ActionResult<List<GroupListVm>>> GetMy()
        {
            try
            {
                var employeeId = GetEmployeeId();

                var dto = await _groupAppService.GetListByEmployeeIdAsync(employeeId);
                var data = _mapper.Map<List<GroupListVm>>(dto);

                return data;
            }
            catch (Exception ex)
            {
                return BadRequest(LogError(_logger, ex));
            }
        }

        [HttpGet("{id}")]
        [ProducesResponseType(StatusCodes.Status200OK)]
        [ProducesResponseType(StatusCodes.Status404NotFound)]
        public async Task<ActionResult<GroupDetailVm>> GetById(Guid id, bool depGroup = false)
        {
            try
            {
                await EnsureDepartmentListCacheAsync();
                await EnsurePositionListCacheAsync();

                var dto = !depGroup
                    ? await _groupAppService.GetByIdAsync(id)
                    : await _departmentAppService.GetDepGroupByIdAsync(id);

                if (dto == null) return NotFound(id);
                var data = _mapper.Map<GroupDetailVm>(dto);
                if (dto.Members != null && data != null)
                {
                    foreach (var s in dto.Members)
                    {
                        var d = data.Members.First((item) => item.EmployeeId.Equals(s.EmployeeId));
                        if (d != null) SetDepartmentNames(s, d);
                    }
                }
                return data;
            }
            catch (Exception ex)
            {
                return BadRequest(LogError(_logger, ex));
            }
        }

        //最多设置两级部门名称
        private void SetDepartmentNames(GroupMemberOutput s, GroupMemberListVm d)
        {
            d.DepartmentNames = new List<string>();
            if (_departmentListCache.ContainsKey(s.PrimaryDepartmentId))
            {
                var item = _departmentListCache[s.PrimaryDepartmentId];
                d.DepartmentNames.Add(item.Name);
                if (item.ParentId.HasValue && _departmentListCache.ContainsKey(item.ParentId.Value))
                {
                    d.DepartmentNames.Add(_departmentListCache[item.ParentId.Value].Name);
                }
            }
        }

        /// <summary>
        /// 创建群组
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        [HttpPost]
        [ProducesResponseType(typeof(ResponseData<Guid>), StatusCodes.Status201Created)]
        public async Task<ActionResult<ResponseData<Guid>>> Create(GroupEditVm model)
        {

            try
            {
                var input = _mapper.Map<GroupEditVm, GroupInput>(model, opts =>
            {
                opts.AfterMap((GroupEditVm s, GroupInput d) =>
                {
                    d.CurrentUserId = GetUserId();
                    d.CurrentEmployeeId = GetEmployeeId();
                });
            });

                var id = await _groupAppService.CreateCustomAsync(input);
                return CreatedAtAction("GetById", new { id }, BuildSuccess(id));
                //return BuildSuccess(id);
                //TODO 这里可能要根据客户端的需求返回一个VM
            }
            catch (Exception ex)
            {
                return BadRequest(LogError(_logger, ex));
            }
        }

        /// <summary>
        /// 修改群组信息（名称等）,编辑成员
        /// </summary>
        /// <param name="id"></param>
        /// <param name="model"></param>
        /// <returns></returns>
        [HttpPut("{id}")]
        public async Task<ActionResult<ResponseData>> Update(Guid id, GroupEditVm model)
        {
            try
            {
                var input = _mapper.Map<GroupEditVm, GroupInput>(model, opts =>
                {
                    opts.AfterMap((GroupEditVm s, GroupInput d) =>
                    {
                        d.Id = id;
                        d.CurrentUserId = GetUserId();
                        d.CurrentEmployeeId = GetEmployeeId();
                    });
                });

                var result = await _groupAppService.UpdateAsync(input);
                if (result.IsSuccess)
                    return BuildSuccess();
                var msg = "未知业务错误";
                if (result.FailedCode == FailedCodes.Group_NotCreatedBy)
                    msg = "不是所有者，不能编辑/添加成员/删除成员";
                return BuildFaild(result.FailedCode, msg);
            }
            catch (Exception ex)
            {
                return BadRequest(LogError(_logger, ex));
            }
        }

        /// <summary>
        /// 删除/解散群组
        /// 只有Owner才行
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpDelete("{id}")]
        public async Task<ActionResult<ResponseData>> Delete(Guid id)
        {
            try
            {
                var result = await _groupAppService.DeleteAsync(new GroupInput
                {
                    Id = id,
                    CurrentUserId = GetUserId(),
                });
                if (result.IsSuccess)
                    return BuildSuccess();
                var msg = "未知业务错误";
                if (result.FailedCode == FailedCodes.Group_NotCreatedBy)
                    msg = "不是所有者，不能解散群组";
                return BuildFaild(result.FailedCode, msg);
            }
            catch (Exception ex)
            {
                return BadRequest(LogError(_logger, ex));
            }
        }

        /// <summary>
        /// 退出群组
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpPut("quit/{id}")]
        public async Task<ActionResult<ResponseData>> Quit(Guid id)
        {
            try
            {
                var result = await _groupAppService.QuitAsync(new GroupInput
                {
                    Id = id,
                    CurrentUserId = GetUserId(),
                    CurrentEmployeeId = GetEmployeeId(),
                    CurrentEmployeeName = GetUserFullName(),
                });
                if (result.IsSuccess)
                    return BuildSuccess();
                return BuildFaild();
            }
            catch (Exception ex)
            {
                return BadRequest(LogError(_logger, ex));
            }
        }

        [HttpGet("scanjoin/{id}")]
        [ProducesResponseType(StatusCodes.Status200OK)]
        [ProducesResponseType(StatusCodes.Status400BadRequest)]
        public async Task<ActionResult<ResponseData<string>>> GetScanJoinCode(Guid id)
        {
            try
            {
                var r = await _groupAppService.CreateScanJoinCodeAsync(id, GetUserId());
                if (!r.IsSuccess) return BadRequest(r.FailedCode);
                return BuildSuccess(r.Output);
            }
            catch (Exception ex)
            {
                return BadRequest(LogError(_logger, ex));
            }
        }

        [HttpPut("scanjoin/{code}")]
        [ProducesResponseType(StatusCodes.Status200OK)]
        [ProducesResponseType(StatusCodes.Status400BadRequest)]
        public async Task<ActionResult<ResponseData>> ScanJoin(string code)
        {
            try
            {
                var r = await _groupAppService.ScanJoinAsync(code, GetEmployeeId(), GetUserFullName());
                if (!r.IsSuccess) return BadRequest(r.FailedCode);
                return BuildSuccess();
            }
            catch (Exception ex)
            {
                return BadRequest(LogError(_logger, ex));
            }
        }
    }
}


<|start_filename|>src/AppComponentApi/ApiClients/ITodoApiClient.cs<|end_filename|>
﻿using AppComponent.ViewModels;
using ConfigMgmt.Entities;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace AppComponent
{
    //TODO 未来重构移动到AppComponentDomain项目中

    public interface ITodoApiClient
    {
        Task<List<TodoListVM>> GetPendingListAsync(ComponentConfig config);
        Task<List<TodoListVM>> GetDoneListAsync(ComponentConfig config);
    }
}


<|start_filename|>src/ConversationMsgStateService/IProcessorConfigurator.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Text;

namespace ConversationMsgStateService
{
    public interface IProcessorConfigurator
    {
        bool IsArchiveGroupProcessTime();
    }

    public class DefaultProcessorConfigurator : IProcessorConfigurator
    {
        public bool IsArchiveGroupProcessTime()
        {
            var hour = DateTimeOffset.Now.Hour;
            return hour >= 0 && hour < 6;
        }
    }
}


<|start_filename|>src/InstantMessageApi/ViewModels/MessageVmBase.cs<|end_filename|>
﻿using System;

namespace InstantMessage.ViewModels
{
    public class MessageVmBase
    {
        /// <summary>
        /// 发送者Id
        /// </summary>
        public Guid SenderId { get; set; }
        /// <summary>
        /// 会话Id
        /// </summary>
        public Guid ConversationId { get; set; }

        /// <summary>
        /// 会话类型
        /// </summary>
        public ConversationType ConversationType { get; set; }
    }
}


<|start_filename|>src/EnterpriseContactApi/ViewModels/EmployeeListVm.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Text;

namespace EnterpriseContact.ViewModels
{
    public class EmployeeListVm
    {
        public Guid Id { get; set; }
      
        public string Name { get; set; }

        public int Gender { get; set; }

        public string PositionName { get; set; }

        public bool IsPrimary { get; set; }

        /// <summary>
        /// 员工所属主部门的名称，及其上级名称，Controller可以选择填充几级
        /// </summary>
        public List<string> DepartmentNames { get; set; }
    }
}


<|start_filename|>src/InstantMessageApi/ViewModels/ImageMessageVm.cs<|end_filename|>
﻿using Newtonsoft.Json;
using System.Collections.Generic;

namespace InstantMessage.ViewModels
{
    public class ImageMessageVm : MessageVmBase
    {
        /// <summary>
        /// 上传图片返回的guid
        /// </summary>
        public string ImageGuid { get; set; }
        /// <summary>
        /// 图片的宽
        /// </summary>
        public int ImageWith { get; set; }
        /// <summary>
        /// 图片的高
        /// </summary>
        public int ImageHeight { get; set; }

        public string ImageName { get; set; }

        private Dictionary<string, object> GetContent()
        {
            var dic = new Dictionary<string, object>
            {
                { "ImageGuid", ImageGuid },
                { "ImageName", ImageName }
            };
            return dic;
        }

        /// <summary>
        /// 获取消息内容  ConversationMsg 中的Content 
        /// </summary>
        /// <returns></returns>
        public string GetJsonContent()
        {
            return JsonConvert.SerializeObject(GetContent());
        }
    }
}


<|start_filename|>src/ConfigMgmtDomain/Dtos/InfoVisibility.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Text;

namespace ConfigMgmt
{
    [Serializable]
    public class InfoVisibility
    {
        /// <summary>
        /// 为True显示手机号，默认隐藏手机号
        /// </summary>
        public bool Mobile { get; set; }
    }
}


<|start_filename|>test/GroupFileServiceTest/Services/MapperTest.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using GroupFile;
using GroupFile.Entities;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace GroupFileServiceTest.Services
{
    [TestClass]
    public class MapperTest : TheGroupFileAppServiceTestBase
    {
        //[TestMethod]
        //public void Mapper_Test()
        //{
        //    var mapper = CreateMapper();
        //    var groupid = Guid.NewGuid();
        //    var fileId = Guid.NewGuid();
        //    var updaloadOn = DateTimeOffset.Now;
        //    var uploaderId = Guid.NewGuid();
        //    var name = "xxx";
        //    var fileItem = new FileItem()
        //    {
        //        DownloadAmount = 10,
        //        GroupId = groupid,
        //        Id = fileId,
        //        Name = name,
        //        StoreId = "md5",
        //        UpdatedOn = updaloadOn,
        //        UploaderId = uploaderId,
        //        Store = new FileStore()
        //        {
        //            Size = 10,
        //        }
        //    };
        //    var result = mapper.Map<FileItemDto>(fileItem);
        //    Assert.AreEqual(uploaderId, result.UploaderId);
        //    Assert.AreEqual(name, result.Name);
        //    Assert.AreEqual(fileId, result.Id);
        //    Assert.AreEqual(updaloadOn, result.UpdatedOn);
        //    Assert.AreEqual(10, result.DownloadAmount);
        //    Assert.AreEqual(10, result.Size);
        //}
    }
}


<|start_filename|>src/EnterpriseContactDomain/Dtos/EmployeeOutput.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;

namespace EnterpriseContact
{
    /// <summary>
    /// 联系人信息
    /// </summary>
    [Serializable]
    public class EmployeeOutput
    {
        public Guid Id { get; set; }

        public string Number { get; set; }

        public string Name { get; set; }

        public Guid? UserId { get; set; }

        public string UserName { get; set; }

        public string IdCardNo { get; set; }

        public string Mobile { get; set; }

        public int Gender { get; set; }

        public string Avatar { get; set; }

        public Guid PrimaryDepartmentId { get; set; }

        public Guid PrimaryPositionId { get; set; }

        public List<Guid> ParttimePositionIds { get; set; }
    }
}


<|start_filename|>src/EnterpriseContactDomain/Dtos/GroupInput.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Text;

namespace EnterpriseContact
{
    [Serializable]
    public class GroupInput
    {
        public Guid? Id { get; set; }

        /// <summary>
        /// 联系人组的名称
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// 联系人组的备注信息
        /// </summary>
        public string Remark { get; set; }

        public Guid CurrentUserId { get; set; }

        public Guid? CurrentEmployeeId { get; set; }

        public string CurrentEmployeeName { get; set; }

        public HashSet<Guid> AddingMemberIds { get; set; }

        public List<string> AddingMemberNames { get; set; }

        public HashSet<Guid> RemovingMemberIds { get; set; }

        public List<string> RemovingMemberNames { get; set; }
    }
}


<|start_filename|>src/ConfigMgmtDomain/MappingProfile.cs<|end_filename|>
﻿using AutoMapper;
using ConfigMgmt.Entities;
using System;
using System.Collections.Generic;
using System.Text;

namespace ConfigMgmt
{
    public class MappingProfile : Profile
    {
        public MappingProfile()
        {
            CreateMap<AppEntrance, AppEntranceDto>()
                .AfterMap((s, d) => {
                    d.ShowBadge = s.ComponentConfig?.ShowBadge == true;
                });
        }
    }
}


<|start_filename|>src/NotifyGatewayWeb/Hubs/INotifyClient.cs<|end_filename|>
﻿using Notification;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace NotifyGatewayWeb.Hubs
{
    public interface INotifyClient
    {
        Task ReceiveEventNotify(List<EventNotifyDto> notifies);
        Task ReceiveMessageNotify(List<MessageNotifyDto> notifies);
    }
}


<|start_filename|>src/InstantMessageApi/ViewModels/VoiceMessageVm.cs<|end_filename|>
﻿using Newtonsoft.Json;
using System.Collections.Generic;

namespace InstantMessage.ViewModels
{
    public class VoiceMessageVm : MessageVmBase
    {
        /// <summary>
        /// 语音上传后返回的guid
        /// </summary>
        public string VoiceGuid { get; set; }
        /// <summary>
        /// 语音长度 单位s
        /// </summary>
        public double Duration { get; set; }

        public string FileName { get; set; }

        private Dictionary<string, object> GetContent()
        {
            var dic = new Dictionary<string, object>
            {
                { "VoiceGuid", VoiceGuid },
                { "Duration", Duration },
                { "FileName", FileName }
            };
            return dic;
        }

        /// <summary>
        /// 获取消息内容  ConversationMsg 中的Content 
        /// </summary>
        /// <returns></returns>
        public string GetJsonContent()
        {
            return JsonConvert.SerializeObject(GetContent());
        }
    }
}


<|start_filename|>src/ConversationCtrlStateService/DomainServices/ConversationManager.cs<|end_filename|>
﻿using InstantMessage;
using InstantMessage.Entities;
using Microsoft.ServiceFabric.Data;
using Notification;
using ServiceFabricContrib;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;

namespace ConversationCtrlStateService.DomainServices
{
    public class ConversationManager
    {
        private readonly IReliableStateManager _stateManager;

        public ConversationManager(IReliableStateManager stateManager)
        {
            _stateManager = stateManager;
        }

        public async Task AddOrUpdateAsync(Guid id, List<Guid> participants, ConversationType type)
        {
            var dictList = await Service.GetListDictAsync(_stateManager);
            using (var tx = _stateManager.CreateTransaction())
            {
                var newEntity = new Conversation
                {
                    Id = id,
                    Type = type,
                    Participants = participants,
                    Updated = DateTimeOffset.UtcNow
                };
                await dictList.AddOrUpdateAsync(tx, id, newEntity, (k, v) => newEntity);
                await tx.CommitAsync();
            }
        }

        public async Task DeleteAsync(Guid id)
        {
            var dictKeyToId = await Service.GetKeyToIdDictAsync(_stateManager);
            var dictList = await Service.GetListDictAsync(_stateManager);
            using (var tx = _stateManager.CreateTransaction())
            {
                var entityWrap = await dictList.TryGetValueAsync(tx, id);
                if (entityWrap.HasValue)
                {
                    var edited = entityWrap.Value.DeepCopy();
                    edited.IsDeleted = true;
                    await dictList.TryUpdateAsync(tx, id, edited, entityWrap.Value);

                    if (entityWrap.Value.Type == ConversationType.P2P && !string.IsNullOrEmpty(entityWrap.Value.Key))
                    {
                        await dictKeyToId.TryRemoveAsync(tx, entityWrap.Value.Key);
                    }

                    await tx.CommitAsync();
                }
            }
        }

        public async Task<Conversation?> GetByIdAsync(Guid id)
        {
            var dictIdToEntity = await Service.GetListDictAsync(_stateManager);
            using (var tx = _stateManager.CreateTransaction())
            {
                var entityWrap = await dictIdToEntity.TryGetValueAsync(tx, id);
                if (entityWrap.HasValue)
                    return entityWrap.Value;
                return null;
            }
        }
    }
}


<|start_filename|>src/EnterpriseContactDomain/AppServices/IDepartmentAppService.cs<|end_filename|>
﻿using Microsoft.ServiceFabric.Services.Remoting;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;

namespace EnterpriseContact
{
    public interface IDepartmentAppService : IService
    {
        Task<List<DepartmentListOutput>> GetRootListAsync();
        Task<List<DepartmentListOutput>> GetListByParentIdAsync(Guid id);
        Task<List<DepartmentListOutput>> SearchByKeywordAsync(string keyword);
        Task<List<DepartmentListOutput>> GetAllListAsync();

        /// <summary>
        /// 得到部门虚拟群组信息
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        Task<GroupOutput> GetDepGroupByIdAsync(Guid id);

        /// <summary>
        /// 通过Id集合得到部门列表信息
        /// </summary>
        /// <param name="ids"></param>
        /// <returns></returns>
        Task<List<DepartmentListOutput>> GetListByIdsAsync(List<Guid> ids);
    }
}


<|start_filename|>src/ConfigMgmtDomain/AppServices/IWorkbenchAppService.cs<|end_filename|>
﻿using ConfigMgmt.Entities;
using Microsoft.ServiceFabric.Services.Remoting;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace ConfigMgmt
{
    /// <summary>
    /// 工作台AppService接口
    /// </summary>
    public interface IWorkbenchAppService : IService
    {
        /// <summary>
        /// 根据ClientId、ClientPlatform和所需角色获取工作台的应用入口列表数据
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        Task<List<AppEntranceDto>> GetAppEntrancesAsync(GetAppEntrancesInput input);

        /// <summary>
        /// 根据ClientId获取工作台的应用入口列表数据
        /// </summary>
        /// <param name="clientId"></param>
        /// <returns></returns>
        Task<List<AppEntrance>> GetAppEntrancesRawByClientIdAsync(string clientId);

        /// <summary>
        /// 添加应用入口数据
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        Task<bool> AddOrUpdateEntrancesAsync(AddEntrancesInput input);

        /// <summary>
        /// 根据应用入口Id移除
        /// </summary>
        /// <param name="clientId"></param>
        /// <param name="entryId"></param>
        /// <returns></returns>
        Task<bool> RemoveEntranceAsync(string clientId, Guid entryId);

        /// <summary>
        /// 根据应用入口Id获取组件数据源配置
        /// </summary>
        /// <param name="entryId"></param>
        /// <returns></returns>
        Task<ComponentConfig> GetComponentConfigAsync(Guid entryId);
    }
}


<|start_filename|>src/EnterpriseContactService/Service.cs<|end_filename|>
﻿using AutoMapper;
using Common;
using EnterpriseContact.Consumers;
using MassTransit;
using AppsOnSF.Common.Options;
using Microsoft.Diagnostics.EventFlow;
using Microsoft.EntityFrameworkCore;
using Microsoft.ServiceFabric.Services.Communication.Runtime;
using Microsoft.ServiceFabric.Services.Remoting.V2.FabricTransport.Runtime;
using ServiceFabricContrib;
using System;
using System.Collections.Generic;
using System.Fabric;
using System.Threading;
using System.Threading.Tasks;
using static Common.Constants;

namespace EnterpriseContact
{
    /// <summary>
    /// An instance of this class is created for each service instance by the Service Fabric runtime.
    /// </summary>
    internal sealed class Service : StatelessServiceEapBase
    {
        private readonly DbContextOptions<ServiceDbContext> _dbOptions;
        private readonly IBusControl _busMdm;
        private readonly IBusControl _busMs;
        private readonly IMapper _mapper;
        private readonly RabbitMQOption _rabbitMqOption;

        public Service(StatelessServiceContext context,
             DiagnosticPipeline diagnosticPipeline)
             : base(context, diagnosticPipeline)
        {
            var connectionString = Context.GetOption<DbOption>("ConnectionStrings").ServiceDbConnection;
            _dbOptions = CreateDbOptions(connectionString);
            _busMs = CreateMsBus(Context);
            (_busMdm, _rabbitMqOption) = CreateMdmBus(this.Context);
            _mapper = CreateMapper();
        }

        private (IBusControl, RabbitMQOption) CreateMdmBus(ServiceContext serviceContext)
        {
            var option = serviceContext.GetOption<RabbitMQOption>("RabbitMQ_mdm");

            var bus = Bus.Factory.CreateUsingRabbitMq(cfg =>
            {
                var host = cfg.Host(new Uri(option.HostAddress), h =>
                {
                    h.Username(option.Username);
                    h.Password(option.Password);
                });
                cfg.ReceiveEndpoint(host, "EAP_EnterpriseContactService", c =>
                {
                    c.Consumer(() => new MdmDataConsumer(_dbOptions, _mapper));
                    c.Consumer(() => new OrgEventDataConsumer(_mapper, _dbOptions, _busMs));
                });

            });
            return (bus, option);
        }

        private IBusControl CreateMsBus(ServiceContext serviceContext)
        {
            var option = serviceContext.GetOption<RabbitMQOption>("RabbitMQ_ms");

            var bus = Bus.Factory.CreateUsingRabbitMq(cfg =>
            {
                var host = cfg.Host(new Uri(option.HostAddress), h =>
                {
                    h.Username(option.Username);
                    h.Password(option.Password);
                });

            });
            return bus;
        }

        /// <summary>
        /// Optional override to create listeners (e.g., TCP, HTTP) for this service replica to handle client or user requests.
        /// </summary>
        /// <returns>A collection of listeners.</returns>
        protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
        {

            return new[]
            {
                new ServiceInstanceListener(_ => new MassTransitListener(_busMdm), "masstransit_mdm"),
                new ServiceInstanceListener(context => new FabricTransportServiceRemotingListener(context,
                    new  DepartmentAppService(context,_dbOptions,_mapper)),ListenerName_DepartmentAppService),
                new ServiceInstanceListener(context => new FabricTransportServiceRemotingListener(context,
                    new  PositionAppService(context,_dbOptions,_mapper)),ListenerName_PositionAppService),
                new ServiceInstanceListener(context => new FabricTransportServiceRemotingListener(context,
                    new  EmployeeAppService(context,_dbOptions,_mapper)),ListenerName_EmployeeAppService),
                new ServiceInstanceListener(context => new FabricTransportServiceRemotingListener(context,
                    new  GroupAppService(context,_dbOptions,_mapper,
                    AppsOnSF.Common.BaseServices.RemotingProxyFactory.CreateSimpleKeyValueService(),
                    _busMs)),
                    ListenerName_GroupAppService)
            };
        }

        private static DbContextOptions<ServiceDbContext> CreateDbOptions(string connectionString)
        {
            var builder = new DbContextOptionsBuilder<ServiceDbContext>()
                //.UseLazyLoadingProxies()
                .UseSqlServer(connectionString);
            return builder.Options;
        }

        private static IMapper CreateMapper()
        {
            var config = new MapperConfiguration(cfg =>
            {
                cfg.AddProfile<DtoMappingProfile>();
            });
            return config.CreateMapper();
        }

        protected override async Task OnOpenAsync(CancellationToken cancellationToken)
        {
            using (var db = new ServiceDbContext(_dbOptions))
            {
                await db.Database.MigrateAsync();
            }
        }
    }
}


<|start_filename|>src/AttachmentDomain/Entities/AttachmentItem.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Text;

namespace Attachment.Entities
{
    [Serializable]
    [DataContract]
    public class AttachmentItem
    {
        /// <summary>
        /// 文件的md5值
        /// </summary>
        [DataMember]
        public string Id { get; set; }

        /// <summary>
        /// 存储的KB，显示的时候需要可能显示为MB，GB等
        /// </summary>
        [DataMember]
        public int Size { get; set; }

        /// <summary>
        /// 存储服务中的路径，比如：attachments/2018/12/28/{id}.ext
        /// </summary>
        [DataMember]
        public string Location { get; set; }

        /// <summary>
        /// 初次上传时间
        /// </summary>
        [DataMember]
        public DateTimeOffset Uploaded { get; set; }

        /// <summary>
        /// 初次上传者的UserId
        /// </summary>
        [DataMember]
        public Guid UploadBy { get; set; }

        /// <summary>
        /// 此附件的上下文Id，比如IM的ConversationId，如果不属于任何上下文就设置为Empty；
        /// 添加后不可修改
        /// </summary>
        [DataMember]
        public Guid ContextId { get; set; } //index

        [DataMember]
        public UploadStatus Status { get; set; }
    }
}


<|start_filename|>test/EnterpriseContactUnitTest/Controllers/OrgControllerTest.cs<|end_filename|>
﻿using EnterpriseContact;
using EnterpriseContact.Controllers;
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace EnterpriseContactUnitTest.Controllers
{
    [TestClass]
    public class OrgControllerTest : TheControllerTestBase
    {
        [TestMethod]
        public async Task TestGetRoots()
        {
            var departments = new List<DepartmentListOutput>
            {
                new DepartmentListOutput
                {
                    Id=Guid.NewGuid(),
                    Name="sclqjt",
                    ParentId=new Guid("7e4dcb5d91d24cd9923d965224c2f396"),
                },
                new DepartmentListOutput
                {
                    Id=Guid.NewGuid(),
                    Name="sclqgs",
                    ParentId=new Guid("7e4dcb5d91d24cd9923d965224c2f396"),
                }
            };

            var mockDepartmentAppService = new Mock<IDepartmentAppService>();
            mockDepartmentAppService.Setup(o => o.GetRootListAsync())
                .ReturnsAsync(departments);

            var mockEmployeeAppService = new Mock<IEmployeeAppService>();
            mockEmployeeAppService.Setup(o => o.GetRootListAsync())
                .ReturnsAsync(new List<EmployeeListOutput>());

            var target = new OrgController(
                CreateMemoryCache(),
                CreateMapper(),
                mockDepartmentAppService.Object,
                new Mock<IPositionAppService>().Object,
                mockEmployeeAppService.Object
                );

            var result = await target.GetRoots();

            var data = result.Value;
            data.Departments.Count.Should().Be(2);
            data.Departments[0].Id.Should().Be(departments[0].Id);
            data.Departments[0].Name.Should().Be(departments[0].Name);
        }
    }
}


<|start_filename|>src/InstantMessageDomain/AppServices/IConversationMsgAppService.cs<|end_filename|>
﻿using InstantMessage.Entities;
using Microsoft.ServiceFabric.Services.Remoting;
using Notification;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;

namespace InstantMessage
{
    public interface IConversationMsgAppService : IService
    {
        Task SendMessageAsync(ConversationMsg msg);

        Task<List<ConversationMsg>> GetMessagesAsync(List<MessageNotifyDto> input);

        /// <summary>
        /// 归档群组消息
        /// </summary>
        /// <param name="id">群组Id</param>
        /// <returns></returns>
        Task ArchiveGroupMessagesAsync(Guid id);

        Task<List<ConversationMsg>> GetOldMessagesAsync(GetOldMessagesInput input);
    }
}


<|start_filename|>src/EnterpriseContactDomain/Dtos/SearchEmployeeInput.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EnterpriseContact
{
    /// <summary>
    /// 搜索选项
    /// </summary>
    [Serializable]
    public class SearchEmployeeInput
    {
        public SearchEmployeeInput()
        {
            this.PageIndex = 1;
            this.PageSize = 20;
        }

        public Guid EmpId { get; set; }

        public string Name { get; set; }

        public string Number { get; set; }

        public string IdCardNo { get; set; }

        public Guid? UserId { get; set; }

        public string UserName { get; set; }

        /// <summary>
        /// 用户所属的部门,包括兼职
        /// </summary>
        public Guid DepartmentId { get; set; }

        public int PageIndex { get; set; }

        public int PageSize { get; set; }
    }

    public enum SearchPattern
    {
        IgnoreCase = 1,

        NotIgnoreCase = 2,
    }
}


<|start_filename|>src/ConversationMsgStateService/PackageRoot/Config/eventFlowConfig.json<|end_filename|>
{
  "inputs": [
    {
      "type": "EventSource",
      "level": "servicefabric:/Diagnostics/EventSourceLevel",
      "sources": [
        { "providerName": "Microsoft-ServiceFabric-Services" },
        { "providerName": "Microsoft-ServiceFabric-Actors" },
        // (replace the following value with your service's ServiceEventSource name)
        { "providerName": "EapSfApp-ConversationMsgStateService" }
      ]
    },
    {
      "type": "Serilog"
    }
  ],
  "outputs": [
    {
      "type": "ElasticSearch",
      "indexNamePrefix": "eapsfapp-conversationmsgstateservice",
      "serviceUri": "servicefabric:/Diagnostics/ElasticsearchHost",
      "basicAuthenticationUserName": "",
      "basicAuthenticationUserPassword": "",
      "eventDocumentTypeName": "Logs",
      "numberOfShards": 1,
      "numberOfReplicas": 1,
      "refreshInterval": "15s"
    }
  ],
  "schemaVersion": "2016-08-11"
}

<|start_filename|>src/AttachmentApi/ViewModels/UploadVm.cs<|end_filename|>
﻿using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Text;

namespace Attachment.ViewModels
{
    public class UploadVm
    {
        /// <summary>
        /// 上传的业务模块上下文Id，可以为Empty
        /// </summary>
        public Guid ContextId { get; set; }
        /// <summary>
        /// 文件的md5值
        /// </summary>
        public string MD5 { get; set; }
        /// <summary>
        /// 文件
        /// </summary>
        public IFormFile File { get; set; }
    }
}


<|start_filename|>src/ConfigMgmtDomain/Entities/TodoCenterBizSystem.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Runtime.Serialization;

namespace ConfigMgmt.Entities
{
    /// <summary>
    /// 待办中心集成的业务系统
    /// </summary>
    [DataContract]
    public class TodoCenterBizSystem
    {
        public const string Id_OA = "oa";
        public const string Id_PM = "pm";
        public const string Id_KINDDEE = "kd";

        [DataMember]
        public string Id { get; set; }

        [DataMember]
        public string AppId { get; set; }

        [DataMember]
        public string Name { get; set; }

        [DataMember]
        public string PendingListUrl { get; set; }

        [DataMember]
        public string PendingListAmountUrl { get; set; }

        [DataMember]
        public string DoneListUrl { get; set; }

        [DataMember]
        public Dictionary<ClientPlatform, string> OpenDetailUris { get; set; }
    }
}


<|start_filename|>test/UnitTestCommon/AppServiceTestBase.cs<|end_filename|>
﻿using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Debug;
using Microsoft.ServiceFabric.Data;
using ServiceFabric.Mocks;
using System;
using System.Collections.Generic;
using System.Fabric;
using System.Numerics;
using System.Text;
using System.Threading;
using MassTransit;
using Microsoft.ServiceFabric.Actors.Runtime;

namespace UnitTestCommon
{
    public abstract class AppServiceTestBase
    {
        protected MockReliableStateManager stateManager =  new MockReliableStateManager();

        protected MockActorStateManager actorStateManager = new MockActorStateManager();

        protected IActorStateManager actorStateManagerFactory(ActorBase actorBase, IActorStateProvider actorStateProvider) 
            => actorStateManager;

        protected static ICodePackageActivationContext codePackageContext = MockCodePackageActivationContext.Default;

        protected static StatefulServiceContext statefulServiceContext = MockStatefulServiceContextFactory.Default;

        protected static StatelessServiceContext statelessServiceContext = MockStatelessServiceContextFactory.Default;

        protected CancellationToken token => CancellationToken.None;

        protected static readonly LoggerFactory TestLoggerFactory
            = new LoggerFactory(new[] { new DebugLoggerProvider() });

        protected static IBusControl CreateBus(Action<IInMemoryReceiveEndpointConfigurator> action)
        {
            var needToDisposeBus = Bus.Factory.CreateUsingInMemory(cfg =>
            {
                cfg.ReceiveEndpoint("UnitTestQueue", action);
            });
            needToDisposeBus.Start();
            return needToDisposeBus;
        }
    }
}


<|start_filename|>src/EnterpriseContactDomain/AppServices/IGroupAppService.cs<|end_filename|>
﻿using Microsoft.ServiceFabric.Services.Remoting;
using ServiceFabricContrib;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace EnterpriseContact
{
    public interface IGroupAppService : IService
    {
        /// <summary>
        /// 检查两个员工是否同处一个白名单
        /// </summary>
        /// <param name="currentEmployeeId"></param>
        /// <param name="targetEmployeeId"></param>
        /// <returns></returns>
        Task<bool> CheckSameWhiteListGroupAsync(Guid currentEmployeeId, Guid targetEmployeeId);

        /// <summary>
        /// 得到某个员工加入的自定义群组列表
        /// </summary>
        /// <param name="employeeId"></param>
        /// <returns></returns>
        Task<List<GroupListOutput>> GetListByEmployeeIdAsync(Guid employeeId);

        /// <summary>
        /// 创建自定义群组列表
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        Task<Guid> CreateCustomAsync(GroupInput input);

        /// <summary>
        /// 得到群组的所有成员的UserId
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        Task<List<Guid>> GetUserIdsByIdAsync(Guid id);

        /// <summary>
        /// 更新自定义群组
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        Task<RemotingResult> UpdateAsync(GroupInput input);

        /// <summary>
        /// 删除自定义群组
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        Task<RemotingResult> DeleteAsync(GroupInput input);

        //TODO 支持部门群组的情况
        /// <summary>
        /// 得到自定义/部门群组信息
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        Task<GroupOutput> GetByIdAsync(Guid id);

        /// <summary>
        /// 基于Id数组得到群组列表
        /// </summary>
        /// <param name="ids"></param>
        /// <returns></returns>
        Task<List<GroupListOutput>> GetListByIdsAsync(List<Guid> ids);

        /// <summary>
        /// 退出自定义群组
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        Task<RemotingResult> QuitAsync(GroupInput input);
        
        /// <summary>
        /// 生成扫码加群的代码
        /// </summary>
        /// <param name="id">群组Id</param>
        /// <param name="userId">当前用户Id</param>
        /// <returns>FailedCode=1：不存在此群组；FailedCode=2：当前用户不是创建者</returns>
        Task<RemotingResult<string>> CreateScanJoinCodeAsync(Guid id, Guid userId);

        /// <summary>
        /// 处理扫码加群
        /// </summary>
        /// <param name="code">二维码内容</param>
        /// <param name="employeeId">当前用户的员工Id</param>
        /// <param name="employeeName">当前用户的员工姓名</param>
        /// <returns>FailedCode=1：code格式或内容不对；FailedCode=2：群组不存在；FailedCode=3：此成员已经在群组中</returns>
        Task<RemotingResult> ScanJoinAsync(string code, Guid employeeId, string employeeName);
    }
}


<|start_filename|>src/ConfigMgmtApi/ApiClients/BadgeApiClient.cs<|end_filename|>
﻿using Common.Services;
using ConfigMgmt.Entities;
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Common.Extensions;
using Serilog.Core;
using ConfigMgmt;
using ApiCommon.Extensions;
using Serilog;

namespace ConfigMgmt
{
    //ref: https://thinkrethink.net/2018/07/25/httpclient-httpclientfactory-asp-net-core/

    /// <summary>
    /// 待办组件Api的类型化HttpClient
    /// </summary>
    public class BadgeApiClient : IBadgeApiClient
    {
        private readonly HttpClient _httpClient;
        private readonly IIdentityService _identityService;
        private readonly IHttpContextAccessor _httpContextAccessor;
        private readonly ILogger _logger;

        public BadgeApiClient(
            HttpClient httpClient,
            IHttpContextAccessor httpContextAccessor,
            IIdentityService identityService)
        {
            _httpClient = httpClient;
            _identityService = identityService;
            _httpContextAccessor = httpContextAccessor;
            _logger = Log.ForContext<BadgeApiClient>();
        }

        public async Task<int?> GetAmountAsync(ComponentConfig config)
        {
            var datasource = config.DataSources.Find(o => o.Key == ComponentDataSourceKeys.BadgeValue);
            if (datasource == null || string.IsNullOrEmpty(datasource.Value))
            {
                _logger.Warning(
                    "{@ComponentConfig} has not config BadgeValue or DataSource Url has not Config", 
                    config);

                return null;
            }

            var url = datasource.Value;
            if (config.AuthType == ComponentDataSourceAuthType.ClientCredential)
            {
                var accessToken = await _identityService.GetClientCredentialAccessTokenAsync(config.AuthUsername, config.AuthPassword, "<PASSWORD>");
                _httpClient.SetBearerToken(accessToken);
            }
            _httpClient.AddUserClaimsHeader(_httpContextAccessor.HttpContext);

            try
            {
                var content = await _httpClient.GetStringAsync(url);
                var result = Newtonsoft.Json.JsonConvert.DeserializeObject<int>(content);
                return result;
            }
            catch (Exception e)
            {
                _logger.Error(e, e.Message);
                return null;
            }
        }
    }
}


<|start_filename|>src/TodoCenterProxyApi/Controllers/PendingListController.cs<|end_filename|>
﻿using System;
using AppComponent.ViewModels;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using TodoCenterProxyApi.Services;
using ApiCommon.Extensions;
using Common.Extensions;
using TodoCenterProxyApi.Extensions;

namespace TodoCenterProxyApi.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class PendingListController : ControllerBase
    {
        private readonly IEnumerable<ITodoService> _todoServices;

        public PendingListController(IEnumerable<ITodoService> todoServices)
        {
            _todoServices = todoServices;
        }

        // GET api/values
        [HttpGet]
        public async Task<ActionResult<IEnumerable<TodoListVM>>> GetAsync()
        {
            
            var taskes = new List<Task<IList<TodoListVM>>>();
            foreach (var service in _todoServices)
            {
                taskes.Add(this.GetPendingListAsync(service));
            }
            await Task.WhenAll(taskes);
            var todoes = taskes.SelectMany(u => u.Result).ToList();
            return todoes;
        }

        private async Task<IList<TodoListVM>> GetPendingListAsync(ITodoService service)
        {
            try
            {
                var todoes = await service.GetAsync(this.HttpContext.DecodeUserClaimsFromRequestHeaders());
                return todoes;
            }
            catch (Exception e)
            {
                ServiceEventSource.Current.Message("Get PendingList Faild,Reason:{0}", e.ToString());
                return new List<TodoListVM>();
            }
        }
    }
}


<|start_filename|>src/EnterpriseContactService/GroupAppService.cs<|end_filename|>
﻿using AutoMapper;
using AutoMapper.QueryableExtensions;
using EnterpriseContact.Entities;
using EnterpriseContact.MsgConstracts;
using InstantMessage;
using MassTransit;
using AppsOnSF.Common.BaseServices;
using Microsoft.EntityFrameworkCore;
using Nest;
using Notification;
using Serilog;
using ServiceFabricContrib;
using System;
using System.Collections.Generic;
using System.Fabric;
using System.Linq;
using System.Threading.Tasks;

namespace EnterpriseContact
{
    //TODO 以后要把对IConversationCtrlAppService的rpc调用改为EventBus调用

    public class GroupAppService : BaseAppService, IGroupAppService
    {
        private const string SKVContainer_ScanJoinCode = "EnterpriseContact_ScanJoinCode";
        private readonly ILogger _logger;
        private readonly ISimpleKeyValueService _simpleKeyValueService;
        private readonly IBusControl _busMs;

        public GroupAppService(StatelessServiceContext serviceContext, DbContextOptions<ServiceDbContext> dbOptions,
            IMapper mapper,
            ISimpleKeyValueService simpleKeyValueService,
            IBusControl busMs)
            : base(serviceContext, dbOptions, mapper)
        {
            _logger = Log.ForContext<GroupAppService>();
            _simpleKeyValueService = simpleKeyValueService;
            _busMs = busMs;
        }

        public async Task<bool> CheckSameWhiteListGroupAsync(Guid currentEmployeeId, Guid targetEmployeeId)
        {
            using (var db = new ServiceDbContext(_dbOptions))
            {
                var queryTargetGroup = from g in db.Groups
                                       from m in g.Members
                                       where g.Type == GroupType.WhiteListChat && m.EmployeeId == targetEmployeeId
                                       select g;
                if (await queryTargetGroup.CountAsync() == 0) return true;

                var query = from m in db.GroupMembers
                            where m.EmployeeId == currentEmployeeId
                            join tg in queryTargetGroup
                                on m.GroupId equals tg.Id
                            select m;

                var count = await query.CountAsync();
                return count > 0;
            }
        }

        public async Task<Guid> CreateCustomAsync(GroupInput input)
        {
            if (string.IsNullOrWhiteSpace(input.Name))
                throw new ArgumentNullException("must not be null", nameof(input.Name));

            if (input.CurrentUserId == Guid.Empty)
                throw new ArgumentNullException("must not be empty", nameof(input.CurrentUserId));

            if (!input.CurrentEmployeeId.HasValue)
                throw new ArgumentNullException("must not be null", nameof(input.CurrentEmployeeId));

            using (var db = new ServiceDbContext(_dbOptions))
            {
                using (var tx = db.Database.BeginTransaction())
                {
                    try
                    {
                        var entity = _mapper.Map<Group>(input);
                        entity.Type = GroupType.CustomChat;
                        db.Groups.Add(entity);

                        await db.SaveChangesAsync();

                        await _busMs.Publish(new GroupAdded
                        {
                            Id = entity.Id
                        });

                        tx.Commit();
                        return entity.Id;
                    }
                    catch (Exception ex)
                    {
                        tx.Rollback();
                        _logger.Error(ex, "CreateCustomAsync Transaction rollback");
                        throw;
                    }
                }
            }
        }

        public async Task<List<Guid>> GetUserIdsByIdAsync(Guid id)
        {
            using (var db = new ServiceDbContext(_dbOptions))
            {
                var query = from member in db.GroupMembers
                            where member.GroupId == id
                            join emp in db.Employees
                               on member.EmployeeId equals emp.Id
                            where emp.UserId.HasValue
                            select emp.UserId.Value;
                return await query.ToListAsync();
            }
        }

        public async Task<List<GroupListOutput>> GetListByEmployeeIdAsync(Guid employeeId)
        {
            using (var db = new ServiceDbContext(_dbOptions))
            {
                var query = from g in db.Groups
                            where g.Type != GroupType.WhiteListChat
                            from m in g.Members
                            where m.EmployeeId == employeeId
                            select g;

                return await query.ProjectTo<GroupListOutput>(_mapper.ConfigurationProvider)
                     .ToListAsync();
            }
        }

        public async Task<RemotingResult> UpdateAsync(GroupInput input)
        {
            if (!input.Id.HasValue)
                throw new ArgumentNullException("must not be null", nameof(input.Id));

            if (input.CurrentUserId == Guid.Empty)
                throw new ArgumentNullException("must not be empty", nameof(input.CurrentUserId));

            if (input.RemovingMemberIds?.Count > 0)
            {
                //如果需要删除成员，那么就需要传递当前员工Id
                if (!input.CurrentEmployeeId.HasValue)
                    throw new ArgumentNullException("must not be null", nameof(input.CurrentEmployeeId));

                if (input.RemovingMemberIds.Contains(input.CurrentEmployeeId.Value))
                    return RemotingResult.Fail(FailedCodes.Group_CannotRemoveOwner);
            }

            using (var db = new ServiceDbContext(_dbOptions))
            {
                using (var tx = db.Database.BeginTransaction())
                {
                    try
                    {
                        var entity = await db.Groups.FirstOrDefaultAsync(o => o.Id == input.Id);

                        if (entity == null)
                            return RemotingResult.Fail();
                        if (entity.CreatedBy != input.CurrentUserId)
                            return RemotingResult.Fail(FailedCodes.Group_NotCreatedBy);

                        var nameUpdated = false;
                        if (!string.IsNullOrWhiteSpace(input.Name) && entity.Name != input.Name)
                        {
                            entity.Name = input.Name;
                            nameUpdated = true;
                        }

                        if (input.Remark != null)
                            entity.Remark = input.Remark;

                        var membersUpdated = false;
                        if (input.AddingMemberIds?.Count > 0
                            || input.RemovingMemberIds?.Count > 0)
                        {
                            //需要编辑成员，就显式加载成员集合
                            await db.Entry(entity).Collection(o => o.Members).LoadAsync();
                            membersUpdated = true;
                        }

                        if (input.AddingMemberIds != null)
                        {
                            foreach (var employeeId in input.AddingMemberIds)
                            {
                                if (!entity.Members.Exists(o => o.EmployeeId == employeeId))
                                    entity.Members.Add(new GroupMember
                                    {
                                        EmployeeId = employeeId,
                                        GroupId = input.Id.Value,
                                        Joined = DateTimeOffset.UtcNow,
                                    });
                            }
                        }

                        if (input.RemovingMemberIds != null)
                        {
                            var willRemoving = new List<GroupMember>();
                            foreach (var employeeId in input.RemovingMemberIds)
                            {
                                var m = entity.Members.Find(o => o.EmployeeId == employeeId);
                                willRemoving.Add(m);
                            }
                            willRemoving.ForEach(o => entity.Members.Remove(o));
                        }

                        await db.SaveChangesAsync();

                        if (membersUpdated)
                            await _busMs.Publish(new GroupMembersUpdated { Id = entity.Id });

                        await SendEventNotifyForUpdateAsync(nameUpdated, input);

                        tx.Commit();
                        return RemotingResult.Success();
                    }
                    catch (Exception ex)
                    {
                        tx.Rollback();
                        _logger.Error(ex, "UpdateAsync Transaction rollback");
                        return RemotingResult.Fail();
                    }
                }

            }
        }

        private async Task SendEventNotifyForUpdateAsync(bool nameUpdated, GroupInput input)
        {
            var groupNameUpdatedNotify = nameUpdated
                ? new EventNotifyDto
                {
                    Target = NotifyTargetType.Conversation,
                    TargetId = input.Id.ToString(),
                    Created = DateTimeOffset.UtcNow,
                    Text = input.Name,
                    TargetCategory = (int)GroupEventNotifyType.GroupNameUpdated
                }
                : null;

            var participantAddedNotify = (input.AddingMemberNames?.Count > 0)
                ? new EventNotifyDto
                {
                    Target = NotifyTargetType.Conversation,
                    TargetId = input.Id.ToString(),
                    Created = DateTimeOffset.UtcNow,
                    Text = string.Join(",", input.AddingMemberNames),
                    TargetCategory = (int)GroupEventNotifyType.ParticipantAdded
                }
                : null;

            var participantRemovedNotify = (input.RemovingMemberNames?.Count > 0)
                ? new EventNotifyDto
                {
                    Target = NotifyTargetType.Conversation,
                    TargetId = input.Id.ToString(),
                    Created = DateTimeOffset.UtcNow,
                    Text = string.Join(",", input.RemovingMemberNames),
                    TargetCategory = (int)GroupEventNotifyType.ParticipantRemoved
                }
                : null;

            var notifies = new List<EventNotifyDto>();

            if (groupNameUpdatedNotify != null)
                notifies.Add(groupNameUpdatedNotify);
            if (participantAddedNotify != null)
                notifies.Add(participantAddedNotify);
            if (participantRemovedNotify != null)
                notifies.Add(participantAddedNotify);

            if (notifies.Count > 0)
                await _busMs.Publish(new GroupNotified { Id = input.Id.Value, Notifies = notifies });
        }

        public async Task<RemotingResult> DeleteAsync(GroupInput input)
        {
            if (!input.Id.HasValue)
                throw new ArgumentNullException("must not be null", nameof(input.Id));

            if (input.CurrentUserId == Guid.Empty)
                throw new ArgumentNullException("must not be empty", nameof(input.CurrentUserId));

            using (var db = new ServiceDbContext(_dbOptions))
            {
                using (var tx = db.Database.BeginTransaction())
                {
                    try
                    {
                        var entity = await db.Groups.FirstOrDefaultAsync(o => o.Id == input.Id);

                        if (entity == null)
                            return RemotingResult.Fail();
                        if (entity.CreatedBy != input.CurrentUserId)
                            return RemotingResult.Fail(FailedCodes.Group_NotCreatedBy);

                        db.Groups.Remove(entity);

                        await db.SaveChangesAsync();

                        await _busMs.Publish(new GroupDeleted { Id = entity.Id });

                        await _busMs.Publish(new GroupNotified
                        {
                            Id = entity.Id,
                            Notifies = new List<EventNotifyDto>
                            {
                                new EventNotifyDto
                                {
                                    Target = NotifyTargetType.Conversation,
                                    TargetId = entity.Id.ToString(),
                                    Created = DateTimeOffset.UtcNow,
                                    Text = entity.Name,
                                    TargetCategory = (int)GroupEventNotifyType.GroupDismissed
                                }
                            }
                        });

                        tx.Commit();
                        return RemotingResult.Success();
                    }
                    catch (Exception ex)
                    {
                        tx.Rollback();
                        _logger.Error(ex, "DeleteAsync Transaction rollback");
                        return RemotingResult.Fail();
                    }
                }
            }
        }

        public async Task<GroupOutput> GetByIdAsync(Guid id)
        {
            using (var db = new ServiceDbContext(_dbOptions))
            {
                var dto = await db.Groups.ProjectTo<GroupOutput>(_mapper.ConfigurationProvider)
                    .FirstOrDefaultAsync(o => o.Id == id);
                if (dto == null) return null;
                var query = from gm in db.GroupMembers
                            where gm.GroupId == id
                            join e in db.Employees
                              on gm.EmployeeId equals e.Id
                            select new GroupMemberOutput
                            {
                                EmployeeId = e.Id,
                                EmployeeName = e.Name,
                                Gender = e.Gender,
                                IsOwner = gm.IsOwner,
                                Joined = gm.Joined,
                                PrimaryDepartmentId = e.PrimaryDepartmentId
                            };

                dto.Members = await query.ToListAsync();

                return dto;
            }
        }

        public async Task<List<GroupListOutput>> GetListByIdsAsync(List<Guid> ids)
        {
            using (var db = new ServiceDbContext(_dbOptions))
            {
                var query = from item in db.Groups
                            where ids.Contains(item.Id)
                            select item;

                return await query.ProjectTo<GroupListOutput>(_mapper.ConfigurationProvider)
                    .ToListAsync();
            }
        }

        public async Task<RemotingResult> QuitAsync(GroupInput input)
        {
            if (!input.Id.HasValue)
                throw new ArgumentNullException("must not be null", nameof(input.Id));

            if (!input.CurrentEmployeeId.HasValue)
                throw new ArgumentNullException("must not be null", nameof(input.CurrentEmployeeId));

            using (var db = new ServiceDbContext(_dbOptions))
            {
                using (var tx = db.Database.BeginTransaction())
                {
                    try
                    {
                        var entity = await db.Groups
                          .Include(o => o.Members)
                          .FirstOrDefaultAsync(o => o.Id == input.Id);

                        if (entity == null)
                            return RemotingResult.Fail();

                        var member = entity.Members.Find(o => o.EmployeeId == input.CurrentEmployeeId.Value);
                        if (member == null)
                            return RemotingResult.Fail();
                        if (member.IsOwner)
                            return RemotingResult.Fail(FailedCodes.Group_OwnerCannotQuit);
                        entity.Members.Remove(member);

                        await db.SaveChangesAsync();

                        await _busMs.Publish(new GroupMembersUpdated { Id = entity.Id });

                        await _busMs.Publish(new GroupNotified
                        {
                            Id = entity.Id,
                            Notifies = new List<EventNotifyDto>
                            {
                                new EventNotifyDto
                                {
                                    Target = NotifyTargetType.Conversation,
                                    TargetId = input.Id.ToString(),
                                    Created = DateTimeOffset.UtcNow,
                                    Text = input.CurrentEmployeeName,
                                    TargetCategory = (int)GroupEventNotifyType.ParticipantQuited
                                }
                            }
                        });

                        tx.Commit();
                        return RemotingResult.Success();
                    }
                    catch (Exception ex)
                    {
                        tx.Rollback();
                        _logger.Error(ex, "QuitAsync Transaction rollback");
                        return RemotingResult.Fail();
                    }
                }
            }
        }

        public async Task<RemotingResult<string>> CreateScanJoinCodeAsync(Guid id, Guid userId)
        {
            if (id == Guid.Empty) throw new ArgumentException("must not be empty", nameof(id));
            if (userId == Guid.Empty) throw new ArgumentException("must not be empty", nameof(userId));

            var createdBy = Guid.Empty;
            using (var db = new ServiceDbContext(_dbOptions))
            {
                var query = from entity in db.Groups
                            where entity.Id == id && entity.Type == GroupType.CustomChat
                            select entity.CreatedBy;
                createdBy = await query.FirstOrDefaultAsync();
                if (createdBy == Guid.Empty) return RemotingResult<string>.Fail(1);
                if (createdBy != userId) return RemotingResult<string>.Fail(2);
            }
            var fingerprint = Guid.NewGuid().ToString("N");
            await _simpleKeyValueService.AddOrUpdate(SKVContainer_ScanJoinCode,
                id.ToString("N"), fingerprint);
            return RemotingResult<string>.Success(id.ToString("N") + "," + fingerprint);
        }

        public async Task<RemotingResult> ScanJoinAsync(string code, Guid employeeId, string employeeName)
        {
            if (code == null) throw new ArgumentNullException(nameof(code));
            if (employeeId == Guid.Empty) throw new ArgumentException("must not be empty", nameof(employeeId));

            var splited = code.Split(',');
            if (splited.Length != 2) return RemotingResult.Fail(1);
            if (!Guid.TryParse(splited[0], out Guid id)) return RemotingResult.Fail(1);
            if (!Guid.TryParse(splited[1], out Guid _)) return RemotingResult.Fail(1);

            var originalFingerprint = await _simpleKeyValueService.CheckAndGet(SKVContainer_ScanJoinCode,
                id.ToString("N"), TimeSpan.FromDays(1));
            if (splited[1] != originalFingerprint) return RemotingResult.Fail(1);

            using (var db = new ServiceDbContext(_dbOptions))
            {
                using (var tx = db.Database.BeginTransaction())
                {
                    try
                    {
                        var entity = await db.Groups.Include(o => o.Members)
                            .Where(o => o.Type == GroupType.CustomChat)
                            .FirstOrDefaultAsync(o => o.Id == id);

                        if (entity == null)
                            return RemotingResult.Fail(2);

                        if (entity.Members.Exists(o => o.EmployeeId == employeeId))
                            return RemotingResult.Fail(3);

                        entity.Members.Add(new GroupMember
                        {
                            EmployeeId = employeeId,
                            GroupId = id,
                            Joined = DateTimeOffset.UtcNow,
                        });

                        await db.SaveChangesAsync();

                        await _busMs.Publish(new GroupMembersUpdated { Id = entity.Id });

                        await _busMs.Publish(new GroupNotified
                        {
                            Id = entity.Id,
                            Notifies = new List<EventNotifyDto>
                            {
                                new EventNotifyDto
                                {
                                    Target = NotifyTargetType.Conversation,
                                    TargetId = entity.Id.ToString(),
                                    Created = DateTimeOffset.UtcNow,
                                    Text = employeeName,
                                    TargetCategory = (int)GroupEventNotifyType.ParticipantAdded
                                }
                            }
                        });

                        tx.Commit();
                        return RemotingResult.Success();
                    }
                    catch (Exception ex)
                    {
                        tx.Rollback();
                        _logger.Error(ex, "UpdateAsync Transaction rollback");
                        return RemotingResult.Fail();
                    }
                }
            }
        }
    }
}


<|start_filename|>src/EnterpriseContactApi/ViewModels/DepartmentListVm.cs<|end_filename|>
﻿using System;

namespace EnterpriseContact.ViewModels
{
    /// <summary>
    /// 部门基本信息
    /// </summary>
    public class DepartmentListVm
    {
        public Guid Id { get; set; }

        public string Name { get; set; }
        public int? IconId { get; set; }
    }

    public class DepartmentSearchListVm : DepartmentListVm
    {
        public string ParentName { get; set; }
    }
}


<|start_filename|>src/ConfigMgmtDomain/Entities/ComponentConfig.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Text;

namespace ConfigMgmt.Entities
{
    /// <summary>
    /// 组件配置信息
    /// </summary>
    [DataContract]
    public class ComponentConfig
    {
        [DataMember]
        public ComponentDataSourceAuthType AuthType { get; set; }

        /// <summary>
        /// 验证用户名，根据不同验证方式保存不同语义的内容；
        /// when AuthType==ClientCredential is ClientId
        /// </summary>
        [DataMember]
        public string AuthUsername { get; set; }

        /// <summary>
        /// 验证密码，根据不同验证方式保存不同语义的内容；
        /// when AuthType==ClientCredential is ClientSecret
        /// </summary>
        [DataMember]
        public string AuthPassword { get; set; }

        /// <summary>
        /// 是否显示角标
        /// </summary>
        [DataMember]
        public bool ShowBadge { get; set; }

        /// <summary>
        /// 组件数据源接口，可以多条
        /// </summary>
        [DataMember]
        public List<ComponentDataSource> DataSources { get; set; }
    }
}


<|start_filename|>src/GroupFileDomain/Constants.cs<|end_filename|>
﻿namespace GroupFile
{
    public static class Constants
    {
    }
}


<|start_filename|>emulator/ClientEmulator/UploadParam.cs<|end_filename|>
﻿using Microsoft.AspNetCore.Http.Internal;
using System;

namespace ClientEmulator
{
    /// <summary>
    /// 文件上传参数
    /// </summary>
    public class UploadParam
    {
        /// <summary>
        /// 群组id
        /// </summary>
        public Guid GroupId { get; set; }
        /// <summary>
        /// 文件的md5值
        /// </summary>
        public string MD5 { get; set; }
        /// <summary>
        /// 文件
        /// </summary>
        public FormFile File { get; set; }
    }
}


<|start_filename|>test/UnitTestCommon/ControllerTestBase.cs<|end_filename|>
﻿using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
using System;
using System.Security.Claims;

namespace UnitTestCommon
{
    public abstract class ControllerTestBase
    {
        protected static IMemoryCache CreateMemoryCache()
        {
            return new MemoryCache(new MemoryCacheOptions());
        }

        protected static readonly Guid User_Id = new Guid("6c8cb9c0dbc4479ca6486e97dc9b8b75");
        protected static readonly Guid User_EmployeeMdmId = new Guid("85a270bd81144a12b380a22ffe9527d4");
        protected const string User_Name = "user1";
        protected const string User_Mobile = "13812345678";
        protected const string User_EmployeeNumber = "001";
        protected const string User_EmployeeName = "张三";
        protected const string User_IdCardNo = "123";
        protected static readonly string[] User_Roles = new string[] { "BI" };

        protected static ControllerContext CreateMockContext()
        {
            var user = new ClaimsPrincipal(new ClaimsIdentity(new Claim[]
            {
                 new Claim(ClaimTypes.NameIdentifier, User_Id.ToString()),
                 new Claim("preferred_username", User_Name),
                 new Claim("name", User_EmployeeName),
                 new Claim("employee_number", User_EmployeeNumber),
                 new Claim("employee_mdmid", User_EmployeeMdmId.ToString()),
                 new Claim("idcard_number", User_IdCardNo),
                 new Claim("phone_number", User_Mobile),
                 new Claim(ClaimTypes.Role, string.Join(',', User_Roles)),
            }, "jwt"));

            return new ControllerContext()
            {
                HttpContext = new DefaultHttpContext() { User = user }
            };
        }
    }
}


<|start_filename|>src/ConversationCtrlStateService/EventBus/DepartmentDeletedConsumer.cs<|end_filename|>
﻿using ConversationCtrlStateService.DomainServices;
using EnterpriseContact.MsgConstracts;
using MassTransit;
using Microsoft.ServiceFabric.Data;
using Serilog;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;

namespace ConversationCtrlStateService.EventBus
{
    public class DepartmentDeletedConsumer : IConsumer<DepartmentDeleted>
    {
        private readonly IReliableStateManager _stateManager;
        private readonly ILogger _logger;
        private readonly ConversationManager _manager;

        public DepartmentDeletedConsumer(IReliableStateManager stateManager)
        {
            _stateManager = stateManager;
            _logger = Log.ForContext<DepartmentDeletedConsumer>();
            _manager = new ConversationManager(stateManager);
        }

        public async Task Consume(ConsumeContext<DepartmentDeleted> context)
        {
            try
            {
                await _manager.DeleteAsync(context.Message.Id);
            }
            catch (Exception ex)
            {
                _logger.Error(ex, ex.Message);
                throw;
            }
        }
    }
}


<|start_filename|>src/EnterpriseContactService/ServiceDbContext.cs<|end_filename|>
﻿using EnterpriseContact.Entities;
using EnterpriseContactService.Entities;
using Microsoft.EntityFrameworkCore;

namespace EnterpriseContact
{
    public class ServiceDbContext : DbContext
    {
        public ServiceDbContext()
        {

        }

        public ServiceDbContext(DbContextOptions<ServiceDbContext> options) : base(options)
        {
        }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            if (!optionsBuilder.IsConfigured)
                optionsBuilder.UseSqlServer(@"Data Source=(local)\SQLEXPRESS;Initial Catalog=EapSfApp_EnterpriseContactDb;Integrated Security=True;MultipleActiveResultSets=true");
        }

        public DbSet<Department> Departments { get; set; }

        public DbSet<Position> Positions { get; set; }

        public DbSet<Employee> Employees { get; set; }

        public DbSet<EmployeePosition> EmployeePositions { get; set; }

        public DbSet<Group> Groups { get; set; }

        public DbSet<GroupMember> GroupMembers { get; set; }

        public DbSet<MdmDataHistory> MdmDataHistories { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Department>()
                .HasIndex(o => o.Number);

            modelBuilder.Entity<Employee>()
               .HasIndex(o => o.Number);
            modelBuilder.Entity<Employee>()
              .HasIndex(o => o.UserId);
            //modelBuilder.Entity<Employee>()
            //  .HasIndex(o => o.UserName);
            //modelBuilder.Entity<Employee>()
            //  .HasIndex(o => o.IdCardNo);
            //modelBuilder.Entity<Employee>()
            // .HasIndex(o => o.PrimaryDepartmentId);

            modelBuilder.Entity<EmployeePosition>()
                .HasKey(o => new { o.EmployeeId, o.PositionId });

            modelBuilder.Entity<Group>()
               .HasIndex(o => o.Type);

            modelBuilder.Entity<GroupMember>()
               .HasKey(o => new { o.GroupId, o.EmployeeId });
        }

    }
}


<|start_filename|>src/NotifyDomain/MsgContracts/SendEventNotify.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Notification.MsgContracts
{
    public class SendEventNotify
    {
        public Guid UserId { get; set; }

        public List<EventNotifyDto> Notifies { get; set; }
    }
}


<|start_filename|>src/GroupFileService/MappingProfile.cs<|end_filename|>
﻿using AutoMapper;
using GroupFile.Entities;
using GroupFile.Models;

namespace GroupFile
{
    public class MappingProfile : Profile
    {
        public MappingProfile()
        {
            this.CreateMap<FileItem, FileItemDto>();
                //.ForMember(dest=>dest.Size, opts => opts.MapFrom(src =>src.Store.Size));
        }
    }
}


<|start_filename|>src/NotifyDomain/Dtos/NotifyDto.cs<|end_filename|>
﻿using System;
using System.Runtime.Serialization;

namespace Notification
{
    [DataContract]
    public abstract class NotifyDto
    {
        [DataMember]
        public NotifyTargetType Target { get; set; }

        /// <summary>
        /// Target=Client，TargetId是客户端Id
        /// Target=BizSystem，TargetId是NotifyBizSystem的Id
        /// Target=Conversation，TargetId是Conversation的Id
        /// </summary>
        [DataMember]
        public string TargetId { get; set; }

        [DataMember]
        public int TargetCategory { get; set; }
    }
}


<|start_filename|>src/EnterpriseContactService/Migrations/20190422024934_add_MdmDataHistory_entity.Designer.cs<|end_filename|>
﻿// <auto-generated />
using System;
using EnterpriseContact;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;

namespace EnterpriseContact.Migrations
{
    [DbContext(typeof(ServiceDbContext))]
    [Migration("20190422024934_add_MdmDataHistory_entity")]
    partial class add_MdmDataHistory_entity
    {
        protected override void BuildTargetModel(ModelBuilder modelBuilder)
        {
#pragma warning disable 612, 618
            modelBuilder
                .HasAnnotation("ProductVersion", "2.2.3-servicing-35854")
                .HasAnnotation("Relational:MaxIdentifierLength", 128)
                .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);

            modelBuilder.Entity("EnterpriseContact.Entities.Department", b =>
                {
                    b.Property<Guid>("Id")
                        .ValueGeneratedOnAdd();

                    b.Property<int?>("IconId");

                    b.Property<string>("Name")
                        .IsRequired()
                        .HasMaxLength(128);

                    b.Property<string>("Number")
                        .IsRequired()
                        .HasMaxLength(128);

                    b.Property<Guid?>("ParentId");

                    b.Property<int>("Sort");

                    b.Property<int>("TypeId");

                    b.HasKey("Id");

                    b.HasIndex("Number");

                    b.HasIndex("ParentId");

                    b.ToTable("Departments");
                });

            modelBuilder.Entity("EnterpriseContact.Entities.Employee", b =>
                {
                    b.Property<Guid>("Id")
                        .ValueGeneratedOnAdd();

                    b.Property<string>("Avatar");

                    b.Property<int>("Gender");

                    b.Property<string>("IdCardNo")
                        .IsRequired()
                        .HasMaxLength(128);

                    b.Property<string>("Mobile")
                        .HasMaxLength(128);

                    b.Property<string>("Name")
                        .IsRequired()
                        .HasMaxLength(128);

                    b.Property<string>("Number")
                        .IsRequired()
                        .HasMaxLength(64);

                    b.Property<Guid>("PrimaryDepartmentId");

                    b.Property<Guid>("PrimaryPositionId");

                    b.Property<Guid?>("UserId");

                    b.Property<string>("UserName")
                        .HasMaxLength(128);

                    b.HasKey("Id");

                    b.HasIndex("Number");

                    b.HasIndex("UserId");

                    b.ToTable("Employees");
                });

            modelBuilder.Entity("EnterpriseContact.Entities.EmployeePosition", b =>
                {
                    b.Property<Guid>("EmployeeId");

                    b.Property<Guid>("PositionId");

                    b.Property<bool>("IsPrimary");

                    b.HasKey("EmployeeId", "PositionId");

                    b.HasIndex("PositionId");

                    b.ToTable("EmployeePositions");
                });

            modelBuilder.Entity("EnterpriseContact.Entities.Group", b =>
                {
                    b.Property<Guid>("Id")
                        .ValueGeneratedOnAdd();

                    b.Property<DateTimeOffset>("Created");

                    b.Property<Guid>("CreatedBy");

                    b.Property<int?>("IconId");

                    b.Property<string>("Name")
                        .IsRequired()
                        .HasMaxLength(128);

                    b.Property<string>("Remark");

                    b.Property<int>("Type");

                    b.Property<DateTimeOffset>("Updated");

                    b.HasKey("Id");

                    b.HasIndex("Type");

                    b.ToTable("Groups");
                });

            modelBuilder.Entity("EnterpriseContact.Entities.GroupMember", b =>
                {
                    b.Property<Guid>("GroupId");

                    b.Property<Guid>("EmployeeId");

                    b.Property<bool>("IsOwner");

                    b.Property<DateTimeOffset>("Joined");

                    b.HasKey("GroupId", "EmployeeId");

                    b.HasIndex("EmployeeId");

                    b.ToTable("GroupMembers");
                });

            modelBuilder.Entity("EnterpriseContact.Entities.Position", b =>
                {
                    b.Property<Guid>("Id")
                        .ValueGeneratedOnAdd();

                    b.Property<Guid>("DepartmentId");

                    b.Property<string>("Name")
                        .IsRequired()
                        .HasMaxLength(128);

                    b.HasKey("Id");

                    b.HasIndex("DepartmentId");

                    b.ToTable("Positions");
                });

            modelBuilder.Entity("EnterpriseContactService.Entities.MdmDataHistory", b =>
                {
                    b.Property<Guid>("Id")
                        .ValueGeneratedOnAdd();

                    b.Property<string>("Description")
                        .HasMaxLength(256);

                    b.Property<string>("HistoryVersion")
                        .IsRequired()
                        .HasMaxLength(64);

                    b.Property<DateTimeOffset>("SyncTime");

                    b.HasKey("Id");

                    b.ToTable("MdmDataHistories");
                });

            modelBuilder.Entity("EnterpriseContact.Entities.Department", b =>
                {
                    b.HasOne("EnterpriseContact.Entities.Department", "Parent")
                        .WithMany()
                        .HasForeignKey("ParentId");
                });

            modelBuilder.Entity("EnterpriseContact.Entities.EmployeePosition", b =>
                {
                    b.HasOne("EnterpriseContact.Entities.Employee", "Employee")
                        .WithMany("Positions")
                        .HasForeignKey("EmployeeId")
                        .OnDelete(DeleteBehavior.Cascade);

                    b.HasOne("EnterpriseContact.Entities.Position", "Position")
                        .WithMany()
                        .HasForeignKey("PositionId")
                        .OnDelete(DeleteBehavior.Cascade);
                });

            modelBuilder.Entity("EnterpriseContact.Entities.GroupMember", b =>
                {
                    b.HasOne("EnterpriseContact.Entities.Employee", "Employee")
                        .WithMany()
                        .HasForeignKey("EmployeeId")
                        .OnDelete(DeleteBehavior.Cascade);

                    b.HasOne("EnterpriseContact.Entities.Group", "Group")
                        .WithMany("Members")
                        .HasForeignKey("GroupId")
                        .OnDelete(DeleteBehavior.Cascade);
                });

            modelBuilder.Entity("EnterpriseContact.Entities.Position", b =>
                {
                    b.HasOne("EnterpriseContact.Entities.Department", "Department")
                        .WithMany("Positions")
                        .HasForeignKey("DepartmentId")
                        .OnDelete(DeleteBehavior.Cascade);
                });
#pragma warning restore 612, 618
        }
    }
}


<|start_filename|>src/InstantMessageDomain/RemotingClient.cs<|end_filename|>
﻿using Microsoft.ServiceFabric.Services.Client;
using Microsoft.ServiceFabric.Services.Remoting.Client;
using Microsoft.ServiceFabric.Services.Remoting.V2.FabricTransport.Client;
using ServiceFabricContrib;
using System;
using System.Collections.Generic;
using System.Fabric;
using System.Threading.Tasks;
using static Common.Constants;

namespace InstantMessage
{
    public static class RemotingClient
    {
        private static ServiceProxyFactory proxyFactory = new ServiceProxyFactory((c) => new FabricTransportServiceRemotingClientFactory());

        private static FabricClient fabricClient = new FabricClient();

        public static IConversationCtrlAppService CreateConversationCtrlAppService()
        {
            var builder = new ServiceUriBuilder(AppName_InstantMessage, ServiceName_ConversationCtrlStateService);
            return proxyFactory.CreateServiceProxy<IConversationCtrlAppService>(
                builder.ToUri());
        }

        public static IConversationMsgAppService CreateConversationMsgAppService(Guid conversationId)
        {
            var builder = new ServiceUriBuilder(AppName_InstantMessage, ServiceName_ConversationMsgStateService);
            return proxyFactory.CreateServiceProxy<IConversationMsgAppService>(builder.ToUri(),
                new ItemId(conversationId).GetPartitionKey());
        }

        public static async Task<IEnumerable<IConversationMsgAppService>> GetAllConversationMsgAppServicesAsync()
        {
            var builder = new ServiceUriBuilder(AppName_InstantMessage, ServiceName_ConversationMsgStateService);
            var serviceUri = builder.ToUri();
            var lst = new List<IConversationMsgAppService>();
            foreach (var p in await fabricClient.QueryManager.GetPartitionListAsync(serviceUri))
            {
                long minKey = (p.PartitionInformation as Int64RangePartitionInformation).LowKey;
                var appService = proxyFactory.CreateServiceProxy<IConversationMsgAppService>(builder.ToUri(),
                    new ServicePartitionKey(minKey));
                lst.Add(appService);
            }
            return lst;
        }
    }
}


<|start_filename|>src/EnterpriseContactDomain/GroupType.cs<|end_filename|>
﻿using System;

namespace EnterpriseContact
{
    public enum GroupType
    {
        /// <summary>
        /// 白名单聊天组，用于控制IM对点对免打扰的功能
        /// </summary>
        WhiteListChat = 1,
        /// <summary>
        /// 部门聊天组，系统自动维护的组，根据部门包含员工自动改变成员
        /// 部门组为虚拟的，不会产生真实数据
        /// </summary>
        DepartmentChat = 2,
        /// <summary>
        /// 自定义聊天组，由用户创建和手动维护的组
        /// </summary>
        CustomChat = 3,
    }
}

<|start_filename|>test/EnterpriseContactUnitTest/AppServices/GroupAppServiceTest.cs<|end_filename|>
﻿using EnterpriseContact;
using EnterpriseContact.Entities;
using EnterpriseContact.MsgConstracts;
using FluentAssertions;
using InstantMessage;
using MassTransit;
using MassTransit.Saga;
using Microsoft.EntityFrameworkCore;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using NSubstitute;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace EnterpriseContactUnitTest.AppServices
{
    [TestClass]
    public class GroupAppServiceTest : TheAppServiceTestBase
    {
        //TODO 需要覆盖IConversationCtrlAppService的远程调用

        [TestMethod]
        public async Task TestCheckSameWhiteListGroupAsync_TargetNotInGroup()
        {
            var (connection, options) = OpenDb();

            try
            {
                var emp1 = new Employee
                {
                    Id = Guid.NewGuid(),
                    UserId = Guid.NewGuid(),
                    Number = "1",
                    IdCardNo = "1",
                    Name = "aaa",
                    PrimaryDepartmentId = Guid.Empty,
                    PrimaryPositionId = Guid.Empty,
                };
                var emp2 = new Employee
                {
                    Id = Guid.NewGuid(),
                    Number = "2",
                    IdCardNo = "2",
                    Name = "bbb",
                    PrimaryDepartmentId = Guid.Empty,
                    PrimaryPositionId = Guid.Empty,
                };
                var gp1 = new Group
                {
                    Id = Guid.NewGuid(),
                    Name = "cxo",
                    Type = GroupType.WhiteListChat,
                    Created = DateTimeOffset.UtcNow,
                    Updated = DateTimeOffset.UtcNow,
                };
                gp1.Members = new List<GroupMember>
                {
                    new GroupMember
                    {
                        EmployeeId = emp1.Id,
                        GroupId = gp1.Id,
                        Joined = DateTimeOffset.UtcNow,
                    },
                    //new GroupMember
                    //{
                    //    EmployeeId = emp2.Id,
                    //    GroupId = gp1.Id,
                    //    Joined = DateTimeOffset.UtcNow,
                    //}
                };
                //var gp2 = new Group
                //{
                //    Id = Guid.NewGuid(),
                //    Name = "gm",
                //    Type = GroupType.WhiteListChat,
                //    Created = DateTimeOffset.UtcNow,
                //    Updated = DateTimeOffset.UtcNow,
                //};
                //gp2.Members = new List<GroupMember>
                //{
                //    new GroupMember
                //    {
                //        EmployeeId = emp1.Id,
                //        GroupId = gp2.Id,
                //        Joined = DateTimeOffset.UtcNow
                //    }
                //};
                using (var db = new ServiceDbContext(options))
                {
                    await db.Employees.AddRangeAsync(emp1, emp2);
                    await db.Groups.AddRangeAsync(gp1);
                    await db.SaveChangesAsync();
                }

                var target = new GroupAppService(statelessServiceContext, options, CreateMapper(),
                    CreateMockSimpleKeyValueService(), Substitute.For<IBusControl>());

                var result = await target.CheckSameWhiteListGroupAsync(emp1.UserId.Value, emp2.Id);
                result.Should().BeTrue();
            }
            finally
            {
                connection.Close();
            }
        }

        [TestMethod]
        public async Task TestCheckSameWhiteListGroupAsync_TargetInSameGroup()
        {
            var (connection, options) = OpenDb();

            try
            {
                var emp1 = new Employee
                {
                    Id = Guid.NewGuid(),
                    UserId = Guid.NewGuid(),
                    Number = "1",
                    IdCardNo = "1",
                    Name = "aaa",
                    PrimaryDepartmentId = Guid.Empty,
                    PrimaryPositionId = Guid.Empty,
                };
                var emp2 = new Employee
                {
                    Id = Guid.NewGuid(),
                    Number = "2",
                    IdCardNo = "2",
                    Name = "bbb",
                    PrimaryDepartmentId = Guid.Empty,
                    PrimaryPositionId = Guid.Empty,
                };
                var gp1 = new Group
                {
                    Id = Guid.NewGuid(),
                    Name = "cxo",
                    Type = GroupType.WhiteListChat,
                    Created = DateTimeOffset.UtcNow,
                    Updated = DateTimeOffset.UtcNow,
                };
                gp1.Members = new List<GroupMember>
                {
                    new GroupMember
                    {
                        EmployeeId = emp1.Id,
                        GroupId = gp1.Id,
                        Joined = DateTimeOffset.UtcNow,
                    },
                    new GroupMember
                    {
                        EmployeeId = emp2.Id,
                        GroupId = gp1.Id,
                        Joined = DateTimeOffset.UtcNow,
                    }
                };
                var gp2 = new Group
                {
                    Id = Guid.NewGuid(),
                    Name = "gm",
                    Type = GroupType.WhiteListChat,
                    Created = DateTimeOffset.UtcNow,
                    Updated = DateTimeOffset.UtcNow,
                };
                gp2.Members = new List<GroupMember>
                {
                    new GroupMember
                    {
                        EmployeeId = emp1.Id,
                        GroupId = gp2.Id,
                        Joined = DateTimeOffset.UtcNow
                    }
                };
                using (var db = new ServiceDbContext(options))
                {
                    await db.Employees.AddRangeAsync(emp1, emp2);
                    await db.Groups.AddRangeAsync(gp1, gp2);
                    await db.SaveChangesAsync();
                }

                var target = new GroupAppService(statelessServiceContext, options, CreateMapper(),
                    CreateMockSimpleKeyValueService(), Substitute.For<IBusControl>());

                var result = await target.CheckSameWhiteListGroupAsync(emp1.Id, emp2.Id);
                result.Should().BeTrue();
            }
            finally
            {
                connection.Close();
            }
        }

        [TestMethod]
        public async Task TestCheckSameWhiteListGroupAsync_TargetInDiffGroup()
        {
            var (connection, options) = OpenDb();

            try
            {
                var emp1 = new Employee
                {
                    Id = Guid.NewGuid(),
                    UserId = Guid.NewGuid(),
                    Number = "1",
                    IdCardNo = "1",
                    Name = "aaa",
                    PrimaryDepartmentId = Guid.Empty,
                    PrimaryPositionId = Guid.Empty,
                };
                var emp2 = new Employee
                {
                    Id = Guid.NewGuid(),
                    Number = "2",
                    IdCardNo = "2",
                    Name = "bbb",
                    PrimaryDepartmentId = Guid.Empty,
                    PrimaryPositionId = Guid.Empty,
                };
                var gp1 = new Group
                {
                    Id = Guid.NewGuid(),
                    Name = "cxo",
                    Type = GroupType.WhiteListChat,
                    Created = DateTimeOffset.UtcNow,
                    Updated = DateTimeOffset.UtcNow,
                };
                gp1.Members = new List<GroupMember>
                {
                    new GroupMember
                    {
                        EmployeeId = emp1.Id,
                        GroupId = gp1.Id,
                        Joined = DateTimeOffset.UtcNow,
                    }
                };
                var gp2 = new Group
                {
                    Id = Guid.NewGuid(),
                    Name = "gm",
                    Type = GroupType.WhiteListChat,
                    Created = DateTimeOffset.UtcNow,
                    Updated = DateTimeOffset.UtcNow,
                };
                gp2.Members = new List<GroupMember>
                {
                    new GroupMember
                    {
                        EmployeeId = emp2.Id,
                        GroupId = gp2.Id,
                        Joined = DateTimeOffset.UtcNow
                    }
                };
                using (var db = new ServiceDbContext(options))
                {
                    await db.Employees.AddRangeAsync(emp1, emp2);
                    await db.Groups.AddRangeAsync(gp1, gp2);
                    await db.SaveChangesAsync();
                }

                var target = new GroupAppService(statelessServiceContext, options, CreateMapper(),
                    CreateMockSimpleKeyValueService(), Substitute.For<IBusControl>());

                var result = await target.CheckSameWhiteListGroupAsync(emp1.UserId.Value, emp2.Id);
                result.Should().BeFalse();
            }
            finally
            {
                connection.Close();
            }
        }

        [TestMethod]
        public async Task TestGetListByEmployeeIdAsync()
        {
            var (connection, options) = OpenDb();

            try
            {
                var emp1 = new Employee
                {
                    Id = Guid.NewGuid(),
                    UserId = Guid.NewGuid(),
                    Number = "1",
                    IdCardNo = "1",
                    Name = "aaa",
                    PrimaryDepartmentId = Guid.Empty,
                    PrimaryPositionId = Guid.Empty,
                };
                var gp1 = new Group
                {
                    Id = Guid.NewGuid(),
                    Name = "aaa",
                    Type = GroupType.CustomChat,
                    Created = DateTimeOffset.UtcNow,
                    Updated = DateTimeOffset.UtcNow,
                };
                gp1.Members = new List<GroupMember>
                {
                    new GroupMember
                    {
                        EmployeeId = emp1.Id,
                        GroupId = gp1.Id,
                        Joined = DateTimeOffset.UtcNow,
                    }
                };
                var gp2 = new Group
                {
                    Id = Guid.NewGuid(),
                    Name = "bbb",
                    Type = GroupType.CustomChat,
                    Created = DateTimeOffset.UtcNow,
                    Updated = DateTimeOffset.UtcNow,
                };
                gp2.Members = new List<GroupMember>
                {
                    new GroupMember
                    {
                        EmployeeId = emp1.Id,
                        GroupId = gp2.Id,
                        Joined = DateTimeOffset.UtcNow
                    }
                };
                using (var db = new ServiceDbContext(options))
                {
                    await db.Employees.AddRangeAsync(emp1);
                    await db.Groups.AddRangeAsync(gp1, gp2);
                    await db.SaveChangesAsync();
                }

                var target = new GroupAppService(statelessServiceContext, options, CreateMapper(),
                    CreateMockSimpleKeyValueService(), Substitute.For<IBusControl>());

                var result = await target.GetListByEmployeeIdAsync(emp1.Id);
                result.Count.Should().Be(2);
            }
            finally
            {
                connection.Close();
            }
        }

        [TestMethod]
        public async Task TestGetListByIdsAsync()
        {
            var (connection, options) = OpenDb();

            try
            {
                var gp1 = new Group
                {
                    Id = Guid.NewGuid(),
                    Name = "aaa",
                    Type = GroupType.CustomChat,
                    Created = DateTimeOffset.UtcNow,
                    Updated = DateTimeOffset.UtcNow,
                };
                var gp2 = new Group
                {
                    Id = Guid.NewGuid(),
                    Name = "bbb",
                    Type = GroupType.CustomChat,
                    Created = DateTimeOffset.UtcNow,
                    Updated = DateTimeOffset.UtcNow,
                };
                using (var db = new ServiceDbContext(options))
                {
                    await db.Groups.AddRangeAsync(gp1, gp2);
                    await db.SaveChangesAsync();
                }

                var target = new GroupAppService(statelessServiceContext, options, CreateMapper(),
                    CreateMockSimpleKeyValueService(), Substitute.For<IBusControl>());

                var result = await target.GetListByIdsAsync(new List<Guid> { gp1.Id, gp2.Id });
                result.Count.Should().Be(2);
            }
            finally
            {
                connection.Close();
            }
        }

        [TestMethod]
        public async Task TestCreateCustomAsync()
        {
            var (connection, options) = OpenDb();

            try
            {
                var emp1 = new Employee
                {
                    Id = Guid.NewGuid(),
                    UserId = Guid.NewGuid(),
                    Number = "1",
                    IdCardNo = "1",
                    Name = "aaa",
                    PrimaryDepartmentId = Guid.Empty,
                    PrimaryPositionId = Guid.Empty,
                };
                var emp2 = new Employee
                {
                    Id = Guid.NewGuid(),
                    UserId = Guid.NewGuid(),
                    Number = "2",
                    IdCardNo = "2",
                    Name = "bbb",
                    PrimaryDepartmentId = Guid.Empty,
                    PrimaryPositionId = Guid.Empty,
                };
                using (var db = new ServiceDbContext(options))
                {
                    await db.Employees.AddRangeAsync(emp1, emp2);
                    await db.SaveChangesAsync();
                }

                var bus = Substitute.For<IBusControl>();
                bus.Publish(Arg.Any<GroupAdded>())
                    .Returns(Task.CompletedTask);

                var target = new GroupAppService(statelessServiceContext, options, CreateMapper(),
                    CreateMockSimpleKeyValueService(), bus);

                var result = await target.CreateCustomAsync(new GroupInput
                {
                    CurrentUserId = emp1.UserId.Value,
                    CurrentEmployeeId = emp1.Id,
                    Name = "aaa",
                    AddingMemberIds = new HashSet<Guid> { emp1.Id, emp2.Id }
                });
                result.Should().NotBe(Guid.Empty);

                await bus.Received(1).Publish(Arg.Is<GroupAdded>(x => x.Id == result));
            }
            finally
            {
                connection.Close();
            }
        }

        [TestMethod]
        public async Task TestUpdateAsync_Success()
        {
            var (connection, options) = OpenDb();

            try
            {
                var emp1 = new Employee
                {
                    Id = Guid.NewGuid(),
                    UserId = Guid.NewGuid(),
                    Number = "1",
                    IdCardNo = "1",
                    Name = "aaa",
                    PrimaryDepartmentId = Guid.Empty,
                    PrimaryPositionId = Guid.Empty,
                };
                var emp2 = new Employee
                {
                    Id = Guid.NewGuid(),
                    Number = "2",
                    IdCardNo = "2",
                    Name = "bbb",
                    PrimaryDepartmentId = Guid.Empty,
                    PrimaryPositionId = Guid.Empty,
                };
                var emp3 = new Employee
                {
                    Id = Guid.NewGuid(),
                    Number = "3",
                    IdCardNo = "3",
                    Name = "ccc",
                    PrimaryDepartmentId = Guid.Empty,
                    PrimaryPositionId = Guid.Empty,
                };
                var gp1 = new Group
                {
                    Id = Guid.NewGuid(),
                    Name = "aaa",
                    Type = GroupType.CustomChat,
                    CreatedBy = emp1.UserId.Value,
                    Created = DateTimeOffset.UtcNow,
                    Updated = DateTimeOffset.UtcNow,
                    Members = new List<GroupMember>
                    {
                        new GroupMember
                        {
                            EmployeeId = emp1.Id,
                            IsOwner = true,
                            Joined = DateTimeOffset.UtcNow,
                        },
                        new GroupMember
                        {
                            EmployeeId = emp2.Id,
                            IsOwner = false,
                            Joined = DateTimeOffset.UtcNow,
                        }
                    }
                };

                using (var db = new ServiceDbContext(options))
                {
                    await db.Employees.AddRangeAsync(emp1, emp2, emp3);
                    await db.Groups.AddRangeAsync(gp1);
                    await db.SaveChangesAsync();
                }

                var target = new GroupAppService(statelessServiceContext, options, CreateMapper(),
                    CreateMockSimpleKeyValueService(), Substitute.For<IBusControl>());

                var result = await target.UpdateAsync(new GroupInput
                {
                    Id = gp1.Id,
                    Name = "bbb",
                    Remark = "ccc",
                    CurrentUserId = emp1.UserId.Value,
                    CurrentEmployeeId = emp1.Id,
                    AddingMemberIds = new HashSet<Guid> { emp3.Id },
                    RemovingMemberIds = new HashSet<Guid> { emp2.Id }
                });
                result.IsSuccess.Should().BeTrue();

                using (var db = new ServiceDbContext(options))
                {
                    var entity = await db.Groups
                        .Include(o => o.Members)
                        .FirstOrDefaultAsync(o => o.Id == gp1.Id);

                    entity.Name.Should().Be("bbb");
                    entity.Remark.Should().Be("ccc");
                    entity.Members.Count.Should().Be(2);
                    entity.Members.Exists(o => o.EmployeeId == emp3.Id).Should().BeTrue();
                    entity.Members.Exists(o => o.EmployeeId == emp2.Id).Should().BeFalse();
                }
            }
            finally
            {
                connection.Close();
            }
        }

        [TestMethod]
        public async Task TestUpdateAsync_Failed_NotFound()
        {
            var (connection, options) = OpenDb();

            try
            {
                var target = new GroupAppService(statelessServiceContext, options, CreateMapper(),
                     CreateMockSimpleKeyValueService(), Substitute.For<IBusControl>());

                var result = await target.UpdateAsync(new GroupInput
                {
                    Id = Guid.NewGuid(),
                    CurrentUserId = Guid.NewGuid(),
                });
                result.IsSuccess.Should().BeFalse();
            }
            finally
            {
                connection.Close();
            }
        }

        [TestMethod]
        public async Task TestUpdateAsync_Failed_CreatedByNotMatch()
        {
            var (connection, options) = OpenDb();

            try
            {
                var gp1 = new Group
                {
                    Id = Guid.NewGuid(),
                    Name = "aaa",
                    Type = GroupType.CustomChat,
                    CreatedBy = Guid.NewGuid(),
                    Created = DateTimeOffset.UtcNow,
                    Updated = DateTimeOffset.UtcNow,
                };

                using (var db = new ServiceDbContext(options))
                {
                    await db.Groups.AddRangeAsync(gp1);
                    await db.SaveChangesAsync();
                }

                var target = new GroupAppService(statelessServiceContext, options, CreateMapper(),
                    CreateMockSimpleKeyValueService(), Substitute.For<IBusControl>());

                var result = await target.UpdateAsync(new GroupInput
                {
                    Id = gp1.Id,
                    CurrentUserId = Guid.NewGuid(),
                });
                result.IsSuccess.Should().BeFalse();
            }
            finally
            {
                connection.Close();
            }
        }

        [TestMethod]
        public async Task TestDeleteAsync_Success()
        {
            var (connection, options) = OpenDb();

            try
            {
                var gp1 = new Group
                {
                    Id = Guid.NewGuid(),
                    Name = "aaa",
                    Type = GroupType.CustomChat,
                    CreatedBy = Guid.NewGuid(),
                    Created = DateTimeOffset.UtcNow,
                    Updated = DateTimeOffset.UtcNow,
                };

                using (var db = new ServiceDbContext(options))
                {
                    await db.Groups.AddRangeAsync(gp1);
                    await db.SaveChangesAsync();
                }

                var target = new GroupAppService(statelessServiceContext, options, CreateMapper(),
                    CreateMockSimpleKeyValueService(), Substitute.For<IBusControl>());

                var result = await target.DeleteAsync(new GroupInput
                {
                    Id = gp1.Id,
                    CurrentUserId = gp1.CreatedBy,
                });
                result.IsSuccess.Should().BeTrue();

                using (var db = new ServiceDbContext(options))
                {
                    var entity = await db.Groups.FindAsync(gp1.Id);
                    entity.Should().BeNull();
                }
            }
            finally
            {
                connection.Close();
            }
        }

        [TestMethod]
        public async Task TestDeleteAsync_Failed_NotFound()
        {
            var (connection, options) = OpenDb();

            try
            {
                var target = new GroupAppService(statelessServiceContext, options, CreateMapper(),
                    CreateMockSimpleKeyValueService(), Substitute.For<IBusControl>());

                var result = await target.DeleteAsync(new GroupInput
                {
                    Id = Guid.NewGuid(),
                    CurrentUserId = Guid.NewGuid(),
                });
                result.IsSuccess.Should().BeFalse();
            }
            finally
            {
                connection.Close();
            }
        }

        [TestMethod]
        public async Task TestDeleteAsync_Failed_CreatedByNotMatch()
        {
            var (connection, options) = OpenDb();

            try
            {
                var gp1 = new Group
                {
                    Id = Guid.NewGuid(),
                    Name = "aaa",
                    Type = GroupType.CustomChat,
                    CreatedBy = Guid.NewGuid(),
                    Created = DateTimeOffset.UtcNow,
                    Updated = DateTimeOffset.UtcNow,
                };

                using (var db = new ServiceDbContext(options))
                {
                    await db.Groups.AddRangeAsync(gp1);
                    await db.SaveChangesAsync();
                }

                var target = new GroupAppService(statelessServiceContext, options, CreateMapper(),
                    CreateMockSimpleKeyValueService(), Substitute.For<IBusControl>());

                var result = await target.DeleteAsync(new GroupInput
                {
                    Id = gp1.Id,
                    CurrentUserId = Guid.NewGuid(),
                });
                result.IsSuccess.Should().BeFalse();
            }
            finally
            {
                connection.Close();
            }
        }

        [TestMethod]
        public async Task TestGetByIdAsync_Success()
        {
            var (connection, options) = OpenDb();

            try
            {
                var emp1 = new Employee
                {
                    Id = Guid.NewGuid(),
                    UserId = Guid.NewGuid(),
                    Number = "1",
                    IdCardNo = "1",
                    Name = "aaa",
                    PrimaryDepartmentId = Guid.Empty,
                    PrimaryPositionId = Guid.Empty,
                };
                var emp2 = new Employee
                {
                    Id = Guid.NewGuid(),
                    Number = "2",
                    IdCardNo = "2",
                    Name = "bbb",
                    PrimaryDepartmentId = Guid.Empty,
                    PrimaryPositionId = Guid.Empty,
                };
                var gp1 = new Group
                {
                    Id = Guid.NewGuid(),
                    Name = "aaa",
                    Type = GroupType.CustomChat,
                    CreatedBy = emp1.UserId.Value,
                    Created = DateTimeOffset.UtcNow,
                    Updated = DateTimeOffset.UtcNow,
                    Members = new List<GroupMember>
                    {
                        new GroupMember
                        {
                            EmployeeId = emp1.Id,
                            IsOwner = true,
                            Joined = DateTimeOffset.UtcNow,
                        },
                        new GroupMember
                        {
                            EmployeeId = emp2.Id,
                            IsOwner = false,
                            Joined = DateTimeOffset.UtcNow,
                        }
                    }
                };

                using (var db = new ServiceDbContext(options))
                {
                    await db.Employees.AddRangeAsync(emp1, emp2);
                    await db.Groups.AddRangeAsync(gp1);
                    await db.SaveChangesAsync();
                }

                var target = new GroupAppService(statelessServiceContext, options, CreateMapper(),
                    CreateMockSimpleKeyValueService(), Substitute.For<IBusControl>());

                var result = await target.GetByIdAsync(gp1.Id);
                result.Should().NotBeNull();
                result.Name.Should().Be("aaa");
                result.Members.Count.Should().Be(2);
                var m1 = result.Members.Find(o => o.EmployeeId == emp1.Id);
                m1.Should().NotBeNull();
                m1.EmployeeName.Should().Be("aaa");
            }
            finally
            {
                connection.Close();
            }
        }

        [TestMethod]
        public async Task TestQuitAsync_Success()
        {
            var (connection, options) = OpenDb();

            try
            {
                var emp1 = new Employee
                {
                    Id = Guid.NewGuid(),
                    UserId = Guid.NewGuid(),
                    Number = "1",
                    IdCardNo = "1",
                    Name = "aaa",
                    PrimaryDepartmentId = Guid.Empty,
                    PrimaryPositionId = Guid.Empty,
                };
                var emp2 = new Employee
                {
                    Id = Guid.NewGuid(),
                    Number = "2",
                    IdCardNo = "2",
                    Name = "bbb",
                    PrimaryDepartmentId = Guid.Empty,
                    PrimaryPositionId = Guid.Empty,
                };
                var gp1 = new Group
                {
                    Id = Guid.NewGuid(),
                    Name = "aaa",
                    Type = GroupType.CustomChat,
                    CreatedBy = emp1.UserId.Value,
                    Created = DateTimeOffset.UtcNow,
                    Updated = DateTimeOffset.UtcNow,
                    Members = new List<GroupMember>
                    {
                        new GroupMember
                        {
                            EmployeeId = emp1.Id,
                            IsOwner = true,
                            Joined = DateTimeOffset.UtcNow,
                        },
                        new GroupMember
                        {
                            EmployeeId = emp2.Id,
                            IsOwner = false,
                            Joined = DateTimeOffset.UtcNow,
                        }
                    }
                };

                using (var db = new ServiceDbContext(options))
                {
                    await db.Employees.AddRangeAsync(emp1, emp2);
                    await db.Groups.AddRangeAsync(gp1);
                    await db.SaveChangesAsync();
                }

                var target = new GroupAppService(statelessServiceContext, options, CreateMapper(),
                     CreateMockSimpleKeyValueService(), Substitute.For<IBusControl>());

                var result = await target.QuitAsync(new GroupInput
                {
                    Id = gp1.Id,
                    CurrentEmployeeId = emp2.Id,
                });
                result.IsSuccess.Should().BeTrue();
            }
            finally
            {
                connection.Close();
            }
        }

        [TestMethod]
        public async Task TestQuitAsync_Failed_OwnerCannotQuit()
        {
            var (connection, options) = OpenDb();

            try
            {
                var emp1 = new Employee
                {
                    Id = Guid.NewGuid(),
                    UserId = Guid.NewGuid(),
                    Number = "1",
                    IdCardNo = "1",
                    Name = "aaa",
                    PrimaryDepartmentId = Guid.Empty,
                    PrimaryPositionId = Guid.Empty,
                };
                var emp2 = new Employee
                {
                    Id = Guid.NewGuid(),
                    Number = "2",
                    IdCardNo = "2",
                    Name = "bbb",
                    PrimaryDepartmentId = Guid.Empty,
                    PrimaryPositionId = Guid.Empty,
                };
                var gp1 = new Group
                {
                    Id = Guid.NewGuid(),
                    Name = "aaa",
                    Type = GroupType.CustomChat,
                    CreatedBy = emp1.UserId.Value,
                    Created = DateTimeOffset.UtcNow,
                    Updated = DateTimeOffset.UtcNow,
                    Members = new List<GroupMember>
                    {
                        new GroupMember
                        {
                            EmployeeId = emp1.Id,
                            IsOwner = true,
                            Joined = DateTimeOffset.UtcNow,
                        },
                        new GroupMember
                        {
                            EmployeeId = emp2.Id,
                            IsOwner = false,
                            Joined = DateTimeOffset.UtcNow,
                        }
                    }
                };

                using (var db = new ServiceDbContext(options))
                {
                    await db.Employees.AddRangeAsync(emp1, emp2);
                    await db.Groups.AddRangeAsync(gp1);
                    await db.SaveChangesAsync();
                }

                var target = new GroupAppService(statelessServiceContext, options, CreateMapper(),
                    CreateMockSimpleKeyValueService(), Substitute.For<IBusControl>());

                var result = await target.QuitAsync(new GroupInput
                {
                    Id = gp1.Id,
                    CurrentEmployeeId = emp1.Id,
                });
                result.IsSuccess.Should().BeFalse();
            }
            finally
            {
                connection.Close();
            }
        }
    }
}


<|start_filename|>src/EnterpriseContactDomain/FailedCodes.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Text;

namespace EnterpriseContact
{
    public static class FailedCodes
    {
        /// <summary>
        /// 不是群组创建者
        /// </summary>
        public const int Group_NotCreatedBy = 101;
        public const int Group_CannotRemoveOwner = 102;
        public const int Group_OwnerCannotQuit = 103;
    }
}


<|start_filename|>test/AttachmentUnitTest/Services/PhotoResizer_ResizeShould.cs<|end_filename|>
﻿using Attachment.Services;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using SystemDrawingImage = System.Drawing.Image;

namespace AttachmentUnitTest.Services
{
    [TestClass]
    public class PhotoResizer_ResizeShould
    {
        [TestMethod]
        public void SavedAndSizeIsCorrect()
        {
            var filePath = "..\\..\\..\\InputAssets\\IMG_2301.jpg";
            var saveFilePath = Path.Combine(Path.GetTempPath(), "IMG_2301.jpg");
            using (var load = new FileStream(filePath, FileMode.Open))
            {
                var image = SystemDrawingImage.FromStream(load, false, false);
                load.Position = 0;
                using (var save = new FileStream(saveFilePath, FileMode.Create))
                {
                    PhotoResizer.Resize(load, save, image.Width, image.Height);
                }
            }

            Assert.IsTrue(File.Exists(saveFilePath));
            using (var fs = new FileStream(saveFilePath, FileMode.Open))
            {
                Assert.IsTrue(fs.Length > 0);
                var image = SystemDrawingImage.FromStream(fs, false, false);
                Assert.IsTrue(image.Width == PhotoResizer.DefaultThumbnailSize
                    || image.Height == PhotoResizer.DefaultThumbnailSize);
            }
        }
    }
}


<|start_filename|>src/SystemConfigStateService/ClientAppService.cs<|end_filename|>
﻿using ConfigMgmt;
using ConfigMgmt.Entities;
using Microsoft.ServiceFabric.Data;
using Microsoft.ServiceFabric.Data.Collections;
using ServiceFabricContrib;
using System;
using System.Fabric;
using System.Threading.Tasks;

namespace SystemConfigStateService
{
    public class ClientAppService : StatefulRemotingService, IClientAppService
    {
        public ClientAppService(StatefulServiceContext serviceContext, IReliableStateManager stateManager)
           : base(serviceContext, stateManager)
        {

        }

        public async Task<bool> AddOrUpdateClientAsync(Client input)
        {
            if (input == null)
                throw new ArgumentNullException(nameof(input));

            if (string.IsNullOrEmpty(input.Id))
                throw new ArgumentException("ClientId不能为空", nameof(input.Id));

            var dictionary = await StateManager.GetOrAddAsync<IReliableDictionary2<string, Client>>(Service.DictionaryName_Client);
            using (var tx = StateManager.CreateTransaction())
            {
                await dictionary.AddOrUpdateAsync(tx, input.Id, input, (k, o) => input);

                await tx.CommitAsync();
                return true;
            }
        }
    }
}


<|start_filename|>src/EnterpriseContactApi/ViewModels/GroupListVm.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Text;

namespace EnterpriseContact.ViewModels
{
    public class GroupListVm
    {
        public Guid Id { get; set; }

        public string Name { get; set; }

        public int? IconId { get; set; }

        public Guid CreatedBy { get; set; }

        /// <summary>
        /// 是否部门群
        /// </summary>
        public bool IsDepartmentGroup { get; set; }
    }
}


<|start_filename|>test/ConfigMgmtUnitTest/AppServices/ClientAppServiceTest.cs<|end_filename|>
﻿using ConfigMgmt.Entities;
using Microsoft.ServiceFabric.Data.Collections;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Threading.Tasks;
using SystemConfigStateService;

namespace ConfigMgmtUnitTest.AppServices
{
    [TestClass]
    public class ClientAppServiceTest : TheAppServiceTestBase
    {
        [TestMethod]
        public async Task TestAddOrUpdateClientAsync()
        {
            var dictClients = await stateManager.GetOrAddAsync<IReliableDictionary2<string, Client>>(Service.DictionaryName_Client);
            var client = Client.Default();

            var appService = new ClientAppService(statefulServiceContext, stateManager);
            var result = await appService.AddOrUpdateClientAsync(client);
            Assert.IsTrue(result);
        }
    }
}


<|start_filename|>test/EnterpriseContactUnitTest/AppServices/EmployeeAppServiceTest.cs<|end_filename|>
﻿using EnterpriseContact;
using EnterpriseContact.Entities;
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace EnterpriseContactUnitTest.AppServices
{
    [TestClass]
    public class EmployeeAppServiceTest : TheAppServiceTestBase
    {
        [TestMethod]
        public async Task TestGetRootListAsync()
        {
            var (connection, options) = OpenDb();

            try
            {
                var dep1 = new Department
                {
                    Id = Guid.NewGuid(),
                    Number = "1",
                    Name = "sclq",
                };
                var pos1 = new Position
                {
                    DepartmentId = dep1.Id,
                    Id = Guid.NewGuid(),
                    Name = "ceo"
                };
                var dep2 = new Department
                {
                    Id = Guid.NewGuid(),
                    ParentId = dep1.Id,
                    Number = "2",
                    Name = "sclqjt"
                };
                var pos2 = new Position
                {
                    DepartmentId = dep2.Id,
                    Id = Guid.NewGuid(),
                    Name = "gm"
                };
                var emp1 = new Employee
                {
                    Id = Guid.NewGuid(),
                    Number = "1",
                    IdCardNo = "1",
                    Name = "aaa",
                    PrimaryDepartmentId = dep1.Id,
                    PrimaryPositionId = pos1.Id,
                };
                emp1.Positions = new List<EmployeePosition>
                {
                    new EmployeePosition
                    {
                        EmployeeId = emp1.Id,
                        PositionId = pos1.Id,
                        IsPrimary = true
                    },
                    new EmployeePosition
                    {
                        EmployeeId = emp1.Id,
                        PositionId = pos2.Id,
                        IsPrimary = false
                    }
                };
                var emp2 = new Employee
                {
                    Id = Guid.NewGuid(),
                    Number = "2",
                    IdCardNo = "2",
                    Name = "bbb",
                    PrimaryDepartmentId = dep2.Id,
                    PrimaryPositionId = pos2.Id,
                };
                emp2.Positions = new List<EmployeePosition>
                {
                    new EmployeePosition
                    {
                        EmployeeId = emp2.Id,
                        PositionId = pos1.Id,
                        IsPrimary = false
                    },
                    new EmployeePosition
                    {
                        EmployeeId = emp2.Id,
                        PositionId = pos2.Id,
                        IsPrimary = true
                    }
                };

                using (var db = new ServiceDbContext(options))
                {
                    await db.Departments.AddRangeAsync(dep1, dep2);
                    await db.Positions.AddRangeAsync(pos1, pos2);
                    await db.Employees.AddRangeAsync(emp1, emp2);
                    await db.SaveChangesAsync();
                }

                var target = new EmployeeAppService(statelessServiceContext, options, CreateMapper());

                var result = await target.GetRootListAsync();

                result.Count.Should().Be(2);
                result[0].Id.Should().Be(emp1.Id);
                result[0].PrimaryDepartmentId.Should().Be(dep1.Id);
                result[0].PrimaryPositionId.Should().Be(pos1.Id);
                result[1].Id.Should().Be(emp2.Id);
                result[1].PrimaryDepartmentId.Should().Be(dep2.Id);
                result[1].PrimaryPositionId.Should().Be(pos2.Id);
            }
            finally
            {
                connection.Close();
            }
        }

        [TestMethod]
        public async Task TestGetListByDepartmentIdAsync()
        {
            var (connection, options) = OpenDb();

            try
            {
                var dep1 = new Department
                {
                    Id = Guid.NewGuid(),
                    Number = "1",
                    Name = "sclq",
                };
                var pos1 = new Position
                {
                    DepartmentId = dep1.Id,
                    Id = Guid.NewGuid(),
                    Name = "ceo"
                };
                var dep2 = new Department
                {
                    Id = Guid.NewGuid(),
                    ParentId = dep1.Id,
                    Number = "2",
                    Name = "sclqjt"
                };
                var pos2 = new Position
                {
                    DepartmentId = dep2.Id,
                    Id = Guid.NewGuid(),
                    Name = "gm"
                };
                var emp1 = new Employee
                {
                    Id = Guid.NewGuid(),
                    Number = "1",
                    IdCardNo = "1",
                    Name = "aaa",
                    PrimaryDepartmentId = dep2.Id,
                    PrimaryPositionId = pos2.Id,
                };
                emp1.Positions = new List<EmployeePosition>
                {
                    new EmployeePosition
                    {
                        EmployeeId = emp1.Id,
                        PositionId = pos2.Id,
                        IsPrimary = true
                    },
                };
                var emp2 = new Employee
                {
                    Id = Guid.NewGuid(),
                    Number = "2",
                    IdCardNo = "2",
                    Name = "bbb",
                    PrimaryDepartmentId = dep2.Id,
                    PrimaryPositionId = pos2.Id,
                };
                emp2.Positions = new List<EmployeePosition>
                {
                    new EmployeePosition
                    {
                        EmployeeId = emp2.Id,
                        PositionId = pos2.Id,
                        IsPrimary = true
                    }
                };

                using (var db = new ServiceDbContext(options))
                {
                    await db.Departments.AddRangeAsync(dep1, dep2);
                    await db.Positions.AddRangeAsync(pos1, pos2);
                    await db.Employees.AddRangeAsync(emp1, emp2);
                    await db.SaveChangesAsync();
                }

                var target = new EmployeeAppService(statelessServiceContext, options, CreateMapper());

                var result = await target.GetListByDepartmentIdAsync(dep1.Id);
                result.Count.Should().Be(0);

                result = await target.GetListByDepartmentIdAsync(dep2.Id);
                result.Count.Should().Be(2);
            }
            finally
            {
                connection.Close();
            }
        }

        [TestMethod]
        public async Task TestSearchByKeywordAsync()
        {
            var (connection, options) = OpenDb();

            try
            {
                var employess = new List<Employee>()
                {
                    new Employee
                    {
                        Id = Guid.NewGuid(),
                        Number = "1",
                        IdCardNo = "1",
                        Name = "aaa",
                        PrimaryDepartmentId = Guid.NewGuid(),
                        PrimaryPositionId = Guid.NewGuid(),
                    },
                    new Employee
                    {
                        Id = Guid.NewGuid(),
                        Number = "2",
                        IdCardNo = "2",
                        Name = "aabb",
                        PrimaryDepartmentId = Guid.NewGuid(),
                        PrimaryPositionId = Guid.NewGuid(),
                    },
                    new Employee
                    {
                        Id = Guid.NewGuid(),
                        Number = "3",
                        IdCardNo = "3",
                        Name = "bbcc",
                        PrimaryDepartmentId = Guid.NewGuid(),
                        PrimaryPositionId = Guid.NewGuid(),
                    },
                };

                using (var db = new ServiceDbContext(options))
                {
                    await db.Employees.AddRangeAsync(employess);

                    await db.SaveChangesAsync();
                }


                var target = new EmployeeAppService(statelessServiceContext, options, CreateMapper());

                var result = await target.SearchByKeywordAsync("aa");
                result.Count.Should().Be(2);

                result = await target.SearchByKeywordAsync("bb");
                result.Count.Should().Be(2);
            }
            finally
            {
                connection.Close();
            }
        }

        [TestMethod]
        public async Task Test_GetByIdAsync_GetByUserIdAsync()
        {
            var (connection, options) = OpenDb();

            try
            {
                var dep0 = new Department
                {
                    Id = Guid.NewGuid(),
                    Number = "0",
                    Name = "sclq",
                };
                var dep1 = new Department
                {
                    Id = Guid.NewGuid(),
                    ParentId = dep0.Id,
                    Number = "1",
                    Name = "sclq-jt",
                };
                var pos1 = new Position
                {
                    DepartmentId = dep1.Id,
                    Id = Guid.NewGuid(),
                    Name = "gm"
                };
                var dep2 = new Department
                {
                    Id = Guid.NewGuid(),
                    ParentId = dep0.Id,
                    Number = "2",
                    Name = "sclq-gs"
                };
                var pos2 = new Position
                {
                    DepartmentId = dep2.Id,
                    Id = Guid.NewGuid(),
                    Name = "ceo"
                };
                var emp1 = new Employee
                {
                    Id = Guid.NewGuid(),
                    UserId = Guid.NewGuid(),
                    Number = "1",
                    IdCardNo = "1",
                    Name = "aaa",
                    PrimaryDepartmentId = dep1.Id,
                    PrimaryPositionId = pos1.Id,
                };
                emp1.Positions = new List<EmployeePosition>
                {
                    new EmployeePosition
                    {
                        EmployeeId = emp1.Id,
                        PositionId = pos1.Id,
                        IsPrimary = true
                    },
                    new EmployeePosition
                    {
                        EmployeeId = emp1.Id,
                        PositionId = pos2.Id,
                        IsPrimary = false,
                    },
                };

                using (var db = new ServiceDbContext(options))
                {
                    await db.Departments.AddRangeAsync(dep0, dep1, dep2);
                    await db.Positions.AddRangeAsync(pos1, pos2);
                    await db.Employees.AddRangeAsync(emp1);
                    await db.SaveChangesAsync();
                }

                var target = new EmployeeAppService(statelessServiceContext, options, CreateMapper());

                var result = await target.GetByIdAsync(emp1.Id);
                result.Name.Should().Be("aaa");
                result.ParttimePositionIds.Count.Should().Be(1);
                result.ParttimePositionIds[0].Should().Be(pos2.Id);

                result = await target.GetByUserIdAsync(emp1.UserId.Value);
                result.Name.Should().Be("aaa");
            }
            finally
            {
                connection.Close();
            }
        }

        [TestMethod]
        public async Task TestGetListByIdsAsync()
        {
            var (connection, options) = OpenDb();

            try
            {
                var employess = new List<Employee>()
                {
                    new Employee
                    {
                        Id = Guid.NewGuid(),
                        Number = "1",
                        IdCardNo = "1",
                        Name = "aaa",
                        PrimaryDepartmentId = Guid.NewGuid(),
                        PrimaryPositionId = Guid.NewGuid(),
                    },
                    new Employee
                    {
                        Id = Guid.NewGuid(),
                        Number = "2",
                        IdCardNo = "2",
                        Name = "aabb",
                        PrimaryDepartmentId = Guid.NewGuid(),
                        PrimaryPositionId = Guid.NewGuid(),
                    },
                    new Employee
                    {
                        Id = Guid.NewGuid(),
                        Number = "3",
                        IdCardNo = "3",
                        Name = "bbcc",
                        PrimaryDepartmentId = Guid.NewGuid(),
                        PrimaryPositionId = Guid.NewGuid(),
                    },
                };

                using (var db = new ServiceDbContext(options))
                {
                    await db.Employees.AddRangeAsync(employess);

                    await db.SaveChangesAsync();
                }


                var target = new EmployeeAppService(statelessServiceContext, options, CreateMapper());

                var result = await target.GetListByIdsAsync(new Guid[] { employess[0].Id, employess[1].Id, employess[2].Id });
                result.Count.Should().Be(3);
            }
            finally
            {
                connection.Close();
            }
        }

        [TestMethod]
        public async Task GetUserIdsByDepartmentIdAsync()
        {
            var (connection, options) = OpenDb();

            try
            {
                var dep1 = new Department
                {
                    Id = Guid.NewGuid(),
                    Number = "1",
                    Name = "sclq",
                };
                var pos1 = new Position
                {
                    DepartmentId = dep1.Id,
                    Id = Guid.NewGuid(),
                    Name = "ceo"
                };
                var pos2 = new Position
                {
                    DepartmentId = dep1.Id,
                    Id = Guid.NewGuid(),
                    Name = "gm"
                };
                var emp1 = new Employee
                {
                    Id = Guid.NewGuid(),
                    Number = "1",
                    IdCardNo = "1",
                    Name = "aaa",
                    PrimaryDepartmentId = dep1.Id,
                    PrimaryPositionId = pos1.Id,
                };
                emp1.Positions = new List<EmployeePosition>
                {
                    new EmployeePosition
                    {
                        EmployeeId = emp1.Id,
                        PositionId = pos1.Id,
                        IsPrimary = true
                    },
                };
                var emp2 = new Employee
                {
                    Id = Guid.NewGuid(),
                    Number = "2",
                    IdCardNo = "2",
                    Name = "bbb",
                    PrimaryDepartmentId = dep1.Id,
                    PrimaryPositionId = pos2.Id,
                };
                emp2.Positions = new List<EmployeePosition>
                {
                    new EmployeePosition
                    {
                        EmployeeId = emp2.Id,
                        PositionId = pos2.Id,
                        IsPrimary = true
                    }
                };

                using (var db = new ServiceDbContext(options))
                {
                    await db.Departments.AddRangeAsync(dep1);
                    await db.Positions.AddRangeAsync(pos1, pos2);
                    await db.Employees.AddRangeAsync(emp1, emp2);
                    await db.SaveChangesAsync();
                }

                var target = new EmployeeAppService(statelessServiceContext, options, CreateMapper());

                var result = await target.GetUserIdsByDepartmentIdAsync(dep1.Id);
                result.Count.Should().Be(0);

                emp1.UserId = Guid.NewGuid();
                emp2.UserId = Guid.NewGuid();
                using (var db = new ServiceDbContext(options))
                {
                    db.Employees.UpdateRange(emp1, emp2);
                    await db.SaveChangesAsync();
                }

                result = await target.GetUserIdsByDepartmentIdAsync(dep1.Id);
                result.Count.Should().Be(2);

            }
            finally
            {
                connection.Close();
            }
        }
    }
}


<|start_filename|>src/AttachmentDomain/AppServices/IAttachmentAppService.cs<|end_filename|>
﻿using Attachment.Entities;
using Microsoft.ServiceFabric.Services.Remoting;
using ServiceFabricContrib;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;

namespace Attachment
{
    public interface IAttachmentAppService : IService
    {
        /// <summary>
        /// 基于文件的md5值得到现有的附件数据项
        /// </summary>
        /// <param name="id">md5 hash value</param>
        /// <returns></returns>
        Task<AttachmentItem> GetByIdAsync(string id);

        /// <summary>
        /// 添加附件数据项
        /// </summary>
        /// <param name="item"></param>
        /// <returns>
        /// </returns>
        Task AddOrUpdateAsync(AttachmentItem item);
        /// <summary>
        /// 基于ContextId删除附件数据项
        /// </summary>
        /// <param name="contextId"></param>
        /// <returns>
        /// FailedCode=1：contextId不存在；
        /// </returns>
        Task<RemotingResult> RemoveByContextIdAsync(Guid contextId);
        /// <summary>
        /// 更新附件数据项的状态
        /// </summary>
        /// <param name="id"></param>
        /// <param name="status"></param>
        /// <returns>
        /// FailedCode=1: 数据项不存在
        /// FailedCode=2: 状态值不能降级
        /// </returns>
        Task<RemotingResult> UpdateStatusByIdAsync(string id, UploadStatus status);
    }
}


<|start_filename|>src/EnterpriseContactService/Entities/Employee.cs<|end_filename|>
﻿using EnterpriseContactService.Entities;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EnterpriseContact.Entities
{
    public class Employee
    {
        public Guid Id { get; set; }

        [Required]
        [StringLength(64)]
        //[Index]
        public string Number { get; set; }

        [Required]
        [StringLength(128)]
        public string Name { get; set; }

        //[Index]
        public Guid? UserId { get; set; }

        [StringLength(128)]
        //[Index]
        public string UserName { get; set; }

        [Required]
        [StringLength(128)]
        //[Index]
        public string IdCardNo { get; set; }

        [StringLength(128)]
        public string Mobile { get; set; }

        public int Gender { get; set; }

        public string Avatar { get; set; }

        //[Index]
        [Required]
        public Guid PrimaryDepartmentId { get; set; }

        [Required]
        public Guid PrimaryPositionId { get; set; }

        public DataSourceType DataSourceType { get; set; }

        public virtual List<EmployeePosition> Positions { get; set; }
    }
}


<|start_filename|>src/ConversationCtrlStateService/EventBus/GroupNotifiedConsumer.cs<|end_filename|>
﻿using ConversationCtrlStateService.DomainServices;
using EnterpriseContact.MsgConstracts;
using MassTransit;
using Microsoft.ServiceFabric.Data;
using Notification;
using Serilog;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;

namespace ConversationCtrlStateService.EventBus
{
    public class GroupNotifiedConsumer : IConsumer<GroupNotified>
    {
        private readonly IReliableStateManager _stateManager;
        private readonly ILogger _logger;
        private readonly Func<Guid, INotifySessionActor> _notifySessionActorFactory;
        private readonly ConversationManager _manager;

        public GroupNotifiedConsumer(IReliableStateManager stateManager, 
            Func<Guid, INotifySessionActor> notifySessionActorFactory)
        {
            _stateManager = stateManager;
            _notifySessionActorFactory = notifySessionActorFactory;
            _manager = new ConversationManager(stateManager);
            _logger = Log.ForContext<GroupNotifiedConsumer>();
        }

        public async Task Consume(ConsumeContext<GroupNotified> context)
        {
            try
            {
                var con = await _manager.GetByIdAsync(context.Message.Id);
                if (con.HasValue)
                {
                    var tasks = new List<Task>();
                    foreach (var userId in con.Value.Participants)
                    {
                        var notifySessionActor = _notifySessionActorFactory(userId);
                        tasks.Add(notifySessionActor.PushEventNotifiesAsync(context.Message.Notifies));
                    }
                    await Task.WhenAll(tasks);
                }
            }
            catch (Exception ex)
            {
                _logger.Error(ex, ex.Message);
                throw;
            }
        }
    }
}


<|start_filename|>test/EnterpriseContactUnitTest/TheAppServiceTestBase.cs<|end_filename|>
﻿using AutoMapper;
using EnterpriseContact;
using InstantMessage;
using AppsOnSF.Common.BaseServices;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using Notification;
using NSubstitute;
using System;
using UnitTestCommon;

namespace EnterpriseContactUnitTest
{
    public class TheAppServiceTestBase : AppServiceTestBase
    {
        protected static IConversationCtrlAppService CreateMockConversationCtrlAppService() 
            => Substitute.For<IConversationCtrlAppService>();

        protected static INotifySessionActor CreateMockNotifySessionActorFactory(Guid userId) 
            => Substitute.For<INotifySessionActor>();

        protected static ISimpleKeyValueService CreateMockSimpleKeyValueService()
           => Substitute.For<ISimpleKeyValueService>();

        protected static IMapper CreateMapper()
        {
            var config = new MapperConfiguration(cfg =>
            {
                cfg.AddProfile<DtoMappingProfile>();
            });
            return config.CreateMapper();
        }

        protected static (SqliteConnection, DbContextOptions<ServiceDbContext>) OpenDb()
        {
            var connection = new SqliteConnection("DataSource=:memory:");
            connection.Open();

            var options = new DbContextOptionsBuilder<ServiceDbContext>()
                //.UseLazyLoadingProxies()
                .EnableDetailedErrors()
                .EnableSensitiveDataLogging()
                .UseLoggerFactory(TestLoggerFactory)
                .UseSqlite(connection)
                .Options;

            // Create the schema in the database
            using (var context = new ServiceDbContext(options))
            {
                context.Database.EnsureCreated();
            }

            return (connection, options);
        }
    }
}


<|start_filename|>src/EnterpriseContactService/Entities/MdmDataHistory.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EnterpriseContactService.Entities
{
    /// <summary>
    /// mdm 数据历史记录
    /// </summary>
    public class MdmDataHistory
    {
        public Guid Id { get; set; }


        [Required]
        [StringLength(64)]
        public string HistoryVersion { get; set; }

        public DateTimeOffset SyncTime { get; set; }

        [StringLength(256)]
        public string Description { get; set; }
    }
}


<|start_filename|>src/EnterpriseContactService/Migrations/20190612063259_adddatasourcetypefield.cs<|end_filename|>
﻿using Microsoft.EntityFrameworkCore.Migrations;

namespace EnterpriseContact.Migrations
{
    public partial class adddatasourcetypefield : Migration
    {
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.AddColumn<int>(
                name: "DataSourceType",
                table: "Positions",
                nullable: false,
                defaultValue: 0);

            migrationBuilder.AddColumn<int>(
                name: "DataSourceType",
                table: "Employees",
                nullable: false,
                defaultValue: 0);

            migrationBuilder.AddColumn<int>(
                name: "DataSourceType",
                table: "EmployeePositions",
                nullable: false,
                defaultValue: 0);

            migrationBuilder.AddColumn<int>(
                name: "DataSourceType",
                table: "Departments",
                nullable: false,
                defaultValue: 0);
        }

        protected override void Down(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.DropColumn(
                name: "DataSourceType",
                table: "Positions");

            migrationBuilder.DropColumn(
                name: "DataSourceType",
                table: "Employees");

            migrationBuilder.DropColumn(
                name: "DataSourceType",
                table: "EmployeePositions");

            migrationBuilder.DropColumn(
                name: "DataSourceType",
                table: "Departments");
        }
    }
}


<|start_filename|>src/EnterpriseContactDomain/AppServices/IPositionAppService.cs<|end_filename|>
﻿using Microsoft.ServiceFabric.Services.Remoting;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace EnterpriseContact
{
    public interface IPositionAppService : IService
    {
        Task<List<PositionListOutput>> GetAllListAsync();
    }
}


<|start_filename|>emulator/ClientEmulator/IMChatForm.Designer.cs<|end_filename|>
﻿namespace ClientEmulator
{
    partial class IMChatForm
    {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.groupBox1 = new System.Windows.Forms.GroupBox();
            this.txtHub_host = new System.Windows.Forms.TextBox();
            this.label7 = new System.Windows.Forms.Label();
            this.btnApply = new System.Windows.Forms.Button();
            this.txtApi_host = new System.Windows.Forms.TextBox();
            this.txtLogin_host = new System.Windows.Forms.TextBox();
            this.Label_APIHost = new System.Windows.Forms.Label();
            this.Label_Host = new System.Windows.Forms.Label();
            this.userGroupConfig = new System.Windows.Forms.GroupBox();
            this.groupuser2 = new System.Windows.Forms.GroupBox();
            this.loginControl2 = new ClientEmulator.LoginControl();
            this.groupuser1 = new System.Windows.Forms.GroupBox();
            this.loginControl1 = new ClientEmulator.LoginControl();
            this.btn_CreateCovnertGroup = new System.Windows.Forms.Button();
            this.tabControl1 = new System.Windows.Forms.TabControl();
            this.groupBox1.SuspendLayout();
            this.userGroupConfig.SuspendLayout();
            this.groupuser2.SuspendLayout();
            this.groupuser1.SuspendLayout();
            this.SuspendLayout();
            // 
            // groupBox1
            // 
            this.groupBox1.Controls.Add(this.txtHub_host);
            this.groupBox1.Controls.Add(this.label7);
            this.groupBox1.Controls.Add(this.btnApply);
            this.groupBox1.Controls.Add(this.txtApi_host);
            this.groupBox1.Controls.Add(this.txtLogin_host);
            this.groupBox1.Controls.Add(this.Label_APIHost);
            this.groupBox1.Controls.Add(this.Label_Host);
            this.groupBox1.Location = new System.Drawing.Point(10, 11);
            this.groupBox1.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
            this.groupBox1.Name = "groupBox1";
            this.groupBox1.Padding = new System.Windows.Forms.Padding(2, 3, 2, 3);
            this.groupBox1.Size = new System.Drawing.Size(221, 147);
            this.groupBox1.TabIndex = 0;
            this.groupBox1.TabStop = false;
            this.groupBox1.Text = "BaseConfig";
            // 
            // txtHub_host
            // 
            this.txtHub_host.Location = new System.Drawing.Point(75, 93);
            this.txtHub_host.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
            this.txtHub_host.Name = "txtHub_host";
            this.txtHub_host.Size = new System.Drawing.Size(138, 20);
            this.txtHub_host.TabIndex = 6;
            this.txtHub_host.Text = "http://localhost:10124/notifyHub";
            // 
            // label7
            // 
            this.label7.AutoSize = true;
            this.label7.Location = new System.Drawing.Point(7, 96);
            this.label7.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
            this.label7.Name = "label7";
            this.label7.Size = new System.Drawing.Size(55, 13);
            this.label7.TabIndex = 5;
            this.label7.Text = "Hub_Host";
            // 
            // btnApply
            // 
            this.btnApply.Location = new System.Drawing.Point(156, 120);
            this.btnApply.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
            this.btnApply.Name = "btnApply";
            this.btnApply.Size = new System.Drawing.Size(56, 20);
            this.btnApply.TabIndex = 4;
            this.btnApply.Text = "Apply";
            this.btnApply.UseVisualStyleBackColor = true;
            this.btnApply.Click += new System.EventHandler(this.button1_Click);
            // 
            // txtApi_host
            // 
            this.txtApi_host.Location = new System.Drawing.Point(75, 65);
            this.txtApi_host.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
            this.txtApi_host.Name = "txtApi_host";
            this.txtApi_host.Size = new System.Drawing.Size(138, 20);
            this.txtApi_host.TabIndex = 3;
            this.txtApi_host.Text = "http://localhost:10121";
            // 
            // txtLogin_host
            // 
            this.txtLogin_host.Location = new System.Drawing.Point(75, 38);
            this.txtLogin_host.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
            this.txtLogin_host.Name = "txtLogin_host";
            this.txtLogin_host.Size = new System.Drawing.Size(138, 20);
            this.txtLogin_host.TabIndex = 2;
            this.txtLogin_host.Text = "http://login.test.scrbg.com";
            // 
            // Label_APIHost
            // 
            this.Label_APIHost.AutoSize = true;
            this.Label_APIHost.Location = new System.Drawing.Point(4, 65);
            this.Label_APIHost.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
            this.Label_APIHost.Name = "Label_APIHost";
            this.Label_APIHost.Size = new System.Drawing.Size(52, 13);
            this.Label_APIHost.TabIndex = 1;
            this.Label_APIHost.Text = "API_Host";
            // 
            // Label_Host
            // 
            this.Label_Host.AutoSize = true;
            this.Label_Host.Location = new System.Drawing.Point(4, 38);
            this.Label_Host.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
            this.Label_Host.Name = "Label_Host";
            this.Label_Host.Size = new System.Drawing.Size(61, 13);
            this.Label_Host.TabIndex = 0;
            this.Label_Host.Text = "Login_Host";
            // 
            // userGroupConfig
            // 
            this.userGroupConfig.Controls.Add(this.groupuser2);
            this.userGroupConfig.Controls.Add(this.groupuser1);
            this.userGroupConfig.Enabled = false;
            this.userGroupConfig.Location = new System.Drawing.Point(10, 183);
            this.userGroupConfig.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
            this.userGroupConfig.Name = "userGroupConfig";
            this.userGroupConfig.Padding = new System.Windows.Forms.Padding(2, 3, 2, 3);
            this.userGroupConfig.Size = new System.Drawing.Size(253, 288);
            this.userGroupConfig.TabIndex = 1;
            this.userGroupConfig.TabStop = false;
            this.userGroupConfig.Text = "UserConfig";
            // 
            // groupuser2
            // 
            this.groupuser2.Controls.Add(this.loginControl2);
            this.groupuser2.Location = new System.Drawing.Point(6, 148);
            this.groupuser2.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
            this.groupuser2.Name = "groupuser2";
            this.groupuser2.Padding = new System.Windows.Forms.Padding(2, 3, 2, 3);
            this.groupuser2.Size = new System.Drawing.Size(242, 128);
            this.groupuser2.TabIndex = 1;
            this.groupuser2.TabStop = false;
            this.groupuser2.Text = "User2";
            // 
            // loginControl2
            // 
            this.loginControl2.Location = new System.Drawing.Point(5, 13);
            this.loginControl2.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
            this.loginControl2.Name = "loginControl2";
            this.loginControl2.Size = new System.Drawing.Size(222, 110);
            this.loginControl2.TabIndex = 0;
            // 
            // groupuser1
            // 
            this.groupuser1.Controls.Add(this.loginControl1);
            this.groupuser1.Location = new System.Drawing.Point(7, 15);
            this.groupuser1.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
            this.groupuser1.Name = "groupuser1";
            this.groupuser1.Padding = new System.Windows.Forms.Padding(2, 3, 2, 3);
            this.groupuser1.Size = new System.Drawing.Size(242, 128);
            this.groupuser1.TabIndex = 0;
            this.groupuser1.TabStop = false;
            this.groupuser1.Text = "User1";
            // 
            // loginControl1
            // 
            this.loginControl1.Location = new System.Drawing.Point(0, 12);
            this.loginControl1.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
            this.loginControl1.Name = "loginControl1";
            this.loginControl1.Size = new System.Drawing.Size(226, 111);
            this.loginControl1.TabIndex = 0;
            // 
            // btn_CreateCovnertGroup
            // 
            this.btn_CreateCovnertGroup.Enabled = false;
            this.btn_CreateCovnertGroup.Location = new System.Drawing.Point(269, 20);
            this.btn_CreateCovnertGroup.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
            this.btn_CreateCovnertGroup.Name = "btn_CreateCovnertGroup";
            this.btn_CreateCovnertGroup.Size = new System.Drawing.Size(158, 27);
            this.btn_CreateCovnertGroup.TabIndex = 3;
            this.btn_CreateCovnertGroup.Text = "CreateGroup";
            this.btn_CreateCovnertGroup.UseVisualStyleBackColor = true;
            // 
            // tabControl1
            // 
            this.tabControl1.Location = new System.Drawing.Point(269, 52);
            this.tabControl1.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
            this.tabControl1.Name = "tabControl1";
            this.tabControl1.SelectedIndex = 0;
            this.tabControl1.Size = new System.Drawing.Size(854, 419);
            this.tabControl1.TabIndex = 4;
            // 
            // IMChatForm
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(1132, 483);
            this.Controls.Add(this.tabControl1);
            this.Controls.Add(this.btn_CreateCovnertGroup);
            this.Controls.Add(this.userGroupConfig);
            this.Controls.Add(this.groupBox1);
            this.Margin = new System.Windows.Forms.Padding(2, 3, 2, 3);
            this.Name = "IMChatForm";
            this.Text = "Form1";
            this.groupBox1.ResumeLayout(false);
            this.groupBox1.PerformLayout();
            this.userGroupConfig.ResumeLayout(false);
            this.groupuser2.ResumeLayout(false);
            this.groupuser1.ResumeLayout(false);
            this.ResumeLayout(false);

        }

        #endregion

        private System.Windows.Forms.GroupBox groupBox1;
        private System.Windows.Forms.Label Label_APIHost;
        private System.Windows.Forms.Label Label_Host;
        private System.Windows.Forms.Button btnApply;
        private System.Windows.Forms.TextBox txtApi_host;
        private System.Windows.Forms.TextBox txtLogin_host;
        private System.Windows.Forms.GroupBox userGroupConfig;
        private System.Windows.Forms.GroupBox groupuser2;
        private System.Windows.Forms.GroupBox groupuser1;
        private System.Windows.Forms.TextBox txtHub_host;
        private System.Windows.Forms.Label label7;
        private System.Windows.Forms.Button btn_CreateCovnertGroup;
        private LoginControl loginControl2;
        private LoginControl loginControl1;
        private System.Windows.Forms.TabControl tabControl1;
    }
}



<|start_filename|>src/ConversationMsgStateService/Service.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Fabric;
using System.Threading;
using System.Threading.Tasks;
using Common;
using InstantMessage;
using InstantMessage.Entities;
using Microsoft.Diagnostics.EventFlow;
using Microsoft.ServiceFabric.Data;
using Microsoft.ServiceFabric.Data.Collections;
using Microsoft.ServiceFabric.Services.Communication.Runtime;
using Microsoft.ServiceFabric.Services.Remoting.V2.FabricTransport.Runtime;
using ServiceFabricContrib;

namespace ConversationMsgStateService
{
    /// <summary>
    /// An instance of this class is created for each service replica by the Service Fabric runtime.
    /// </summary>
    public sealed class Service : StatefulServiceEapBase
    {
        public Service(StatefulServiceContext context,
           DiagnosticPipeline diagnosticPipeline)
           : base(context, diagnosticPipeline)
        {
        }

        /// <summary>
        /// Optional override to create listeners (e.g., HTTP, Service Remoting, WCF, etc.) for this service replica to handle client or user requests.
        /// </summary>
        /// <remarks>
        /// For more information on service communication, see https://aka.ms/servicefabricservicecommunication
        /// </remarks>
        /// <returns>A collection of listeners.</returns>
        protected override IEnumerable<ServiceReplicaListener> CreateServiceReplicaListeners()
        {
            return new[]
            {
                new ServiceReplicaListener((c)=>
                    new FabricTransportServiceRemotingListener(c,new ConversationMsgAppService(Context,StateManager)))
            };
        }

        /// <summary>
        /// This is the main entry point for your service replica.
        /// This method executes when this replica of your service becomes primary and has write status.
        /// </summary>
        /// <param name="cancellationToken">Canceled when Service Fabric needs to shut down this service replica.</param>
        protected override Task RunAsync(CancellationToken cancellationToken)
        {
            try
            {
                ServiceEventSource.Current.ServiceMessage(Context, "inside RunAsync for ConversationMsgStateService");

                return Task.WhenAll(
                    ConversationMsgQueueProcessor.ProcessQueueAsync(
                        StateManager, RemotingClient.CreateConversationCtrlAppService(), Notification.RemotingClient.CreateNotifySessionActor, cancellationToken)
                    //ArchiveGroupQueueProcessor.ProcessAsync(
                    //    StateManager, new DefaultProcessorConfigurator(), cancellationToken)
                        );
            }
            catch (Exception e)
            {
                ServiceEventSource.Current.ServiceMessage(Context, "RunAsync Failed, {0}", e);
                throw;
            }
        }

        public const string MessageListDictNamePrefix = "MessageListDict";
        public const string MessageIndexDictNamePrefix = "MessageIndexDict";//NOTE 数据达到100万的时候，插入会接近2.5s，到时候应该提供归档的处理（超过10万就归档）
        public const string MessageProcessQueueName = "MessageProcessQueue";
        public const string PendingArchiveGroupQueueName = "PendingArchiveGroupQueue";
        public const string ArchivedGroupMessageDictName = "ArchivedGroupMessageDict";

        public static async Task<IReliableDictionary2<Guid, ConversationMsg>> GetMessageListDictByIdAsync(IReliableStateManager stateManager, Guid msgId)
        {
            var key = msgId.ToString().Substring(0, 1).ToLower();
            return await stateManager.GetOrAddAsync<IReliableDictionary2<Guid, ConversationMsg>>(MessageListDictNamePrefix + "/" + key);
        }

        public static async Task<List<IReliableDictionary2<Guid, ConversationMsg>>> GetAllMessageListDictsAsync(IReliableStateManager stateManager)
        {
            var lst = new List<IReliableDictionary2<Guid, ConversationMsg>>();
            foreach (var key in StringExtensions.PartitionKeys)
            {
                lst.Add(await stateManager.GetOrAddAsync<IReliableDictionary2<Guid, ConversationMsg>>(MessageListDictNamePrefix + "/" + key));
            }
            return lst;
        }

        public static async Task<IReliableDictionary2<Guid, List<Guid>>> GetMessageIndexDictByIdAsync(IReliableStateManager stateManager, Guid conversationId)
        {
            var key = conversationId.ToString().Substring(0, 1).ToLower();
            return await stateManager.GetOrAddAsync<IReliableDictionary2<Guid, List<Guid>>>(MessageIndexDictNamePrefix + "/" + key);
        }

        public static async Task<List<IReliableDictionary2<Guid, List<Guid>>>> GetAllMessageIndexDictsAsync(IReliableStateManager stateManager)
        {
            var lst = new List<IReliableDictionary2<Guid, List<Guid>>>();
            foreach (var key in StringExtensions.PartitionKeys)
            {
                lst.Add(await stateManager.GetOrAddAsync<IReliableDictionary2<Guid, List<Guid>>>(MessageIndexDictNamePrefix + "/" + key));
            }
            return lst;
        }

        public static async Task<IReliableConcurrentQueue<ConversationMsg>> GetMessageProcessQueue(IReliableStateManager stateManager)
        {
            return await stateManager.GetOrAddAsync<IReliableConcurrentQueue<ConversationMsg>>(MessageProcessQueueName);
        }

        public static async Task<IReliableConcurrentQueue<Guid>> GetPendingArchiveGroupQueue(IReliableStateManager stateManager)
        {
            return await stateManager.GetOrAddAsync<IReliableConcurrentQueue<Guid>>(PendingArchiveGroupQueueName);
        }
    }
}


<|start_filename|>test/AttachmentUnitTest/AppServices/AttachmentAppServiceTest.cs<|end_filename|>
﻿using Attachment;
using Attachment.Entities;
using AttachmentStateService;
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using UnitTestCommon;

namespace AttachmentUnitTest.AppServices
{
    [TestClass]
    public class AttachmentAppServiceTest : AppServiceTestBase
    {
        [TestMethod]
        public async Task Test_AddOrUpdateAsync_GetByIdAsync()
        {
            var id = Guid.NewGuid().ToString("N");
            var userId = Guid.NewGuid();
            var dto = new AttachmentItem
            {
                ContextId = Guid.Empty,
                Id = id,
                Location = $"attachments/2018/12/28/{id}.ext",
                Size = 100,
                Status = UploadStatus.Uploading,
                Uploaded = DateTimeOffset.UtcNow,
                UploadBy = userId
            };
            var target = new AttachmentAppService(statefulServiceContext, stateManager);
            await target.AddOrUpdateAsync(dto);

            var result = await target.GetByIdAsync(id);
            result.Location.Should().Be(dto.Location);
            result.Status.Should().Be(dto.Status);

            dto.Status = UploadStatus.Uploaded;
            await target.AddOrUpdateAsync(dto);
            result = await target.GetByIdAsync(id);
            result.Status.Should().Be(UploadStatus.Uploaded);
        }

        [TestMethod]
        public async Task Test_GetByIdAsync_ReturnNull()
        {
            var target = new AttachmentAppService(statefulServiceContext, stateManager);
            var result2 = await target.GetByIdAsync(Guid.NewGuid().ToString("N"));
            result2.Should().BeNull();
        }
    }
}


<|start_filename|>src/InstantMessageApi/ViewModels/FileMessageVm.cs<|end_filename|>
﻿using Newtonsoft.Json;
using System.Collections.Generic;

namespace InstantMessage.ViewModels
{
    public class FileMessageVm : MessageVmBase
    {
        /// <summary>
        /// 文件上传后返回的guid
        /// </summary>
        public string FileGuid { get; set; }
        /// <summary>
        /// 文件名
        /// </summary>
        public string FileName { get; set; }
        /// <summary>
        /// 文件大小
        /// </summary>
        public long FileSize { get; set; }

        private Dictionary<string, object> GetContent()
        {
            var dic = new Dictionary<string, object>
            {
                { "FileGuid", FileGuid },
                { "FileName", FileName },
                { "FileSize", FileSize }
        };
            return dic;
        }

        /// <summary>
        /// 获取消息内容  ConversationMsg 中的Content 
        /// </summary>
        /// <returns></returns>
        public string GetJsonContent()
        {
            return JsonConvert.SerializeObject(GetContent());
        }
    }
}


<|start_filename|>src/SystemConfigStateService/Service.cs<|end_filename|>
﻿using System.Collections.Generic;
using System.Fabric;
using AutoMapper;
using ConfigMgmt;
using Microsoft.Diagnostics.EventFlow;
using Microsoft.ServiceFabric.Services.Communication.Runtime;
using Microsoft.ServiceFabric.Services.Remoting.V2.FabricTransport.Runtime;
using Common;
using static Common.Constants;

namespace SystemConfigStateService
{
    /// <summary>
    /// An instance of this class is created for each service replica by the Service Fabric runtime.
    /// </summary>
    public sealed class Service : StatefulServiceEapBase
    {
        public const string DictionaryName_Client = "ClientList";
        public const string DictionaryName_TodoCenterBizSystem = "TodoCenterBizSystemList";
        public const string DictionaryName_MsgNotifyBizSystem = "MsgNotifyBizSystemList";
        public const string DictionaryName_AppEntrance = "AppEntrancList";

        public Service(StatefulServiceContext context,
            DiagnosticPipeline diagnosticPipeline)
            : base(context, diagnosticPipeline)
        {
        }

        /// <summary>
        /// Optional override to create listeners (e.g., HTTP, Service Remoting, WCF, etc.) for this service replica to handle client or user requests.
        /// </summary>
        /// <remarks>
        /// For more information on service communication, see https://aka.ms/servicefabricservicecommunication
        /// </remarks>
        /// <returns>A collection of listeners.</returns>
        protected override IEnumerable<ServiceReplicaListener> CreateServiceReplicaListeners()
        {
            var mapper = CreateMapper();
            return new[]
            {
                new ServiceReplicaListener((c)=>
                    new FabricTransportServiceRemotingListener(c,new ClientAppService(Context,StateManager))
                    ,ListenerName_ClientAppService),
                new ServiceReplicaListener((c)=>
                    new FabricTransportServiceRemotingListener(c,
                    new WorkbenchAppService(Context,StateManager,mapper))
                    ,ListenerName_WorkbenchAppService),
                new ServiceReplicaListener((c)=>
                    new FabricTransportServiceRemotingListener(c,new BizSystemAppService(Context,StateManager))
                    ,ListenerName_TodoCenterAppService)
            };
        }

        private IMapper CreateMapper()
        {
            var config = new MapperConfiguration(cfg => { cfg.AddProfile<MappingProfile>(); });
            return config.CreateMapper();
        }
    }
}


<|start_filename|>src/UserConfigStateService/Service.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Fabric;
using System.Linq;
using System.Threading.Tasks;
using ConfigMgmt;
using Microsoft.Diagnostics.EventFlow;
using Microsoft.ServiceFabric.Data.Collections;
using Microsoft.ServiceFabric.Services.Communication.Runtime;
using Microsoft.ServiceFabric.Services.Remoting.Runtime;
using Microsoft.ServiceFabric.Services.Remoting.V2.FabricTransport.Runtime;
using Common;
using ServiceFabricContrib;
using static Common.Constants;

namespace UserConfigStateService
{
    /// <summary>
    /// An instance of this class is created for each service replica by the Service Fabric runtime.
    /// </summary>
    internal sealed class Service : StatefulServiceEapBase
    {
        private const string DictionaryName = "UserConfiges";

        public Service(StatefulServiceContext context,
            DiagnosticPipeline diagnosticPipeline)
            : base(context, diagnosticPipeline)
        {
        }

        /// <summary>
        /// Optional override to create listeners (e.g., HTTP, Service Remoting, WCF, etc.) for this service replica to handle client or user requests.
        /// </summary>
        /// <remarks>
        /// For more information on service communication, see https://aka.ms/servicefabricservicecommunication
        /// </remarks>
        /// <returns>A collection of listeners.</returns>
        protected override IEnumerable<ServiceReplicaListener> CreateServiceReplicaListeners()
        {
            return new[]
            {
                new ServiceReplicaListener((c)=>
                    new FabricTransportServiceRemotingListener(c,new UserFavoriteAppService(Context,StateManager))
                    ,ListenerName_UserFavoriteAppService),
                new ServiceReplicaListener((c)=>
                    new FabricTransportServiceRemotingListener(c,new UserSettingAppService(Context,StateManager))
                    ,ListenerName_UserSettingAppService)
            };
        }
    }
}


<|start_filename|>test/ConversationServiceTest/Controllers/InstantMessageControllerTest.cs<|end_filename|>
﻿using Common;
using EnterpriseContact;
using EnterpriseContact.Services;
using FluentAssertions;
using InstantMessage;
using InstantMessage.Controllers;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using NSubstitute;
using NSubstitute.Routing.Handlers;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using UnitTestCommon;
using System.Linq;
using InstantMessage.Entities;
using ServiceFabricContrib;

namespace ConversationServiceTest.Controllers
{
    [TestClass]
    public class InstantMessageControllerTest : ControllerTestBase
    {
        [TestMethod]
        public async Task CreateDepConverationAsync()
        {
            var conversationCtrlAppService = Substitute.For<IConversationCtrlAppService>();
            conversationCtrlAppService.GetByIdAsync(Arg.Any<Guid>())
                .Returns(default(Conversation?));
            conversationCtrlAppService.AddDepAsync(Arg.Any<AddDepConversationInput>())
                .Returns(RemotingResult.Success());

            Task<IEnumerable<IConversationMsgAppService>> getConversationMsgAppServices()
            {
                var conversationMsgAppServices = new List<IConversationMsgAppService> { Substitute.For<IConversationMsgAppService>() };
                return Task.FromResult(conversationMsgAppServices.AsEnumerable());
            }

            var target = new InstantMessageController(conversationCtrlAppService,
                _ => Substitute.For<IConversationMsgAppService>(),
                getConversationMsgAppServices,
                Substitute.For<IEmployeeCacheService>(),
                Substitute.For<IGroupAppService>(),
                Substitute.For<IDepartmentAppService>())
            {
                ControllerContext = CreateMockContext()
            };

            var result = await target.CreateDepConverationAsync(Guid.NewGuid());
            result.Value.Should().BeOfType<ResponseData>();
            result.Value.Status.Should().Be(0);

            var depId = Guid.NewGuid();
            conversationCtrlAppService.GetByIdAsync(depId)
             .Returns(new Conversation
             {
                 Id = depId,
                 Participants = new List<Guid> { User_Id },
                 Type = ConversationType.DepartmentGroup
             });
            result = await target.CreateDepConverationAsync(depId);
            result.Value.Status.Should().Be(0);

            conversationCtrlAppService.GetByIdAsync(depId)
            .Returns(new Conversation
            {
                Id = depId,
                Participants = new List<Guid> { Guid.NewGuid() },
                Type = ConversationType.DepartmentGroup
            });
            result = await target.CreateDepConverationAsync(depId);
            result.Value.Status.Should().Be(1);

            conversationCtrlAppService.GetByIdAsync(depId)
               .Returns(default(Conversation?));
            conversationCtrlAppService.AddDepAsync(Arg.Is<AddDepConversationInput>(x => x.DepartmentId == depId && x.UserId == User_Id))
               .Returns(RemotingResult.Fail(1));
            result = await target.CreateDepConverationAsync(depId);
            result.Value.Status.Should().Be(2);

            conversationCtrlAppService.AddDepAsync(Arg.Is<AddDepConversationInput>(x => x.DepartmentId == depId && x.UserId == User_Id))
               .Returns(RemotingResult.Fail(2));
            result = await target.CreateDepConverationAsync(depId);
            result.Value.Status.Should().Be(3);
        }
    }
}


<|start_filename|>src/ConversationMsgStateService/ConversationMsgAppService.cs<|end_filename|>
﻿using InstantMessage;
using InstantMessage.Entities;
using Microsoft.ServiceFabric.Data;
using Microsoft.ServiceFabric.Data.Collections;
using Notification;
using Serilog;
using ServiceFabricContrib;
using System;
using System.Collections.Generic;
using System.Fabric;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace ConversationMsgStateService
{
    public class ConversationMsgAppService : StatefulRemotingService, IConversationMsgAppService
    {
        public ConversationMsgAppService(StatefulServiceContext serviceContext,
            IReliableStateManager stateManager) : base(serviceContext, stateManager)
        {
            _logger = Log.ForContext<ConversationMsgAppService>();
        }

        private const int MaxMessageAmount = 20;
        private readonly ILogger _logger;


        private static async Task<List<Guid>> BatchGetMsgIds(List<MessageNotifyDto> input,
            List<IReliableDictionary2<Guid, List<Guid>>> indexDicts, ITransaction tx)
        {
            var willBatchGetMsgIds = new List<Guid>();
            foreach (var indexDict in indexDicts)
            {
                foreach (var notify in input)
                {
                    var id = notify.TargetId.ToGuid();
                    if (await indexDict.ContainsKeyAsync(tx, id))
                    {
                        var lstMessageIndex = (await indexDict.TryGetValueAsync(tx, id)).Value;
                        var latestMsgIndex = notify.LatestMsgId == Guid.Empty
                            ? lstMessageIndex.Count - MaxMessageAmount
                            : lstMessageIndex.IndexOf(notify.LatestMsgId);
                        if (latestMsgIndex < 0) latestMsgIndex = 0;
                        var willGetMsgIds = (lstMessageIndex.Count - latestMsgIndex) > MaxMessageAmount
                            ? lstMessageIndex.GetRange(lstMessageIndex.Count - MaxMessageAmount, MaxMessageAmount)
                            : lstMessageIndex.GetRange(latestMsgIndex, lstMessageIndex.Count - latestMsgIndex);

                        willBatchGetMsgIds.AddRange(willGetMsgIds);
                    }
                }
            }

            return willBatchGetMsgIds;
        }

        public async Task<List<ConversationMsg>> GetMessagesAsync(List<MessageNotifyDto> input)
        {
            if (input == null)
                throw new ArgumentNullException(nameof(input));

            _logger.Debug("GetMessagesAsync, conversationIds is {ids}", string.Join(",", input.Select(o => o.TargetId)));

            var indexDicts = await Service.GetAllMessageIndexDictsAsync(StateManager);
            var listDicts = await Service.GetAllMessageListDictsAsync(StateManager);
            var token = CancellationToken.None;
            var result = new List<ConversationMsg>();
            using (var tx = StateManager.CreateTransaction())
            {
                var willBatchGetMsgIds = await BatchGetMsgIds(input, indexDicts, tx);

                foreach (var listDict in listDicts)
                {
                    result.AddRange(await listDict.GetAllByIdsAsync(tx, willBatchGetMsgIds, token));
                }
            }

            _logger.Debug("GetMessagesAsync msg count is {count}", result.Count);
            return result;
        }

        private static async Task<List<Guid>> GetOldGetMsgIds(GetOldMessagesInput input,
          IReliableDictionary2<Guid, List<Guid>> indexDict, ITransaction tx)
        {
            if (await indexDict.ContainsKeyAsync(tx, input.Id))
            {
                var lstMessageIndex = (await indexDict.TryGetValueAsync(tx, input.Id)).Value;
                var oldestMsgIndex = lstMessageIndex.IndexOf(input.OldestMsgId);
                if (oldestMsgIndex == 0) return null;

                var endIndex = oldestMsgIndex - 1;
                var startIndex = oldestMsgIndex - MaxMessageAmount;
                var amount = MaxMessageAmount;
                if (startIndex < 0)
                {
                    startIndex = 0;
                    amount = endIndex - startIndex + 1;
                }

                return lstMessageIndex.GetRange(startIndex, amount);
            }

            return null;
        }

        public async Task<List<ConversationMsg>> GetOldMessagesAsync(GetOldMessagesInput input)
        {
            if (input.Id == Guid.Empty) throw new ArgumentException("ConversationId is empty", nameof(input.Id));
            if (input.OldestMsgId == Guid.Empty) throw new ArgumentException("OldestMsgId is empty", nameof(input.OldestMsgId));

            _logger.Debug("GetOldMessagesAsync, conversationId is {id}", input.Id);

            var result = new List<ConversationMsg>();
            var token = CancellationToken.None;
            var indexDict = await Service.GetMessageIndexDictByIdAsync(StateManager, input.Id);
            var listDicts = await Service.GetAllMessageListDictsAsync(StateManager);

            using (var tx = StateManager.CreateTransaction())
            {
                var willOldGetMsgIds = await GetOldGetMsgIds(input, indexDict, tx);
                if (willOldGetMsgIds == null) return new List<ConversationMsg>();

                foreach (var listDict in listDicts)
                {
                    result.AddRange(await listDict.GetAllByIdsAsync(tx, willOldGetMsgIds, token));
                }
            }

            _logger.Debug("GetOldMessagesAsync, msg count is {count}", result.Count);
            return result;
        }

        public async Task SendMessageAsync(ConversationMsg msg)
        {
            var queue = await Service.GetMessageProcessQueue(StateManager);
            using (var tx = StateManager.CreateTransaction())
            {
                _logger.Debug("Enqueue ConversationMsg: {msgId}", msg.Id);

                await queue.EnqueueAsync(tx, msg);
                await tx.CommitAsync();
            }
        }

        public async Task ArchiveGroupMessagesAsync(Guid id)
        {
            var queue = await Service.GetPendingArchiveGroupQueue(StateManager);
            using (var tx = StateManager.CreateTransaction())
            {
                _logger.Debug("Enqueue ConversationId: {id}", id);

                await queue.EnqueueAsync(tx, id);
                await tx.CommitAsync();
            }
        }
    }
}


<|start_filename|>test/EnterpriseContactUnitTest/Services/EmployeeCacheServiceTest.cs<|end_filename|>
﻿using Common;
using EnterpriseContact;
using EnterpriseContact.Services;
using FluentAssertions;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using NSubstitute;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;

namespace EnterpriseContactUnitTest.Services
{
    [TestClass]
    public class EmployeeCacheServiceTest
    {
        [TestMethod]
        public async Task ByUserIdAsync()
        {
            var id1 = Guid.NewGuid();
            var id2 = Guid.NewGuid();

            var cache = Substitute.For<IMemoryCache>();

            var employeeAppService = Substitute.For<IEmployeeAppService>();
            employeeAppService.GetCacheDataAsync()
                .Returns(new List<EmployeeCacheOutput>
                {
                    new EmployeeCacheOutput
                    {
                        Id=Guid.NewGuid(),
                        UserId=id1,
                    },
                    new EmployeeCacheOutput
                    {
                        Id=Guid.NewGuid(),
                        UserId=id2
                    }
                });
            var target = new EmployeeCacheService(cache, employeeAppService);
            var result = await target.ByUserIdAsync(id1, id2);

            result.Should().NotBeNullOrEmpty();
        }

        [TestMethod]
        public async Task ByNumberAsync()
        {
            var id1 = Guid.NewGuid().ToString("N");
            var id2 = Guid.NewGuid().ToString("N");

            var cache = Substitute.For<IMemoryCache>();
            var employeeAppService = Substitute.For<IEmployeeAppService>();
            employeeAppService.GetCacheDataAsync()
                .Returns(new List<EmployeeCacheOutput>
                {
                    new EmployeeCacheOutput
                    {
                        Id=Guid.NewGuid(),
                        Number=id1,
                    },
                    new EmployeeCacheOutput
                    {
                        Id=Guid.NewGuid(),
                        Number=id2
                    }
                });

            var target = new EmployeeCacheService(cache, employeeAppService);
            var result = await target.ByNumberAsync(id1, id2);

            result.Should().NotBeNullOrEmpty();
        }

        [TestMethod]
        public async Task ByIdAsync()
        {
            var id1 = Guid.NewGuid();
            var id2 = Guid.NewGuid();

            var cache = Substitute.For<IMemoryCache>();
            var employeeAppService = Substitute.For<IEmployeeAppService>();
            employeeAppService.GetCacheDataAsync()
                .Returns(new List<EmployeeCacheOutput>
                {
                    new EmployeeCacheOutput
                    {
                        Id=id1,
                    },
                    new EmployeeCacheOutput
                    {
                        Id=id2,
                    }
                });

            var target = new EmployeeCacheService(cache, employeeAppService);
            var result = await target.ByIdAsync(id1, id2);

            result.Should().NotBeNullOrEmpty();
        }
    }
}


<|start_filename|>src/ConfigMgmtDomain/AppServices/IUserFavoriteAppService.cs<|end_filename|>
﻿using Microsoft.ServiceFabric.Services.Remoting;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;

namespace ConfigMgmt
{
    public interface IUserFavoriteAppService : IService
    {
        Task<bool> AddEmployeeAsync(Guid userId, Guid employeeId);

        Task<bool> RemoveEmployeeAsync(Guid userId, Guid employeeId);

        Task<Guid[]> GetEmployeesAsync(Guid userId);
        Task<bool> IsFavoritedAsync(Guid userId, Guid employeeId);
    }
}


<|start_filename|>src/EnterpriseContactDomain/Enums.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Text;

namespace EnterpriseContact
{
    [Flags]
    public enum GroupEventNotifyType
    {
        ParticipantAdded = 1,
        ParticipantRemoved = 2,
        GroupDismissed = 3,
        ParticipantQuited = 4,
        /// <summary>
        /// 群组名称修改
        /// </summary>
        GroupNameUpdated = 5,
    }
}


<|start_filename|>src/ApiCommon/ResponseModels/ResponseData.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Common
{
    /// <summary>
    /// 封装返回数据，一般在变更数据（非HTTPGET）的时候才使用
    /// </summary>
    public class ResponseData
    {
        /// <summary>
        /// 状态 0 表示成功
        /// </summary>
        public int Status { get; set; }

        /// <summary>
        /// 信息
        /// </summary>
        public string Message { get; set; }

        /// <summary>
        /// 构建成功的返回值
        /// </summary>
        /// <returns></returns>
        public static ResponseData BuildSuccessResponse(string message = "Success")
        {
            return new ResponseData
            {
                Status = StatusConst.SucessStatus,
                Message = message,
            };
        }

        public static ResponseData BuildFailedResponse(int status = StatusConst.DefaultFailureStatus, string message = "Failed")
        {
            return new ResponseData
            {
                Status = status,
                Message = message,
            };
        }
    }

    /// <summary>
    /// 请求返回数据模板
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class ResponseData<T>: ResponseData
    {
        /// <summary>
        /// 返回的数据
        /// </summary>
        public T Data { get; set; }

        /// <summary>
        /// 构建成功的返回值
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public static ResponseData<T> BuildSuccessResponse(T data)
        {
            return new ResponseData<T>
            {
                Status = StatusConst.SucessStatus,
                Message = "Success",
                Data = data
            };
        }

        new public static ResponseData<T> BuildFailedResponse(int status = StatusConst.DefaultFailureStatus, string message = "Failed")
        {
            return new ResponseData<T>
            {
                Status = status,
                Message = message,
            };
        }
    }
}


<|start_filename|>src/ConfigMgmtApi/Controllers/UserSettingController.cs<|end_filename|>
﻿using Common;
using Microsoft.AspNetCore.Mvc;
using Serilog;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;

namespace ConfigMgmt.Controllers
{
    [Route("api/v1/usersetting")]
    public class UserSettingController: EapBaseController
    {
        private readonly Func<Guid, IUserSettingAppService> _userSettingAppServiceFactory;
        private readonly ILogger _logger;

        public UserSettingController(Func<Guid,IUserSettingAppService> userSettingAppServiceFactory)
        {
            _userSettingAppServiceFactory = userSettingAppServiceFactory;
            _logger = Log.ForContext<UserSettingController>();
        }

        [HttpPost("infoVisibility")]
        public async Task<ActionResult<ResponseData>> SetInfoVisibility(InfoVisibility model)
        {
            try
            {
                var userId = GetUserId();
                await _userSettingAppServiceFactory(userId).SetInfoVisibilityAsync(userId, model);
                return BuildSuccess();
            }
            catch (Exception ex)
            {
                return BadRequest(LogError(_logger, ex));
            }
        }


        [HttpGet("infoVisibility")]
        public async Task<ActionResult<InfoVisibility>> GetInfoVisibility()
        {
            try
            {
                var userId = GetUserId();
                var dto = await _userSettingAppServiceFactory(userId).GetInfoVisibilityAsync(userId);
                return dto;
            }
            catch (Exception ex)
            {
                return BadRequest(LogError(_logger, ex));
            }
        }
    }
}


<|start_filename|>src/GroupFileDomain/Dtos/UploadInput.cs<|end_filename|>
﻿using System;

namespace GroupFile.Dtos
{
    /// <summary>
    /// 上传参数
    /// </summary>
    public class UploadInput
    {
        /// <summary>
        /// 文件存储Id
        /// </summary>
        public string StoreId { get; set; }
        /// <summary>
        /// 群组Id
        /// </summary>
        public Guid GroupId { get; set; }
        /// <summary>
        /// 文件名
        /// </summary>
        public string FileName { get; set; }
    }
}


<|start_filename|>test/UnitTestCommon/ManagerTestBase.cs<|end_filename|>
﻿using Microsoft.ServiceFabric.Actors.Runtime;
using ServiceFabric.Mocks;
using System.Fabric;
using System.Threading;

namespace UnitTestCommon
{
    public abstract class ManagerTestBase
    {
        protected MockReliableStateManager stateManager = new MockReliableStateManager();

        protected MockActorStateManager actorStateManager = new MockActorStateManager();

        protected IActorStateManager actorStateManagerFactory(ActorBase actorBase, IActorStateProvider actorStateProvider)
            => actorStateManager;

        protected static ICodePackageActivationContext codePackageContext = MockCodePackageActivationContext.Default;

        protected static StatefulServiceContext statefulServiceContext = MockStatefulServiceContextFactory.Default;

        protected static StatelessServiceContext statelessServiceContext = MockStatelessServiceContextFactory.Default;

        protected CancellationToken token => CancellationToken.None;
    }
}


<|start_filename|>emulator/ClientEmulator/IMChatForm.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using ClientEmulator.Config;
using ClientEmulator.Services;
using ClientEmulator.Services.Models;
using Microsoft.AspNetCore.Http.Connections;
using Microsoft.AspNetCore.SignalR.Client;

namespace ClientEmulator
{
    public partial class IMChatForm : Form
    {

        public IMChatForm()
        {
            InitializeComponent();
            this.loginControl1.OnUserLoginSuccessEvent += LoginControl_OnUserLoginSuccessEvent;
            this.loginControl2.OnUserLoginSuccessEvent += LoginControl_OnUserLoginSuccessEvent;

            {
                this.loginControl1.txtUserName.Text = "003139";
                this.loginControl1.txtPassword.Text = "<PASSWORD>";
                this.loginControl2.txtUserName.Text = "yangrugang";
                this.loginControl2.txtPassword.Text = "<PASSWORD>";
            }
        }

        private void LoginControl_OnUserLoginSuccessEvent(object sender, UserInfo userinfo)
        {
            var tabPage = new TabPage();
            tabPage.Location = new Point(4, 25);
            tabPage.Padding = new Padding(3);
            tabPage.Size = new Size(953, 412);
            tabPage.TabIndex = 0;
            tabPage.Text = $"sender:{userinfo.UserName}";
            tabPage.UseVisualStyleBackColor = true;
            tabControl1.Controls.Add(tabPage);
            var chatBox = new ChatBox(userinfo);
            tabPage.Controls.Add(chatBox);
            ChatBox.Chatboxs.Add(chatBox);
        }

        //Apply URL Config
        private void button1_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrEmpty(txtLogin_host.Text)
            || string.IsNullOrEmpty(txtApi_host.Text)
            || string.IsNullOrEmpty(txtHub_host.Text))
            {
                MessageBox.Show("Ensure Address!", "ERROR");
                return;
            }

            URLConfig.API_Host = txtApi_host.Text;
            URLConfig.Login_Host = txtLogin_host.Text;
            URLConfig.Hub_Host = txtHub_host.Text;
            btnApply.Enabled = false;
            txtLogin_host.Enabled = false;
            txtApi_host.Enabled = false;
            txtHub_host.Enabled = false;
            userGroupConfig.Enabled = true;
        }
    }
}


<|start_filename|>src/InstantMessageApi/ViewModels/ConversationVm.cs<|end_filename|>
﻿using InstantMessage.Entities;
using System;
using System.Collections.Generic;
using System.Text;

namespace InstantMessage.ViewModels
{
    public class ConversationVm
    {
        /// <summary>
        /// 会话id
        /// </summary>
        public Guid Id { get; set; }
        /// <summary>
        /// 会话topic
        /// </summary>
        public string Topic { get; set; }
        /// <summary>
        /// 会话头像
        /// </summary>
        //NOTE 通过Group接口获取 public string Image { get; set; }

        /// <summary>
        /// 会话类型
        /// </summary>
        public ConversationType ConversationType { get; set; }

        /// <summary>
        /// 会话消息
        /// </summary>
        public List<MessageVm> MessageList { get; set; }


        public static ConversationVm Create(Conversation conversation)
        {

            return new ConversationVm
            {
                Id = conversation.Id,
                ConversationType = conversation.Type,
                MessageList = new List<MessageVm>()
            };
        }
    }
}


<|start_filename|>src/GroupFileDomain/RemotingClient.cs<|end_filename|>
﻿using Microsoft.ServiceFabric.Services.Remoting.Client;
using Microsoft.ServiceFabric.Services.Remoting.V2.FabricTransport.Client;
using ServiceFabricContrib;
using System;
using System.Collections.Generic;
using System.Fabric;
using System.Text;
using static Common.Constants;

namespace GroupFile
{
    public static class RemotingClient
    {
        private static ServiceProxyFactory proxyFactory = new ServiceProxyFactory((c) => new FabricTransportServiceRemotingClientFactory());

        private static FabricClient fabricClient = new FabricClient();

        public static IControlAppService CreateGroupFileControlAppService()
        {
            var builder = new ServiceUriBuilder(AppName_GroupFile, ServiceName_GroupFileService);
            return proxyFactory.CreateServiceProxy<IControlAppService>(
                builder.ToUri(), listenerName: ListenerName_ControlAppService);
        }
    }
}


<|start_filename|>src/InstantMessageApi/ViewModels/TextMessageVm.cs<|end_filename|>
﻿namespace InstantMessage.ViewModels
{
    public class TextMessageVm : MessageVmBase
    {
        /// <summary>
        /// 文本消息内容
        /// </summary>
        public string Content { get; set; }
    }
}


<|start_filename|>src/AttachmentStateService/Service.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Fabric;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Common;
using Microsoft.Diagnostics.EventFlow;
using Microsoft.ServiceFabric.Data.Collections;
using Microsoft.ServiceFabric.Services.Communication.Runtime;
using Microsoft.ServiceFabric.Services.Remoting.V2.FabricTransport.Runtime;
using Microsoft.ServiceFabric.Services.Runtime;

namespace AttachmentStateService
{
    /// <summary>
    /// An instance of this class is created for each service replica by the Service Fabric runtime.
    /// </summary>
    internal sealed class Service : StatefulServiceEapBase
    {
        public Service(StatefulServiceContext context,
             DiagnosticPipeline diagnosticPipeline)
             : base(context, diagnosticPipeline)
        {
        }

        /// <summary>
        /// Optional override to create listeners (e.g., HTTP, Service Remoting, WCF, etc.) for this service replica to handle client or user requests.
        /// </summary>
        /// <remarks>
        /// For more information on service communication, see https://aka.ms/servicefabricservicecommunication
        /// </remarks>
        /// <returns>A collection of listeners.</returns>
        protected override IEnumerable<ServiceReplicaListener> CreateServiceReplicaListeners()
        {
            return new[]
                       {
                new ServiceReplicaListener((c)=>
                    new FabricTransportServiceRemotingListener(c,
                        new AttachmentAppService(Context,StateManager)))
            };
        }
    }
}


<|start_filename|>src/GroupFileApi/Controllers/GroupFileController.cs<|end_filename|>
﻿using Attachment;
using Attachment.Entities;
using Common;
using EnterpriseContact;
using EnterpriseContact.Services;
using GroupFile.Dtos;
using GroupFile.Extensions;
using InstantMessage;
using InstantMessage.Entities;
using InstantMessage.ViewModels;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
using Serilog;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace GroupFile.Controllers
{
    //群组验证
    [Route("api/v1/groupfile")]
    [Produces("application/json")]
    public class GroupFileController : EapBaseController
    {
        private readonly IControlAppService _groupFileControlApp;
        private readonly Func<string, IAttachmentAppService> _attachmentAppServiceFactory;
        private readonly Func<Guid, IConversationMsgAppService> _conversationMsgAppServiceFactory;
        private readonly ILogger _logger;
        private readonly IGroupAppService _groupAppService;
        private readonly IDepartmentAppService _departmentAppService;
        private readonly IEmployeeCacheService _employeeCacheService;
        private readonly IMemoryCache _cache;

        private const int MaxAttachmentSize = 10 * 1024 * 1024;

        public GroupFileController(IControlAppService controlAppService,
            IEmployeeCacheService employeeCacheService,
            IGroupAppService groupAppService,
            IDepartmentAppService departmentAppService,
            IMemoryCache cache,
            Func<string, IAttachmentAppService> attachmentAppServiceFactory,
            Func<Guid, IConversationMsgAppService> conversationMsgAppServiceFactory)
        {
            _groupFileControlApp = controlAppService;
            _attachmentAppServiceFactory = attachmentAppServiceFactory;
            _groupAppService = groupAppService;
            _conversationMsgAppServiceFactory = conversationMsgAppServiceFactory;
            _cache = cache;
            _logger = Log.ForContext<GroupFileController>();
            _departmentAppService = departmentAppService;
            _employeeCacheService = employeeCacheService;
        }

        [Route("{groupId}/files")]
        [HttpGet]
        public async Task<ActionResult<IEnumerable<FileItemDto>>> GetGroupFilesAsync(Guid groupId, bool depGroup, int page, int size)
        {
            try
            {
                if (Guid.Empty.Equals(groupId) || page < 1)
                    return null;
                var uploaderId = GetEmployeeId();
                var group = !depGroup
                    ? await _groupAppService.GetByIdAsync(groupId)
                    : await _departmentAppService.GetDepGroupByIdAsync(groupId);
                if (group == null)
                    return null;
                if (!IsMemeberInGroup(group.Members, uploaderId))
                    return null;
                var fileItems = await _groupFileControlApp.GetFileItemsAsync(groupId, page, size);
                if (fileItems != null)
                {
                    #region 获取上传者信息
                    var employeeIds = fileItems.DistinctBy(f => new { f.UploaderId })
                        .Select(x => x.UploaderId).ToArray();

                    var employees = await _employeeCacheService.ByIdAsync(employeeIds);

                    foreach (var item in fileItems)
                    {
                        var employee = employees.Find(x => x.Id == item.UploaderId);
                        if (employee != null)
                            item.UploaderName = employee.Name;
                    }
                    #endregion

                    #region 获取文件信息
                    var fileMd5s = fileItems.DistinctBy(f => new { f.StoreId });
                    List<Task<AttachmentItem>> attTasks = new List<Task<AttachmentItem>>();
                    foreach (var item in fileMd5s)
                    {
                        attTasks.Add(_attachmentAppServiceFactory(item.StoreId).GetByIdAsync(item.StoreId));
                    }
                    await Task.WhenAll(attTasks);
                    attTasks.ForEach(t =>
                    {
                        var e = t.Result;
                        if (e != null)
                        {
                            var fs = fileItems.FindAll(f => f.StoreId.Equals(e.Id));
                            if (fs != null)
                                foreach (var f in fs) f.Size = e.Size;
                        }
                    });
                    #endregion
                }
                return fileItems;
            }
            catch (Exception ex)
            {
                return BadRequest(LogError(_logger, ex));
            }
        }


        [Route("upload/{groupId}")]
        [HttpPut]
        public async Task<ActionResult<ResponseData<FileItemDto>>> UploadFileAsync(Guid groupId, string storeId, string fileName, bool depGroup = false)
        {
            try
            {
                if (string.IsNullOrEmpty(fileName) || string.IsNullOrEmpty(storeId) || Guid.Empty.Equals(groupId))
                    return ResponseData<FileItemDto>.BuildFailedResponse(message: "参数错误");
                var uploaderId = GetEmployeeId();
                var group = !depGroup
                    ? await _groupAppService.GetByIdAsync(groupId)
                    : await _departmentAppService.GetDepGroupByIdAsync(groupId);
                if (group == null)
                    return ResponseData<FileItemDto>.BuildFailedResponse(message: "群组不存在");
                if (!IsMemeberInGroup(group.Members, uploaderId))
                    return ResponseData<FileItemDto>.BuildFailedResponse(message: "无上传权限");
                var attachment = await _attachmentAppServiceFactory(storeId).GetByIdAsync(storeId);
                if (attachment == null)
                    return ResponseData<FileItemDto>.BuildFailedResponse(message: "文件上传失败");
                var input = new UploadInput
                {
                    FileName = fileName,
                    GroupId = groupId,
                    StoreId = storeId
                };
                await SendMessageAsync(groupId, GetUserId(), fileName, attachment);
                var result = await _groupFileControlApp.UploadFileAsync(input, uploaderId);
                _cache.Remove(groupId);
                if (result != null)
                {
                    result.Size = attachment.Size;
                    result.UploaderName = GetUserFullName();
                    return ResponseData<FileItemDto>.BuildSuccessResponse(result);
                }
                else
                    return ResponseData<FileItemDto>.BuildFailedResponse();
            }
            catch (Exception ex)
            {
                return BadRequest(LogError(_logger, ex));
            }
        }

        [Route("delete/{groupId}/{fileId}")]
        [HttpPut]
        public async Task<ActionResult<ResponseData>> Delete(Guid groupId, Guid fileId, bool depGroup = false)
        {
            try
            {
                var operatorId = GetEmployeeId();
                var group = !depGroup
                            ? await _groupAppService.GetByIdAsync(groupId)
                            : await _departmentAppService.GetDepGroupByIdAsync(groupId);
                if (group == null)
                    return ResponseData.BuildFailedResponse(message: "群组不存在");
                if (IsGroupOwner(group.Members, operatorId) || await IsFileOwnerAsync(fileId, operatorId))
                {
                    await _groupFileControlApp.DeleteFileItemAsync(fileId);
                    _cache.Remove(groupId);
                    return ResponseData.BuildSuccessResponse();
                }
                else
                {
                    return ResponseData.BuildFailedResponse(message: "没有操作权限");
                }
            }
            catch (Exception ex)
            {
                return BadRequest(LogError(_logger, ex));
            }
        }

        /// <summary>
        /// 返回attachment的id
        /// </summary>
        /// <param name="fileId"></param>
        /// <returns></returns>
        [Route("download/{fileId}")]
        [HttpGet]
        public async Task<ActionResult<string>> DownloadFileAsync(Guid fileId)
        {
            if (Guid.Empty.Equals(fileId))
                return string.Empty;

            try
            {
                return await this._groupFileControlApp.DownloadFileAsync(fileId);
            }
            catch (Exception ex)
            {
                LogError(_logger, ex);
                return string.Empty;
            }
        }

        /// <summary>
        /// 群组附件容量剩余
        /// </summary>
        /// <param name="groupId"></param>
        /// <returns></returns>
        [Route("freespace/{groupId}")]
        [HttpGet]
        public async Task<ActionResult<FreeSpaceDto>> FreeSpaceAsync(Guid groupId)
        {
            if (this._cache.TryGetValue<FreeSpaceDto>(groupId, out var freeSpaceDto))
            {
                return freeSpaceDto;
            }
            var groupFiles = await this._groupFileControlApp.GetFileItemsAsync(groupId, 1, int.MaxValue);
            var storeIds = groupFiles.Select(u => u.StoreId).ToArray();
            int stordBytes = 0;
            foreach (var storeId in storeIds)
            {
                var attachment = await this._attachmentAppServiceFactory.Invoke(storeId).GetByIdAsync(storeId);
                if (attachment != null)
                    stordBytes += attachment.Size;
            }
            var freeSpace = MaxAttachmentSize - stordBytes;
            freeSpaceDto = new FreeSpaceDto()
            {
                TotalSpace = MaxAttachmentSize,
                FreeSpace = freeSpace < 0 ? 0 : freeSpace
            };
            this._cache.Set(groupId, freeSpaceDto);
            return freeSpaceDto;
        }

        private async Task<GroupOutput> GetGroupAsync(Guid groupId)
        {
            return await _groupAppService.GetByIdAsync(groupId);
        }

        private async Task SendMessageAsync(Guid groupId, Guid senderId, string fileName, AttachmentItem attachment)
        {
            var fileMsg = new FileMessageVm
            {
                FileGuid = attachment.Id,
                FileName = fileName,
                FileSize = attachment.Size,
            };
            var msg = ConversationMsg.Create(groupId, senderId, fileMsg.GetJsonContent(), ConversationMsgType.File);
            var conversationMsgAppService = _conversationMsgAppServiceFactory(msg.ConversationId);
            await conversationMsgAppService?.SendMessageAsync(msg);
        }

        private bool IsGroupOwner(List<GroupMemberOutput> memebers, Guid employeeId)
        {
            if (memebers?.Count == 0)
                return false;
            var u = memebers?.First((m) => employeeId.Equals(m?.EmployeeId));
            return u != null && u.IsOwner;
        }

        private bool IsMemeberInGroup(List<GroupMemberOutput> memebers, Guid employeeId)
        {
            if (memebers?.Count == 0)
                return false;
            var u = memebers?.First((m) => employeeId.Equals(m?.EmployeeId));
            return u != null;
        }

        private async Task<bool> IsFileOwnerAsync(Guid fileId, Guid employeeId)
        {
            var fileItem = await _groupFileControlApp.GetFileItemAsync(fileId);
            return fileItem?.UploaderId == employeeId;
        }
    }
}


<|start_filename|>test/ConfigMgmtUnitTest/Controllers/UserSettingControllerTest.cs<|end_filename|>
﻿using Common;
using ConfigMgmt;
using ConfigMgmt.Controllers;
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using NSubstitute;
using System.Threading.Tasks;
using UnitTestCommon;

namespace ConfigMgmtUnitTest.Controllers
{
    [TestClass]
    public class UserSettingControllerTest : ControllerTestBase
    {
        [TestMethod]
        public async Task TestSetInfoVisibility()
        {
            var value = new InfoVisibility() { Mobile = true };
            var userSettingAppService = Substitute.For<IUserSettingAppService>();
            userSettingAppService.SetInfoVisibilityAsync(User_Id, value)
               .Returns(Task.CompletedTask);

            var target = new UserSettingController(_ => userSettingAppService);
            target.ControllerContext = CreateMockContext();

            var result = await target.SetInfoVisibility(value);
            var data = result.Value;
            data.Should().BeOfType<ResponseData>();
            data.Status.Should().Be(0);
        }

        [TestMethod]
        public async Task TestGetInfoVisibility()
        {
            var value = new InfoVisibility() { Mobile = true };
            var userSettingAppService = Substitute.For<IUserSettingAppService>();
            userSettingAppService.GetInfoVisibilityAsync(User_Id)
               .Returns(Task.FromResult(value));

            var target = new UserSettingController(_ => userSettingAppService);
            target.ControllerContext = CreateMockContext();

            var result = await target.GetInfoVisibility();
            var data = result.Value;
            data.Should().BeOfType<InfoVisibility>();
            data.Mobile.Should().BeTrue();
        }
    }
}


<|start_filename|>test/ConversationServiceTest/TheAppServiceTestBase.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using InstantMessage;
using InstantMessage.Entities;
using Microsoft.EntityFrameworkCore.ChangeTracking.Internal;
using Microsoft.ServiceFabric.Data.Collections;
using ServiceFabricContrib;
using UnitTestCommon;
using Common;

namespace ConversationServiceTest
{
    public class TheAppServiceTestBase : AppServiceTestBase
    {
        protected IReliableDictionary2<string, Guid> dictKeyToId { get; set; }
        protected IReliableDictionary2<Guid, Conversation> dictIdToEntity { get; set; }
        protected static Guid SenderId = Guid.NewGuid();
        private static Guid ReciverId = Guid.NewGuid();
        protected static Conversation DefaultConversation { get; set; } = new Conversation()
        {
            Id = Guid.NewGuid(),
            Key = Guid.NewGuid().ToString("N").ComputeMd5(),
            IsDeleted = false,
            Type = ConversationType.P2P,
            Updated = DateTimeOffset.Now,
            Participants = new List<Guid>() { SenderId, ReciverId }
        };
        protected static ConversationMsg DefaultConversationMsg { get; set; } = new ConversationMsg()
        {
            Id = Guid.NewGuid(),
            ConversationId = DefaultConversation.Id,
            Content = "hello,world",
            SenderId = SenderId,
            Time = DateTimeOffset.Now,
            Type = ConversationMsgType.Text
        };
        protected static ConversationMsg DefaultConversationMsg2 { get; set; } = new ConversationMsg()
        {
            Id = Guid.NewGuid(),
            ConversationId = DefaultConversation.Id,
            Content = "hello,world2",
            SenderId = SenderId,
            Time = DateTimeOffset.Now,
            Type = ConversationMsgType.Text
        };

      
        protected IReliableConcurrentQueue<ConversationMsg> MessageProccessQueue { get; set; }

        protected IReliableDictionary2<Guid, List<Guid>> MessageIndexDict { get; set; }
        protected IReliableDictionary2<Guid, ConversationMsg> MessageListDict { get; set; }

        private async Task InitDictAsync()
        {
            dictKeyToId = await ConversationCtrlStateService.Service.GetKeyToIdDictAsync(stateManager);
            dictIdToEntity = await ConversationCtrlStateService.Service.GetListDictAsync(stateManager);

            MessageProccessQueue = await ConversationMsgStateService.Service.GetMessageProcessQueue(stateManager);
            MessageIndexDict = await ConversationMsgStateService.Service.GetMessageIndexDictByIdAsync(stateManager, DefaultConversation.Id);
            MessageListDict = await ConversationMsgStateService.Service.GetMessageListDictByIdAsync(stateManager, DefaultConversationMsg.Id);
        }

        protected async Task InitDictDataAsync()
        {
            await InitDictAsync();

            var conversation = DefaultConversation.DeepCopy(); //need deep copy
            var message1 = DefaultConversationMsg.DeepCopy();
            var message2 = DefaultConversationMsg2.DeepCopy();
            using (var tx = stateManager.CreateTransaction())
            {
                await dictKeyToId.AddOrUpdateAsync(tx, conversation.Key, conversation.Id, (k, v) => conversation.Id);
                await dictIdToEntity.AddOrUpdateAsync(tx, conversation.Id, conversation, (k, v) => conversation);

                await MessageIndexDict.SetAsync(tx, message1.ConversationId, new List<Guid>() { message1.Id, message2.Id });
                await MessageListDict.SetAsync(tx, message1.Id, message1);
                await MessageListDict.SetAsync(tx, message2.Id, message2);

                await tx.CommitAsync();
            }
        }

        protected async Task InitQueueDataAsync()
        {
            await InitDictAsync();

            var message1 = DefaultConversationMsg.DeepCopy();
            using (var tx = stateManager.CreateTransaction())
            {
                await MessageProccessQueue.EnqueueAsync(tx, message1);

                await tx.CommitAsync();
            }
        }

        //public async Task InitArchivedAndPendingMessageQueueAsync()
        //{
        //    using (var tx = stateManager.CreateTransaction())
        //    {
        //        var msg = DefaultPendingMsg.DeepCopy();
        //        //pre-init pending a Group meesage
        //        await pendingArchiveQueue.EnqueueAsync(tx, msg.ConversationId);
        //        await dictGroupMessagesIndices.AddAsync(tx, msg.ConversationId, new List<Guid>() { msg.Id });
        //        await dictGroupMessages.AddAsync(tx, msg.Id, msg);
        //        await tx.CommitAsync();
        //    }
        //}
    }
}


<|start_filename|>src/ConfigMgmtDomain/Entities/UserDeviceBinding.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Text;

namespace ConfigMgmt.Entities
{
    /// <summary>
    /// 用户设备绑定聚合
    /// </summary>
    [DataContract]
    public class UserDeviceBinding
    {
        /// <summary>
        /// UserId
        /// </summary>
        [DataMember]
        public Guid Id { get; set; }

        [DataMember]
        public Dictionary<string, DeviceBindingInfo> Devices { get; set; }

        public static UserDeviceBinding Create(Guid userId, string deviceCode, Guid appEntranceId)
        {
            if (userId == Guid.Empty)
                throw new ArgumentOutOfRangeException("must not be empty", nameof(userId));

            if (string.IsNullOrEmpty(deviceCode))
                throw new ArgumentNullException(nameof(deviceCode));

            if (appEntranceId == Guid.Empty)
                throw new ArgumentOutOfRangeException("must not be empty", nameof(appEntranceId));

            return new UserDeviceBinding
            {
                Id = userId,
                Devices = new Dictionary<string, DeviceBindingInfo>
                {
                    [deviceCode] = DeviceBindingInfo.Create(appEntranceId)
                }
            };
        }

        public void AddOrUpdateLatestAuthed(string deviceCode, Guid appEntranceId)
        {
            if (string.IsNullOrEmpty(deviceCode))
                throw new ArgumentNullException(nameof(deviceCode));

            if (appEntranceId == Guid.Empty)
                throw new ArgumentOutOfRangeException("must not be empty", nameof(appEntranceId));

            if (Devices.ContainsKey(deviceCode))
            {
                var info = Devices[deviceCode];
                if (info.AppEntranceAuths.ContainsKey(appEntranceId))
                {
                    var auth = info.AppEntranceAuths[appEntranceId];
                    auth.LatestAuthed = DateTimeOffset.UtcNow;
                }
                else
                    info.AppEntranceAuths[appEntranceId] = AppEntranceAuth.Create();
            }
            else
                Devices[deviceCode] = DeviceBindingInfo.Create(appEntranceId);
        }

        public bool CheckWhetherAuth(string deviceCode, Guid appEntranceId, TimeSpan validity)
        {
            if (string.IsNullOrEmpty(deviceCode))
                throw new ArgumentNullException(nameof(deviceCode));

            if (appEntranceId == Guid.Empty)
                throw new ArgumentOutOfRangeException("must not be empty", nameof(appEntranceId));

            if (Devices?.ContainsKey(deviceCode) == true)
            {
                var info = Devices[deviceCode];
                if (info.AppEntranceAuths?.ContainsKey(appEntranceId) == true)
                {
                    var auth = info.AppEntranceAuths[appEntranceId];
                    return (DateTimeOffset.UtcNow - auth.LatestAuthed) > validity;//最后验证时间超过有效期
                }
                else
                    return true;
            }
            else
                return true;
        }
    }


    /// <summary>
    /// 设备绑定信息
    /// </summary>
    [DataContract]
    public class DeviceBindingInfo
    {
        [DataMember]
        public Dictionary<Guid, AppEntranceAuth> AppEntranceAuths { get; set; }

        internal static DeviceBindingInfo Create(Guid appEntranceId)
        {
            return new DeviceBindingInfo
            {
                AppEntranceAuths = new Dictionary<Guid, AppEntranceAuth>
                {
                    [appEntranceId] = AppEntranceAuth.Create()
                }
            };
        }
    }

    [DataContract]
    public class AppEntranceAuth
    {
        [DataMember]
        public DateTimeOffset LatestAuthed { get; set; }

        internal static AppEntranceAuth Create()
        {
            return new AppEntranceAuth { LatestAuthed = DateTimeOffset.UtcNow };
        }
    }
}


<|start_filename|>emulator/ClientEmulator/ChatBox.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Net.Http;
using System.Runtime.Remoting.Messaging;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using ClientEmulator.Config;
using ClientEmulator.Services;
using ClientEmulator.Services.Models;
using InstantMessage;
using InstantMessage.ViewModels;
using Newtonsoft.Json;

namespace ClientEmulator
{
    public partial class ChatBox : UserControl
    {
        public ChatHub _chatHub { get; set; }
        private readonly BizService _bizService;

        public static List<ChatBox> Chatboxs { get; set; } = new List<ChatBox>();

        public event Action<ConversationType, string> OnChatCreatedEvent;

        public ChatBox(UserInfo userInfo)
        {
            _chatHub = new ChatHub(userInfo);
            _bizService = new BizService(new HttpClient() { BaseAddress = new Uri(URLConfig.API_Host) });
            InitializeComponent();
            InitMessageBoxEvent();

        }

        private void InitMessageBoxEvent()
        {
            this._chatHub.OnSendMessageEvent += _chatHub_OnSendMessageEvent;
            this._chatHub.OnNewMessagesEvent += _chatHub_OnNewMessagesEvent;
            this._chatHub.OnNewMessageNotifyEvent += _chatHub_OnNewMessageNotifyEvent;
            this._chatHub.OnNewEventNotifyEvent += _chatHub_OnNewEventNotifyEvent;
            this.OnChatCreatedEvent += ChatBox_OnChatCreatedEvent;
            this._bizService.OnSuccessEvent += _bizService_OnSuccessEvent;
            this._bizService.OnErrorOrExceptionEvent += _bizService_OnErrorOrExceptionEvent;
        }

        private async void _bizService_OnErrorOrExceptionEvent(HttpResponseMessage obj)
        {
            var error = await obj.Content.ReadAsStringAsync();
            AddMessage("BizSystem:" + error);
        }

        private void _bizService_OnSuccessEvent(string obj)
        {
            AddMessage("BizSystem:" + obj);
        }

        private void _chatHub_OnNewMessageNotifyEvent(List<Notification.MessageNotifyDto> messageNotifyDto)
        {
            AddMessage("Recived-MessageNotifyDto:" + JsonConvert.SerializeObject(messageNotifyDto));
        }

        private void ChatBox_OnChatCreatedEvent(ConversationType type, string id)
        {
            this.lab_convarsationType.Text = type.ToString();
            this.lab_conversationId.Text = id;
        }

        private void _chatHub_OnNewEventNotifyEvent(List<Notification.EventNotifyDto> eventNotifyDto)
        {
            AddMessage("RecivedEventNotify:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " " + JsonConvert.SerializeObject(eventNotifyDto));
        }

        //发送消息
        private void _chatHub_OnSendMessageEvent(string reciverId, string message)
        {
            AddMessage("Send:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " " + message);
        }

        //收到消息
        private void _chatHub_OnNewMessagesEvent(List<ConversationVm> conversations)
        {
            var messages = conversations.SelectMany(u => u.MessageList).ToList();
            foreach (var message in messages)
            {
                AddMessage("Recived:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " " + JsonConvert.SerializeObject(message));
            }

        }

        public void DisplayConversationInfo(ConversationType type, string id)
        {
            this.lab_conversationId.Text = id;
            this.lab_convarsationType.Text = type.ToString();
        }

        private async void button1_Click(object sender, EventArgs e)
        {
            var message = txt_message.Text;
            if (string.IsNullOrEmpty(message))
                return;
            if (UserInfo.LoginedUsers.Count <= 1)
            {
                MessageBox.Show("需要再登录一枚用户", "ERROR");
                return;
            }
            var otherUser = UserInfo.LoginedUsers.FirstOrDefault(u => u.UserId != this._chatHub._userInfo.UserId);
            try
            {
                var result = await this._chatHub.SendTextAsync(otherUser.UserId, message);
                AddMessage(result);
            }
            catch (Exception ex)
            {
                AddMessage(ex.ToString());
            }

        }

        private void btn_connect_Click(object sender, EventArgs e)
        {
            try
            {
                this._chatHub.ConnectAsync();
                btn_connect.Enabled = false;
                btn_disconnect.Enabled = true;
            }
            catch (Exception ex)
            {
                AddMessage(ex.ToString());
            }

        }

        private void btn_disconnect_Click(object sender, EventArgs e)
        {
            try
            {
                this._chatHub.DisConnectAsync();
                btn_connect.Enabled = true;
                btn_disconnect.Enabled = false;
            }
            catch (Exception ex)
            {
                AddMessage(ex.ToString());
            }

        }

        private async void btn_creategroup_Click(object sender, EventArgs e)
        {
            var otherUser = UserInfo.LoginedUsers.FirstOrDefault(u => u.UserId != this._chatHub._userInfo.UserId);
            var result = await this._chatHub.CreateGroupAsync(this._chatHub._userInfo, otherUser, "测试群组名称", "备注信息");
            foreach (var chatBox in Chatboxs)
            {
                chatBox.OnChatCreatedEvent?.Invoke(ConversationType.Group, result);
            }
            AddMessage(result);
        }

        private async void btn_quitGruop_Click(object sender, EventArgs e)
        {
            var response = await this._chatHub.QuitGroupAsync();
            AddMessage(response);
        }

        private async void btn_deleteGroup_Click(object sender, EventArgs e)
        {
            var response = await this._chatHub.DeleteGroupAsync();
            foreach (var chatBox in Chatboxs)
            {
                chatBox.DisplayConversationInfo(ChatHub.ConversationType, ChatHub.ConversationId.ToString());
            }
            AddMessage(response);
        }

        private async void btn_CreateConversation_Click(object sender, EventArgs e)
        {
            if (UserInfo.LoginedUsers.Count <= 1)
            {
                MessageBox.Show("需要再登录一枚用户", "ERROR");
                return;
            }
            var otherUser = UserInfo.LoginedUsers.FirstOrDefault(u => u.UserId != this._chatHub._userInfo.UserId);
            var convId = await this._chatHub.CreateConversationAsync(otherUser.UserId);
            AddMessage("ConversationId:P2P:" + convId);
            foreach (var chatBox in Chatboxs)
            {
                chatBox.OnChatCreatedEvent?.Invoke(ConversationType.P2P, convId);
            }

        }

        private void AddMessage(string message)
        {
            this.chat_messages.Text += message + Environment.NewLine + Environment.NewLine;
        }

        private async void btn_todo_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrEmpty(this.txt_component.Text))
            {
                MessageBox.Show("ComponentId不能为空");
                return;
            }
            await this._bizService.GetPendingList(this._chatHub._userInfo.AccessToken, this.txt_component.Text);
        }

        private async void btn_complete_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrEmpty(this.txt_component.Text))
            {
                MessageBox.Show("ComponentId不能为空");
                return;
            }
            await this._bizService.GetDoneList(this._chatHub._userInfo.AccessToken, this.txt_component.Text);
        }

        private async void btn_messageHistory_Click(object sender, EventArgs e)
        {
            var result = await this._chatHub.GetMessageHistoryAsync();
            AddMessage("History:" + result);
        }

        private async void btn_deleteP2pConversation_Click(object sender, EventArgs e)
        {
            if (ChatHub.ConversationType != ConversationType.P2P)
            {
                MessageBox.Show("当前不是P2p模式,请转变成p2p模式！");
                return;
            }

            var s = await this._chatHub.DeleteP2PConversationAsync();
            AddMessage("DELETE P2P Conversation:" + s);
        }
    }
}


<|start_filename|>src/ConfigMgmtDomain/Entities/ComponentDataSource.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Runtime.Serialization;

namespace ConfigMgmt.Entities
{
    [DataContract]
    public class ComponentDataSource
    {
        public ComponentDataSource()
        {

        }

        public ComponentDataSource(string key, string value)
        {
            Key = key;
            Value = value;
        }

        /// <summary>
        /// 组件数据源接口Key
        /// </summary>
        [DataMember]
        public string Key { get; set; }

        /// <summary>
        /// 组件数据源接口地址
        /// </summary>
        [DataMember]
        public string Value { get; set; }

        /// <summary>
        /// 组件数据源接口描述
        /// </summary>
        [DataMember]
        public string Description { get; set; }

        /// <summary>
        /// 组件数据源接口参数
        /// </summary>
        [DataMember]
        public Dictionary<string, string> Parameters { get; set; }
    }

    public static class ComponentDataSourceKeys
    {
        public const string BadgeValue = "badge_value";

        #region Todo component
        public const string TodoPendingList = "pending_list";
        public const string TodoDoneList = "done_list";
        #endregion
    }
}


<|start_filename|>src/EnterpriseContactApi/TheBaseController.cs<|end_filename|>
﻿using Common;
using AutoMapper;
using Microsoft.Extensions.Caching.Memory;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace EnterpriseContact
{
    public abstract class TheBaseController : EapBaseController
    {
        protected readonly IMapper _mapper;
        protected readonly IMemoryCache _cache;
        protected readonly IDepartmentAppService _departmentAppService;
        protected readonly IPositionAppService _positionAppService;

        public TheBaseController(
            IMemoryCache cache,
            IMapper mapper,
            IDepartmentAppService departmentAppService,
            IPositionAppService positionAppService
            )
        {
            _cache = cache;
            _mapper = mapper;
            _departmentAppService = departmentAppService;
            _positionAppService = positionAppService;
        }

        protected Dictionary<Guid, DepartmentListOutput> _departmentListCache;
        protected async Task EnsureDepartmentListCacheAsync()
        {
            if (_departmentListCache == null)
            {
                if (!_cache.TryGetValue(CacheKeys.EnterpriseContact_DepartmentList, out Dictionary<Guid, DepartmentListOutput> data))
                {
                    var dto = await _departmentAppService.GetAllListAsync();
                    if (dto == null || dto.Count == 0)
                        data = new Dictionary<Guid, DepartmentListOutput>();
                    else
                        data = dto.ToDictionary(o => o.Id, o => o);

                    var cacheEntryOptions = new MemoryCacheEntryOptions()
                       .SetAbsoluteExpiration(CacheKeys.DefaultCacheAbsoluteExpiration);

                    _cache.Set(CacheKeys.EnterpriseContact_DepartmentList, data, cacheEntryOptions);
                }
                _departmentListCache = data;
            }
        }

        protected Dictionary<Guid, PositionListOutput> _positionListCache;

        protected async Task EnsurePositionListCacheAsync()
        {
            if (_positionListCache == null)
            {
                if (!_cache.TryGetValue(CacheKeys.EnterpriseContact_PositionList, out Dictionary<Guid, PositionListOutput> data))
                {
                    var dto = await _positionAppService.GetAllListAsync();
                    if (dto == null || dto.Count == 0)
                        data = new Dictionary<Guid, PositionListOutput>();
                    else
                        data = dto.ToDictionary(o => o.Id, o => o);

                    var cacheEntryOptions = new MemoryCacheEntryOptions()
                       .SetAbsoluteExpiration(CacheKeys.DefaultCacheAbsoluteExpiration);

                    _cache.Set(CacheKeys.EnterpriseContact_PositionList, data, cacheEntryOptions);
                }
                _positionListCache = data;
            }
        }
    }
}


<|start_filename|>emulator/ClientEmulator/Services/BizService.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using ClientEmulator.Config;

namespace ClientEmulator.Services
{
    public class BizService
    {
        private readonly HttpClient _httpClient;
        public event Action<HttpResponseMessage> OnErrorOrExceptionEvent;
        public event Action<string> OnSuccessEvent;

        public BizService(HttpClient httpClient)
        {
            _httpClient = httpClient;
        }

        /// <summary>
        /// 获取代办信息
        /// </summary>
        /// <param name="accessToken"></param>
        /// <param name="componentId"></param>
        /// <returns></returns>
        public async Task GetPendingList(string accessToken, string componentId)
        {
            this._httpClient.SetBearerToken(accessToken);
            var response = await this._httpClient.GetAsync(BizUrlConfig.PUll_TODO_GET + "?appid=" + componentId);
            if (!response.IsSuccessStatusCode)
            {
                this.OnErrorOrExceptionEvent?.Invoke(response);
                return;
            }

            var content = await response.Content.ReadAsStringAsync();
            this.OnSuccessEvent?.Invoke(content);
        }

        /// <summary>
        /// 获取已经完成的信息
        /// </summary>
        /// <param name="accessToken"></param>
        /// <param name="componentId"></param>
        /// <returns></returns>
        public async Task GetDoneList(string accessToken, string componentId)
        {
            this._httpClient.SetBearerToken(accessToken);
            var response = await this._httpClient.GetAsync(BizUrlConfig.PUll_DONE_GET + "?appid=" + componentId);
            if (!response.IsSuccessStatusCode)
            {
                this.OnErrorOrExceptionEvent?.Invoke(response);
                return;
            }

            var content = await response.Content.ReadAsStringAsync();
            this.OnSuccessEvent?.Invoke(content);
        }
    }
}


<|start_filename|>test/EnterpriseContactUnitTest/TheControllerTestBase.cs<|end_filename|>
﻿using AutoMapper;
using EnterpriseContact;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
using System;
using System.Security.Claims;
using UnitTestCommon;

namespace EnterpriseContactUnitTest
{
    public class TheControllerTestBase : ControllerTestBase
    {
        protected static IMapper CreateMapper()
        {
            var config = new MapperConfiguration(cfg =>
            {
                cfg.AddProfile<VmMappingProfile>();
            });
            return config.CreateMapper();
        }
    }
}


<|start_filename|>src/ConfigMgmtApi/TheStartup.cs<|end_filename|>
﻿using Microsoft.Extensions.DependencyInjection;
using System;

namespace ConfigMgmt
{
    public static class TheStartup
    {
        public static void ConfigureServices(IServiceCollection services)
        {
            services.AddHttpClient<IBadgeApiClient, BadgeApiClient>();

            services.AddSingleton(RemotingClient.CreateClientAppService());
            services.AddSingleton(RemotingClient.CreateBizSystemAppService());
            services.AddSingleton(RemotingClient.CreateWorkbenchAppService());

            services.AddScoped<Func<Guid, IUserSettingAppService>>(u => RemotingClient.CreateUserSettingAppService);
            services.AddScoped<Func<Guid, IUserFavoriteAppService>>(u => RemotingClient.CreateUserFavoriteAppService);

        }
    }
}


<|start_filename|>src/ApiCommon/Filters/CheckTokenActionFilter.cs<|end_filename|>
﻿using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System;

namespace Common.Filters
{
    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
    public class CheckTokenAttribute : Attribute, IActionFilter
    {
        public void OnActionExecuted(ActionExecutedContext context)
        {
            //nothing
        }

        public void OnActionExecuting(ActionExecutingContext context)
        {
            if (context.HttpContext.Request.Headers.ContainsKey("token"))
            {
                var token = context.HttpContext.Request.Headers["token"];
                if (Guid.TryParse(token, out Guid tokenGuid)
                    && tokenGuid == new Guid("0<PASSWORD>-<PASSWORD>-<PASSWORD>-<PASSWORD>-<PASSWORD>"))
                    return;
            }
            context.Result = new BadRequestObjectResult("missing token or wrong token");
        }
    }
}


<|start_filename|>src/ConfigMgmtDomain/Enums.cs<|end_filename|>
﻿using System;

namespace ConfigMgmt
{
    /// <summary>
    /// 消息提醒方式
    /// </summary>
    [Flags]
    public enum MsgNotifyApproach
    {
        /// <summary>
        /// 通过TT客户端发送
        /// </summary>
        APP = 1,
        /// <summary>
        /// 通过短信发送
        /// </summary>
        SMS = 2
    }

    /// <summary>
    /// 移动平台客户端平台
    /// </summary>
    [Flags]
    public enum ClientPlatform
    {
        Desktop = 1,
        Android = 2,
        iOS = 4
    }

    /// <summary>
    /// 工作台应用跳转验证方式
    /// </summary>
    [Flags]
    public enum AppEntryAuthType
    {
        /// <summary>
        /// 不验证
        /// </summary>
        None = 0,
        /// <summary>
        /// 临时密码
        /// </summary>
        TempPassword = 1,
        /// <summary>
        /// 访问令牌
        /// </summary>
        AccessToken = 2
    }

    /// <summary>
    /// 工作台应用跳转验证方式
    /// </summary>
    [Flags]
    public enum ComponentDataSourceAuthType
    {
        /// <summary>
        /// 不验证
        /// </summary>
        None = 0,
        /// <summary>
        /// 使用OAuth2.0的ClientCredential验证方式
        /// </summary>
        ClientCredential = 1
    }
}


<|start_filename|>src/AttachmentApi/Services/IAttachmentStoreService.cs<|end_filename|>
﻿using Attachment.Entities;
using Attachment.ViewModels;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;

namespace Attachment.Services
{
    public interface IAttachmentStoreService
    {
        Task FillMemoryStreamAsync(AttachmentItem dto, MemoryStream ms);

        Task TryDeleteFileAsync(string objectName);

        Task<string> StoreFileAsync(UploadVm model, DateTimeOffset now);
    }
}


<|start_filename|>src/BizSystemNotifyService/EventBus/BizSystemNotifyMsgConsumer.cs<|end_filename|>
﻿using Base.Eap.Notify.MsgContracts;
using ConfigMgmt;
using ConfigMgmt.Entities;
using EnterpriseContact.Services;
using MassTransit;
using Nest;
using Notification;
using Serilog;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace BizSystemNotifyService.EventBus
{
    public class BizSystemNotifyMsgConsumer : IConsumer<BizSystemNotifyMsg>
    {
        private readonly IBizSystemAppService _bizSystemAppService;
        private readonly Func<Guid, INotifySessionActor> _notifySessionActorFactory;
        private readonly IEmployeeCacheService _employeeCacheService;
        private readonly ILogger _logger;

        public BizSystemNotifyMsgConsumer(IBizSystemAppService bizSystemAppService,
           Func<Guid, INotifySessionActor> notifySessionActorFactory,
           IEmployeeCacheService employeeCacheService)
        {
            _bizSystemAppService = bizSystemAppService;
            _notifySessionActorFactory = notifySessionActorFactory;
            _employeeCacheService = employeeCacheService;
            _logger = Log.ForContext<BizSystemNotifyMsgConsumer>();
        }

        private BizSystemNotifyMsg msg;
        private MsgNotifyBizSystem bizSystem;

        public async Task Consume(ConsumeContext<BizSystemNotifyMsg> context)
        {
            if (string.IsNullOrEmpty(context.Message.SystemId))
                throw new ArgumentNullException(nameof(context.Message.SystemId));

            msg = context.Message;

            bizSystem = await _bizSystemAppService.GetByIdForNotifyMsgAsync(msg.SystemId);

            if (bizSystem == null)
            {
                _logger.Warning("BizSystem is not found: {BizSystemId}", msg.SystemId);
                return;
            }

            if (bizSystem.Approachs == null || bizSystem.Approachs.Length == 0) return;

            if (bizSystem.UseAllApproachs)
            {
                foreach (var approach in bizSystem.Approachs)
                {
                    await ProcessApproachAsync(context, approach);
                }
            }
            else
            {
                var approach = bizSystem.Approachs[0];
                await ProcessApproachAsync(context, approach);
            }

            /* 1 get bizsystem
             * 2 approach is APP:
             * 2.1 get userid for receiver
             * 2.2 get session actor for user
             * 2.3 pushnotify into actor
             * 3 approach is SMS:
             * 3.1 get mobile for receiver
             * 3.2 send sms msg into CEF queue
             * 4 user is online
             * 4.1 send notify into notify queue
             * 5 user is offline
             * 5.1 get device
             * 5.1 send push into push queue
             * 6 notify queue get msg
             * 6.1 send via SignalR
             * 7 push queue get msg
             * 7.1 send via Azure Notification Hubs
            */
        }

        private async Task ProcessApproachAsync(ISendEndpointProvider sendEndpoint, MsgNotifyApproach approach)
        {
            if (approach == MsgNotifyApproach.APP)
            {
                var actors = await GetUserSessionActorsAsync();
                var notify = CreateNotify();
                var tasks = actors.Select(o => o.PushEventNotifyAsync(notify));
                await Task.WhenAll(tasks);
            }
            else if (approach == MsgNotifyApproach.SMS)
            {
                throw new NotSupportedException("SMS notification coming soon");
                string[] numbers = GetUserMobileNumbers();
                var sms = CreateSms();
                await sendEndpoint.Send(sms);
            }
        }

        private SendNotifyCommandImpl CreateSms()
        {
            throw new NotImplementedException();
        }

        private string[] GetUserMobileNumbers()
        {
            throw new NotImplementedException();
        }

        private EventNotifyDto CreateNotify()
        {
            var dto = new EventNotifyDto
            {
                Target = NotifyTargetType.BizSystem,
                TargetId = msg.SystemId,
                TargetCategory = msg.Category,
                Created = DateTimeOffset.UtcNow,
                Title = bizSystem.Name
            };
            if (string.IsNullOrEmpty(msg.Content) && bizSystem.PatternOfCategory.ContainsKey(msg.Category))
                dto.Text = CreateTextFromPattern(bizSystem.PatternOfCategory[msg.Category], msg.Parameters);
            else
            {
                dto.Text = msg.Content;
                dto.Parameters = msg.Parameters;
            }
            return dto;
        }

        private string CreateTextFromPattern(string pattern, Dictionary<string, string> parameters)
        {
            foreach (var kvp in parameters)
            {
                var p = "$(" + kvp.Key + ")";
                pattern = pattern.Replace(p, kvp.Value);
            }
            return pattern;
        }

        private async Task<IEnumerable<INotifySessionActor>> GetUserSessionActorsAsync()
        {
            var userIds = msg.ReceiverUserIds?.Length > 0
                ? msg.ReceiverUserIds
                : msg.ReceiverNumbers?.Length > 0
                    ? (await _employeeCacheService.ByNumberAsync(msg.ReceiverNumbers)).Where(o => o.UserId.HasValue).Select(o => o.UserId.Value)
                    : new Guid[0];
            return userIds.Select(o => _notifySessionActorFactory(o));
        }
    }

    public class SendNotifyCommandImpl : SendNotifyCommand
    {
        public string[] PhoneNumbers { get; set; }
        public string TemplateName { get; set; }
        public Dictionary<string, string> TemplateParam { get; set; }
    }

    interface SendNotifyCommand
    {
        string[] PhoneNumbers { get; set; }
        string TemplateName { get; set; }
        Dictionary<string, string> TemplateParam { get; set; }
    }
}


<|start_filename|>src/ServiceCommon/ActorEapBase.cs<|end_filename|>
﻿using Microsoft.ServiceFabric.Actors;
using Microsoft.ServiceFabric.Actors.Runtime;
using System;
using System.Collections.Generic;
using System.Text;

namespace Common
{
    public class ActorEapBase : Actor
    {
        protected ActorEapBase(
            ActorServiceEapBase actorService,
            ActorId actorId)
            : base(actorService, actorId)
        {
            ActorService = actorService;
        }

        public new ActorServiceEapBase ActorService { get; }
    }
}


<|start_filename|>test/ConversationServiceTest/AppServices/ArchiveGroupQueueProcessorTest.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using ConversationMsgStateService;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using UnitTestCommon;

namespace ConversationServiceTest.AppServices
{
    [TestClass]
    public class ArchiveGroupQueueProcessorTest : TheAppServiceTestBase
    {
        //[TestMethod]
        //public async Task ProcessAsync_Test()
        //{
        //    //Arrage:
        //    await InitDicAsync();
        //    await InitMessageQueueAsync();
        //    await InitArchivedAndPendingMessageQueueAsync();
        //    var source = new CancellationTokenSource();
        //    var mock_config = new Mock<IProcessorConfigurator>();
        //    mock_config.Setup(u => u.IsArchiveGroupProcessTime()).Returns(true);

        //    //Act:
        //    ThreadPool.QueueUserWorkItem(u =>
        //    {
        //        ArchiveGroupQueueProcessor.ProcessAsync(stateManager, mock_config.Object, source.Token).GetAwaiter()
        //            .GetResult();
        //    });
        //    Thread.Sleep(3000);
        //    source.Cancel();
        //    Thread.Sleep(3000);
        //    //Assert:
        //    using (var tx = stateManager.CreateTransaction())
        //    {
        //        var pendingCount = await pendingArchiveQueue.GetCountAsync(tx);
        //        Assert.AreEqual(0, pendingCount);
        //    }
        //}
    }
}


<|start_filename|>src/EnterpriseContactDomain/MsgConstracts/GroupEvent.cs<|end_filename|>
﻿using Notification;
using System;
using System.Collections.Generic;
using System.Text;

namespace EnterpriseContact.MsgConstracts
{
    public abstract class BaseGroupEvent
    {
        public Guid Id { get; set; }
    }

    public class GroupAdded : BaseGroupEvent
    {
    }

    public class GroupMembersUpdated : BaseGroupEvent
    {
    }

    public class GroupNotified : BaseGroupEvent
    {
        public List<EventNotifyDto> Notifies { get; set; }
    }

    public class GroupDeleted : BaseGroupEvent
    {
    }
}


<|start_filename|>test/ConversationServiceTest/DomainServices/ConversationManagerTest.cs<|end_filename|>
﻿using ConversationCtrlStateService;
using ConversationCtrlStateService.DomainServices;
using InstantMessage;
using InstantMessage.Entities;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using UnitTestCommon;

namespace ConversationServiceTest.DomainServices
{
    [TestClass]
    public class ConversationManagerTest: ManagerTestBase
    {
        [TestMethod]
        public async Task AddOrUpdateAsync()
        {
            var target = new ConversationManager(stateManager);

            var id = Guid.NewGuid();

            await target.AddOrUpdateAsync(id, 
                new List<Guid> { Guid.NewGuid(), Guid.NewGuid(), }, 
                ConversationType.CustomGroup);

            //Assert:
            using (var tx = stateManager.CreateTransaction())
            {
                var dictIdToEntity = await Service.GetListDictAsync(stateManager);

                var idToEntity = await dictIdToEntity.TryGetValueAsync(tx, id);
                var conversation = idToEntity.Value;
                Assert.IsTrue(idToEntity.HasValue);
                Assert.IsTrue(conversation.Type == ConversationType.CustomGroup);
                Assert.IsTrue(conversation.Participants.Count == 2);
            }
        }
    }
}


<|start_filename|>src/ConfigMgmtDomain/AppServices/IClientAppService.cs<|end_filename|>
﻿using ConfigMgmt.Entities;
using Microsoft.ServiceFabric.Services.Remoting;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;

namespace ConfigMgmt
{
    /// <summary>
    /// 移动平台客户端AppService
    /// </summary>
    public interface IClientAppService : IService
    {
        Task<bool> AddOrUpdateClientAsync(Client input);
    }
}


<|start_filename|>src/EnterpriseContactService/Entities/DataSourceType.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EnterpriseContactService.Entities
{
    public enum DataSourceType
    {
        MDM = 0,

        Manual = 1
    }
}


<|start_filename|>src/ApiGateway/NotifyStartup.cs<|end_filename|>
﻿using Microsoft.Extensions.DependencyInjection;
using Notification;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace ApiGateway
{
    public static class NotifyStartup
    {
        public static void ConfigureServices(IServiceCollection services)
        {
            services.AddScoped<Func<Guid, INotifySessionActor>>(u => RemotingClient.CreateNotifySessionActor);
        }
    }
}


<|start_filename|>src/EnterpriseContactDomain/Dtos/GroupMemberOutput.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EnterpriseContact
{
    [Serializable]
    public class GroupMemberOutput
    {
        public Guid EmployeeId { get; set; }

        public string EmployeeName { get; set; }

        public int Gender { get; set; }

        public bool IsOwner { get; set; }
        
        public Guid PrimaryDepartmentId { get; set; }

        public DateTimeOffset Joined { get; set; }
    }
}


<|start_filename|>src/ConversationCtrlStateService/ConversationCtrlAppService.cs<|end_filename|>
﻿using InstantMessage;
using InstantMessage.Entities;
using Microsoft.ServiceFabric.Data;
using Microsoft.ServiceFabric.Data.Collections;
using ServiceFabricContrib;
using System;
using System.Collections.Generic;
using System.Fabric;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using Common;
using Notification;
using EnterpriseContact;
using ConversationCtrlStateService.DomainServices;

namespace ConversationCtrlStateService
{
    public class ConversationCtrlAppService : StatefulRemotingService, IConversationCtrlAppService
    {
        private readonly IEmployeeAppService _employeeAppService;
        private readonly ConversationManager _manager;

        public ConversationCtrlAppService(StatefulServiceContext serviceContext,
            IReliableStateManager stateManager,
            IEmployeeAppService employeeAppService) : base(serviceContext, stateManager)
        {
            _employeeAppService = employeeAppService;
            _manager = new ConversationManager(stateManager);
        }

        public async Task DeleteAsync(Guid id) => await _manager.DeleteAsync(id);

        public async Task<Conversation> GetOrAddP2PAsync(Guid senderId, Guid receiverId)
        {
            var dictKeyToId = await Service.GetKeyToIdDictAsync(StateManager);
            var dictIdToEntity = await Service.GetListDictAsync(StateManager);
            var key = GenerateConversationKey(senderId, receiverId);
            using (var tx = StateManager.CreateTransaction())
            {
                var newId = Guid.NewGuid();
                var id = await dictKeyToId.GetOrAddAsync(tx, key, newId);
                var newEntity = new Conversation
                {
                    Id = id,
                    Key = key,
                    Type = ConversationType.P2P,
                    Participants = new List<Guid> { senderId, receiverId },
                    Updated = DateTimeOffset.UtcNow
                };
                var entity = await dictIdToEntity.GetOrAddAsync(tx, id, newEntity);
                await tx.CommitAsync();
                return entity;
            }
        }

        public async Task<Conversation?> GetByIdAsync(Guid id) => await _manager.GetByIdAsync(id);

        public static string GenerateConversationKey(Guid senderId, Guid receiverId)
        {
            var lst = new List<string>
            {
                senderId.ToString("N"),
                receiverId.ToString("N")
            };
            lst.Sort();
            var source = string.Join(",", lst);
            return source.ComputeMd5();
        }

        public async Task<RemotingResult> AddDepAsync(AddDepConversationInput input)
        {
            var userIds = await _employeeAppService.GetUserIdsByDepartmentIdAsync(input.DepartmentId);

            if (!userIds.Contains(input.UserId))
                return RemotingResult.Fail(1);
            if (userIds.Count < 2)
                return RemotingResult.Fail(2);

            await _manager.AddOrUpdateAsync(input.DepartmentId, userIds, ConversationType.DepartmentGroup);
            return RemotingResult.Success();
        }

        public async Task<List<Conversation>> GetByIdsAsync(List<Guid> ids)
        {
            var result = new List<Conversation>();
            var dictIdToEntity = await Service.GetListDictAsync(StateManager);
            using (var tx = StateManager.CreateTransaction())
            {
                foreach (var id in ids)
                {
                    var entityWrap = await dictIdToEntity.TryGetValueAsync(tx, id);
                    if (entityWrap.HasValue)
                        result.Add(entityWrap.Value);
                }
            }
            return result;
        }
    }
}


<|start_filename|>src/EnterpriseContactDomain/Dtos/EmployeeListOutput.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Text;

namespace EnterpriseContact
{
    [Serializable]
    public class EmployeeListOutput
    {
        public Guid Id { get; set; }

        public string Name { get; set; }

        public int Gender { get; set; }

        public string Avatar { get; set; }

        public Guid PrimaryDepartmentId { get; set; }

        public Guid PrimaryPositionId { get; set; }

        public string PositionName { get; set; }

        public Guid? PositionId { get; set; }
    }
}


<|start_filename|>emulator/ClientEmulator/MainForm.cs<|end_filename|>
﻿using IdentityModel.Client;
using RestSharp;
using RestSharp.Authenticators;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows;
using System.Diagnostics;
using System.Net.Http.Headers;
using System.Net.Http;
using System.Threading;
using Newtonsoft.Json;
using MassTransit;
using Base.Eap.Notify.MsgContracts;

namespace ClientEmulator
{
    public partial class MainForm : Form
    {

        private string UploadFilePath;
        private string DownloadPath;
        SynchronizationContext m_SyncContext = null;
        public MainForm()
        {
            InitializeComponent();
            m_SyncContext = SynchronizationContext.Current;
            Load += MainForm_Load;
        }

        private void MainForm_Load(object sender, EventArgs e)
        {
            var folder = Directory.GetCurrentDirectory();
            UploadFilePath = Directory.CreateDirectory(folder).Parent.Parent.FullName;
            DownloadPath = UploadFilePath;
            textLocation.Text = UploadFilePath + "\\icon_master.png";
            textGroupId.Text = "59624bf1-6566-4a5a-8b6f-a65fe2cc3f2e";
            TextFileId.Text = "7dab0af0-2e04-49bf-84ca-44113ae8d660";
            textBaseAddress.Text = "http://localhost:10121/api/groupfile";
            System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false;
            JsonConvert.DefaultSettings = () =>
            {
                var setting = new JsonSerializerSettings();
                setting.Formatting = Formatting.Indented;
                return setting;
            };
        }

        private string accessToken;
        private async void btnLogin_ClickAsync(object sender, EventArgs e)
        {
            var response = await RequestTokenAsync(txtAuthority.Text, txtUsername.Text, txtPassword.Text);
            response.Show();
            if (!response.IsError)
            {
                accessToken = response.AccessToken;
                txtAccessToken.Text = accessToken;
                MessageBox.Show("登录成功");
            }
            else
                MessageBox.Show("登录失败： " + response.Error);
        }

        #region Utility
        static async Task<TokenResponse> RequestTokenAsync(string authority, string username, string pwd)
        {
            var discoClient = new DiscoveryClient(authority)
            {
                Policy = new DiscoveryPolicy
                {
                    RequireHttps = false
                }
            };
            var disco = await discoClient.GetAsync();// await DiscoveryClient.GetAsync(Authority);
            if (disco.IsError) throw new Exception(disco.Error);

            var client = new TokenClient(
                disco.TokenEndpoint,
                "OM_TT_Desktop_001",
                "tt<PASSWORD>");
            return await client.RequestResourceOwnerPasswordAsync(username, pwd,
                "openid phone profile profile.ext sso.sts eap.api");
        }
        #endregion

        private RestClient CreateRestClient()
        {
            var client = new RestClient(txtAuthority.Text);
            client.Authenticator = new OAuth2AuthorizationRequestHeaderAuthenticator(accessToken, "Bearer");
            return client;
        }

        private async void btnTempToken_Click(object sender, EventArgs e)
        {
            var client = CreateRestClient();

            var request = new RestRequest("api/TempToken", Method.GET);

            var response = await client.ExecuteTaskAsync<string>(request);
            if (response.IsSuccessful)
            {
                MessageBox.Show("请求成功");
                txtTempToken.Text = response.Data;
            }
            else
                MessageBox.Show($"请求失败： {response.ErrorMessage}, HttpStatusCode: {response.StatusCode} ");
        }

        private void tabPage3_Click(object sender, EventArgs e)
        {

        }


        private void buttonChoose_Click(object sender, EventArgs e)
        {
            using (OpenFileDialog openFileDialog = new OpenFileDialog())
            {
                openFileDialog.Filter = "AllFiles|*.*";
                if (openFileDialog.ShowDialog() == DialogResult.OK)
                {
                    //Get the path of specified file
                    textLocation.Text = openFileDialog.FileName;
                }
            }
        }

        private void buttonUpload_Click(object sender, EventArgs e)
        {
            var uploadUrl = textBaseAddress.Text.Trim() + "/upload";
            if (Guid.TryParse(textGroupId.Text.ToString(), out Guid groupId))
            {
                var param = new UploadParam()
                {
                    MD5 = Guid.NewGuid().ToString("N"),
                    GroupId = groupId,
                };
                var location = textLocation.Text.ToString();
                Task.Run(async () => await UploadFileAsync(uploadUrl, location, param));
            }
            else
            {
                UpdateUploadMsg("群组Id不正确");
            }
        }

        /// <summary>
        /// 上传文件
        /// </summary>
        /// <param name="url"></param>
        /// <param name="path"></param>
        /// <returns></returns>
        public async Task<string> UploadFileAsync(string url, string path, UploadParam fileParam)
        {
            Debug.WriteLine("---------------------------------------");
            Debug.WriteLine("URL = " + url);
            //Debug.WriteLine("Content = " + JsonConvert.SerializeObject(fileParam));
            Debug.WriteLine("---------------------------------------");
            var uploadMessage = "";
            try
            {
                using (var client = new HttpClient())
                {
                    client.SetBearerToken(accessToken);
                    using (var content = new MultipartFormDataContent("Upload----" + DateTime.Now.Ticks.ToString("x")))
                    {
                        using (var stream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read))
                        {

                            content.Add(new StringContent(fileParam.GroupId.ToString()), "GroupId");
                            content.Add(new StringContent(fileParam.MD5.ToString()), "MD5");
                            var streamContent = new StreamContent(stream);
                            streamContent.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
                            streamContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data")
                            {
                                Name = "file", //上传文件的name标识,用于action参数构造
                                FileName = Path.GetFileName(path), //上传的文件的名称
                            };
                            content.Add(streamContent);
                            try
                            {
                                client.DefaultRequestHeaders.Accept.Clear();
                                //client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

                                client.DefaultRequestHeaders
                                    .Accept
                                    .Add(new MediaTypeWithQualityHeaderValue("application/json"));
                                client.DefaultRequestHeaders.Connection.Add("Keep-Alive");
                                using (var httpResponseMessage = await client.PostAsync(url, content))
                                {
                                    var responseContent = "";
                                    if (httpResponseMessage.IsSuccessStatusCode)
                                    {
                                        responseContent = await httpResponseMessage.Content.ReadAsStringAsync();
                                        uploadMessage = " UploadFileAsync " + httpResponseMessage.StatusCode + "\n" + responseContent;
                                    }
                                    else
                                    {
                                        uploadMessage = " UploadFileAsync Failed " + httpResponseMessage.StatusCode;
                                    }
                                    Debug.WriteLine(uploadMessage);
                                    UpdateUploadMsg(uploadMessage);
                                    return responseContent;
                                }
                            }
                            catch (Exception e)
                            {
                                uploadMessage = "UploadFileAsync Exception === " + e.Message;
                                Debug.WriteLine(uploadMessage);
                                UpdateUploadMsg(uploadMessage);
                                return "";
                            }
                        }
                    }
                }
            }
            catch (Exception e)
            {
                uploadMessage = "UploadFileAsync Exception === " + e.Message;
                Debug.WriteLine(uploadMessage);
                UpdateUploadMsg(uploadMessage);
                return "";
            }
        }

        private void UpdateUploadMsg(string msg)
        {
            m_SyncContext.Post((message) =>
            {
                TextUploadMsg.Text = msg;
            }, msg);
        }

        private void UpdateDownloadMsg(string msg)
        {
            m_SyncContext.Post((message) =>
            {
                TextDownloadMsg.Text = message.ToString();
            }, msg);
        }
        private void buttonDownload_Click(object sender, EventArgs e)
        {
            DownloadFile(TextFileId.Text);
        }

        private void DownloadFile(string fileId)
        {
            var filename = "download.png";
            var filePath = DownloadPath + "\\" + filename;
            var downloadUrl = textBaseAddress.Text.Trim() + "/download/{0}";

            try
            {
                using (var client = new HttpClient())
                {
                    try
                    {
                        FileStream fs;
                        client.SetBearerToken(accessToken);
                        var bytes = client.GetByteArrayAsync(string.Format(downloadUrl, fileId)).Result;

                        if (File.Exists(filePath))
                        {
                            File.Delete(filePath);
                        }
                        fs = new System.IO.FileStream(filePath, System.IO.FileMode.CreateNew);
                        fs.Write(bytes, 0, bytes.Length);
                        fs.Close();
                        if (File.Exists(filePath))
                        {
                            UpdateDownloadMsg(" download success " + filePath);
                        }
                        else
                        {
                            UpdateDownloadMsg(" download failed file not exist " + filePath);
                        }
                    }
                    catch (Exception e)
                    {
                        UpdateDownloadMsg(" download failed " + e.Message);
                    }
                }
            }
            catch (Exception e)
            {
                UpdateDownloadMsg(" download failed " + e.Message);
            }
        }

        private void BtnAttachment_SelectFile_Click(object sender, EventArgs e)
        {
            using (OpenFileDialog openFileDialog = new OpenFileDialog())
            {
                openFileDialog.Filter = "AllFiles|*.*";
                if (openFileDialog.ShowDialog() == DialogResult.OK)
                {
                    txtAttachment_FilePath.Text = openFileDialog.FileName;
                }
            }
        }

        private void BtnAttachment_UploadFile_Click(object sender, EventArgs e)
        {
            Debug.WriteLine("---------------------------------------");
            var fakedMd5 = Guid.NewGuid().ToString();
            Debug.WriteLine("fakedMd5:" + fakedMd5);
            var uploadMessage = "";
            using (var client = new HttpClient())
            {
                client.BaseAddress = new Uri(txtBaseAddress.Text);
                client.SetBearerToken(accessToken);
                using (var content = new MultipartFormDataContent("Upload----" + DateTime.Now.Ticks.ToString("x")))
                {
                    using (var stream = File.Open(txtAttachment_FilePath.Text, FileMode.Open, FileAccess.Read, FileShare.Read))
                    {
                        content.Add(new StringContent(txtAttachment_ContextId.Text), "ContextId");
                        content.Add(new StringContent(fakedMd5), "MD5");
                        var streamContent = new StreamContent(stream);
                        streamContent.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
                        streamContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data")
                        {
                            Name = "file", //上传文件的name标识,用于action参数构造
                            FileName = Path.GetFileName(txtAttachment_FilePath.Text), //上传的文件的名称
                        };
                        content.Add(streamContent);
                        client.DefaultRequestHeaders.Accept.Clear();
                        //client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

                        client.DefaultRequestHeaders
                            .Accept
                            .Add(new MediaTypeWithQualityHeaderValue("application/json"));
                        client.DefaultRequestHeaders.Connection.Add("Keep-Alive");

                        var task = Task.Run(async () =>
                        {
                            using (var httpResponseMessage = await client.PostAsync("attachment", content))
                            {
                                var responseContent = "";
                                if (httpResponseMessage.IsSuccessStatusCode)
                                {
                                    responseContent = await httpResponseMessage.Content.ReadAsStringAsync();
                                    uploadMessage = " UploadFileAsync " + httpResponseMessage.StatusCode + "\n" + responseContent;
                                }
                                else
                                {
                                    uploadMessage = " UploadFileAsync Failed " + httpResponseMessage.StatusCode;
                                }
                            }
                        });

                        Task.WaitAny(task);
                        MessageBox.Show(uploadMessage);
                    }
                }
            }

        }

        private void button1_Click(object sender, EventArgs e)
        {
            var form = new IMChatForm();
            form.Show();
        }

        private IBusControl _bus;
        private void BtnConnectRabbitMq_Click(object sender, EventArgs e)
        {
            var address = txtRabbitMqAddress.Text.EndsWith("/") ? txtRabbitMqAddress.Text : txtRabbitMqAddress.Text + "/";
            var userName = txtRabbitMqUsername.Text;
            var passwrod = txtRabbitMqPassword.Text;

            if (string.IsNullOrEmpty(address) || string.IsNullOrEmpty(userName) || string.IsNullOrEmpty(passwrod))
            {
                MessageBox.Show("请输入正确的数据!");
                return;
            }

            try
            {
                _bus = Bus.Factory.CreateUsingRabbitMq(cfg =>
                {
                    var host = cfg.Host(new Uri(address), h =>
                    {
                        h.Username(userName);
                        h.Password(<PASSWORD>);
                    });
                });

                var endpointUri = new Uri(address + BizSystemNotifyMsg.RabbitMqReceiveEndpointName);

                EndpointConvention.Map<BizSystemNotifyMsg>(endpointUri);

                MessageBox.Show("连接成功");
            }
            catch (Exception ex)
            {
                Debug.Write(ex.ToString());
                MessageBox.Show("启动失败,请检查工作环境");
            }
        }

        private void BtnSendNotifyMsg_Click(object sender, EventArgs e)
        {
            _bus.Send(new BizSystemNotifyMsg
            {
                SystemId = txtNotifyBizSystemId.Text,
                Category = int.Parse(txtNotifyMsgCategory.Text),
                Content = txtNotifyMsgContent.Text,
                ReceiverUserIds = txtNotifyMsgUserIds.Text.Split(',').Select(x => new Guid(x)).ToArray()
            }) ;

            MessageBox.Show("发送成功");
        }
    }
}


<|start_filename|>src/InstantMessageDomain/Dtos/AddDepConversationInput.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Text;

namespace InstantMessage
{
    [Serializable]
    public class AddDepConversationInput
    {
        public Guid UserId { get; set; }

        public Guid DepartmentId { get; set; }
    }
}


<|start_filename|>src/NotifyGatewayWeb/EventBus/SendEventNotifyConsumer.cs<|end_filename|>
﻿using MassTransit;
using Microsoft.AspNetCore.SignalR;
using Notification.MsgContracts;
using NotifyGatewayWeb.Hubs;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace NotifyGatewayWeb.EventBus
{
    public class SendEventNotifyConsumer : IConsumer<SendEventNotify>
    {
        private readonly IHubContext<NotifyHub, INotifyClient> _hubContext;

        public SendEventNotifyConsumer(IHubContext<NotifyHub, INotifyClient> hubContext)
        {
            _hubContext = hubContext;
        }

        public async Task Consume(ConsumeContext<SendEventNotify> context)
        {
            try
            {
                await _hubContext.Clients.User(context.Message.UserId.ToString()).ReceiveEventNotify(context.Message.Notifies);
            }
            catch (Exception ex)
            {
                ServiceEventSource.Current.Message($"SendEventNotifyConsumer Error: UserId: {context.Message.UserId}, Message: {ex.Message}");
            }
        }
    }
}


<|start_filename|>src/AppComponentApi/Controllers/TodoComponentController.cs<|end_filename|>
﻿using Common;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using AppComponent.ViewModels;
using ConfigMgmt;
using ConfigMgmt.Entities;
using Microsoft.AspNetCore.Http;
using Serilog;

namespace AppComponent.Controllers
{
    [Route("api/v1/appcom/todo")]
    public class TodoComponentController : EapBaseController
    {
        private readonly ITodoApiClient _todoApiClient;
        private readonly IWorkbenchAppService _workbenchAppService;
        private readonly ILogger _logger;

        public TodoComponentController(ITodoApiClient todoApiClient, IWorkbenchAppService workbenchAppService)
        {
            _todoApiClient = todoApiClient;
            _workbenchAppService = workbenchAppService;
            _logger = Log.ForContext<TodoComponentController>();
        }

        [Route("Pending")]
        [ProducesResponseType(StatusCodes.Status200OK)]
        [ProducesResponseType(StatusCodes.Status404NotFound)]
        public async Task<ActionResult<List<TodoListVM>>> GetPendingAsync(Guid appId)
        {
            try
            {
                var config = await _workbenchAppService.GetComponentConfigAsync(appId);
                if (config == null)
                    return NotFound(new { message = "componentConfigId Not Found" });
                var list = await _todoApiClient.GetPendingListAsync(config);
                return list;
            }
            catch (Exception ex)
            {
                return BadRequest(LogError(_logger, ex));
            }
        }

        [Route("Done")]
        [ProducesResponseType(StatusCodes.Status200OK)]
        [ProducesResponseType(StatusCodes.Status404NotFound)]
        public async Task<ActionResult<List<TodoListVM>>> GetDoneAsync(Guid appId)
        {
            try
            {
                var config = await this._workbenchAppService.GetComponentConfigAsync(appId);
                if (config == null)
                    return NotFound(new { message = "ComponentConfigId Not Found" });
                var list = await _todoApiClient.GetDoneListAsync(config);
                return list;
            }
            catch (Exception ex)
            {
                return BadRequest(LogError(_logger, ex));
            }
        }
    }
}


<|start_filename|>src/ConfigMgmtDomain/Entities/Client.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Text;

namespace ConfigMgmt.Entities
{
    /// <summary>
    /// 移动平台客户端，可以多个客户端实例
    /// </summary>
    [DataContract]
    public class Client
    {
        [DataMember]
        public string Id { get; set; }

        [DataMember]
        public string Name { get; set; }

        /// <summary>
        /// 支持的客户端平台
        /// </summary>
        [DataMember]
        public ClientPlatform SupportPlatforms { get; set; }

        [DataMember]
        public DateTimeOffset Created { get; set; }

        [DataMember]
        public DateTimeOffset? Modified { get; set; }

        [DataMember]
        public DateTimeOffset? Deleted { get; set; }

        [DataMember]
        public List<Guid> AppEntranceIds { get; set; }

        [DataMember]
        public DateTimeOffset? WorkbenchConfigTimestamp { get; set; }

        public const string DefaultId = "default";

        public static Client Default()
        {
            return new Client
            {
                Id = DefaultId,
                Name = "默认",
                SupportPlatforms = ClientPlatform.Android | ClientPlatform.Desktop | ClientPlatform.iOS,
                Created = DateTimeOffset.UtcNow,
                AppEntranceIds = new List<Guid>()
            };
        }
    }
}


<|start_filename|>src/ConfigMgmtDomain/AppServices/IUserSettingAppService.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.ServiceFabric.Services.Remoting;

namespace ConfigMgmt
{
    public interface IUserSettingAppService : IService
    {
        Task<InfoVisibility> GetInfoVisibilityAsync(Guid userId);

        Task SetInfoVisibilityAsync(Guid userId, InfoVisibility value);

        /// <summary>
        /// 更新用户的应用入口验证状态
        /// </summary>
        /// <param name="updateAppEntranceAuthStateInput"></param>
        /// <returns></returns>
        Task SetAppEntranceAuthStateAsync(Guid userId, AppEntranceAuthStateInput updateAppEntranceAuthStateInput);
        
        /// <summary>
        /// 得到用户应用入口验证状态
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="deviceCode"></param>
        /// <param name="appEntranceId"></param>
        /// <returns>true需要验证，false不需要验证</returns>
        Task<bool> GetAppEntranceAuthStateAsync(Guid userId, string deviceCode, Guid appEntranceId);
    }
}


<|start_filename|>src/InstantMessageApi/ViewModels/MessageVm.cs<|end_filename|>
﻿using InstantMessage.Entities;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Text;

namespace InstantMessage.ViewModels
{
    public class MessageVm
    {
        /// <summary>
        /// 会话id
        /// </summary>
        public Guid ConversationId { get; set; }
        /// <summary>
        /// 发送时间
        /// </summary>
        public DateTimeOffset SendTime { get; set; }
        /// <summary>
        /// 消息Id
        /// </summary>
        public Guid Id { get; set; }
        /// <summary>
        /// 发送者id
        /// </summary>
        public Guid SenderId { get; set; }
        /// <summary>
        /// 发送者姓名
        /// </summary>
        public string SenderName { get; set; }
        /// <summary>
        /// 发送者性别
        /// </summary>
        public int SenderGender { get; set; }
        /// <summary>
        /// 消息内容 json
        /// </summary>
        public string Message { get; set; }

        /// <summary>
        /// 消息类型
        /// </summary>
        public ConversationMsgType Type { get; set; }

        public static MessageVm Create(ConversationMsg msg)
        {
            var model = new MessageVm
            {
                Id = msg.Id,
                SendTime = msg.Time,
                Type = msg.Type,
                SenderId = msg.SenderId,
                ConversationId = msg.ConversationId
            };
            switch (msg.Type)
            {
                case ConversationMsgType.Text:
                    model.Message = JsonConvert.SerializeObject(new TextMessageVm { Content = msg.Content });
                    break;
                default:
                    model.Message = msg.Content;
                    break;
            }
            return model;
        }

        public MessageVm SetSenderInfo(string senderName, int senderGender)
        {
            SenderName = senderName;
            SenderGender = senderGender;
            return this;
        }
    }
}


<|start_filename|>src/SystemConfigStateService/BizSystemAppService.cs<|end_filename|>
﻿using ConfigMgmt;
using ConfigMgmt.Entities;
using Microsoft.ServiceFabric.Data;
using Microsoft.ServiceFabric.Data.Collections;
using ServiceFabricContrib;
using System;
using System.Collections.Generic;
using System.Fabric;
using System.Threading;
using System.Threading.Tasks;

namespace SystemConfigStateService
{
    public class BizSystemAppService : StatefulRemotingService, IBizSystemAppService
    {
        public BizSystemAppService(StatefulServiceContext serviceContext, IReliableStateManager stateManager) :
            base(serviceContext, stateManager)
        {
        }

        public async Task<bool> AddOrUpdateForMsgNotifyAsync(MsgNotifyBizSystem input)
        {
            if (input == null)
                throw new ArgumentNullException(nameof(input));

            var dict = await StateManager.GetOrAddAsync<IReliableDictionary2<string, MsgNotifyBizSystem>>(Service.DictionaryName_MsgNotifyBizSystem);
            using (var tx = StateManager.CreateTransaction())
            {
                await dict.AddOrUpdateAsync(tx, input.Id, input, (k, v) => input);

                await tx.CommitAsync();
                return true;
            }
        }

        public async Task<bool> AddOrUpdateForTodoCenterAsync(TodoCenterBizSystem input)
        {
            if (input == null)
                throw new ArgumentNullException(nameof(input));

            var dict = await StateManager.GetOrAddAsync<IReliableDictionary2<string, TodoCenterBizSystem>>(Service.DictionaryName_TodoCenterBizSystem);
            using (var tx = StateManager.CreateTransaction())
            {
                await dict.AddOrUpdateAsync(tx, input.Id, input, (k, v) => input);

                await tx.CommitAsync();
                return true;
            }
        }

        public async Task<List<MsgNotifyBizSystem>> GetAllForMsgNotifyAsync()
        {
            var token = CancellationToken.None;
            var dict = await StateManager.GetOrAddAsync<IReliableDictionary2<string, MsgNotifyBizSystem>>(Service.DictionaryName_MsgNotifyBizSystem);
            using (var tx = StateManager.CreateTransaction())
            {
                return await dict.GetAllAsync(tx, token);
            }
        }

        public async Task<List<TodoCenterBizSystem>> GetAllForTodoCenterAsync()
        {
            var token = CancellationToken.None;
            var dict = await StateManager.GetOrAddAsync<IReliableDictionary2<string, TodoCenterBizSystem>>(Service.DictionaryName_TodoCenterBizSystem);
            using (var tx = StateManager.CreateTransaction())
            {
                return await dict.GetAllAsync(tx, token);
            }
        }

        public async Task<MsgNotifyBizSystem> GetByIdForNotifyMsgAsync(string id)
        {
            var token = CancellationToken.None;
            var dict = await StateManager.GetOrAddAsync<IReliableDictionary2<string, MsgNotifyBizSystem>>(Service.DictionaryName_MsgNotifyBizSystem);
            using (var tx = StateManager.CreateTransaction())
            {
                var wrap= await dict.TryGetValueAsync(tx, id);
                if (wrap.HasValue) return wrap.Value;
            }
            return null;
        }

        public async Task<bool> RemoveForMsgNotifyAsync(string id)
        {
            var dict = await StateManager.GetOrAddAsync<IReliableDictionary2<string, MsgNotifyBizSystem>>(Service.DictionaryName_MsgNotifyBizSystem);
            using (var tx = StateManager.CreateTransaction())
            {
                var result = await dict.TryRemoveAsync(tx, id);

                await tx.CommitAsync();
                return result.HasValue;
            }
        }

        public async Task<bool> RemoveForTodoCenterAsync(string id)
        {
            var dict = await StateManager.GetOrAddAsync<IReliableDictionary2<string, TodoCenterBizSystem>>(Service.DictionaryName_TodoCenterBizSystem);
            using (var tx = StateManager.CreateTransaction())
            {
                var result = await dict.TryRemoveAsync(tx, id);

                await tx.CommitAsync();
                return result.HasValue;
            }
        }
    }
}


<|start_filename|>test/GroupFileServiceTest/ControllerApi/GroupFileControllerTest.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EnterpriseContact;
using GroupFile;
using GroupFile.Controllers;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;

namespace GroupFileServiceTest.ControllerApi
{
    [TestClass]
    public class GroupFileControllerTest : TheGroupFileAppServiceTestBase
    {
        private const string Md5 = "xxxxxxx";
        private const string Location = "http://xxxx";

        [TestMethod]
        public async Task GetGroupFilesAsync_Test()
        {

            //var controller = new GroupFileController();
        }


        //private Mock<IControlAppService> MockControlAppService()
        //{
        //    var mock = new Mock<IControlAppService>();
        //    mock.Setup(u => u.CheckFileExistAsync(It.IsAny<string>())).ReturnsAsync(true);
        //    mock.Setup(u => u.DownloadFileAsync(It.IsAny<Guid>())).ReturnsAsync(Location);
        //    return mock;
        //}

        //private Mock<IFileStorageService> MockFileStorageService()
        //{
        //    return new Mock<IFileStorageService>();
        //}

        private Mock<IGroupAppService> MockGroupAppService()
        {
            return new Mock<IGroupAppService>();
        }

        private Mock<IEmployeeAppService> MockEmployeeAppService()
        {
            return new Mock<IEmployeeAppService>();
        }
    }
}


<|start_filename|>src/EnterpriseContactDomain/Constants.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Text;

namespace EnterpriseContact
{
    public static class Constants
    {
    }
}


<|start_filename|>src/GroupFileService/ServiceDbContextFactory.cs<|end_filename|>
﻿using GroupFile.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using System;
using System.Collections.Generic;
using System.Text;

namespace GroupFile
{
    public class ServiceDbContextFactory : IDesignTimeDbContextFactory<ServiceDbContext>
    {
        public ServiceDbContext CreateDbContext(string[] args)
        {
            var optionsBuilder = new DbContextOptionsBuilder<ServiceDbContext>();
            //optionsBuilder.UseSqlServer("name=GFSfApp_GroupFileDb");
            optionsBuilder.UseSqlServer(@"Data Source=(local)\SQLEXPRESS;Initial Catalog=EapSfApp_GroupFileDb;Integrated Security=True;MultipleActiveResultSets=true");

            return new ServiceDbContext(optionsBuilder.Options);
        }
    }
}


<|start_filename|>emulator/ClientEmulator/Config/BizUrlConfig.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ClientEmulator.Config
{
    public class BizUrlConfig
    {
        /// <summary>
        /// the url of create_conversation
        /// </summary>
        public const string Create_Conversation_POST = "/api/v1/im/p2p";

        /// <summary>
        /// the url of send_text
        /// </summary>
        public const string Send_Text_POST = "api/v1/im/message/sendText";

        /// <summary>
        /// the url of get_new_messages
        /// </summary>
        public const string GetNewMessageList_POST = "/api/v1/im/message/getNew";

        public const string GetMessageHistory_POST = "/api/v1/im/message/getHistory";
        public const string DeleteP2pConversation_DELETE = "/api/v1/im/p2p/{0}";

        public const string CreateGroup_POST = "/api/v1/group";
        public const string QuitGroup_PUT = "/api/v1/group/{0}/quit";
        public const string DeleteGroup_DELETE = "/api/v1/group/{0}";


        public const string PUll_TODO_GET = "/api/v1/appcom/todo/Pending";
        public const string PUll_DONE_GET = "/api/v1/appcom/todo/Done";

    }
}


<|start_filename|>emulator/ClientEmulator/LoginControl.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using ClientEmulator.Services;
using ClientEmulator.Services.Models;

namespace ClientEmulator
{
    public partial class LoginControl : UserControl
    {
        private readonly AuthenticationService _authenticationService;

        public event Action<object, UserInfo> OnUserLoginSuccessEvent;

        public LoginControl()
        {
            InitializeComponent();
            this._authenticationService = new AuthenticationService();
        }

        private async void btn_login_Click(object sender, EventArgs e)
        {
            var button = (Button)sender;
            var userName = txtUserName.Text;
            var password = <PASSWORD>.Text;
            if (string.IsNullOrEmpty(userName) ||
                string.IsNullOrEmpty(password))
            {
                MessageBox.Show("请输入正确的用户名和密码", "ERROR");
                return;
            }
            button.Enabled = false;
            button.Text = "正在登录...";
            try
            {
                var userinfo = await this._authenticationService.LoginAsync(userName.Trim(), password.Trim());
                UserInfo.LoginedUsers.Add(userinfo);
                txtUserName.Enabled = false;
                txtPassword.Enabled = false;
                txtToken.Text = userinfo.AccessToken;
                button.Text = "Login";
                lab_Status.Text = "Success";
                lab_Status.BackColor = Color.MediumSeaGreen;
                OnUserLoginSuccessEvent?.Invoke(this, userinfo);
            }
            catch (Exception ex)
            {
                lab_Status.Text = "ERROR";
                lab_Status.BackColor = Color.Red;
                button.Enabled = true;
                button.Text = "Login";
                txtUserName.Enabled = true;
                txtPassword.Enabled = true;
                MessageBox.Show(ex.ToString(), "ERROR");
            }
        }
    }
}


<|start_filename|>src/Base.Eap.Notify.MsgContracts/BizSystemNotifyMsg.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;

namespace Base.Eap.Notify.MsgContracts
{
    /// <summary>
    /// 业务系统发送的提醒消息
    /// </summary>
    public class BizSystemNotifyMsg
    {
        public const string RabbitMqReceiveEndpointName = "Base.Eap.BizSystemNotifyService";

        /// <summary>
        /// 发送业务系统
        /// </summary>
        public string SystemId { get; set; }

        /// <summary>
        /// 消息类别，如果设置为0，那么就是使用Content
        /// </summary>
        public int Category { get; set; }

        /// <summary>
        /// 消息内容
        /// </summary>
        public string Content { get; set; }

        /// <summary>
        /// 消息类别参数，与消息内容任选其一，如果是使用SMS发送，一般需要设置此属性
        /// </summary>
        public Dictionary<string, string> Parameters { get; set; }

        /// <summary>
        /// 接收人的员工号，ReceiverNumbers ReceiverUserIds ReceiverMobiles设置其一，建议使用ReceiverUserIds
        /// </summary>
        public string[] ReceiverNumbers { get; set; }

        /// <summary>
        /// 接收人的UserId，ReceiverNumbers ReceiverUserIds ReceiverMobiles设置其一，建议使用ReceiverUserIds
        /// </summary>
        public Guid[] ReceiverUserIds { get; set; }

        /// <summary>
        /// 接收人的手机号，ReceiverNumbers ReceiverUserIds ReceiverMobiles设置其一，建议使用ReceiverUserIds
        /// </summary>
        [Obsolete("未来手机号不应该由业务系统提供，应该提供UserId或者员工号")]
        public string[] ReceiverMobiles { get; set; }
    }
}


<|start_filename|>src/ApiCommon/CommonServicesExtension.cs<|end_filename|>
﻿using AppsOnSF.Common.BaseServices;
using Base.Csi.Sms.MsgContracts;
using Common.Services;
using MassTransit;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Text;

namespace Common
{
    public static class CommonServicesExtension
    {
        public const string RabbitMqReceiveEndpointNames_SmsService = "Base.Csi.SmsService";

        public static IServiceCollection AddCommonServices(this IServiceCollection services, IBusControl bus, string busHost)
        {
            services.AddSingleton<IMobileCodeSender>(_ => new MobileCodeSender(
                bus, RemotingProxyFactory.CreateSimpleKeyValueService()));

            EndpointConvention.Map<SendMobileCodeCommand>(
                new Uri(busHost + RabbitMqReceiveEndpointNames_SmsService));

            return services;
        }
    }
}


<|start_filename|>src/NotifySessionActor/TheActor.cs<|end_filename|>
﻿using Common;
using MassTransit;
using Microsoft.ServiceFabric.Actors;
using Microsoft.ServiceFabric.Actors.Runtime;
using Notification;
using Notification.MsgContracts;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace NotifySessionActor
{
    /// <summary>
    /// NotifySessionActor
    /// </summary>
    /// <remarks>
    /// This class represents an actor.
    /// Every ActorID maps to an instance of this class.
    /// The StatePersistence attribute determines persistence and replication of actor state:
    ///  - Persisted: State is written to disk and replicated.
    ///  - Volatile: State is kept in memory only and replicated.
    ///  - None: State is kept in memory only and not replicated.
    /// </remarks>
    [StatePersistence(StatePersistence.Volatile)]
    public class TheActor : ActorEapBase, INotifySessionActor
    {
        public bool IsInUnitTest = false;

        public const string ConnectionIdListPropertyName = "ConnectionIdList";
        public const string MessageListPropertyName = "MessageNotifyList";
        public const string EventListPropertyName = "EventNotifyList";

        /// <summary>
        /// Initializes a new instance of NotifySessionActor
        /// </summary>
        /// <param name="actorService">The Microsoft.ServiceFabric.Actors.Runtime.ActorService that will host this actor instance.</param>
        /// <param name="actorId">The Microsoft.ServiceFabric.Actors.ActorId for this actor instance.</param>
        public TheActor(TheActorService actorService, ActorId actorId)
             : base(actorService, actorId)
        {
        }

        public new TheActorService ActorService => (TheActorService)base.ActorService;

        public async Task AddConnectionIdAsync(string id)
        {
            if (string.IsNullOrEmpty(id))
                throw new ArgumentNullException(nameof(id));

            try
            {
                var listConnectionId = await StateManager.GetOrAddStateAsync(ConnectionIdListPropertyName, new List<string>());
                if (!listConnectionId.Contains(id))
                    listConnectionId.Add(id);
                await StateManager.SetStateAsync(ConnectionIdListPropertyName, listConnectionId);

                var listMessageNotifyWrap = await StateManager.TryGetStateAsync<List<MessageNotifyDto>>(MessageListPropertyName);
                if (listMessageNotifyWrap.HasValue)
                {
                    await ActorService.EventBus.Send(new SendMessageNotify
                    {
                        UserId = this.GetActorId().GetGuidId(),
                        Notifies = listMessageNotifyWrap.Value
                    });
                    await StateManager.RemoveStateAsync(MessageListPropertyName);
                }

                var listEventNotifyWrap = await StateManager.TryGetStateAsync<List<EventNotifyDto>>(EventListPropertyName);
                if (listEventNotifyWrap.HasValue)
                {
                    await ActorService.EventBus.Send(new SendEventNotify
                    {
                        UserId = this.GetActorId().GetGuidId(),
                        Notifies = listEventNotifyWrap.Value
                    });
                    await StateManager.RemoveStateAsync(EventListPropertyName);
                }
            }
            catch (Exception ex)
            {
                ActorEventSource.Current.Message(ex.ToString());
                if (IsInUnitTest) throw;
            }
        }

        public async Task PushEventNotifyAsync(EventNotifyDto dto)
        {
            var listConnectionIdsWrap = await StateManager.TryGetStateAsync<List<string>>(ConnectionIdListPropertyName);
            if (listConnectionIdsWrap.HasValue && listConnectionIdsWrap.Value.Count > 0)
            {
                await ActorService.EventBus.Send(new SendEventNotify
                {
                    UserId = this.GetActorId().GetGuidId(),
                    Notifies = new List<EventNotifyDto> { dto }
                });
            }
            else
            {
                var listPushNotify = await StateManager.GetOrAddStateAsync(
                    EventListPropertyName, new List<EventNotifyDto>());
                listPushNotify.Add(dto);
                await StateManager.SetStateAsync(EventListPropertyName, listPushNotify);
            }
        }

        public async Task PushEventNotifiesAsync(List<EventNotifyDto> dtos)
        {
            var listConnectionIdsWrap = await StateManager.TryGetStateAsync<List<string>>(ConnectionIdListPropertyName);
            if (listConnectionIdsWrap.HasValue && listConnectionIdsWrap.Value.Count > 0)
            {
                await ActorService.EventBus.Send(new SendEventNotify
                {
                    UserId = this.GetActorId().GetGuidId(),
                    Notifies = dtos
                });
            }
            else
            {
                var listPushNotify = await StateManager.GetOrAddStateAsync(
                    EventListPropertyName, new List<EventNotifyDto>());
                listPushNotify.AddRange(dtos);
                await StateManager.SetStateAsync(EventListPropertyName, listPushNotify);
            }
        }

        public async Task PushMsgNotifyAsync(MessageNotifyDto dto)
        {
            var listConnectionIdsWrap = await StateManager.TryGetStateAsync<List<string>>(ConnectionIdListPropertyName);
            if (listConnectionIdsWrap.HasValue && listConnectionIdsWrap.Value.Count > 0)
            {
                await ActorService.EventBus.Send(new SendMessageNotify
                {
                    UserId = this.GetActorId().GetGuidId(),
                    Notifies = new List<MessageNotifyDto> { dto }
                });
            }
            else
            {
                var listPushNotify = await StateManager.GetOrAddStateAsync(
                    MessageListPropertyName, new List<MessageNotifyDto>());
                listPushNotify.Add(dto);
                await StateManager.SetStateAsync(MessageListPropertyName, listPushNotify);
            }
        }

        public async Task PushMsgNotifiesAsync(List<MessageNotifyDto> dtos)
        {
            var listConnectionIdsWrap = await StateManager.TryGetStateAsync<List<string>>(ConnectionIdListPropertyName);
            if (listConnectionIdsWrap.HasValue && listConnectionIdsWrap.Value.Count > 0)
            {
                await ActorService.EventBus.Send(new SendMessageNotify
                {
                    UserId = this.GetActorId().GetGuidId(),
                    Notifies = dtos
                });
            }
            else
            {
                var listPushNotify = await StateManager.GetOrAddStateAsync(
                    MessageListPropertyName, new List<MessageNotifyDto>());
                listPushNotify.AddRange(dtos);
                await StateManager.SetStateAsync(MessageListPropertyName, listPushNotify);
            }
        }

        public async Task RemoveConnectionIdAsync(string id)
        {
            if (string.IsNullOrEmpty(id))
                throw new ArgumentNullException(nameof(id));

            try
            {
                var wrap = await StateManager.TryGetStateAsync<List<string>>(ConnectionIdListPropertyName);
                if (wrap.HasValue)
                {
                    var listConnectionIds = wrap.Value;
                    listConnectionIds.Remove(id);
                    if (listConnectionIds.Count == 0)
                        await StateManager.RemoveStateAsync(ConnectionIdListPropertyName);
                    else
                        await StateManager.SetStateAsync(ConnectionIdListPropertyName, listConnectionIds);
                }
            }
            catch (Exception ex)
            {
                ActorEventSource.Current.Message(ex.ToString());
                if (IsInUnitTest) throw;
            }
        }

        /// <summary>
        /// This method is called whenever an actor is activated.
        /// An actor is activated the first time any of its methods are invoked.
        /// </summary>
        protected override Task OnActivateAsync()
        {
            ActorEventSource.Current.ActorMessage(this, "Actor activated.");

            return Task.CompletedTask;
        }
    }
}


<|start_filename|>src/EnterpriseContactService/DtoMappingProfile.cs<|end_filename|>
﻿using AutoMapper;
using Base.Mdm.Org.MsgContracts;
using EnterpriseContact.Entities;
using System;
using System.Collections.Generic;
using System.Linq;

namespace EnterpriseContact
{
    public class DtoMappingProfile : Profile
    {
        public DtoMappingProfile()
        {
            CreateMap<Department, DepartmentListOutput>();
            CreateMap<Position, PositionListOutput>();
            CreateMap<Employee, EmployeeListOutput>();
            CreateMap<Employee, EmployeeOutput>();
            CreateMap<Employee, EmployeeCacheOutput>();
            CreateMap<GroupInput, Group>()
              .AfterMap((s, d) =>
              {
                  d.Id = Guid.NewGuid();
                  d.CreatedBy = s.CurrentUserId;
                  d.Created = DateTimeOffset.UtcNow;
                  d.Updated = DateTimeOffset.UtcNow;
                  d.Members = new List<GroupMember>();
                  foreach (var m in s.AddingMemberIds)
                  {
                      d.Members.Add(new GroupMember
                      {
                          EmployeeId = m,
                          GroupId = d.Id,
                          IsOwner = (m == s.CurrentEmployeeId),
                          Joined = DateTimeOffset.UtcNow,
                      });
                  }
              });
            CreateMap<Group, GroupOutput>()
                .ForMember(o => o.Members, opt => opt.Ignore());


            CreateMap<OrgUnitEntityMsg, Department>()
                .ForMember(u => u.Parent, opt => opt.Ignore())
                .AfterMap((s, d) =>
                {
                    d.Positions = this.ConvertDbPositions(s, d);
                    var adminParent = s.Parents.FirstOrDefault(x => x.Type == OrgUnitParentType.Admin);
                    d.ParentId = adminParent?.Id;
                });
            CreateMap<PositionEntityMsg, Position>();
            CreateMap<ContactEntityMsg, Employee>()
                .ForMember(u => u.Positions, opt => opt.Ignore())
                .AfterMap((s, d) =>
                {
                    d.Id = s.ContactId;
                    var primaryPosition = s.Positions.FirstOrDefault(x => x.IsPrimary);
                    d.PrimaryDepartmentId = primaryPosition?.OrgUnitId ?? Guid.Empty;
                    d.PrimaryPositionId = primaryPosition?.PositionId ?? Guid.Empty;
                    d.Positions = ConvertDbEmployeePosition(s, d);
                    if (string.IsNullOrEmpty(d.IdCardNo)) d.IdCardNo = "NA";
                });
        }

        private List<Position> ConvertDbPositions(OrgUnitEntityMsg s, Department d)
        {
            var result = new List<Position>();
            foreach (var positionEntityMsg in s.Positions)
            {
                var item = new Position()
                {
                    DepartmentId = s.Id,
                    Id = positionEntityMsg.Id,
                    Name = positionEntityMsg.Name,
                    Department = d
                };
                result.Add(item);
            }
            return result;
        }


        private List<EmployeePosition> ConvertDbEmployeePosition(ContactEntityMsg s, Employee d)
        {
            var result = new List<EmployeePosition>();
            foreach (var positionValueMsg in s.Positions)
            {
                var item = new EmployeePosition()
                {
                    Employee = d,
                    EmployeeId = s.ContactId,
                    IsPrimary = positionValueMsg.IsPrimary,
                    PositionId = positionValueMsg.PositionId,
                };
                result.Add(item);
            }
            return result;

        }
    }
}


<|start_filename|>src/ConfigMgmtApi/Controllers/WorkbenchController.cs<|end_filename|>
﻿using Common;
using Common.Services;
using ConfigMgmt.Entities;
using ConfigMgmt.ViewModels;
using IdentityModel.Client;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Serilog;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;

namespace ConfigMgmt.Controllers
{
    [Route("api/v1/workbench")]
    public partial class WorkbenchController : EapBaseController
    {
        private readonly IWorkbenchAppService _workbenchAppService;
        private readonly IBadgeApiClient _badgeApiClient;
        private readonly IClientAppService _clientAppService;
        private readonly IBizSystemAppService _bizSystemAppService;
        private readonly IMobileCodeSender _mobileCodeSender;
        private readonly Func<Guid, IUserSettingAppService> _userSettingAppServiceFactory;
        private readonly ILogger _logger;

        public WorkbenchController(IWorkbenchAppService workbenchAppService,
            IClientAppService clientAppService,
            IBizSystemAppService bizSystemAppService,
            IBadgeApiClient badgeApiClient,
            IMobileCodeSender mobileCodeSender,
            Func<Guid, IUserSettingAppService> userSettingAppServiceFactory)
        {
            _workbenchAppService = workbenchAppService;
            _badgeApiClient = badgeApiClient;
            _clientAppService = clientAppService;
            _bizSystemAppService = bizSystemAppService;
            _mobileCodeSender = mobileCodeSender;
            _userSettingAppServiceFactory = userSettingAppServiceFactory;
            _logger = Log.ForContext<WorkbenchController>();
        }

        [HttpGet]
        public async Task<ActionResult<List<AppEntranceDto>>> GetAll(ClientPlatform clientPlatform, string clientId = Client.DefaultId)
        {
            try
            {
                var input = new GetAppEntrancesInput
                {
                    ClientId = clientId,
                    UserRoles = GetUserRoles().ToList(),
                    ClientPlatform = clientPlatform,
                };

                return await _workbenchAppService.GetAppEntrancesAsync(input);
            }
            catch (Exception ex)
            {
                return BadRequest(LogError(_logger, ex));
            }
        }

        [HttpGet("badge")]
        [ProducesResponseType(StatusCodes.Status200OK)]
        [ProducesResponseType(StatusCodes.Status404NotFound)]
        public async Task<ActionResult<int>> GetBadgeAmount(Guid entryId)
        {
            try
            {
                var config = await _workbenchAppService.GetComponentConfigAsync(entryId);

                if (config == null || config.DataSources == null)
                    return 0;

                var result = await _badgeApiClient.GetAmountAsync(config);
                if (result == null)
                    return 0;

                return result.Value;
            }
            catch (Exception ex)
            {
                return BadRequest(LogError(_logger, ex));
            }
        }

        /// <summary>
        /// 发送手机验证码
        /// </summary>
        /// <returns>Data=手机号后4位, Status=1 手机号不存在 </returns>
        [HttpGet("authcode")]
        public async Task<ActionResult<ResponseData<string>>> SendAuthCode()
        {
            try
            {
                var mobile = GetUserMobile();
                if (string.IsNullOrEmpty(mobile)) return BuildFaild<string>(1);

                var code = await _mobileCodeSender.SendAsync(mobile);

#if DEBUG
                return BuildSuccess(mobile.Substring(mobile.Length - 4, 4) + ":" + code);
#else
                return BuildSuccess(mobile.Substring(mobile.Length - 4, 4));
#endif
            }
            catch (Exception ex)
            {
                return BadRequest(LogError(_logger, ex));
            }
        }

        /// <summary>
        /// 验证手机验证码
        /// </summary>
        /// <param name="model"></param>
        /// <returns>true=验证成功</returns>
        [HttpPost("authcode")]
        public async Task<ActionResult<bool>> CheckAuthCode(CheckAuthCodeVm model)
        {
            try
            {
                if (model == null)
                    throw new ArgumentNullException(nameof(model));

                var mobile = GetUserMobile();
                if (string.IsNullOrEmpty(mobile)) return false;

                var verified = await _mobileCodeSender.CheckAsync(mobile, model.MobileCode);
                if (!verified) return false;

                var userId = GetUserId();
                var userSettingAppService = _userSettingAppServiceFactory(userId);
                await userSettingAppService.SetAppEntranceAuthStateAsync(userId, new AppEntranceAuthStateInput
                {
                    AppEntranceId = model.AppEntranceId,
                    DeviceCode = model.DeviceCode
                });

                return true;
            }
            catch (Exception ex)
            {
                return BadRequest(LogError(_logger, ex));
            }
        }

        /// <summary>
        /// 检查用户的验证状态
        /// </summary>
        /// <param name="appEntranceId"></param>
        /// <param name="deviceCode"></param>
        /// <returns>true验证无效需要验证，false验证有效不需要验证</returns>
        [HttpGet("authstatus")]
        public async Task<ActionResult<bool>> CheckAuthStatus(Guid appEntranceId, string deviceCode)
        {
            try
            {
                if (appEntranceId == Guid.Empty) throw new ArgumentOutOfRangeException(nameof(appEntranceId));
                if (string.IsNullOrEmpty(deviceCode)) throw new ArgumentNullException(nameof(deviceCode));

                var userId = GetUserId();
                var userSettingAppService = _userSettingAppServiceFactory(userId);
                return await userSettingAppService.GetAppEntranceAuthStateAsync(userId, deviceCode, appEntranceId);
            }
            catch (Exception ex)
            {
                return BadRequest(LogError(_logger, ex));
            }
        }
    }
}


<|start_filename|>src/InstantMessageApi/Controllers/InstantMessageController.cs<|end_filename|>
﻿using Common;
using EnterpriseContact;
using EnterpriseContact.Services;
using InstantMessage.Entities;
using InstantMessage.ViewModels;
using Microsoft.AspNetCore.Mvc;
using Notification;
using Serilog;
using ServiceFabricContrib;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace InstantMessage.Controllers
{
    [Route("api/v1/im")]
    public partial class InstantMessageController : EapBaseController
    {
        private readonly Func<Guid, IConversationMsgAppService> _conversationMsgAppServiceFactory;
        private readonly ILogger _logger;
        private readonly IConversationCtrlAppService _conversationCtrlAppService;
        private readonly Func<Task<IEnumerable<IConversationMsgAppService>>> _allConversationMsgAppServiceFactory;
        private readonly IEmployeeCacheService _employeeMappingService;
        private readonly IGroupAppService _groupAppService;
        private readonly IDepartmentAppService _departmentAppService;

        public InstantMessageController(
            IConversationCtrlAppService conversationCtrlAppService,
            Func<Guid, IConversationMsgAppService> conversationMsgAppServiceFactory,
            Func<Task<IEnumerable<IConversationMsgAppService>>> allConversationMsgAppServiceFactory,
            IEmployeeCacheService employeeMappingService,
            IGroupAppService groupAppService,
            IDepartmentAppService departmentAppService
            )
        {
            _conversationCtrlAppService = conversationCtrlAppService;
            _conversationMsgAppServiceFactory = conversationMsgAppServiceFactory;
            _logger = Log.ForContext<InstantMessageController>();
            _allConversationMsgAppServiceFactory = allConversationMsgAppServiceFactory;
            _employeeMappingService = employeeMappingService;
            _groupAppService = groupAppService;
            _departmentAppService = departmentAppService;
        }

        /// <summary>
        /// 消息记录
        /// </summary>
        /// <param name="param"></param>
        /// <returns></returns>
        [HttpPost("message/getHistory")]
        public async Task<ActionResult<List<MessageVm>>> GetMessageHistory(MessageNotifyDto model)
        {
            try
            {
                var service = _conversationMsgAppServiceFactory(model.TargetId.ToGuid());
                var messages = await service.GetOldMessagesAsync(new GetOldMessagesInput()
                {
                    Id = model.TargetId.ToGuid(),
                    OldestMsgId = model.LatestMsgId,
                    //Type = Enum.Parse<ConversationType>(model.TargetCategory.ToString())
                });

                var tasks = new List<Task<MessageVm>>();
                foreach (var msg in messages)
                {
                    tasks.Add(BuildMessageVmAsync(msg));
                }
                await Task.WhenAll(tasks);
                return tasks.Select(o => o.Result).ToList();
            }
            catch (Exception ex)
            {
                return BadRequest(LogError(_logger, ex));
            }
        }

        private async Task<SendMessageResult> SendAsync(ConversationMsg msg, ConversationType conversationType)
        {
            var entity = await _conversationCtrlAppService.GetByIdAsync(msg.ConversationId);
            var conversationMsgAppService = _conversationMsgAppServiceFactory(msg.ConversationId);
            if (entity.HasValue && !entity.Value.IsDeleted)
                if (entity.Value.Participants.Contains(msg.SenderId))
                {
                    await conversationMsgAppService.SendMessageAsync(msg);

                    return new SendMessageResult()
                    {
                        Success = true,
                        Message = "发送消息成功",
                        MessageId = msg.Id,
                        SendTime = msg.Time
                    };
                }
                else
                    return new SendMessageResult()
                    {
                        Success = false,
                        Message = conversationType == ConversationType.P2P ? "用户不属于此会话..." : "用户不在群组中..."
                    };
            else
                return new SendMessageResult()
                {
                    Success = false,
                    Message = "对话不存在"
                };
        }

        /// <summary>
        /// 发送文本消息
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        [HttpPost("message/sendText")]
        public async Task<ActionResult<SendMessageResult>> SendTextMessageAsync(TextMessageVm model)
        {
            try
            {
                var msg = ConversationMsg.Create(model.ConversationId, model.SenderId, model.Content, ConversationMsgType.Text);

                var result = await SendAsync(msg, model.ConversationType);
                return result;
            }
            catch (Exception ex)
            {
                return BadRequest(LogError(_logger, ex));
            }
        }

        /// <summary>
        /// 发送图片消息
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        [HttpPost("message/sendImage")]
        public async Task<ActionResult<SendMessageResult>> SendImageMessageAsync(ImageMessageVm model)
        {
            try
            {
                var msg = ConversationMsg.Create(model.ConversationId, model.SenderId, model.GetJsonContent(),
                    ConversationMsgType.Image);

                var result = await SendAsync(msg, model.ConversationType);
                return result;
            }
            catch (Exception ex)
            {
                return BadRequest(LogError(_logger, ex));
            }
        }

        /// <summary>
        /// 发送文件消息
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        [HttpPost("message/sendFile")]
        public async Task<ActionResult<SendMessageResult>> SendFileMessageAsync(FileMessageVm model)
        {
            try
            {
                var msg = ConversationMsg.Create(model.ConversationId, model.SenderId, model.GetJsonContent(),
                    ConversationMsgType.File);

                var result = await SendAsync(msg, model.ConversationType);
                return result;
            }
            catch (Exception ex)
            {
                return BadRequest(LogError(_logger, ex));
            }
        }

        /// <summary>
        /// 发送语音消息
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        [HttpPost("message/sendVoice")]
        public async Task<ActionResult<SendMessageResult>> SendVoiceMessageAsync(VoiceMessageVm model)
        {
            try
            {
                var msg = ConversationMsg.Create(model.ConversationId, model.SenderId, model.GetJsonContent(),
                    ConversationMsgType.Voice);

                var result = await SendAsync(msg, model.ConversationType);
                return result;
            }
            catch (Exception ex)
            {
                return BadRequest(LogError(_logger, ex));
            }
        }

        /// <summary>
        /// 创建P2P会话
        /// </summary>
        /// <param name="param">参数</param>
        /// <remarks>参数中的用户Id均为单点登录返回的Sub</remarks>
        /// <returns>返回数据中的data就是会话Id</returns>
        [HttpPost("p2p")]
        public async Task<ActionResult<Guid>> CreateP2PConverationAsync(CreateP2PConversationVm model)
        {
            try
            {
                //NOTE 此处暂时不验证白名单，依赖客户端的控制
                var conv = await _conversationCtrlAppService.GetOrAddP2PAsync(model.SenderId, model.RecieverId);
                return conv.Id;
            }
            catch (Exception ex)
            {
                return BadRequest(LogError(_logger, ex));
            }
        }

        /// <summary>
        /// 删除现有的P2P会话，在白名单验证失效后，可以避免被打扰
        /// </summary>
        [HttpDelete("p2p/{id}")]
        public async Task<IActionResult> DeleteP2PConverationAsync(Guid id)
        {
            try
            {
                await _conversationCtrlAppService.DeleteAsync(id);
                return Ok();
            }
            catch (Exception ex)
            {
                return BadRequest(LogError(_logger, ex));
            }
        }

        /// <summary>
        /// 同步客户端和服务器的时间差
        /// </summary>
        /// <returns></returns>
        [HttpGet("time")]
        public ActionResult<TimeSpan> SyncTimeDiff(DateTimeOffset current)
        {
            var diff = DateTimeOffset.UtcNow - current;
            return diff;
        }

        /// <summary>
        /// 创建部门会话
        /// </summary>
        /// <param name="departmentId">部门Id，即为会话Id</param>
        /// <returns>Status 1:不是现存会话的成员 2:不是部门成员 3:只有一个成员无法创建会话 4:现存的会话不是部门会话</returns>
        [HttpPost("dep")]
        public async Task<ActionResult<ResponseData>> CreateDepConverationAsync([FromQuery]Guid departmentId)
        {
            try
            {
                var userId = GetUserId();
                var conv = await _conversationCtrlAppService.GetByIdAsync(departmentId);
                if (conv.HasValue)
                {
                    if (conv.Value.Type != ConversationType.DepartmentGroup)
                        return BuildFaild(4);
                    if (conv.Value.Participants.Contains(userId))
                        return BuildSuccess();
                    return BuildFaild(1);
                }
                else
                {
                    var result = await _conversationCtrlAppService.AddDepAsync(new AddDepConversationInput
                    {
                        UserId = userId,
                        DepartmentId = departmentId
                    });

                    if (result.FailedCode == 1)
                        return BuildFaild(2);
                    if (result.FailedCode == 2)
                        return BuildFaild(3);
                    return BuildSuccess();
                }
            }
            catch (Exception ex)
            {
                return BadRequest(LogError(_logger, ex));
            }
        }
    }
}


<|start_filename|>emulator/ClientEmulator/Services/AuthenticationService.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using IdentityModel;
using IdentityModel.Client;
using ClientEmulator.Config;
using ClientEmulator.Services.Models;
using Newtonsoft.Json;

namespace ClientEmulator.Services
{
    public class AuthenticationService
    {
        /// <summary>
        /// 登录,返回AccessToken
        /// </summary>
        /// <param name="userName"></param>
        /// <param name="password"></param>
        /// <returns></returns>
        public async Task<UserInfo> LoginAsync(string userName, string password)
        {
            var client = new HttpClient();
            var uri = new Uri(URLConfig.Login_Host);
            var disco = await client.GetDiscoveryDocumentAsync(new DiscoveryDocumentRequest()
            {
                Address = URLConfig.Login_Host,
                Policy = new DiscoveryPolicy()
                {
                    RequireHttps = uri.Scheme.Equals("https", StringComparison.CurrentCultureIgnoreCase)
                }
            });
            if (disco.IsError) throw new Exception(disco.Error);
            try
            {
                var tokenResponse = await client.RequestPasswordTokenAsync(
                    this.CreatePasswordTokenRequest(disco.TokenEndpoint,
                    userName,
                    password));
                if (tokenResponse.IsError)
                    throw new Exception(tokenResponse.Raw);
                var userinfoResponse = await client.GetUserInfoAsync(new UserInfoRequest()
                {
                    Address = disco.UserInfoEndpoint,
                    Token = tokenResponse.AccessToken
                });
                if (userinfoResponse.IsError)
                    throw new Exception(tokenResponse.Raw);
                var userinfo = ConverToUserInfoModel(userinfoResponse);
                userinfo.AccessToken = tokenResponse.AccessToken;
                return userinfo;
            }
            catch (Exception e)
            {
                throw e;
            }


        }

        private UserInfo ConverToUserInfoModel(UserInfoResponse userinfo)
        {
            var username_claim = userinfo.Claims.FirstOrDefault(u => u.Type == "preferred_username");
            var name_claim = userinfo.Claims.FirstOrDefault(u => u.Type == "name");
            var sub_claim = userinfo.Claims.FirstOrDefault(u => u.Type == "sub");
            var mdmId_claim = userinfo.Claims.FirstOrDefault(u => u.Type == "employee_mdmid");
            if (username_claim == null || name_claim == null || sub_claim == null || mdmId_claim == null)
                throw new Exception("UserClaims not full,Calims:" + JsonConvert.SerializeObject(userinfo.Claims));
            return new UserInfo()
            {
                Name = name_claim.Value,
                UserName = username_claim.Value,
                UserId = sub_claim.Value,
                UserMdmId = new Guid(mdmId_claim.Value)
            };
        }

        /// <summary>
        /// 获取用户的UserInfo消息
        /// </summary>
        /// <param name="accessToken"></param>
        /// <returns></returns>
        public async Task<string> GetUserInfoAsync(string accessToken)
        {
            return "";
        }

        private PasswordTokenRequest CreatePasswordTokenRequest(string address, string userName, string password)
        {
            var request = new PasswordTokenRequest()
            {
                Address = address,
                ClientCredentialStyle = ClientCredentialStyle.AuthorizationHeader,
                ClientId = ClientConfig.ClientId,
                ClientSecret = ClientConfig.ClientSercet,
                UserName = userName,
                Password = password,
                Scope = string.Join(" ", ClientConfig.Scopes),
            };
            return request;
        }
    }
}


<|start_filename|>src/EnterpriseContactDomain/MsgConstracts/DepartmentEvent.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Text;

namespace EnterpriseContact.MsgConstracts
{
    public abstract class BaseDepartmentEvent
    {
        public Guid Id { get; set; }
    }

    public class DepartmentDeleted : BaseDepartmentEvent
    { }

    public class DepartmentMembersUpdated : BaseDepartmentEvent
    { }
}


<|start_filename|>emulator/ClientEmulator/Services/ChatHub.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using ClientEmulator.Config;
using ClientEmulator.Services.Models;
using InstantMessage;
using InstantMessage.ViewModels;
using Microsoft.AspNetCore.Http.Connections;
using Microsoft.AspNetCore.SignalR.Client;
using Newtonsoft.Json;
using Notification;
using Guid = System.Guid;

namespace ClientEmulator.Services
{
    public class ChatHub
    {
        //消息
        private static readonly string _receiveMessageNotify_methodName = "ReceiveMessageNotify";
        private static readonly string _receiveEventNotify = "ReceiveEventNotify";

        private HubConnection _connection { get; set; }

        /// <summary>
        /// 新消息提醒
        /// </summary>
        public delegate void OnNewMessagesDelegate(List<ConversationVm> conversations);
        public event OnNewMessagesDelegate OnNewMessagesEvent;

        /// <summary>
        /// 事件提醒
        /// </summary>
        public delegate void OnNewEventDelate(List<EventNotifyDto> eventNotifyDto);
        public event OnNewEventDelate OnNewEventNotifyEvent;

        public delegate void OnNewMessageEventDelate(List<MessageNotifyDto> messageNotifyDto);

        public event OnNewMessageEventDelate OnNewMessageNotifyEvent;

        public event Action<string, string> OnSendMessageEvent;

        private readonly ConversationService _conversationService;
        public readonly UserInfo _userInfo;

        public static Guid ConversationId { get; set; }

        public static ConversationType ConversationType { get; set; }

        public static Guid LastMsgId { get; set; }

        public ChatHub(UserInfo userInfo)
        {
            _userInfo = userInfo;
            _conversationService = new ConversationService(new HttpClient() { BaseAddress = new Uri(URLConfig.API_Host) });
        }

        public async void ConnectAsync()
        {
            var connection = new HubConnectionBuilder()
                .WithUrl(URLConfig.Hub_Host, option =>
                {
                    option.Transports = HttpTransportType.ServerSentEvents | HttpTransportType.WebSockets |
                                        HttpTransportType.LongPolling;
                    option.AccessTokenProvider = () => Task.FromResult(_userInfo.AccessToken);
                })
                .Build();
            connection.On<List<MessageNotifyDto>>(_receiveMessageNotify_methodName, ReceiveMessageNotify);
            connection.On<List<EventNotifyDto>>(_receiveEventNotify, ReceiveEventNotify);
            _connection = connection;
            await connection.StartAsync();
        }

        public async void DisConnectAsync()
        {
            await _connection.StopAsync();
        }

        /// <summary>
        /// 必须下创建会话,然后再来初始化
        /// </summary>
        public async Task<string> CreateConversationAsync(string reciverId)
        {
            try
            {
                var conversationId =
                    await this._conversationService.CreateConversationAsync(_userInfo.UserId, reciverId,
                        _userInfo.AccessToken);
                ConversationId = conversationId;
                ConversationType = ConversationType.P2P;
                return ConversationId.ToString();
            }
            catch (Exception e)
            {
                return e.ToString();
            }

        }

        public async Task<string> CreateGroupAsync(UserInfo owner, UserInfo partner, string name, string remark)
        {
            try
            {
                var response = await this._conversationService.CreateGroupAsync(owner, partner, name, remark);
                ConversationId = response.Data;
                ConversationType = ConversationType.Group;
                return JsonConvert.SerializeObject(response);
            }
            catch (Exception e)
            {
                return e.ToString();
            }
        }

        public async Task<string> SendTextAsync(string reciverId, string message)
        {
            try
            {
                var result = await this._conversationService.SendTextAsync(ConversationId.ToString(), _userInfo.UserId,
                    message, _userInfo.AccessToken, ConversationType);
                if (result.Success)
                    this.OnSendMessageEvent?.Invoke(reciverId, message);
                return JsonConvert.SerializeObject(result);
            }
            catch (Exception e)
            {
                return e.ToString();
            }
        }

        public async Task<string> GetMessageHistoryAsync()
        {
            try
            {
                var history = await this._conversationService.GetMessageHistryAsync(new MessageNotifyDto()
                {
                    LatestMsgId = LastMsgId,
                    Target = NotifyTargetType.Conversation,
                    TargetId = ConversationId.ToString(),
                    TargetCategory = (int)ConversationType
                }, this._userInfo.AccessToken);
                return JsonConvert.SerializeObject(history);
            }
            catch (Exception ex)
            {
                return ex.ToString();
            }
        }

        public async Task<string> DeleteP2PConversationAsync()
        {
            try
            {
                var result = await this._conversationService.DeleteP2PConversation(ConversationId, _userInfo.AccessToken);
                return result;
            }
            catch (Exception ex)
            {
                return ex.ToString();
            }
        }

        public async Task<string> QuitGroupAsync()
        {
            try
            {
                var response = await this._conversationService.QuitGroup(this._userInfo, ConversationId);
                return JsonConvert.SerializeObject(response);
            }
            catch (Exception e)
            {
                return e.ToString();
            }
        }

        public async Task<string> DeleteGroupAsync()
        {
            try
            {
                var response = await this._conversationService.DeleteGroup(this._userInfo, ConversationId);
                return JsonConvert.SerializeObject(response);
            }
            catch (Exception e)
            {
                return e.ToString();
            }
        }


        private async void ReceiveMessageNotify(List<MessageNotifyDto> messages)
        {
            if (messages == null || messages.Count == 0)
                return;
            try
            {
                this.OnNewMessageNotifyEvent?.Invoke(messages);
                var conversationVms =
                    await this._conversationService.GetNewMessageListAsync(messages, _userInfo.AccessToken);
                var msg = conversationVms.SelectMany(u => u.MessageList).ToList();
                if (msg.Count > 0)
                    LastMsgId = msg.Last().Id;
                this.OnNewMessagesEvent?.Invoke(conversationVms);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString(), "ERROR");
            }
        }

        private async void ReceiveEventNotify(List<EventNotifyDto> events)
        {
            this.OnNewEventNotifyEvent?.Invoke(events);
        }
    }
}


<|start_filename|>src/InstantMessageApi/TheStartup.cs<|end_filename|>
﻿using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace InstantMessage
{
    public static class TheStartup
    {
        public static void ConfigureServices(IServiceCollection services)
        {
            services.AddSingleton(RemotingClient.CreateConversationCtrlAppService());

            services.AddScoped<Func<Guid, IConversationMsgAppService>>(u => RemotingClient.CreateConversationMsgAppService);
            services.AddScoped<Func<Task<IEnumerable<IConversationMsgAppService>>>>(u => RemotingClient.GetAllConversationMsgAppServicesAsync);
        }
    }
}


<|start_filename|>src/NotifyGatewayWeb/Hubs/NotifyHub.cs<|end_filename|>
﻿using Notification;
using Microsoft.AspNetCore.SignalR;
using System.Collections.Generic;
using System.Threading.Tasks;
using System;
using System.Diagnostics;
using System.Security.Claims;
using Microsoft.AspNetCore.Authorization;

namespace NotifyGatewayWeb.Hubs
{
    [Authorize]
    public class NotifyHub : Hub<INotifyClient>
    {
        public override async Task OnConnectedAsync()
        {
            try
            {
                await RemotingClient.CreateNotifySessionActor(GetUserId())
                    .AddConnectionIdAsync(Context.ConnectionId);
            }
            catch (Exception e)
            {
                Debug.WriteLine("OnConnectedAsync Exception === " + e.Message);
            }
            await base.OnConnectedAsync();
        }

        private Guid GetUserId()
        {
            var claim = Context.User.FindFirst(ClaimTypes.NameIdentifier);
            if (claim == null) return Guid.Empty;
            return claim.Value.ToGuid();
        }

        public override async Task OnDisconnectedAsync(Exception exception)
        {
            try
            {
                await RemotingClient.CreateNotifySessionActor(GetUserId())
                    .RemoveConnectionIdAsync(Context.ConnectionId);
            }
            catch (Exception e)
            {
                Debug.WriteLine("OnDisconnectedAsync Exception === " + e.Message);
            }
            await base.OnDisconnectedAsync(exception);
        }
    }
}


<|start_filename|>src/ConfigMgmtDomain/Entities/AppEntrance.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Runtime.Serialization;

namespace ConfigMgmt.Entities
{
    [DataContract]
    public class AppEntrance
    {
        [DataMember]
        public string ClientId { get; set; }

        [DataMember]
        public Guid Id { get; set; }

        [DataMember]
        public string Title { get; set; }

        [DataMember]
        public string Icon { get; set; }

        /// <summary>
        /// 关联文件夹Id
        /// </summary>
        [DataMember]
        public Guid? FolderId { get; set; }

        /// <summary>
        /// 是否是文件夹的配置
        /// </summary>
        [DataMember]
        public bool IsFolder { get; set; }

        /// <summary>
        /// 应用Id 下载需要
        /// </summary>
        [DataMember]
        public string AppId { get; set; } = "";

        /// <summary>
        /// 图标显示尺寸比例，1=1x，2=高2x，3=宽2x，4=高宽2x
        /// </summary>
        [DataMember]
        public int IconDisplaySize { get; set; }

        [DataMember]
        public int Order { get; set; }

        [DataMember]
        public AppEntryAuthType AuthType { get; set; }

        /// <summary>
        /// 显示和进入此应用入口需要的系统角色；
        /// 如果为empty，那么任何人皆可进入；
        /// 如果有多个，需要都满足
        /// </summary>
        [DataMember]
        public string[] NeedRoles { get; set; }

        [DataMember]
        public bool NeedAuth { get; set; }

        /// <summary>
        /// 组件配置信息，只有入口是组件的才需要
        /// </summary>
        [DataMember]
        public ComponentConfig ComponentConfig { get; set; }

        /// <summary>
        /// 不同客户端平台上的启动Uri
        /// </summary>
        [DataMember]
        public Dictionary<ClientPlatform, string> Uris { get; set; }
    }
}


<|start_filename|>src/EnterpriseContactService/Entities/EmployeePosition.cs<|end_filename|>
﻿using EnterpriseContactService.Entities;
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace EnterpriseContact.Entities
{
    public class EmployeePosition
    {
        public Guid EmployeeId { get; set; }

        public virtual Employee Employee { get; set; }

        public Guid PositionId { get; set; }

        public virtual Position Position { get; set; }

        public bool IsPrimary { get; set; }

        public DataSourceType DataSourceType { get; set; }
    }
}


<|start_filename|>src/ConversationCtrlStateService/Service.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Fabric;
using System.Threading;
using System.Threading.Tasks;
using Common;
using ConversationCtrlStateService.EventBus;
using InstantMessage;
using InstantMessage.Entities;
using MassTransit;
using AppsOnSF.Common.Options;
using Microsoft.Diagnostics.EventFlow;
using Microsoft.ServiceFabric.Data;
using Microsoft.ServiceFabric.Data.Collections;
using Microsoft.ServiceFabric.Services.Communication.Runtime;
using Microsoft.ServiceFabric.Services.Remoting.FabricTransport.Runtime;
using Microsoft.ServiceFabric.Services.Remoting.V2.FabricTransport.Runtime;
using Notification;
using ServiceFabricContrib;

namespace ConversationCtrlStateService
{
    /// <summary>
    /// An instance of this class is created for each service instance by the Service Fabric runtime.
    /// </summary>
    public sealed class Service : StatefulServiceEapBase
    {
        public const string ConversationKeyToIdDictName = "Conversation/KeyToId";
        public const string ConversationListDictName = "Conversation/List";

        private readonly IBusControl _busMs;


        public Service(StatefulServiceContext context,
             DiagnosticPipeline diagnosticPipeline)
             : base(context, diagnosticPipeline)
        {
            _busMs = CreateMsBus(context);
        }

        private IBusControl CreateMsBus(ServiceContext serviceContext)
        {
            var option = serviceContext.GetOption<RabbitMQOption>("RabbitMQ_ms");

            var bus = Bus.Factory.CreateUsingRabbitMq(cfg =>
            {
                var host = cfg.Host(new Uri(option.HostAddress), h =>
                {
                    h.Username(option.Username);
                    h.Password(option.Password);
                });
                cfg.ReceiveEndpoint(host, "EAP_ConversationCtrlStateService", c =>
                {
                    c.Consumer(() => new DepartmentDeletedConsumer(StateManager));
                    c.Consumer(() => new DepartmentMembersUpdatedConsumer(StateManager, EnterpriseContact.RemotingClient.CreateEmployeeAppService()));
                    c.Consumer(() => new GroupAddedConsumer(StateManager, EnterpriseContact.RemotingClient.CreateGroupAppService()));
                    c.Consumer(() => new GroupDeletedConsumer(StateManager));
                    c.Consumer(() => new GroupMembersUpdatedConsumer(StateManager, EnterpriseContact.RemotingClient.CreateGroupAppService()));
                    c.Consumer(() => new GroupNotifiedConsumer(StateManager, Notification.RemotingClient.CreateNotifySessionActor));
                });

            });
            return bus;
        }

        /// <summary>
        /// Optional override to create listeners (e.g., HTTP, Service Remoting, WCF, etc.) for this service replica to handle client or user requests.
        /// </summary>
        /// <remarks>
        /// For more information on service communication, see https://aka.ms/servicefabricservicecommunication
        /// </remarks>
        /// <returns>A collection of listeners.</returns>
        protected override IEnumerable<ServiceReplicaListener> CreateServiceReplicaListeners()
        {
            return new[]
            {
                new ServiceReplicaListener((c)=>
                    new FabricTransportServiceRemotingListener(c,
                        new ConversationCtrlAppService(Context,StateManager,
                        EnterpriseContact.RemotingClient.CreateEmployeeAppService())))
            };
        }

        public static async Task<IReliableDictionary2<string, Guid>> GetKeyToIdDictAsync(IReliableStateManager stateManager)
        {
            return await stateManager.GetOrAddAsync<IReliableDictionary2<string, Guid>>(ConversationKeyToIdDictName);
        }

        public static async Task<IReliableDictionary2<Guid, Conversation>> GetListDictAsync(IReliableStateManager stateManager)
        {
            return await stateManager.GetOrAddAsync<IReliableDictionary2<Guid, Conversation>>(ConversationListDictName);
        }

        protected override async Task RunAsync(CancellationToken cancellationToken)
        {
            try
            {
                ServiceEventSource.Current.ServiceMessage(Context, "inside RunAsync for ConversationCtrlStateService Service");

                await _busMs.StartAsync();

                while (true)
                {
                    if (cancellationToken.IsCancellationRequested)
                    {
                        //Service Fabric wants us to stop
                        await _busMs.StopAsync();

                        cancellationToken.ThrowIfCancellationRequested();
                    }

                    await Task.Delay(TimeSpan.FromSeconds(1));
                }
            }
            catch (OperationCanceledException e)
            {
                ServiceEventSource.Current.ServiceMessage(Context, "RunAsync Stoped, {0}", e);
            }
            catch (Exception e)
            {
                ServiceEventSource.Current.ServiceMessage(Context, "RunAsync Failed, {0}", e);
                throw;
            }
        }
    }
}


<|start_filename|>src/EnterpriseContactService/EmployeeAppService.cs<|end_filename|>
﻿using AutoMapper;
using AutoMapper.QueryableExtensions;
using Microsoft.EntityFrameworkCore;
using Microsoft.ServiceFabric.Data;
using System;
using System.Collections.Generic;
using System.Fabric;
using System.Linq;
using System.Threading.Tasks;

namespace EnterpriseContact
{
    public class EmployeeAppService : BaseAppService, IEmployeeAppService
    {
        public EmployeeAppService(StatelessServiceContext serviceContext, DbContextOptions<ServiceDbContext> dbOptions, IMapper mapper)
            : base(serviceContext, dbOptions, mapper)
        {
        }

        public async Task<EmployeeOutput> GetByIdAsync(Guid id)
        {
            using (var db = new ServiceDbContext(_dbOptions))
            {
                var dto = await db.Employees.Where(o => o.Id == id)
                    .ProjectTo<EmployeeOutput>(_mapper.ConfigurationProvider)
                    .FirstOrDefaultAsync();
                if (dto != null)
                {
                    var query = from item in db.EmployeePositions
                                where item.EmployeeId == id && !item.IsPrimary
                                select item.PositionId;
                    dto.ParttimePositionIds = await query.ToListAsync();
                }
                return dto;
            }
        }

        public async Task<EmployeeOutput> GetByUserIdAsync(Guid userId)
        {
            using (var db = new ServiceDbContext(_dbOptions))
            {
                var dto = await db.Employees.Where(o => o.UserId == userId)
                    .ProjectTo<EmployeeOutput>(_mapper.ConfigurationProvider)
                    .FirstOrDefaultAsync();
                if (dto != null)
                {
                    var query = from item in db.EmployeePositions
                                where item.EmployeeId == dto.Id && !item.IsPrimary
                                select item.PositionId;
                    dto.ParttimePositionIds = await query.ToListAsync();
                }
                return dto;
            }
        }

        public async Task<List<EmployeeListOutput>> GetListByDepartmentIdAsync(Guid departmentId)
        {
            using (var db = new ServiceDbContext(_dbOptions))
            {
                return await SelectByDepartmentId(db, departmentId);
            }
        }

        public async Task<List<EmployeeListOutput>> GetListByIdsAsync(Guid[] ids)
        {
            using (var db = new ServiceDbContext(_dbOptions))
            {
                var query = from item in db.Employees
                            where ids.Contains(item.Id)
                            orderby item.Number
                            select item;

                return await query.ProjectTo<EmployeeListOutput>(_mapper.ConfigurationProvider)
                    .ToListAsync();
            }
        }

        public async Task<List<EmployeeCacheOutput>> GetCacheDataAsync()
        {
            using (var db = new ServiceDbContext(_dbOptions))
            {
                var query = from item in db.Employees
                            select item;
                return await query.ProjectTo<EmployeeCacheOutput>(_mapper.ConfigurationProvider)
                    .ToListAsync();
            }
        }

        public async Task<List<EmployeeListOutput>> GetRootListAsync()
        {
            using (var db = new ServiceDbContext(_dbOptions))
            {
                var rootDep = await db.Departments
                    .Where(o => o.ParentId == null)
                    .Select(o => o.Id)
                    .FirstOrDefaultAsync();

                return await SelectByDepartmentId(db, rootDep);
            }
        }

        public async Task<List<EmployeeListOutput>> SearchByKeywordAsync(string keyword)
        {
            using (var db = new ServiceDbContext(_dbOptions))
            {
                var query = from item in db.Employees
                            where item.Name.Contains(keyword)
                            select item;
                return await query.ProjectTo<EmployeeListOutput>(_mapper.ConfigurationProvider)
                    .ToListAsync();
            }
        }

        private Task<List<EmployeeListOutput>> SelectByDepartmentId(ServiceDbContext db, Guid departmentId)
        {
            //兼职人员也返回
            var query = from item in db.Employees
                        from ep in item.Positions
                        join pos in db.Positions.Where(o => o.DepartmentId == departmentId)
                            on ep.PositionId equals pos.Id
                        orderby item.Number
                        select new EmployeeListOutput
                        {
                            Id = item.Id,
                            Name = item.Name,
                            PositionId = pos.Id,
                            PositionName = pos.Name,
                            PrimaryDepartmentId = item.PrimaryDepartmentId,
                            PrimaryPositionId = item.PrimaryPositionId
                        };

            return query.ToListAsync();
        }

        public async Task<List<Guid>> GetUserIdsByDepartmentIdAsync(Guid departmentId)
        {
            using (var db = new ServiceDbContext(_dbOptions))
            {
                var query = from item in db.Employees
                            where item.UserId.HasValue
                            from ep in item.Positions
                            join pos in db.Positions.Where(o => o.DepartmentId == departmentId)
                                on ep.PositionId equals pos.Id
                            orderby item.Number
                            select item.UserId.Value;

                return await query.ToListAsync();
            }
        }
    }
}


<|start_filename|>src/BizSystemNotifyService/Service.cs<|end_filename|>
using System;
using System.Collections.Generic;
using System.Fabric;
using System.Fabric.Result;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Base.Eap.Notify.MsgContracts;
using BizSystemNotifyService.EventBus;
using Common;
using EnterpriseContact.Services;
using MassTransit;
using AppsOnSF.Common.Options;
using Microsoft.Diagnostics.EventFlow;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.ServiceFabric.Services.Communication.Runtime;
using Microsoft.ServiceFabric.Services.Runtime;
using ServiceFabricContrib;

namespace BizSystemNotifyService
{
    /// <summary>
    /// An instance of this class is created for each service instance by the Service Fabric runtime.
    /// </summary>
    internal sealed class Service : StatelessServiceEapBase
    {
        private readonly IBusControl _bus;

        public Service(StatelessServiceContext context,
          DiagnosticPipeline diagnosticPipeline)
          : base(context, diagnosticPipeline)
        {
            _bus = CreateBus(context);
        }

        //private const string RabbitMqReceiveEndpointName = "Base.Eap.BizSystemNotifyService";

        private IBusControl CreateBus(ServiceContext serviceContext)
        {
            var option = serviceContext.GetOption<RabbitMQOption>("RabbitMQ");

            var cache = new MemoryCache(new MemoryCacheOptions());

            var bus = Bus.Factory.CreateUsingRabbitMq(cfg =>
            {
                var host = cfg.Host(new Uri(option.HostAddress), h =>
                {
                    h.Username(option.Username);
                    h.Password(<PASSWORD>.Password);
                });
                cfg.ReceiveEndpoint(host, BizSystemNotifyMsg.RabbitMqReceiveEndpointName, c =>
                {
                    c.Consumer(() => new BizSystemNotifyMsgConsumer(
                        ConfigMgmt.RemotingClient.CreateBizSystemAppService(),
                        Notification.RemotingClient.CreateNotifySessionActor,
                        new EmployeeCacheService(cache, EnterpriseContact.RemotingClient.CreateEmployeeAppService())
                        ));
                });

            });
            return bus;
        }

        /// <summary>
        /// Optional override to create listeners (e.g., TCP, HTTP) for this service replica to handle client or user requests.
        /// </summary>
        /// <returns>A collection of listeners.</returns>
        protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
        {
            return new[]
            {
                new ServiceInstanceListener(_ => new MassTransitListener(_bus), "masstransit"),
            };
        }
    }
}


<|start_filename|>test/ConversationServiceTest/AppServices/ConversationMsgQuquProcessorTest.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using ConversationMsgStateService;
using InstantMessage;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using Notification;
using ServiceFabricContrib;
using UnitTestCommon;

namespace ConversationServiceTest.AppServices
{
    [TestClass]
    public class ConversationMsgQuquProcessorTest : TheAppServiceTestBase
    {
        [TestMethod]
        public async Task ProcessQueueAsync_Test()
        {
            //Arrage:
            await InitQueueDataAsync();

            var conversation = DefaultConversation.DeepCopy();
            var mock_conversationCtrlAppService = new Mock<IConversationCtrlAppService>();
            var mock_notifySessionActor = new Mock<INotifySessionActor>();
            mock_conversationCtrlAppService.Setup(u => u.DeleteAsync(It.IsAny<Guid>())).Returns(Task.CompletedTask);
            mock_conversationCtrlAppService.Setup(u => u.GetByIdAsync(It.IsAny<Guid>())).ReturnsAsync(conversation);
            mock_notifySessionActor.Setup(u => u.PushMsgNotifyAsync(It.IsAny<MessageNotifyDto>())).Returns(Task.CompletedTask);
            Func<Guid, INotifySessionActor> notifySessionActorFactory = (id) => mock_notifySessionActor.Object;

            //Act:
            CancellationTokenSource source = new CancellationTokenSource();
            ThreadPool.QueueUserWorkItem(u =>
            {
                Thread.Sleep(200);
                source.Cancel();
            });
            
            ConversationMsgQueueProcessor.IsInUnitTest = true;
            await ConversationMsgQueueProcessor.ProcessQueueAsync(stateManager,
                mock_conversationCtrlAppService.Object, notifySessionActorFactory, source.Token);

            //Assert:
            var msg1 = DefaultConversationMsg.DeepCopy();
            using (var tx = stateManager.CreateTransaction())
            {
                Assert.AreEqual(0, MessageProccessQueue.Count);
                var msgIndex = await MessageIndexDict.TryGetValueAsync(tx, msg1.ConversationId);
                var msgIds = msgIndex.Value;
                var msg = await MessageListDict.TryGetValueAsync(tx, msgIds[0]);
                Assert.IsTrue(msgIndex.HasValue);
                Assert.AreEqual(1, msgIds.Count);
                Assert.AreEqual(msg1, msg.Value);
                mock_conversationCtrlAppService.Verify(u => u.GetByIdAsync(It.IsAny<Guid>()), Times.Once);
                mock_notifySessionActor.Verify(u => u.PushMsgNotifyAsync(It.IsAny<MessageNotifyDto>()), Times.AtLeast(2));
            }
        }
    }
}


<|start_filename|>src/InstantMessageApi/ViewModels/SendMessageResult.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Text;

namespace InstantMessage.ViewModels
{
    /// <summary>
    /// 消息发送的结果
    /// </summary>
    public class SendMessageResult
    {
        /// <summary>
        /// 标识发送是否成功
        /// </summary>
        public bool Success { get; set; }

        /// <summary>
        /// 原因补充
        /// </summary>
        public string Message { get; set; }

        /// <summary>
        /// 消息Id
        /// </summary>
        public Guid MessageId { get; set; }
        /// <summary>
        /// 消息发送时间
        /// </summary>
        public DateTimeOffset SendTime { get; set; }
    }
}


<|start_filename|>src/ConversationMsgStateService/ArchiveGroupQueueProcessor.cs<|end_filename|>
﻿using InstantMessage.Entities;
using Microsoft.ServiceFabric.Data;
using Microsoft.ServiceFabric.Data.Collections;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using static ConversationMsgStateService.ConversationMsgAppService;

namespace ConversationMsgStateService
{
    public static class ArchiveGroupQueueProcessor
    {
        //public static async Task ProcessAsync(IReliableStateManager stateManager,
        //    IProcessorConfigurator configurator,
        //    CancellationToken cancellationToken)
        //{
        //    var queue = await Service.GetPendingArchiveGroupQueue(stateManager);
        //    var dictGroupMessagesIndices = await stateManager.GetOrAddAsync<IReliableDictionary2<Guid, List<Guid>>>(GroupMessageIndexDictName);
        //    var dictGroupMessages = await stateManager.GetOrAddAsync<IReliableDictionary2<Guid, ConversationMsg>>(GroupMessageDictName);
        //    var dictArchiveGroupMessages = await stateManager.GetOrAddAsync<IReliableDictionary2<Guid, List<ConversationMsg>>>(ArchivedGroupMessageDictName);

        //    while (!cancellationToken.IsCancellationRequested)
        //    {
        //        if (configurator.IsArchiveGroupProcessTime())
        //        {
        //            bool doDelay = false;
        //            using (var tx = stateManager.CreateTransaction())
        //            {
        //                var idWrap = await queue.TryDequeueAsync(tx);
        //                doDelay = !idWrap.HasValue;
        //                if (idWrap.HasValue)
        //                {
        //                    var id = idWrap.Value;

        //                    var msgIdsWrap = await dictGroupMessagesIndices.TryGetValueAsync(tx, id);
        //                    if (msgIdsWrap.HasValue)
        //                    {
        //                        var lstMsg = new List<ConversationMsg>();
        //                        var msgIds = msgIdsWrap.Value;
        //                        var enumerator = (await dictGroupMessages.CreateEnumerableAsync(tx,
        //                            o => msgIds.Contains(o), EnumerationMode.Unordered)).GetAsyncEnumerator();
        //                        while (await enumerator.MoveNextAsync(cancellationToken))
        //                        {
        //                            lstMsg.Add(enumerator.Current.Value);
        //                        }

        //                        //remove msg from GroupMessageDict
        //                        foreach (var msg in lstMsg)
        //                        {
        //                            await dictGroupMessages.TryRemoveAsync(tx, msg.Id);
        //                        }

        //                        //add archived msg into ArchivedGroupMessageDict
        //                        await dictArchiveGroupMessages.TryAddAsync(tx, id, lstMsg);

        //                        await tx.CommitAsync();
        //                    }
        //                }
        //            }
        //            if (doDelay) await Task.Delay(TimeSpan.FromMinutes(3));
        //        }
        //        else
        //            await Task.Delay(TimeSpan.FromSeconds(30));
        //    }
        //}
    }
}
