diff --git a/backend/IMTest/Service/AuthServiceTest.cs b/backend/IMTest/Service/AuthServiceTest.cs index 2f26b6f..8bf0142 100644 --- a/backend/IMTest/Service/AuthServiceTest.cs +++ b/backend/IMTest/Service/AuthServiceTest.cs @@ -4,13 +4,14 @@ using Moq; using AutoMapper; using IM_API.Services; using IM_API.Models; -using IM_API.Dtos; using IM_API.Exceptions; using IM_API.Tools; using Microsoft.Extensions.Logging; using System; using System.Threading; using System.Threading.Tasks; +using IM_API.Dtos.Auth; +using IM_API.Dtos.User; public class AuthServiceTests { diff --git a/backend/IMTest/Service/FriendServiceTest.cs b/backend/IMTest/Service/FriendServiceTest.cs index 05ff1da..5096971 100644 --- a/backend/IMTest/Service/FriendServiceTest.cs +++ b/backend/IMTest/Service/FriendServiceTest.cs @@ -1,25 +1,30 @@ -using Xunit; -using Microsoft.EntityFrameworkCore; -using Moq; -using AutoMapper; -using Microsoft.Extensions.Logging; -using System.Collections.Generic; -using System.Threading.Tasks; -using System; -using IM_API.Services; -using IM_API.Models; -using IM_API.Dtos; +using AutoMapper; +using IM_API.Domain.Events; +using IM_API.Dtos.Friend; using IM_API.Exceptions; -using IM_API.Tools; +using IM_API.Models; +using IM_API.Services; +using MassTransit; // 必须引入 +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using Moq; +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Xunit; public class FriendServiceTests { + private readonly Mock _mockEndpoint = new(); + private readonly Mock> _mockLogger = new(); + + #region 辅助构造方法 private ImContext CreateDbContext() { var options = new DbContextOptionsBuilder() - .UseInMemoryDatabase(Guid.NewGuid().ToString()) + .UseInMemoryDatabase(Guid.NewGuid().ToString()) // 确保每个测试数据库隔离 .Options; - return new ImContext(options); } @@ -27,132 +32,115 @@ public class FriendServiceTests { var config = new MapperConfiguration(cfg => { + // 补充你业务中实际需要的映射规则 cfg.CreateMap(); cfg.CreateMap(); - cfg.CreateMap(); cfg.CreateMap() - .ForMember(dest => dest.UserId, opt => opt.MapFrom(src => src.ResponseUser)) - .ForMember(dest => dest.FriendId, opt => opt.MapFrom(src => src.RequestUser)) - .ForMember(dest => dest.RemarkName, opt => opt.MapFrom(src => "AutoAdded")); + .ForMember(d => d.UserId, o => o.MapFrom(s => s.ResponseUser)) + .ForMember(d => d.FriendId, o => o.MapFrom(s => s.RequestUser)); }); - return config.CreateMapper(); } private FriendService CreateService(ImContext context) { - var logger = new Mock>(); - return new FriendService(context, logger.Object, CreateMapper()); + // 注入 Mock 对象和真实的 Mapper/Context + return new FriendService(context, _mockLogger.Object, CreateMapper(), _mockEndpoint.Object); } + #endregion - // --------------------------- 测试 BlockFriendAsync --------------------------- [Fact] - public async Task BlockFriendAsync_Should_Set_Status_To_Blocked() + public async Task SendFriendRequestAsync_Success_ShouldSaveAndPublish() { + // Arrange var context = CreateDbContext(); - context.Friends.Add(new Friend - { - Id = 1, - UserId = 10, - FriendId = 20, - StatusEnum = FriendStatus.Added, - RemarkName = "test remark" - }); + context.Users.AddRange( + new User { Id = 1, Username = "Sender", Password = "..." }, + new User { Id = 2, Username = "Receiver", Password = "..." } + ); await context.SaveChangesAsync(); - var service = CreateService(context); + var dto = new FriendRequestDto { ToUserId = 2, Description = "Hello" }; - var result = await service.BlockeFriendAsync(1); - - Assert.True(result); - Assert.Equal(FriendStatus.Blocked, context.Friends.Find(1).StatusEnum); - } - - [Fact] - public async Task BlockFriendAsync_Should_Throw_When_NotFound() - { - var service = CreateService(CreateDbContext()); - - await Assert.ThrowsAsync(() => service.BlockeFriendAsync(99)); - } - - // --------------------------- 删除好友关系 --------------------------- - [Fact] - public async Task DeleteFriendAsync_Should_Remove_Friend() - { - var context = CreateDbContext(); - context.Friends.Add(new Friend - { - Id = 2, - UserId = 1, - FriendId = 3, - RemarkName = "remark", - StatusEnum = FriendStatus.Added - }); - await context.SaveChangesAsync(); - - var service = CreateService(context); - - var result = await service.DeleteFriendAsync(2); - - Assert.True(result); - Assert.Empty(context.Friends); - } - - // --------------------------- 获取好友列表 --------------------------- - [Fact] - public async Task GetFriendListAsync_Should_Return_Only_Added_Friends() - { - var context = CreateDbContext(); - context.Friends.AddRange(new List - { - new Friend{ UserId = 1, FriendId = 2, RemarkName ="a1", StatusEnum = FriendStatus.Added }, - new Friend{ UserId = 1, FriendId = 3, RemarkName ="a2", StatusEnum = FriendStatus.Blocked } - }); - await context.SaveChangesAsync(); - - var service = CreateService(context); - - var result = await service.GetFriendListAsync(1, 1, 10, false); - - Assert.Single(result); - } - - // --------------------------- 发起好友请求 --------------------------- - [Fact] - public async Task SendFriendRequestAsync_Should_Succeed() - { - var context = CreateDbContext(); - context.Users.Add(new User { Id = 10, Username = "A", Password = "123" }); - context.Users.Add(new User { Id = 20, Username = "B", Password = "123" }); - await context.SaveChangesAsync(); - - var service = CreateService(context); - - var result = await service.SendFriendRequestAsync(new FriendRequestDto - { - FromUserId = 10, - ToUserId = 20 - }); + // Act + var result = await service.SendFriendRequestAsync(dto); + // Assert Assert.True(result); Assert.Single(context.FriendRequests); - Assert.Single(context.Friends); + + // 验证事件是否发布到了 MQ + _mockEndpoint.Verify(x => x.Publish( + It.Is(e => e.FromUserId == 1 && e.ToUserId == 2), + It.IsAny()), + Times.Once); } [Fact] - public async Task SendFriendRequestAsync_Should_Throw_When_User_NotFound() + public async Task SendFriendRequestAsync_UserNotFound_ShouldThrow() { + // Arrange var context = CreateDbContext(); - context.Users.Add(new User { Id = 10, Username = "A", Password = "123" }); - await context.SaveChangesAsync(); + var service = CreateService(context); + var dto = new FriendRequestDto { ToUserId = 99 }; // 不存在的用户 + // Act & Assert + await Assert.ThrowsAsync(() => service.SendFriendRequestAsync(dto)); + } + + [Fact] + public async Task SendFriendRequestAsync_AlreadyExists_ShouldThrow() + { + // Arrange + var context = CreateDbContext(); + context.Users.Add(new User { Id = 2 }); + context.FriendRequests.Add(new FriendRequest + { + RequestUser = 1, + ResponseUser = 2, + State = (sbyte)FriendRequestState.Pending + }); + await context.SaveChangesAsync(); var service = CreateService(context); - await Assert.ThrowsAsync(() => service.SendFriendRequestAsync(new FriendRequestDto - { - FromUserId = 10, - ToUserId = 99 - })); + // Act & Assert + await Assert.ThrowsAsync(() => service.SendFriendRequestAsync(new FriendRequestDto { ToUserId = 2 })); } -} + + [Fact] + public async Task BlockFriendAsync_ValidId_ShouldUpdateStatus() + { + // Arrange + var context = CreateDbContext(); + var friend = new Friend { Id = 50, UserId = 1, FriendId = 2, StatusEnum = FriendStatus.Added }; + context.Friends.Add(friend); + await context.SaveChangesAsync(); + var service = CreateService(context); + + // Act + await service.BlockeFriendAsync(50); + + // Assert + var updated = await context.Friends.FindAsync(50); + Assert.Equal(FriendStatus.Blocked, updated.StatusEnum); + } + + [Fact] + public async Task GetFriendListAsync_ShouldFilterByStatus() + { + // Arrange + var context = CreateDbContext(); + context.Friends.AddRange( + new Friend { UserId = 1, FriendId = 2, StatusEnum = FriendStatus.Added }, + new Friend { UserId = 1, FriendId = 3, StatusEnum = FriendStatus.Blocked } + ); + await context.SaveChangesAsync(); + var service = CreateService(context); + + // Act + var result = await service.GetFriendListAsync(1, 1, 10, false); + + // Assert + Assert.Single(result); // 只应该拿到 Added 状态的 + } +} \ No newline at end of file diff --git a/backend/IMTest/Service/UserServiceTest.cs b/backend/IMTest/Service/UserServiceTest.cs index ca00e60..9df831d 100644 --- a/backend/IMTest/Service/UserServiceTest.cs +++ b/backend/IMTest/Service/UserServiceTest.cs @@ -4,12 +4,12 @@ using Moq; using AutoMapper; using IM_API.Services; using IM_API.Models; -using IM_API.Dtos; using IM_API.Exceptions; using IM_API.Tools; using Microsoft.Extensions.Logging; using System; using System.Threading.Tasks; +using IM_API.Dtos.User; public class UserServiceTests { diff --git a/backend/IMTest/bin/Debug/net8.0/IMTest.deps.json b/backend/IMTest/bin/Debug/net8.0/IMTest.deps.json index ffb6077..63c1c96 100644 --- a/backend/IMTest/bin/Debug/net8.0/IMTest.deps.json +++ b/backend/IMTest/bin/Debug/net8.0/IMTest.deps.json @@ -55,6 +55,42 @@ } }, "coverlet.collector/6.0.0": {}, + "MassTransit/8.5.5": { + "dependencies": { + "MassTransit.Abstractions": "8.5.5", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.0", + "Microsoft.Extensions.Hosting.Abstractions": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2" + }, + "runtime": { + "lib/net8.0/MassTransit.dll": { + "assemblyVersion": "8.5.5.0", + "fileVersion": "8.5.5.0" + } + } + }, + "MassTransit.Abstractions/8.5.5": { + "runtime": { + "lib/net8.0/MassTransit.Abstractions.dll": { + "assemblyVersion": "8.5.5.0", + "fileVersion": "8.5.5.0" + } + } + }, + "MassTransit.RabbitMQ/8.5.5": { + "dependencies": { + "MassTransit": "8.5.5", + "RabbitMQ.Client": "7.1.2" + }, + "runtime": { + "lib/net8.0/MassTransit.RabbitMqTransport.dll": { + "assemblyVersion": "8.5.5.0", + "fileVersion": "8.5.5.0" + } + } + }, "Microsoft.AspNetCore.Authentication.Abstractions/2.3.0": { "dependencies": { "Microsoft.AspNetCore.Http.Abstractions": "2.3.0", @@ -326,6 +362,15 @@ } } }, + "Microsoft.Extensions.Diagnostics.HealthChecks/8.0.0": { + "dependencies": { + "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "8.0.0", + "Microsoft.Extensions.Hosting.Abstractions": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2" + } + }, + "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions/8.0.0": {}, "Microsoft.Extensions.FileProviders.Abstractions/8.0.0": { "dependencies": { "Microsoft.Extensions.Primitives": "8.0.0" @@ -843,6 +888,18 @@ } } }, + "RabbitMQ.Client/7.1.2": { + "dependencies": { + "System.IO.Pipelines": "8.0.0", + "System.Threading.RateLimiting": "8.0.0" + }, + "runtime": { + "lib/net8.0/RabbitMQ.Client.dll": { + "assemblyVersion": "7.0.0.0", + "fileVersion": "7.1.2.0" + } + } + }, "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": {}, "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": {}, "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": {}, @@ -1455,6 +1512,7 @@ } }, "System.Threading.Channels/8.0.0": {}, + "System.Threading.RateLimiting/8.0.0": {}, "System.Threading.Tasks/4.3.0": { "dependencies": { "Microsoft.NETCore.Platforms": "1.1.0", @@ -1573,6 +1631,7 @@ "dependencies": { "AutoMapper": "12.0.1", "AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.0", + "MassTransit.RabbitMQ": "8.5.5", "Microsoft.AspNetCore.Authentication.JwtBearer": "8.0.21", "Microsoft.AspNetCore.SignalR": "1.2.0", "Microsoft.VisualStudio.Azure.Containers.Tools.Targets": "1.22.1", @@ -1625,6 +1684,27 @@ "path": "coverlet.collector/6.0.0", "hashPath": "coverlet.collector.6.0.0.nupkg.sha512" }, + "MassTransit/8.5.5": { + "type": "package", + "serviceable": true, + "sha512": "sha512-bSg8k5q+rP1s+dIGXLLbctqDGdIkfDjdxwNWtCUH7xNCN9ZuM7mqSPQPIFgaYIi34e81m4FqAqo4CAHuWPkhRA==", + "path": "masstransit/8.5.5", + "hashPath": "masstransit.8.5.5.nupkg.sha512" + }, + "MassTransit.Abstractions/8.5.5": { + "type": "package", + "serviceable": true, + "sha512": "sha512-0mn2Ay17dD6z5tgSLjbVRlldSbL9iowzFEfVgVfBXVG5ttz9dSWeR4TrdD6pqH93GWXp4CvSmF8i1HqxLX7DZw==", + "path": "masstransit.abstractions/8.5.5", + "hashPath": "masstransit.abstractions.8.5.5.nupkg.sha512" + }, + "MassTransit.RabbitMQ/8.5.5": { + "type": "package", + "serviceable": true, + "sha512": "sha512-UxWn4o90YVMF9PBkJeoskOFPneh6YtnI1fLJHtvZiSAG0eoiRrWPGa+6FQCvjkQ/ljCKfjzok2eGZc/vmNZ01A==", + "path": "masstransit.rabbitmq/8.5.5", + "hashPath": "masstransit.rabbitmq.8.5.5.nupkg.sha512" + }, "Microsoft.AspNetCore.Authentication.Abstractions/2.3.0": { "type": "package", "serviceable": true, @@ -1870,6 +1950,20 @@ "path": "microsoft.extensions.diagnostics.abstractions/8.0.1", "hashPath": "microsoft.extensions.diagnostics.abstractions.8.0.1.nupkg.sha512" }, + "Microsoft.Extensions.Diagnostics.HealthChecks/8.0.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-P9SoBuVZhJPpALZmSq72aQEb9ryP67EdquaCZGXGrrcASTNHYdrUhnpgSwIipgM5oVC+dKpRXg5zxobmF9xr5g==", + "path": "microsoft.extensions.diagnostics.healthchecks/8.0.0", + "hashPath": "microsoft.extensions.diagnostics.healthchecks.8.0.0.nupkg.sha512" + }, + "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions/8.0.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-AT2qqos3IgI09ok36Qag9T8bb6kHJ3uT9Q5ki6CySybFsK6/9JbvQAgAHf1pVEjST0/N4JaFaCbm40R5edffwg==", + "path": "microsoft.extensions.diagnostics.healthchecks.abstractions/8.0.0", + "hashPath": "microsoft.extensions.diagnostics.healthchecks.abstractions.8.0.0.nupkg.sha512" + }, "Microsoft.Extensions.FileProviders.Abstractions/8.0.0": { "type": "package", "serviceable": true, @@ -2073,6 +2167,13 @@ "path": "pomelo.entityframeworkcore.mysql/8.0.3", "hashPath": "pomelo.entityframeworkcore.mysql.8.0.3.nupkg.sha512" }, + "RabbitMQ.Client/7.1.2": { + "type": "package", + "serviceable": true, + "sha512": "sha512-y3c6ulgULScWthHw5PLM1ShHRLhxg0vCtzX/hh61gRgNecL3ZC3WoBW2HYHoXOVRqTl99Br9E7CZEytGZEsCyQ==", + "path": "rabbitmq.client/7.1.2", + "hashPath": "rabbitmq.client.7.1.2.nupkg.sha512" + }, "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": { "type": "package", "serviceable": true, @@ -2605,6 +2706,13 @@ "path": "system.threading.channels/8.0.0", "hashPath": "system.threading.channels.8.0.0.nupkg.sha512" }, + "System.Threading.RateLimiting/8.0.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-7mu9v0QDv66ar3DpGSZHg9NuNcxDaaAcnMULuZlaTpP9+hwXhrxNGsF5GmLkSHxFdb5bBc1TzeujsRgTrPWi+Q==", + "path": "system.threading.ratelimiting/8.0.0", + "hashPath": "system.threading.ratelimiting.8.0.0.nupkg.sha512" + }, "System.Threading.Tasks/4.3.0": { "type": "package", "serviceable": true, diff --git a/backend/IMTest/bin/Debug/net8.0/IMTest.dll b/backend/IMTest/bin/Debug/net8.0/IMTest.dll index a4a7827..0b2a470 100644 Binary files a/backend/IMTest/bin/Debug/net8.0/IMTest.dll and b/backend/IMTest/bin/Debug/net8.0/IMTest.dll differ diff --git a/backend/IMTest/bin/Debug/net8.0/IMTest.pdb b/backend/IMTest/bin/Debug/net8.0/IMTest.pdb index d7f873d..6bac1a3 100644 Binary files a/backend/IMTest/bin/Debug/net8.0/IMTest.pdb and b/backend/IMTest/bin/Debug/net8.0/IMTest.pdb differ diff --git a/backend/IMTest/bin/Debug/net8.0/IM_API.deps.json b/backend/IMTest/bin/Debug/net8.0/IM_API.deps.json index edb2cfb..28566e9 100644 --- a/backend/IMTest/bin/Debug/net8.0/IM_API.deps.json +++ b/backend/IMTest/bin/Debug/net8.0/IM_API.deps.json @@ -10,6 +10,7 @@ "dependencies": { "AutoMapper": "12.0.1", "AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.0", + "MassTransit.RabbitMQ": "8.5.5", "Microsoft.AspNetCore.Authentication.JwtBearer": "8.0.21", "Microsoft.AspNetCore.SignalR": "1.2.0", "Microsoft.EntityFrameworkCore.Design": "8.0.21", @@ -56,6 +57,42 @@ } } }, + "MassTransit/8.5.5": { + "dependencies": { + "MassTransit.Abstractions": "8.5.5", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2", + "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.0", + "Microsoft.Extensions.Hosting.Abstractions": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2" + }, + "runtime": { + "lib/net8.0/MassTransit.dll": { + "assemblyVersion": "8.5.5.0", + "fileVersion": "8.5.5.0" + } + } + }, + "MassTransit.Abstractions/8.5.5": { + "runtime": { + "lib/net8.0/MassTransit.Abstractions.dll": { + "assemblyVersion": "8.5.5.0", + "fileVersion": "8.5.5.0" + } + } + }, + "MassTransit.RabbitMQ/8.5.5": { + "dependencies": { + "MassTransit": "8.5.5", + "RabbitMQ.Client": "7.1.2" + }, + "runtime": { + "lib/net8.0/MassTransit.RabbitMqTransport.dll": { + "assemblyVersion": "8.5.5.0", + "fileVersion": "8.5.5.0" + } + } + }, "Microsoft.AspNetCore.Authentication.Abstractions/2.3.0": { "dependencies": { "Microsoft.AspNetCore.Http.Abstractions": "2.3.0", @@ -565,6 +602,15 @@ } } }, + "Microsoft.Extensions.Diagnostics.HealthChecks/8.0.0": { + "dependencies": { + "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "8.0.0", + "Microsoft.Extensions.Hosting.Abstractions": "8.0.1", + "Microsoft.Extensions.Logging.Abstractions": "8.0.2", + "Microsoft.Extensions.Options": "8.0.2" + } + }, + "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions/8.0.0": {}, "Microsoft.Extensions.FileProviders.Abstractions/8.0.0": { "dependencies": { "Microsoft.Extensions.Primitives": "8.0.0" @@ -764,6 +810,18 @@ } } }, + "RabbitMQ.Client/7.1.2": { + "dependencies": { + "System.IO.Pipelines": "8.0.0", + "System.Threading.RateLimiting": "8.0.0" + }, + "runtime": { + "lib/net8.0/RabbitMQ.Client.dll": { + "assemblyVersion": "7.0.0.0", + "fileVersion": "7.1.2.0" + } + } + }, "StackExchange.Redis/2.9.32": { "dependencies": { "Microsoft.Extensions.Logging.Abstractions": "8.0.2", @@ -922,7 +980,8 @@ } }, "System.Text.Encodings.Web/8.0.0": {}, - "System.Threading.Channels/8.0.0": {} + "System.Threading.Channels/8.0.0": {}, + "System.Threading.RateLimiting/8.0.0": {} } }, "libraries": { @@ -952,6 +1011,27 @@ "path": "humanizer.core/2.14.1", "hashPath": "humanizer.core.2.14.1.nupkg.sha512" }, + "MassTransit/8.5.5": { + "type": "package", + "serviceable": true, + "sha512": "sha512-bSg8k5q+rP1s+dIGXLLbctqDGdIkfDjdxwNWtCUH7xNCN9ZuM7mqSPQPIFgaYIi34e81m4FqAqo4CAHuWPkhRA==", + "path": "masstransit/8.5.5", + "hashPath": "masstransit.8.5.5.nupkg.sha512" + }, + "MassTransit.Abstractions/8.5.5": { + "type": "package", + "serviceable": true, + "sha512": "sha512-0mn2Ay17dD6z5tgSLjbVRlldSbL9iowzFEfVgVfBXVG5ttz9dSWeR4TrdD6pqH93GWXp4CvSmF8i1HqxLX7DZw==", + "path": "masstransit.abstractions/8.5.5", + "hashPath": "masstransit.abstractions.8.5.5.nupkg.sha512" + }, + "MassTransit.RabbitMQ/8.5.5": { + "type": "package", + "serviceable": true, + "sha512": "sha512-UxWn4o90YVMF9PBkJeoskOFPneh6YtnI1fLJHtvZiSAG0eoiRrWPGa+6FQCvjkQ/ljCKfjzok2eGZc/vmNZ01A==", + "path": "masstransit.rabbitmq/8.5.5", + "hashPath": "masstransit.rabbitmq.8.5.5.nupkg.sha512" + }, "Microsoft.AspNetCore.Authentication.Abstractions/2.3.0": { "type": "package", "serviceable": true, @@ -1246,6 +1326,20 @@ "path": "microsoft.extensions.diagnostics.abstractions/8.0.1", "hashPath": "microsoft.extensions.diagnostics.abstractions.8.0.1.nupkg.sha512" }, + "Microsoft.Extensions.Diagnostics.HealthChecks/8.0.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-P9SoBuVZhJPpALZmSq72aQEb9ryP67EdquaCZGXGrrcASTNHYdrUhnpgSwIipgM5oVC+dKpRXg5zxobmF9xr5g==", + "path": "microsoft.extensions.diagnostics.healthchecks/8.0.0", + "hashPath": "microsoft.extensions.diagnostics.healthchecks.8.0.0.nupkg.sha512" + }, + "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions/8.0.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-AT2qqos3IgI09ok36Qag9T8bb6kHJ3uT9Q5ki6CySybFsK6/9JbvQAgAHf1pVEjST0/N4JaFaCbm40R5edffwg==", + "path": "microsoft.extensions.diagnostics.healthchecks.abstractions/8.0.0", + "hashPath": "microsoft.extensions.diagnostics.healthchecks.abstractions.8.0.0.nupkg.sha512" + }, "Microsoft.Extensions.FileProviders.Abstractions/8.0.0": { "type": "package", "serviceable": true, @@ -1393,6 +1487,13 @@ "path": "pomelo.entityframeworkcore.mysql/8.0.3", "hashPath": "pomelo.entityframeworkcore.mysql.8.0.3.nupkg.sha512" }, + "RabbitMQ.Client/7.1.2": { + "type": "package", + "serviceable": true, + "sha512": "sha512-y3c6ulgULScWthHw5PLM1ShHRLhxg0vCtzX/hh61gRgNecL3ZC3WoBW2HYHoXOVRqTl99Br9E7CZEytGZEsCyQ==", + "path": "rabbitmq.client/7.1.2", + "hashPath": "rabbitmq.client.7.1.2.nupkg.sha512" + }, "StackExchange.Redis/2.9.32": { "type": "package", "serviceable": true, @@ -1553,6 +1654,13 @@ "sha512": "sha512-CMaFr7v+57RW7uZfZkPExsPB6ljwzhjACWW1gfU35Y56rk72B/Wu+sTqxVmGSk4SFUlPc3cjeKND0zktziyjBA==", "path": "system.threading.channels/8.0.0", "hashPath": "system.threading.channels.8.0.0.nupkg.sha512" + }, + "System.Threading.RateLimiting/8.0.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-7mu9v0QDv66ar3DpGSZHg9NuNcxDaaAcnMULuZlaTpP9+hwXhrxNGsF5GmLkSHxFdb5bBc1TzeujsRgTrPWi+Q==", + "path": "system.threading.ratelimiting/8.0.0", + "hashPath": "system.threading.ratelimiting.8.0.0.nupkg.sha512" } } } \ No newline at end of file diff --git a/backend/IMTest/bin/Debug/net8.0/IM_API.dll b/backend/IMTest/bin/Debug/net8.0/IM_API.dll index 562d8b5..3da801b 100644 Binary files a/backend/IMTest/bin/Debug/net8.0/IM_API.dll and b/backend/IMTest/bin/Debug/net8.0/IM_API.dll differ diff --git a/backend/IMTest/bin/Debug/net8.0/IM_API.exe b/backend/IMTest/bin/Debug/net8.0/IM_API.exe index 1292e00..65fde7e 100644 Binary files a/backend/IMTest/bin/Debug/net8.0/IM_API.exe and b/backend/IMTest/bin/Debug/net8.0/IM_API.exe differ diff --git a/backend/IMTest/bin/Debug/net8.0/IM_API.pdb b/backend/IMTest/bin/Debug/net8.0/IM_API.pdb index 632c921..e74275f 100644 Binary files a/backend/IMTest/bin/Debug/net8.0/IM_API.pdb and b/backend/IMTest/bin/Debug/net8.0/IM_API.pdb differ diff --git a/backend/IMTest/bin/Debug/net8.0/appsettings.json b/backend/IMTest/bin/Debug/net8.0/appsettings.json index 9da907c..fc895fb 100644 --- a/backend/IMTest/bin/Debug/net8.0/appsettings.json +++ b/backend/IMTest/bin/Debug/net8.0/appsettings.json @@ -16,5 +16,11 @@ "ConnectionStrings": { "DefaultConnection": "Server=frp-era.com;Port=26582;Database=IM;User=product;Password=12345678;", "Redis": "192.168.5.100:6379" + }, + "RabbitMQOptions": { + "Host": "192.168.5.100", + "Port": 5672, + "Username": "test", + "Password": "123456" } } diff --git a/backend/IMTest/obj/Debug/net8.0/IMTest.AssemblyInfo.cs b/backend/IMTest/obj/Debug/net8.0/IMTest.AssemblyInfo.cs index 9319c84..ffdca41 100644 --- a/backend/IMTest/obj/Debug/net8.0/IMTest.AssemblyInfo.cs +++ b/backend/IMTest/obj/Debug/net8.0/IMTest.AssemblyInfo.cs @@ -14,7 +14,7 @@ using System.Reflection; [assembly: System.Reflection.AssemblyCompanyAttribute("IMTest")] [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] -[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+7ebf23ddd8a0d5536167313c4cf37af1552a5057")] +[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+22038345c32db146ffb78173c5410f954daa64e0")] [assembly: System.Reflection.AssemblyProductAttribute("IMTest")] [assembly: System.Reflection.AssemblyTitleAttribute("IMTest")] [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] diff --git a/backend/IMTest/obj/Debug/net8.0/IMTest.AssemblyInfoInputs.cache b/backend/IMTest/obj/Debug/net8.0/IMTest.AssemblyInfoInputs.cache index 8fe6643..44b9dd3 100644 --- a/backend/IMTest/obj/Debug/net8.0/IMTest.AssemblyInfoInputs.cache +++ b/backend/IMTest/obj/Debug/net8.0/IMTest.AssemblyInfoInputs.cache @@ -1 +1 @@ -ca0390a4d5773daae2e747d7512c190701a7a942186c769e42327a4864733f0b +ee1fc45f192938903a153f1c2e3b53f60a2184cb806b87d9b57b487095b98264 diff --git a/backend/IMTest/obj/Debug/net8.0/IMTest.GeneratedMSBuildEditorConfig.editorconfig b/backend/IMTest/obj/Debug/net8.0/IMTest.GeneratedMSBuildEditorConfig.editorconfig index c718a10..aa41851 100644 --- a/backend/IMTest/obj/Debug/net8.0/IMTest.GeneratedMSBuildEditorConfig.editorconfig +++ b/backend/IMTest/obj/Debug/net8.0/IMTest.GeneratedMSBuildEditorConfig.editorconfig @@ -1,7 +1,5 @@ is_global = true build_property.TargetFramework = net8.0 -build_property.TargetFrameworkIdentifier = .NETCoreApp -build_property.TargetFrameworkVersion = v8.0 build_property.TargetPlatformMinVersion = build_property.UsingMicrosoftNETSdkWeb = build_property.ProjectTypeGuids = diff --git a/backend/IMTest/obj/Debug/net8.0/IMTest.GlobalUsings.g.cs b/backend/IMTest/obj/Debug/net8.0/IMTest.GlobalUsings.g.cs index fe43752..2cd3d38 100644 --- a/backend/IMTest/obj/Debug/net8.0/IMTest.GlobalUsings.g.cs +++ b/backend/IMTest/obj/Debug/net8.0/IMTest.GlobalUsings.g.cs @@ -1,9 +1,9 @@ // -global using System; -global using System.Collections.Generic; -global using System.IO; -global using System.Linq; -global using System.Net.Http; -global using System.Threading; -global using System.Threading.Tasks; -global using Xunit; +global using global::System; +global using global::System.Collections.Generic; +global using global::System.IO; +global using global::System.Linq; +global using global::System.Net.Http; +global using global::System.Threading; +global using global::System.Threading.Tasks; +global using global::Xunit; diff --git a/backend/IMTest/obj/Debug/net8.0/IMTest.assets.cache b/backend/IMTest/obj/Debug/net8.0/IMTest.assets.cache index 94ea5c2..5432714 100644 Binary files a/backend/IMTest/obj/Debug/net8.0/IMTest.assets.cache and b/backend/IMTest/obj/Debug/net8.0/IMTest.assets.cache differ diff --git a/backend/IMTest/obj/Debug/net8.0/IMTest.csproj.AssemblyReference.cache b/backend/IMTest/obj/Debug/net8.0/IMTest.csproj.AssemblyReference.cache index c202171..be9dd6e 100644 Binary files a/backend/IMTest/obj/Debug/net8.0/IMTest.csproj.AssemblyReference.cache and b/backend/IMTest/obj/Debug/net8.0/IMTest.csproj.AssemblyReference.cache differ diff --git a/backend/IMTest/obj/Debug/net8.0/IMTest.csproj.CoreCompileInputs.cache b/backend/IMTest/obj/Debug/net8.0/IMTest.csproj.CoreCompileInputs.cache index a628f3f..290ddcd 100644 --- a/backend/IMTest/obj/Debug/net8.0/IMTest.csproj.CoreCompileInputs.cache +++ b/backend/IMTest/obj/Debug/net8.0/IMTest.csproj.CoreCompileInputs.cache @@ -1 +1 @@ -b97252705aa63c43e1310042fe4fd7115206f72ef0f55d39109c2b849fe2a37e +97ad978fabe5fb8f7f258c9878659ee9a5f2492332ad5efd70b1d456ebc42a59 diff --git a/backend/IMTest/obj/Debug/net8.0/IMTest.csproj.FileListAbsolute.txt b/backend/IMTest/obj/Debug/net8.0/IMTest.csproj.FileListAbsolute.txt index d11a777..6923254 100644 --- a/backend/IMTest/obj/Debug/net8.0/IMTest.csproj.FileListAbsolute.txt +++ b/backend/IMTest/obj/Debug/net8.0/IMTest.csproj.FileListAbsolute.txt @@ -140,3 +140,7 @@ C:\Users\nanxun\Documents\IM\backend\IMTest\obj\Debug\net8.0\refint\IMTest.dll C:\Users\nanxun\Documents\IM\backend\IMTest\obj\Debug\net8.0\IMTest.pdb C:\Users\nanxun\Documents\IM\backend\IMTest\obj\Debug\net8.0\IMTest.genruntimeconfig.cache C:\Users\nanxun\Documents\IM\backend\IMTest\obj\Debug\net8.0\ref\IMTest.dll +C:\Users\nanxun\Documents\IM\backend\IMTest\bin\Debug\net8.0\MassTransit.dll +C:\Users\nanxun\Documents\IM\backend\IMTest\bin\Debug\net8.0\MassTransit.Abstractions.dll +C:\Users\nanxun\Documents\IM\backend\IMTest\bin\Debug\net8.0\MassTransit.RabbitMqTransport.dll +C:\Users\nanxun\Documents\IM\backend\IMTest\bin\Debug\net8.0\RabbitMQ.Client.dll diff --git a/backend/IMTest/obj/Debug/net8.0/IMTest.dll b/backend/IMTest/obj/Debug/net8.0/IMTest.dll index a4a7827..0b2a470 100644 Binary files a/backend/IMTest/obj/Debug/net8.0/IMTest.dll and b/backend/IMTest/obj/Debug/net8.0/IMTest.dll differ diff --git a/backend/IMTest/obj/Debug/net8.0/IMTest.pdb b/backend/IMTest/obj/Debug/net8.0/IMTest.pdb index d7f873d..6bac1a3 100644 Binary files a/backend/IMTest/obj/Debug/net8.0/IMTest.pdb and b/backend/IMTest/obj/Debug/net8.0/IMTest.pdb differ diff --git a/backend/IMTest/obj/Debug/net8.0/ref/IMTest.dll b/backend/IMTest/obj/Debug/net8.0/ref/IMTest.dll index 79b0187..8cd8d14 100644 Binary files a/backend/IMTest/obj/Debug/net8.0/ref/IMTest.dll and b/backend/IMTest/obj/Debug/net8.0/ref/IMTest.dll differ diff --git a/backend/IMTest/obj/Debug/net8.0/refint/IMTest.dll b/backend/IMTest/obj/Debug/net8.0/refint/IMTest.dll index 79b0187..8cd8d14 100644 Binary files a/backend/IMTest/obj/Debug/net8.0/refint/IMTest.dll and b/backend/IMTest/obj/Debug/net8.0/refint/IMTest.dll differ diff --git a/backend/IMTest/obj/IMTest.csproj.nuget.dgspec.json b/backend/IMTest/obj/IMTest.csproj.nuget.dgspec.json index ab66086..3a75a9e 100644 --- a/backend/IMTest/obj/IMTest.csproj.nuget.dgspec.json +++ b/backend/IMTest/obj/IMTest.csproj.nuget.dgspec.json @@ -49,7 +49,7 @@ "auditLevel": "low", "auditMode": "direct" }, - "SdkAnalysisLevel": "10.0.100" + "SdkAnalysisLevel": "9.0.300" }, "frameworks": { "net8.0": { @@ -96,7 +96,7 @@ "privateAssets": "all" } }, - "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\10.0.102/PortableRuntimeIdentifierGraph.json" + "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.304/PortableRuntimeIdentifierGraph.json" } } }, @@ -141,7 +141,7 @@ "auditLevel": "low", "auditMode": "direct" }, - "SdkAnalysisLevel": "10.0.100" + "SdkAnalysisLevel": "9.0.300" }, "frameworks": { "net8.0": { @@ -223,7 +223,7 @@ "privateAssets": "all" } }, - "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\10.0.102/PortableRuntimeIdentifierGraph.json" + "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.304/PortableRuntimeIdentifierGraph.json" } } } diff --git a/backend/IMTest/obj/IMTest.csproj.nuget.g.props b/backend/IMTest/obj/IMTest.csproj.nuget.g.props index 89b6bef..a72789c 100644 --- a/backend/IMTest/obj/IMTest.csproj.nuget.g.props +++ b/backend/IMTest/obj/IMTest.csproj.nuget.g.props @@ -7,7 +7,7 @@ $(UserProfile)\.nuget\packages\ C:\Users\nanxun\.nuget\packages\;C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages PackageReference - 7.0.0 + 6.14.1 diff --git a/backend/IMTest/obj/project.assets.json b/backend/IMTest/obj/project.assets.json index 411bfb3..04dbca1 100644 --- a/backend/IMTest/obj/project.assets.json +++ b/backend/IMTest/obj/project.assets.json @@ -8805,7 +8805,7 @@ "auditLevel": "low", "auditMode": "direct" }, - "SdkAnalysisLevel": "10.0.100" + "SdkAnalysisLevel": "9.0.300" }, "frameworks": { "net8.0": { @@ -8852,16 +8852,8 @@ "privateAssets": "all" } }, - "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\10.0.102/PortableRuntimeIdentifierGraph.json" + "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.304/PortableRuntimeIdentifierGraph.json" } } - }, - "logs": [ - { - "code": "Undefined", - "level": "Warning", - "warningLevel": 1, - "message": "读取缓存文件 C:\\Users\\nanxun\\Documents\\IM\\backend\\IMTest\\obj\\project.nuget.cache 时遇到问题: '<' is an invalid start of a property name. Expected a '\"'. Path: $ | LineNumber: 2 | BytePositionInLine: 0." - } - ] + } } \ No newline at end of file diff --git a/backend/IMTest/obj/project.nuget.cache b/backend/IMTest/obj/project.nuget.cache index 18f8666..8ba4b76 100644 --- a/backend/IMTest/obj/project.nuget.cache +++ b/backend/IMTest/obj/project.nuget.cache @@ -1,6 +1,6 @@ { "version": 2, - "dgSpecHash": "j7OjEXb1ZGE=", + "dgSpecHash": "tVGTA3KwBHQ=", "success": true, "projectFilePath": "C:\\Users\\nanxun\\Documents\\IM\\backend\\IMTest\\IMTest.csproj", "expectedPackageFiles": [ @@ -169,15 +169,5 @@ "C:\\Users\\nanxun\\.nuget\\packages\\xunit.extensibility.execution\\2.5.3\\xunit.extensibility.execution.2.5.3.nupkg.sha512", "C:\\Users\\nanxun\\.nuget\\packages\\xunit.runner.visualstudio\\2.5.3\\xunit.runner.visualstudio.2.5.3.nupkg.sha512" ], - "logs": [ - { - "code": "Undefined", - "level": "Warning", - "message": "读取缓存文件 C:\\Users\\nanxun\\Documents\\IM\\backend\\IMTest\\obj\\project.nuget.cache 时遇到问题: '<' is an invalid start of a property name. Expected a '\"'. Path: $ | LineNumber: 2 | BytePositionInLine: 0.", - "projectPath": "C:\\Users\\nanxun\\Documents\\IM\\backend\\IMTest\\IMTest.csproj", - "warningLevel": 1, - "filePath": "C:\\Users\\nanxun\\Documents\\IM\\backend\\IMTest\\IMTest.csproj", - "targetGraphs": [] - } - ] + "logs": [] } \ No newline at end of file diff --git a/backend/IM_API/Application/EventHandlers/FriendAddHandler/FriendAddConversationHandler.cs b/backend/IM_API/Application/EventHandlers/FriendAddHandler/FriendAddConversationHandler.cs index 99f62fe..e621e48 100644 --- a/backend/IM_API/Application/EventHandlers/FriendAddHandler/FriendAddConversationHandler.cs +++ b/backend/IM_API/Application/EventHandlers/FriendAddHandler/FriendAddConversationHandler.cs @@ -1,13 +1,23 @@ using IM_API.Domain.Events; +using IM_API.Interface.Services; using MassTransit; namespace IM_API.Application.EventHandlers.FriendAddHandler { public class FriendAddConversationHandler : IConsumer { - public Task Consume(ConsumeContext context) + private readonly IConversationService _cService; + public FriendAddConversationHandler(IConversationService cService) { - throw new NotImplementedException(); + _cService = cService; + } + + public async Task Consume(ConsumeContext context) + { + var @event = context.Message; + await _cService.MakeConversationAsync(@event.RequestUserId, @event.ResponseUserId, Models.ChatType.PRIVATE); + await _cService.MakeConversationAsync(@event.ResponseUserId, @event.RequestUserId, Models.ChatType.PRIVATE); + } } } diff --git a/backend/IM_API/Application/EventHandlers/FriendAddHandler/FriendAddDBHandler.cs b/backend/IM_API/Application/EventHandlers/FriendAddHandler/FriendAddDBHandler.cs new file mode 100644 index 0000000..052cdd5 --- /dev/null +++ b/backend/IM_API/Application/EventHandlers/FriendAddHandler/FriendAddDBHandler.cs @@ -0,0 +1,29 @@ +using IM_API.Domain.Events; +using IM_API.Interface.Services; +using MassTransit; + +namespace IM_API.Application.EventHandlers.FriendAddHandler +{ + public class FriendAddDBHandler : IConsumer + { + private readonly IFriendSerivce _friendService; + private readonly ILogger _logger; + public FriendAddDBHandler(IFriendSerivce friendService, ILogger logger) + { + _friendService = friendService; + _logger = logger; + } + + public async Task Consume(ConsumeContext context) + { + var @event = context.Message; + + //为请求发起人添加好友记录 + await _friendService.MakeFriendshipAsync( + @event.RequestUserId, @event.ResponseUserId, @event.RequestInfo.RemarkName); + //为接收人添加好友记录 + await _friendService.MakeFriendshipAsync( + @event.ResponseUserId, @event.RequestUserId, @event.requestUserRemarkname); + } + } +} diff --git a/backend/IM_API/Application/EventHandlers/FriendAddHandler/FriendAddSignalRHandler.cs b/backend/IM_API/Application/EventHandlers/FriendAddHandler/FriendAddSignalRHandler.cs index 31cb723..77e046c 100644 --- a/backend/IM_API/Application/EventHandlers/FriendAddHandler/FriendAddSignalRHandler.cs +++ b/backend/IM_API/Application/EventHandlers/FriendAddHandler/FriendAddSignalRHandler.cs @@ -1,28 +1,37 @@ using IM_API.Domain.Events; +using IM_API.Dtos; +using IM_API.Hubs; using IM_API.Interface.Services; using IM_API.Models; using MassTransit; +using Microsoft.AspNetCore.SignalR; namespace IM_API.Application.EventHandlers.FriendAddHandler { public class FriendAddSignalRHandler : IConsumer { - private readonly IFriendSerivce _friendService; - public FriendAddSignalRHandler(IFriendSerivce friendSerivce) + private readonly IHubContext _chathub; + public FriendAddSignalRHandler(IHubContext chathub) { - _friendService = friendSerivce; + _chathub = chathub; } public async Task Consume(ConsumeContext context) { var @event = context.Message; - //为请求发起人添加好友记录 - await _friendService.MakeFriendshipAsync( - @event.RequestUser.Id, @event.ResponseUser.Id, @event.RequestInfo.RemarkName); - //为接收人添加好友记录 - await _friendService.MakeFriendshipAsync( - @event.ResponseUser.Id, @event.RequestUser.Id, @event.requestUserRemarkname); - + var usersList = new List { + @event.RequestUserId.ToString(), @event.ResponseUserId.ToString() + }; + var res = new HubResponse("Event", new MessageBaseDto() + { + ChatType = ChatType.PRIVATE, + Content = "您有新的好友关系已添加", + MsgId = @event.EventId.ToString(), + ReceiverId = @event.ResponseUserId, + SenderId = @event.RequestUserId, + TimeStamp = DateTime.UtcNow + }); + await _chathub.Clients.Users(usersList).SendAsync("ReceiveMessage", res); } } } diff --git a/backend/IM_API/Application/EventHandlers/GroupRequestHandler/GroupRequestSignalRHandler.cs b/backend/IM_API/Application/EventHandlers/GroupRequestHandler/GroupRequestSignalRHandler.cs new file mode 100644 index 0000000..8fd19ec --- /dev/null +++ b/backend/IM_API/Application/EventHandlers/GroupRequestHandler/GroupRequestSignalRHandler.cs @@ -0,0 +1,13 @@ +using IM_API.Domain.Events; +using MassTransit; + +namespace IM_API.Application.EventHandlers.GroupRequestHandler +{ + public class GroupRequestSignalRHandler : IConsumer + { + Task IConsumer.Consume(ConsumeContext context) + { + throw new NotImplementedException(); + } + } +} diff --git a/backend/IM_API/Application/EventHandlers/MessageCreatedHandler/SignalREventHandler.cs b/backend/IM_API/Application/EventHandlers/MessageCreatedHandler/SignalREventHandler.cs index 643642c..a103658 100644 --- a/backend/IM_API/Application/EventHandlers/MessageCreatedHandler/SignalREventHandler.cs +++ b/backend/IM_API/Application/EventHandlers/MessageCreatedHandler/SignalREventHandler.cs @@ -28,13 +28,13 @@ namespace IM_API.Application.EventHandlers.MessageCreatedHandler MessageBaseDto messageBaseDto = new MessageBaseDto { MsgId = @event.MessageId.ToString(), - ChatType = @event.ChatType.ToString(), + ChatType = @event.ChatType, Content = @event.MessageContent, GroupMemberId = null, ReceiverId = @event.MsgRecipientId, SenderId = @event.MsgSenderId, TimeStamp = @event.MessageCreated, - Type = @event.MessageMsgType.ToString() + Type = @event.MessageMsgType }; await _hub.Clients.Users(@event.MsgRecipientId.ToString()).SendAsync("ReceiveMessage", messageBaseDto); } diff --git a/backend/IM_API/Application/EventHandlers/RequestFriendHandler/RequestFriendSignalRHandler.cs b/backend/IM_API/Application/EventHandlers/RequestFriendHandler/RequestFriendSignalRHandler.cs new file mode 100644 index 0000000..e39be9d --- /dev/null +++ b/backend/IM_API/Application/EventHandlers/RequestFriendHandler/RequestFriendSignalRHandler.cs @@ -0,0 +1,37 @@ +using IM_API.Domain.Events; +using IM_API.Dtos; +using IM_API.Dtos.Friend; +using IM_API.Hubs; +using IM_API.Interface.Services; +using MassTransit; +using Microsoft.AspNetCore.SignalR; + +namespace IM_API.Application.EventHandlers.RequestFriendHandler +{ + public class RequestFriendSignalRHandler:IConsumer + { + private readonly IHubContext _hub; + private readonly IUserService _userService; + public RequestFriendSignalRHandler(IHubContext hubContext, IUserService userService) + { + _hub = hubContext; + _userService = userService; + } + + public async Task Consume(ConsumeContext context) + { + var @event = context.Message; + var userInfo = await _userService.GetUserInfoAsync(@event.FromUserId); + var res = new HubResponse("Event", new FriendRequestResDto() + { + RequestUser = @event.FromUserId, + ResponseUser = @event.ToUserId, + Created = DateTime.UtcNow, + Description = @event.Description, + Avatar = userInfo.Avatar, + NickName = userInfo.NickName + }); + await _hub.Clients.User(@event.ToUserId.ToString()).SendAsync("ReceiveMessage", res); + } + } +} diff --git a/backend/IM_API/Configs/MQConfig.cs b/backend/IM_API/Configs/MQConfig.cs index 70c8d9e..b694705 100644 --- a/backend/IM_API/Configs/MQConfig.cs +++ b/backend/IM_API/Configs/MQConfig.cs @@ -1,5 +1,6 @@ using IM_API.Application.EventHandlers.FriendAddHandler; using IM_API.Application.EventHandlers.MessageCreatedHandler; +using IM_API.Application.EventHandlers.RequestFriendHandler; using MassTransit; namespace IM_API.Configs @@ -12,8 +13,10 @@ namespace IM_API.Configs { x.AddConsumer(); x.AddConsumer(); - x.AddConsumer(); + x.AddConsumer(); x.AddConsumer(); + x.AddConsumer(); + x.AddConsumer(); x.UsingRabbitMq((ctx,cfg) => { @@ -22,6 +25,7 @@ namespace IM_API.Configs h.Username(options.Username); h.Password(options.Password); }); + cfg.ConfigureEndpoints(ctx); }); }); diff --git a/backend/IM_API/Configs/MapperConfig.cs b/backend/IM_API/Configs/MapperConfig.cs index e196430..a117a54 100644 --- a/backend/IM_API/Configs/MapperConfig.cs +++ b/backend/IM_API/Configs/MapperConfig.cs @@ -1,6 +1,11 @@ using AutoMapper; using IM_API.Domain.Events; using IM_API.Dtos; +using IM_API.Dtos.Auth; +using IM_API.Dtos.Conversation; +using IM_API.Dtos.Friend; +using IM_API.Dtos.Group; +using IM_API.Dtos.User; using IM_API.Models; namespace IM_API.Configs @@ -46,6 +51,13 @@ namespace IM_API.Configs .ForMember(dest => dest.StateEnum , opt => opt.MapFrom(src => FriendRequestState.Pending)) .ForMember(dest => dest.Description , opt => opt.MapFrom(src => src.Description)) ; + + CreateMap() + .ForMember(dest => dest.ToUserId, opt => opt.MapFrom(src => src.ResponseUser)) + .ForMember(dest => dest.FromUserId, opt => opt.MapFrom(src => src.RequestUser)) + .ForMember(dest => dest.RemarkName, opt => opt.MapFrom(src => src.RemarkName)) + .ForMember(dest => dest.Description, opt => opt.MapFrom(src => src.Description)) + ; //消息模型转换 CreateMap() .ForMember(dest => dest.Type , opt => opt.MapFrom(src => src.MsgTypeEnum.ToString())) @@ -59,8 +71,8 @@ namespace IM_API.Configs ; CreateMap() .ForMember(dest => dest.Sender, opt => opt.MapFrom(src => src.SenderId)) - .ForMember(dest => dest.ChatTypeEnum,opt => opt.MapFrom(src => Enum.Parse(src.ChatType,true))) - .ForMember(dest => dest.MsgTypeEnum, opt => opt.MapFrom(src => Enum.Parse(src.Type,true))) + .ForMember(dest => dest.ChatTypeEnum,opt => opt.MapFrom(src => src.ChatType)) + .ForMember(dest => dest.MsgTypeEnum, opt => opt.MapFrom(src => src.Type)) .ForMember(dest => dest.Created, opt => opt.MapFrom(src => src.TimeStamp)) .ForMember(dest => dest.Content, opt => opt.MapFrom(src => src.Content)) .ForMember(dest => dest.Recipient, opt => opt.MapFrom(src => src.ReceiverId)) @@ -124,6 +136,21 @@ namespace IM_API.Configs CreateMap() .ForMember(dest => dest.TargetAvatar, opt => opt.MapFrom(src => src.Avatar)) .ForMember(dest => dest.TargetName, opt => opt.MapFrom(src => src.Name)); + + + //群模型转换 + CreateMap() + .ForMember(dest => dest.Status, opt => opt.MapFrom(src => src.StatusEnum)) + .ForMember(dest => dest.AllMembersBanned, opt => opt.MapFrom(src => src.AllMembersBannedEnum)) + .ForMember(dest => dest.Auhority, opt => opt.MapFrom(src => src.AuhorityEnum)); + + CreateMap() + .ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.Name)) + .ForMember(dest => dest.Avatar, opt => opt.MapFrom(src => src.Avatar)) + .ForMember(dest => dest.Created, opt => opt.MapFrom(src => DateTime.UtcNow)) + .ForMember(dest => dest.AllMembersBannedEnum, opt => opt.MapFrom(src => GroupAllMembersBanned.ALLOWED)) + .ForMember(dest => dest.AuhorityEnum, opt => opt.MapFrom(src => GroupAuhority.REQUIRE_CONSENT)) + .ForMember(dest => dest.StatusEnum, opt => opt.MapFrom(src => GroupStatus.Normal)); } } } diff --git a/backend/IM_API/Controllers/AuthController.cs b/backend/IM_API/Controllers/AuthController.cs index 612e647..a9fbf3a 100644 --- a/backend/IM_API/Controllers/AuthController.cs +++ b/backend/IM_API/Controllers/AuthController.cs @@ -1,5 +1,7 @@ using AutoMapper; using IM_API.Dtos; +using IM_API.Dtos.Auth; +using IM_API.Dtos.User; using IM_API.Interface.Services; using IM_API.Tools; using Microsoft.AspNetCore.Http; diff --git a/backend/IM_API/Controllers/ConversationController.cs b/backend/IM_API/Controllers/ConversationController.cs index 61c98fd..311b106 100644 --- a/backend/IM_API/Controllers/ConversationController.cs +++ b/backend/IM_API/Controllers/ConversationController.cs @@ -1,4 +1,5 @@ using IM_API.Dtos; +using IM_API.Dtos.Conversation; using IM_API.Interface.Services; using IM_API.Models; using Microsoft.AspNetCore.Authorization; diff --git a/backend/IM_API/Controllers/FriendController.cs b/backend/IM_API/Controllers/FriendController.cs index e077d54..ea8c53f 100644 --- a/backend/IM_API/Controllers/FriendController.cs +++ b/backend/IM_API/Controllers/FriendController.cs @@ -1,4 +1,5 @@ using IM_API.Dtos; +using IM_API.Dtos.Friend; using IM_API.Interface.Services; using IM_API.Models; using Microsoft.AspNetCore.Authorization; @@ -60,7 +61,7 @@ namespace IM_API.Controllers /// [HttpPost] public async Task HandleRequest( - [FromRoute]int id, [FromBody]FriendRequestHandleDto dto + [FromQuery]int id, [FromBody]FriendRequestHandleDto dto ) { await _friendService.HandleFriendRequestAsync(new HandleFriendRequestDto() diff --git a/backend/IM_API/Controllers/UserController.cs b/backend/IM_API/Controllers/UserController.cs index 074a9ca..6b8587e 100644 --- a/backend/IM_API/Controllers/UserController.cs +++ b/backend/IM_API/Controllers/UserController.cs @@ -1,4 +1,5 @@ using IM_API.Dtos; +using IM_API.Dtos.User; using IM_API.Interface.Services; using IM_API.Tools; using Microsoft.AspNetCore.Authorization; diff --git a/backend/IM_API/Domain/Events/FriendAddEvent.cs b/backend/IM_API/Domain/Events/FriendAddEvent.cs index fefce14..d2e6378 100644 --- a/backend/IM_API/Domain/Events/FriendAddEvent.cs +++ b/backend/IM_API/Domain/Events/FriendAddEvent.cs @@ -1,4 +1,4 @@ -using IM_API.Dtos; +using IM_API.Dtos.Friend; namespace IM_API.Domain.Events { @@ -8,12 +8,12 @@ namespace IM_API.Domain.Events /// /// 发起请求用户 /// - public UserInfoDto RequestUser { get; init; } + public int RequestUserId { get; init; } public string? requestUserRemarkname { get; init; } /// /// 接受请求用户 /// - public UserInfoDto ResponseUser { get; init; } + public int ResponseUserId { get; init; } public FriendRequestDto RequestInfo { get; init; } /// diff --git a/backend/IM_API/Domain/Events/GroupInviteEvent.cs b/backend/IM_API/Domain/Events/GroupInviteEvent.cs new file mode 100644 index 0000000..b4bc47c --- /dev/null +++ b/backend/IM_API/Domain/Events/GroupInviteEvent.cs @@ -0,0 +1,10 @@ +namespace IM_API.Domain.Events +{ + public record GroupInviteEvent : DomainEvent + { + public override string EventType => "IM.GROUPS_GROUP_INVITE"; + public required List Ids { get; init; } + public int GroupId { get; init; } + public int UserId { get; init; } + } +} diff --git a/backend/IM_API/Domain/Events/GroupRequestEvent.cs b/backend/IM_API/Domain/Events/GroupRequestEvent.cs new file mode 100644 index 0000000..8c65739 --- /dev/null +++ b/backend/IM_API/Domain/Events/GroupRequestEvent.cs @@ -0,0 +1,13 @@ +using IM_API.Dtos.Group; + +namespace IM_API.Domain.Events +{ + public record GroupRequestEvent : DomainEvent + { + public override string EventType => "IM.GROUPS_GROUP_REQUEST"; + public int GroupId { get; init; } + public int UserId { get; set; } + public string Description { get; set; } + + } +} diff --git a/backend/IM_API/Domain/Events/RequestFriendEvent.cs b/backend/IM_API/Domain/Events/RequestFriendEvent.cs new file mode 100644 index 0000000..62a80ce --- /dev/null +++ b/backend/IM_API/Domain/Events/RequestFriendEvent.cs @@ -0,0 +1,10 @@ +namespace IM_API.Domain.Events +{ + public record RequestFriendEvent : DomainEvent + { + public override string EventType => "IM.FRIENDS_FRIEND_REQUEST"; + public int FromUserId { get; init; } + public int ToUserId { get; init; } + public string Description { get; init; } + } +} diff --git a/backend/IM_API/Dtos/AuthDto.cs b/backend/IM_API/Dtos/Auth/AuthDto.cs similarity index 64% rename from backend/IM_API/Dtos/AuthDto.cs rename to backend/IM_API/Dtos/Auth/AuthDto.cs index 73f6d74..27b95a7 100644 --- a/backend/IM_API/Dtos/AuthDto.cs +++ b/backend/IM_API/Dtos/Auth/AuthDto.cs @@ -1,4 +1,4 @@ -namespace IM_API.Dtos +namespace IM_API.Dtos.Auth { public record RefreshDto(string refreshToken); } diff --git a/backend/IM_API/Dtos/LoginDto.cs b/backend/IM_API/Dtos/Auth/LoginDto.cs similarity index 70% rename from backend/IM_API/Dtos/LoginDto.cs rename to backend/IM_API/Dtos/Auth/LoginDto.cs index bf29b7d..ce9f9dd 100644 --- a/backend/IM_API/Dtos/LoginDto.cs +++ b/backend/IM_API/Dtos/Auth/LoginDto.cs @@ -1,4 +1,6 @@ -namespace IM_API.Dtos +using IM_API.Dtos.User; + +namespace IM_API.Dtos.Auth { public class LoginDto { @@ -8,9 +10,9 @@ public DateTime ExpireAt { get; set; } public LoginDto(UserInfoDto userInfo,string token, string refreshToken, DateTime expireAt) { this.userInfo = userInfo; - this.Token = token; - this.RefreshToken = refreshToken; - this.ExpireAt = expireAt; + Token = token; + RefreshToken = refreshToken; + ExpireAt = expireAt; } } } diff --git a/backend/IM_API/Dtos/LoginRequestDto.cs b/backend/IM_API/Dtos/Auth/LoginRequestDto.cs similarity index 95% rename from backend/IM_API/Dtos/LoginRequestDto.cs rename to backend/IM_API/Dtos/Auth/LoginRequestDto.cs index 16cd470..f4d7e0f 100644 --- a/backend/IM_API/Dtos/LoginRequestDto.cs +++ b/backend/IM_API/Dtos/Auth/LoginRequestDto.cs @@ -1,7 +1,7 @@ using IM_API.Tools; using System.ComponentModel.DataAnnotations; -namespace IM_API.Dtos +namespace IM_API.Dtos.Auth { public class LoginRequestDto { diff --git a/backend/IM_API/Dtos/RegisterRequestDto.cs b/backend/IM_API/Dtos/Auth/RegisterRequestDto.cs similarity index 96% rename from backend/IM_API/Dtos/RegisterRequestDto.cs rename to backend/IM_API/Dtos/Auth/RegisterRequestDto.cs index 83aaea4..c32dd87 100644 --- a/backend/IM_API/Dtos/RegisterRequestDto.cs +++ b/backend/IM_API/Dtos/Auth/RegisterRequestDto.cs @@ -1,6 +1,6 @@ using System.ComponentModel.DataAnnotations; -namespace IM_API.Dtos +namespace IM_API.Dtos.Auth { public class RegisterRequestDto { diff --git a/backend/IM_API/Dtos/ClearConversationsDto.cs b/backend/IM_API/Dtos/Conversation/ClearConversationsDto.cs similarity index 93% rename from backend/IM_API/Dtos/ClearConversationsDto.cs rename to backend/IM_API/Dtos/Conversation/ClearConversationsDto.cs index 34cb3df..fe1067d 100644 --- a/backend/IM_API/Dtos/ClearConversationsDto.cs +++ b/backend/IM_API/Dtos/Conversation/ClearConversationsDto.cs @@ -1,4 +1,4 @@ -namespace IM_API.Dtos +namespace IM_API.Dtos.Conversation { public class ClearConversationsDto { diff --git a/backend/IM_API/Dtos/ConversationDto.cs b/backend/IM_API/Dtos/Conversation/ConversationDto.cs similarity index 97% rename from backend/IM_API/Dtos/ConversationDto.cs rename to backend/IM_API/Dtos/Conversation/ConversationDto.cs index 585bde6..0efcf36 100644 --- a/backend/IM_API/Dtos/ConversationDto.cs +++ b/backend/IM_API/Dtos/Conversation/ConversationDto.cs @@ -1,6 +1,6 @@ using IM_API.Models; -namespace IM_API.Dtos +namespace IM_API.Dtos.Conversation { public class ConversationDto { diff --git a/backend/IM_API/Dtos/FriendDto.cs b/backend/IM_API/Dtos/Friend/FriendDto.cs similarity index 90% rename from backend/IM_API/Dtos/FriendDto.cs rename to backend/IM_API/Dtos/Friend/FriendDto.cs index 55ed3c7..8f2ecb5 100644 --- a/backend/IM_API/Dtos/FriendDto.cs +++ b/backend/IM_API/Dtos/Friend/FriendDto.cs @@ -1,7 +1,8 @@ -using IM_API.Models; +using IM_API.Dtos.User; +using IM_API.Models; using System.ComponentModel.DataAnnotations; -namespace IM_API.Dtos +namespace IM_API.Dtos.Friend { public record FriendInfoDto { diff --git a/backend/IM_API/Dtos/FriendRequestDto.cs b/backend/IM_API/Dtos/Friend/FriendRequestDto.cs similarity index 94% rename from backend/IM_API/Dtos/FriendRequestDto.cs rename to backend/IM_API/Dtos/Friend/FriendRequestDto.cs index 106117b..fdef132 100644 --- a/backend/IM_API/Dtos/FriendRequestDto.cs +++ b/backend/IM_API/Dtos/Friend/FriendRequestDto.cs @@ -1,6 +1,6 @@ using System.ComponentModel.DataAnnotations; -namespace IM_API.Dtos +namespace IM_API.Dtos.Friend { public class FriendRequestDto { diff --git a/backend/IM_API/Dtos/FriendRequestResDto.cs b/backend/IM_API/Dtos/Friend/FriendRequestResDto.cs similarity index 96% rename from backend/IM_API/Dtos/FriendRequestResDto.cs rename to backend/IM_API/Dtos/Friend/FriendRequestResDto.cs index 1fbf76d..45e94dd 100644 --- a/backend/IM_API/Dtos/FriendRequestResDto.cs +++ b/backend/IM_API/Dtos/Friend/FriendRequestResDto.cs @@ -1,6 +1,6 @@ using IM_API.Models; -namespace IM_API.Dtos +namespace IM_API.Dtos.Friend { public class FriendRequestResDto { diff --git a/backend/IM_API/Dtos/HandleFriendRequestDto.cs b/backend/IM_API/Dtos/Friend/HandleFriendRequestDto.cs similarity index 95% rename from backend/IM_API/Dtos/HandleFriendRequestDto.cs rename to backend/IM_API/Dtos/Friend/HandleFriendRequestDto.cs index 3b90c04..afd2d00 100644 --- a/backend/IM_API/Dtos/HandleFriendRequestDto.cs +++ b/backend/IM_API/Dtos/Friend/HandleFriendRequestDto.cs @@ -1,4 +1,4 @@ -namespace IM_API.Dtos +namespace IM_API.Dtos.Friend { public class HandleFriendRequestDto { diff --git a/backend/IM_API/Dtos/Group/GroupCreateDto.cs b/backend/IM_API/Dtos/Group/GroupCreateDto.cs new file mode 100644 index 0000000..208371c --- /dev/null +++ b/backend/IM_API/Dtos/Group/GroupCreateDto.cs @@ -0,0 +1,17 @@ +using IM_API.Models; + +namespace IM_API.Dtos.Group +{ + public class GroupCreateDto + { + /// + /// 群聊名称 + /// + public string Name { get; set; } = null!; + + /// + /// 群头像 + /// + public string Avatar { get; set; } = null!; + } +} diff --git a/backend/IM_API/Dtos/Group/GroupInfoDto.cs b/backend/IM_API/Dtos/Group/GroupInfoDto.cs new file mode 100644 index 0000000..c9d8d9c --- /dev/null +++ b/backend/IM_API/Dtos/Group/GroupInfoDto.cs @@ -0,0 +1,51 @@ +using IM_API.Models; + +namespace IM_API.Dtos.Group +{ + public class GroupInfoDto + { + public int Id { get; set; } + + /// + /// 群聊名称 + /// + public string Name { get; set; } = null!; + + /// + /// 群主 + /// + public int GroupMaster { get; set; } + + /// + /// 群权限 + /// (0:需管理员同意,1:任意人可加群,2:不允许任何人加入) + /// + public GroupAuhority Auhority { get; set; } + + /// + /// 全员禁言(0允许发言,2全员禁言) + /// + public GroupAllMembersBanned AllMembersBanned { get; set; } + + /// + /// 群聊状态 + /// (1:正常,2:封禁) + /// + public GroupStatus Status { get; set; } + + /// + /// 群公告 + /// + public string? Announcement { get; set; } + + /// + /// 群聊创建时间 + /// + public DateTime Created { get; set; } + + /// + /// 群头像 + /// + public string Avatar { get; set; } = null!; + } +} diff --git a/backend/IM_API/Dtos/HubResponse.cs b/backend/IM_API/Dtos/HubResponse.cs new file mode 100644 index 0000000..eb167bf --- /dev/null +++ b/backend/IM_API/Dtos/HubResponse.cs @@ -0,0 +1,47 @@ +using IM_API.Tools; + +namespace IM_API.Dtos +{ + public class HubResponse + { + public int Code { get; init; } + public string Method { get; init; } + public HubResponseType Type { get; init; } + public string Message { get; init; } + public T? Data { get; init; } + public HubResponse(string method) + { + Code = CodeDefine.SUCCESS.Code; + Message = CodeDefine.SUCCESS.Message; + Type = HubResponseType.ActionStatus; + } + public HubResponse(string method,T data) + { + Code = CodeDefine.SUCCESS.Code; + Message = CodeDefine.SUCCESS.Message; + Type = HubResponseType.ActionStatus; + Data = data; + } + public HubResponse(CodeDefine codedefine,string method) + { + Code = codedefine.Code; + Method = method; + Message = codedefine.Message; + Type = HubResponseType.ActionStatus; + } + public HubResponse(CodeDefine codeDefine, string method, HubResponseType type, T? data) + { + Code = codeDefine.Code; + Method = method; + Type = type; + Message = codeDefine.Message; + Data = data; + } + } + public enum HubResponseType + { + ChatMsg = 1, // 聊天内容 + SystemNotice = 2, // 系统通知(如:申请好友成功) + ActionStatus = 3 // 状态变更(如:对方正在输入、已读回执) + } +} diff --git a/backend/IM_API/Dtos/MessageDto.cs b/backend/IM_API/Dtos/MessageDto.cs index c7bcac6..a546b89 100644 --- a/backend/IM_API/Dtos/MessageDto.cs +++ b/backend/IM_API/Dtos/MessageDto.cs @@ -1,10 +1,12 @@ -namespace IM_API.Dtos +using IM_API.Models; + +namespace IM_API.Dtos { public record MessageBaseDto { // 使用 { get; init; } 确保对象创建后不可修改,且支持无参构造 - public string Type { get; init; } = default!; - public string ChatType { get; init; } = default!; + public MessageMsgType Type { get; init; } = default!; + public ChatType ChatType { get; init; } = default!; public string? MsgId { get; init; } public int SenderId { get; init; } public int ReceiverId { get; init; } diff --git a/backend/IM_API/Dtos/UpdateUserDto.cs b/backend/IM_API/Dtos/User/UpdateUserDto.cs similarity index 90% rename from backend/IM_API/Dtos/UpdateUserDto.cs rename to backend/IM_API/Dtos/User/UpdateUserDto.cs index e7949e7..4ac40f2 100644 --- a/backend/IM_API/Dtos/UpdateUserDto.cs +++ b/backend/IM_API/Dtos/User/UpdateUserDto.cs @@ -1,6 +1,6 @@ using System.ComponentModel.DataAnnotations; -namespace IM_API.Dtos +namespace IM_API.Dtos.User { public class UpdateUserDto { diff --git a/backend/IM_API/Dtos/UserDto.cs b/backend/IM_API/Dtos/User/UserDto.cs similarity index 95% rename from backend/IM_API/Dtos/UserDto.cs rename to backend/IM_API/Dtos/User/UserDto.cs index 655d1a1..d8a1a57 100644 --- a/backend/IM_API/Dtos/UserDto.cs +++ b/backend/IM_API/Dtos/User/UserDto.cs @@ -1,7 +1,7 @@ using IM_API.Models; using System.ComponentModel.DataAnnotations; -namespace IM_API.Dtos +namespace IM_API.Dtos.User { public record PasswordResetDto { diff --git a/backend/IM_API/Dtos/UserInfoDto.cs b/backend/IM_API/Dtos/User/UserInfoDto.cs similarity index 97% rename from backend/IM_API/Dtos/UserInfoDto.cs rename to backend/IM_API/Dtos/User/UserInfoDto.cs index c5deba6..f77bb24 100644 --- a/backend/IM_API/Dtos/UserInfoDto.cs +++ b/backend/IM_API/Dtos/User/UserInfoDto.cs @@ -2,7 +2,7 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -namespace IM_API.Dtos +namespace IM_API.Dtos.User { public class UserInfoDto { diff --git a/backend/IM_API/Hubs/ChatHub.cs b/backend/IM_API/Hubs/ChatHub.cs index 37a26eb..da23832 100644 --- a/backend/IM_API/Hubs/ChatHub.cs +++ b/backend/IM_API/Hubs/ChatHub.cs @@ -40,17 +40,17 @@ namespace IM_API.Hubs } await base.OnConnectedAsync(); } - public async Task SendMessage(MessageBaseDto dto) + public async Task> SendMessage(MessageBaseDto dto) { if (!Context.User.Identity.IsAuthenticated) { await Clients.Caller.SendAsync("ReceiveMessage", new BaseResponse(CodeDefine.AUTH_FAILED)); Context.Abort(); - return; + return new HubResponse(CodeDefine.AUTH_FAILED, "SendMessage"); } var userIdStr = Context.User.FindFirstValue(ClaimTypes.NameIdentifier); MessageBaseDto msgInfo = null; - if(dto.ChatType.ToLower() == ChatType.PRIVATE.ToString().ToLower()) + if(dto.ChatType == ChatType.PRIVATE) { msgInfo = await _messageService.SendPrivateMessageAsync(int.Parse(userIdStr), dto.ReceiverId, dto); } @@ -58,19 +58,19 @@ namespace IM_API.Hubs { msgInfo = await _messageService.SendGroupMessageAsync(int.Parse(userIdStr), dto.ReceiverId, dto); } - return; + return new HubResponse("SendMessage", msgInfo); } - public async Task ClearUnreadCount(int conversationId) + public async Task> ClearUnreadCount(int conversationId) { if (!Context.User.Identity.IsAuthenticated) { await Clients.Caller.SendAsync("ReceiveMessage", new BaseResponse(CodeDefine.AUTH_FAILED)); Context.Abort(); - return; + return new HubResponse(CodeDefine.AUTH_FAILED, "ClearUnreadCount"); ; } var userIdStr = Context.User.FindFirstValue(ClaimTypes.NameIdentifier); await _conversationService.ClearUnreadCountAsync(int.Parse(userIdStr), conversationId); - return; + return new HubResponse("ClearUnreadCount"); } } } diff --git a/backend/IM_API/IM_API.csproj b/backend/IM_API/IM_API.csproj index 92d775a..b47084f 100644 --- a/backend/IM_API/IM_API.csproj +++ b/backend/IM_API/IM_API.csproj @@ -31,4 +31,8 @@ + + + + diff --git a/backend/IM_API/Interface/Services/IAuthService.cs b/backend/IM_API/Interface/Services/IAuthService.cs index 1c4ce71..be275d6 100644 --- a/backend/IM_API/Interface/Services/IAuthService.cs +++ b/backend/IM_API/Interface/Services/IAuthService.cs @@ -1,4 +1,5 @@ -using IM_API.Dtos; +using IM_API.Dtos.Auth; +using IM_API.Dtos.User; using IM_API.Models; namespace IM_API.Interface.Services diff --git a/backend/IM_API/Interface/Services/IConversationService.cs b/backend/IM_API/Interface/Services/IConversationService.cs index 474bb16..dfc3642 100644 --- a/backend/IM_API/Interface/Services/IConversationService.cs +++ b/backend/IM_API/Interface/Services/IConversationService.cs @@ -1,4 +1,4 @@ -using IM_API.Dtos; +using IM_API.Dtos.Conversation; using IM_API.Models; namespace IM_API.Interface.Services @@ -42,5 +42,13 @@ namespace IM_API.Interface.Services /// /// Task ClearUnreadCountAsync(int userId, int conversationId); + /// + /// 为用户创建会话 + /// + /// + /// + /// + /// + Task MakeConversationAsync(int userAId, int userBId, ChatType chatType); } } diff --git a/backend/IM_API/Interface/Services/IFriendSerivce.cs b/backend/IM_API/Interface/Services/IFriendSerivce.cs index b4d96a8..b4e29d3 100644 --- a/backend/IM_API/Interface/Services/IFriendSerivce.cs +++ b/backend/IM_API/Interface/Services/IFriendSerivce.cs @@ -1,4 +1,4 @@ -using IM_API.Dtos; +using IM_API.Dtos.Friend; using IM_API.Models; namespace IM_API.Interface.Services diff --git a/backend/IM_API/Interface/Services/IGroupService.cs b/backend/IM_API/Interface/Services/IGroupService.cs new file mode 100644 index 0000000..b337df8 --- /dev/null +++ b/backend/IM_API/Interface/Services/IGroupService.cs @@ -0,0 +1,39 @@ +using IM_API.Dtos.Group; + +namespace IM_API.Interface.Services +{ + public interface IGroupService + { + /// + /// 邀请好友入群 + /// + /// 操作者ID + /// 群ID + /// 邀请的用户列表 + /// + Task InviteUsers(int userId,int groupId, List userIds); + /// + /// 加入群聊 + /// + /// 操作者ID + /// 群ID + /// + Task JoinGroup(int userId,int groupId); + /// + /// 创建群聊 + /// + /// 操作者ID + /// 群信息 + /// 邀请用户列表 + /// + Task CreateGroup(int userId, GroupCreateDto groupCreateDto, List userIds); + /// + /// 删除群 + /// + /// 操作者ID + /// 群ID + /// + Task DeleteGroup(int userId, int groupId); + //Task UpdateGroupAuthori + } +} diff --git a/backend/IM_API/Interface/Services/IUserService.cs b/backend/IM_API/Interface/Services/IUserService.cs index 698606f..147ae9a 100644 --- a/backend/IM_API/Interface/Services/IUserService.cs +++ b/backend/IM_API/Interface/Services/IUserService.cs @@ -1,4 +1,4 @@ -using IM_API.Dtos; +using IM_API.Dtos.User; using IM_API.Models; namespace IM_API.Interface.Services diff --git a/backend/IM_API/Models/Friendrequest.cs b/backend/IM_API/Models/Friendrequest.cs index 3897a5e..2c969b9 100644 --- a/backend/IM_API/Models/Friendrequest.cs +++ b/backend/IM_API/Models/Friendrequest.cs @@ -32,6 +32,11 @@ public partial class FriendRequest /// public sbyte State { get; set; } + /// + /// 备注 + /// + public string RemarkName { get; set; } = null!; + public virtual User RequestUserNavigation { get; set; } = null!; public virtual User ResponseUserNavigation { get; set; } = null!; diff --git a/backend/IM_API/Models/Group.cs b/backend/IM_API/Models/Group.cs index 86522af..d58e0aa 100644 --- a/backend/IM_API/Models/Group.cs +++ b/backend/IM_API/Models/Group.cs @@ -53,5 +53,7 @@ public partial class Group public virtual User GroupMasterNavigation { get; set; } = null!; + public virtual ICollection GroupMembers { get; set; } = new List(); + public virtual ICollection GroupRequests { get; set; } = new List(); } diff --git a/backend/IM_API/Models/Groupmember.cs b/backend/IM_API/Models/Groupmember.cs index 849ea37..e28b91b 100644 --- a/backend/IM_API/Models/Groupmember.cs +++ b/backend/IM_API/Models/Groupmember.cs @@ -27,7 +27,7 @@ public partial class GroupMember /// public DateTime Created { get; set; } - public virtual User Group { get; set; } = null!; + public virtual Group Group { get; set; } = null!; public virtual User User { get; set; } = null!; } diff --git a/backend/IM_API/Models/ImContext.cs b/backend/IM_API/Models/ImContext.cs index eac2179..aeae98b 100644 --- a/backend/IM_API/Models/ImContext.cs +++ b/backend/IM_API/Models/ImContext.cs @@ -281,6 +281,9 @@ public partial class ImContext : DbContext entity.Property(e => e.Description) .HasComment("申请附言 ") .HasColumnType("text"); + entity.Property(e => e.RemarkName) + .HasMaxLength(20) + .HasComment("备注"); entity.Property(e => e.RequestUser) .HasComment("申请人 ") .HasColumnType("int(11)"); @@ -429,12 +432,12 @@ public partial class ImContext : DbContext .HasComment("用户编号") .HasColumnType("int(11)"); - entity.HasOne(d => d.Group).WithMany(p => p.GroupMemberGroups) + entity.HasOne(d => d.Group).WithMany(p => p.GroupMembers) .HasForeignKey(d => d.GroupId) .OnDelete(DeleteBehavior.ClientSetNull) .HasConstraintName("group_member_ibfk_2"); - entity.HasOne(d => d.User).WithMany(p => p.GroupMemberUsers) + entity.HasOne(d => d.User).WithMany(p => p.GroupMembers) .HasForeignKey(d => d.UserId) .OnDelete(DeleteBehavior.ClientSetNull) .HasConstraintName("group_member_ibfk_1"); diff --git a/backend/IM_API/Models/User.cs b/backend/IM_API/Models/User.cs index c390560..0e91c99 100644 --- a/backend/IM_API/Models/User.cs +++ b/backend/IM_API/Models/User.cs @@ -73,9 +73,7 @@ public partial class User public virtual ICollection GroupInviteInvitedUserNavigations { get; set; } = new List(); - public virtual ICollection GroupMemberGroups { get; set; } = new List(); - - public virtual ICollection GroupMemberUsers { get; set; } = new List(); + public virtual ICollection GroupMembers { get; set; } = new List(); public virtual ICollection GroupRequests { get; set; } = new List(); diff --git a/backend/IM_API/Program.cs b/backend/IM_API/Program.cs index 2ad90d3..8454049 100644 --- a/backend/IM_API/Program.cs +++ b/backend/IM_API/Program.cs @@ -9,6 +9,8 @@ using Microsoft.EntityFrameworkCore; using Microsoft.IdentityModel.Tokens; using StackExchange.Redis; using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; namespace IM_API { @@ -38,7 +40,12 @@ namespace IM_API builder.Services.AddAllService(configuration); - builder.Services.AddSignalR(); + builder.Services.AddSignalR().AddJsonProtocol(options => + { + // öַ + options.PayloadSerializerOptions.Converters.Add(new JsonStringEnumConverter()); + options.PayloadSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; + }); //Դ builder.Services.AddCors(options => { @@ -110,6 +117,10 @@ namespace IM_API { // ISO 8601 ʽ options.JsonSerializerOptions.Converters.Add(new UtcDateTimeConverter()); + // öתΪַ + options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()); + // 飺շ + options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; }); builder.Services.AddModelValidation(configuration); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle diff --git a/backend/IM_API/Services/AuthService.cs b/backend/IM_API/Services/AuthService.cs index d4fd6b4..a387c36 100644 --- a/backend/IM_API/Services/AuthService.cs +++ b/backend/IM_API/Services/AuthService.cs @@ -1,5 +1,6 @@ using AutoMapper; -using IM_API.Dtos; +using IM_API.Dtos.Auth; +using IM_API.Dtos.User; using IM_API.Exceptions; using IM_API.Interface.Services; using IM_API.Models; diff --git a/backend/IM_API/Services/ConversationService.cs b/backend/IM_API/Services/ConversationService.cs index 9cfb3cc..9d93669 100644 --- a/backend/IM_API/Services/ConversationService.cs +++ b/backend/IM_API/Services/ConversationService.cs @@ -1,5 +1,5 @@ using AutoMapper; -using IM_API.Dtos; +using IM_API.Dtos.Conversation; using IM_API.Exceptions; using IM_API.Interface.Services; using IM_API.Models; @@ -134,5 +134,27 @@ namespace IM_API.Services return true; } + + public async Task MakeConversationAsync(int userAId, int userBId, ChatType chatType) + { + var userAcExist = await _context.Conversations.AnyAsync(x => x.UserId == userAId && x.TargetId == userBId); + if (userAcExist) return; + var streamKey = chatType == ChatType.PRIVATE ? + StreamKeyBuilder.Private(userAId, userBId) : StreamKeyBuilder.Group(userBId); + var conversation = new Conversation() + { + ChatType = (int)chatType, + LastMessage = "", + LastMessageTime = DateTime.UtcNow, + LastReadMessageId = -1, + StreamKey = streamKey, + TargetId = userBId, + UnreadCount = 0, + UserId = userAId + + }; + _context.Conversations.Add(conversation); + await _context.SaveChangesAsync(); + } } } \ No newline at end of file diff --git a/backend/IM_API/Services/FriendService.cs b/backend/IM_API/Services/FriendService.cs index a372be8..692294b 100644 --- a/backend/IM_API/Services/FriendService.cs +++ b/backend/IM_API/Services/FriendService.cs @@ -1,9 +1,11 @@ using AutoMapper; -using IM_API.Dtos; +using IM_API.Domain.Events; +using IM_API.Dtos.Friend; using IM_API.Exceptions; using IM_API.Interface.Services; using IM_API.Models; using IM_API.Tools; +using MassTransit; using Microsoft.EntityFrameworkCore; namespace IM_API.Services @@ -13,11 +15,13 @@ namespace IM_API.Services private readonly ImContext _context; private readonly ILogger _logger; private readonly IMapper _mapper; - public FriendService(ImContext context, ILogger logger, IMapper mapper) + private readonly IPublishEndpoint _endpoint; + public FriendService(ImContext context, ILogger logger, IMapper mapper, IPublishEndpoint endpoint) { _context = context; _logger = logger; _mapper = mapper; + _endpoint = endpoint; } #region 拉黑好友 public async Task BlockeFriendAsync(int friendId) @@ -113,27 +117,31 @@ namespace IM_API.Services var friend = await _context.Friends.FirstOrDefaultAsync( x => x.UserId == friendRequest.RequestUser && x.FriendId == friendRequest.ResponseUser ); - if (friend is null) throw new BaseException(CodeDefine.FRIEND_RELATION_NOT_FOUND); - - if (friend.StatusEnum != FriendStatus.Pending) throw new BaseException(CodeDefine.FRIEND_REQUEST_EXISTS); + if (friend != null) throw new BaseException(CodeDefine.ALREADY_FRIENDS); //处理好友请求操作 switch (requestDto.Action) { //拒绝后标记 case HandleFriendRequestAction.Reject: - friend.StatusEnum = FriendStatus.Declined; friendRequest.StateEnum = FriendRequestState.Declined; break; //同意后标记 case HandleFriendRequestAction.Accept: - friend.StatusEnum = FriendStatus.Added; friendRequest.StateEnum = FriendRequestState.Passed; + await _endpoint.Publish(new FriendAddEvent() + { + AggregateId = friendRequest.Id.ToString(), + OccurredAt = DateTime.UtcNow, + Created = DateTime.UtcNow, + EventId = Guid.NewGuid(), + OperatorId = friendRequest.ResponseUser, + RequestInfo = _mapper.Map(friendRequest), + requestUserRemarkname = requestDto.RemarkName, + RequestUserId = friendRequest.RequestUser, + ResponseUserId = friendRequest.ResponseUser - //根据当前好友请求为被申请方添加一条好友记录(注意:好友记录为双向) - var ResponseFriend = _mapper.Map(friendRequest); - if (!string.IsNullOrEmpty(requestDto.RemarkName)) ResponseFriend.RemarkName = requestDto.RemarkName; - _context.Friends.Add(ResponseFriend); + }); break; //无效操作 @@ -172,6 +180,16 @@ namespace IM_API.Services var friendRequst = _mapper.Map(dto); _context.FriendRequests.Add(friendRequst); await _context.SaveChangesAsync(); + await _endpoint.Publish(new RequestFriendEvent() + { + AggregateId = friendRequst.Id.ToString(), + OccurredAt = friendRequst.Created, + Description = friendRequst.Description, + EventId = Guid.NewGuid(), + FromUserId = friendRequst.RequestUser, + ToUserId = friendRequst.ResponseUser, + OperatorId = friendRequst.RequestUser + }); return true; } #endregion diff --git a/backend/IM_API/Services/GroupService.cs b/backend/IM_API/Services/GroupService.cs new file mode 100644 index 0000000..b05ed05 --- /dev/null +++ b/backend/IM_API/Services/GroupService.cs @@ -0,0 +1,110 @@ +using AutoMapper; +using IM_API.Domain.Events; +using IM_API.Dtos.Group; +using IM_API.Exceptions; +using IM_API.Interface.Services; +using IM_API.Models; +using IM_API.Tools; +using MassTransit; +using Microsoft.EntityFrameworkCore; +using System; + +namespace IM_API.Services +{ + public class GroupService : IGroupService + { + private readonly ImContext _context; + private readonly IMapper _mapper; + private readonly ILogger _logger; + private readonly IPublishEndpoint _endPoint; + public GroupService(ImContext context, IMapper mapper, ILogger logger, IPublishEndpoint publishEndpoint) + { + _context = context; + _mapper = mapper; + _logger = logger; + _endPoint = publishEndpoint; + } + + private async Task> GetGroupInvites(int userId, int groupId, List ids) + { + DateTime dateTime = DateTime.UtcNow; + //验证被邀请用户是否为好友 + var validFriendIds = await _context.Friends + .Where(f => f.UserId == userId && ids.Contains(f.FriendId)) + .Select(f => f.FriendId) + .ToListAsync(); + //创建群成员对象 + return validFriendIds.Select(fid => new GroupInvite + { + Created = dateTime, + GroupId = groupId, + InvitedUser = fid, + StateEnum = GroupInviteState.Pending, + InviteUser = userId + }).ToList(); + } + + public async Task CreateGroup(int userId, GroupCreateDto groupCreateDto, List userIds) + { + using var transaction = await _context.Database.BeginTransactionAsync(); + try + { + //先创建群 + DateTime dateTime = DateTime.UtcNow; + Group group = _mapper.Map(groupCreateDto); + group.GroupMaster = userId; + _context.Groups.Add(group); + await _context.SaveChangesAsync(); + var groupInvites = new List(); + if (userIds.Count > 0) + { + groupInvites = await GetGroupInvites(userId,group.Id, userIds); + _context.GroupInvites.AddRange(groupInvites); + } + var groupMember = new GroupMember + { + UserId = userId, + Created = dateTime, + RoleEnum = GroupMemberRole.Master, + GroupId = group.Id + }; + _context.GroupMembers.Add(groupMember); + await _context.SaveChangesAsync(); + await transaction.CommitAsync(); + await _endPoint.Publish(new GroupInviteEvent + { + AggregateId = userId.ToString(), + GroupId = group.Id, + EventId = Guid.NewGuid(), + OccurredAt = dateTime, + Ids = groupInvites.Select(x => x.Id).ToList(), + OperatorId = userId, + UserId = userId + }); + return _mapper.Map(group); + } + catch + { + await transaction.RollbackAsync(); + throw; + } + } + + public Task DeleteGroup(int userId, int groupId) + { + throw new NotImplementedException(); + } + + public async Task InviteUsers(int userId, int groupId, List userIds) + { + var group = await _context.Groups.FirstOrDefaultAsync( + x => x.Id == groupId) ?? throw new BaseException(CodeDefine.GROUP_NOT_FOUND); + + } + + public Task JoinGroup(int userId, int groupId) + { + throw new NotImplementedException(); + } + } +} diff --git a/backend/IM_API/Services/UserService.cs b/backend/IM_API/Services/UserService.cs index 0608268..dc1e797 100644 --- a/backend/IM_API/Services/UserService.cs +++ b/backend/IM_API/Services/UserService.cs @@ -1,5 +1,5 @@ using AutoMapper; -using IM_API.Dtos; +using IM_API.Dtos.User; using IM_API.Exceptions; using IM_API.Interface.Services; using IM_API.Models; diff --git a/frontend/web/.env b/frontend/web/.env index c7dcb08..d3d8fa0 100644 --- a/frontend/web/.env +++ b/frontend/web/.env @@ -1,4 +1,4 @@ #VITE_API_BASE_URL = http://localhost:5202/api -#VITE_SIGNALR_BASE_URL = http://localhost:5202/chat +#VITE_SIGNALR_BASE_URL = http://localhost:5202/chat/ VITE_API_BASE_URL = https://im.test.nxsir.cn/api VITE_SIGNALR_BASE_URL = https://im.test.nxsir.cn/chat/ \ No newline at end of file diff --git a/frontend/web/src/components/addMenu.vue b/frontend/web/src/components/addMenu.vue index c35642e..66a61b2 100644 --- a/frontend/web/src/components/addMenu.vue +++ b/frontend/web/src/components/addMenu.vue @@ -67,12 +67,12 @@ const vClickOutside = { \ No newline at end of file diff --git a/frontend/web/src/components/contacts/contactShow.vue b/frontend/web/src/components/contacts/contactShow.vue new file mode 100644 index 0000000..78b3790 --- /dev/null +++ b/frontend/web/src/components/contacts/contactShow.vue @@ -0,0 +1,82 @@ + + + + + \ No newline at end of file diff --git a/frontend/web/src/components/groups/CreateGroup.vue b/frontend/web/src/components/groups/CreateGroup.vue new file mode 100644 index 0000000..6a4dd93 --- /dev/null +++ b/frontend/web/src/components/groups/CreateGroup.vue @@ -0,0 +1,98 @@ + + + + + \ No newline at end of file diff --git a/frontend/web/src/components/groups/groupsShow.vue b/frontend/web/src/components/groups/groupsShow.vue new file mode 100644 index 0000000..7095cf4 --- /dev/null +++ b/frontend/web/src/components/groups/groupsShow.vue @@ -0,0 +1,81 @@ + + + + + \ No newline at end of file diff --git a/frontend/web/src/components/user/SearchUser.vue b/frontend/web/src/components/user/SearchUser.vue index 2312c39..4b72ac4 100644 --- a/frontend/web/src/components/user/SearchUser.vue +++ b/frontend/web/src/components/user/SearchUser.vue @@ -1,399 +1,175 @@ - - + + \ No newline at end of file diff --git a/frontend/web/src/constants/friendAction.js b/frontend/web/src/constants/friendAction.js new file mode 100644 index 0000000..7832eab --- /dev/null +++ b/frontend/web/src/constants/friendAction.js @@ -0,0 +1,17 @@ +export const FRIEND_ACTIONS = Object.freeze({ + /**接受 */ + Accept: 'Accept', + /**拒绝 */ + Reject: 'Reject' +}); + +export const FRIEND_REQUEST_STATUS = Object.freeze({ + /**待处理 */ + Pending: 'Pending', + /**通过 */ + Passed: 'Passed', + /**已拒绝 */ + Declined: 'Declined', + /**已拉黑 */ + Blocked: 'Blocked' +}) \ No newline at end of file diff --git a/frontend/web/src/constants/systemBaseStatus.js b/frontend/web/src/constants/systemBaseStatus.js new file mode 100644 index 0000000..7344a9d --- /dev/null +++ b/frontend/web/src/constants/systemBaseStatus.js @@ -0,0 +1,3 @@ +export const SYSTEM_BASE_STATUS = Object.freeze({ + SUCCESS: 0 +}); \ No newline at end of file diff --git a/frontend/web/src/services/friend.js b/frontend/web/src/services/friend.js index 90b8788..3eb7e67 100644 --- a/frontend/web/src/services/friend.js +++ b/frontend/web/src/services/friend.js @@ -1,4 +1,5 @@ import { request } from "./api"; +import { FRIEND_ACTIONS } from "@/constants/friendAction"; export const friendService = { @@ -28,5 +29,16 @@ export const friendService = { * @param {*} limit * @returns */ - getFriendRequests: (page = 1, limit = 100) => request.get(`/friend/requests?page=${page}&limit=${limit}`) + getFriendRequests: (page = 1, limit = 100) => request.get(`/friend/requests?page=${page}&limit=${limit}`), + + /** + * 处理好友请求 + * @param {*} friendRequestId + * @param {typeof FRIEND_ACTIONS[keyof typeof FRIEND_ACTIONS]} action + * @returns + */ + handleFriendRequest: (friendRequestId, action, remarkname) => request.post(`/Friend/HandleRequest?id=${friendRequestId}`, { + remarkName: remarkname, + action: action + }) } \ No newline at end of file diff --git a/frontend/web/src/utils/db/baseDb.js b/frontend/web/src/utils/db/baseDb.js index b28674a..493184c 100644 --- a/frontend/web/src/utils/db/baseDb.js +++ b/frontend/web/src/utils/db/baseDb.js @@ -5,7 +5,7 @@ const STORE_NAME = 'messages'; const CONVERSARION_STORE_NAME = 'conversations'; const CONTACT_STORE_NAME = 'contacts'; -export const dbPromise = openDB(DBNAME, 4, { +export const dbPromise = openDB(DBNAME, 5, { upgrade(db) { if (!db.objectStoreNames.contains(STORE_NAME)) { const store = db.createObjectStore(STORE_NAME, { keyPath: 'msgId' }); @@ -21,6 +21,7 @@ export const dbPromise = openDB(DBNAME, 4, { const store = db.createObjectStore(CONTACT_STORE_NAME, { keyPath: 'id' }); store.createIndex('by-id', 'id'); store.createIndex('by-username', 'username'); + store.createIndex('by-friendId', 'friendId', { unique: true }); } } }) \ No newline at end of file diff --git a/frontend/web/src/views/contact/ContactList.vue b/frontend/web/src/views/contact/ContactList.vue index 56adb59..13cb7ad 100644 --- a/frontend/web/src/views/contact/ContactList.vue +++ b/frontend/web/src/views/contact/ContactList.vue @@ -23,18 +23,13 @@
标签
- -
我的好友
-
- -
-
{{ c.remarkName }}
-
+
+ +
+ + +
@@ -54,14 +49,36 @@ import GroupChatModal from '@/components/groups/GroupChatModal.vue' import feather from 'feather-icons'; import { useContactStore } from '@/stores/contact'; import { useRouter } from 'vue-router'; +import contactShow from '@/components/contacts/contactShow.vue'; +import groupsShow from '@/components/groups/groupsShow.vue'; -const router = useRouter(); const searchQuery = ref('') -const activeContactId = ref(null) const contactStore = useContactStore(); const groupModal = ref(false); +const contactTab = ref(0); + +const myGroups = ref([ + { + id: 1, + name: "产品设计交流群", + avatar: "https://api.dicebear.com/7.x/avataaars/svg?seed=1", + lastMessage: "那个UI设计的初稿已经发在群文件了,大家记得看下。", + lastTime: "14:20", + unread: 3, + online: true + }, + { + id: 2, + name: "周五羽毛球小分队", + avatar: "https://api.dicebear.com/7.x/avataaars/svg?seed=2", + lastMessage: "这周五晚上 8 点,老地方见!", + lastTime: "昨天", + unread: 0, + online: false + } +]); const filteredContacts = computed(() => { @@ -82,10 +99,7 @@ const filteredContacts = computed(() => { }) }) -const routeUserInfo = (id) => { - router.push(`/contacts/info/${id}`); - activeContactId.value = id; -} + // 发送事件给父组件(用于切换回聊天Tab并打开会话) const emit = defineEmits(['start-chat']) @@ -95,6 +109,7 @@ const showGroupList = () => { } + onMounted(async () => { await contactStore.loadContactList(); }) @@ -145,11 +160,38 @@ onMounted(async () => { } .group-title { - padding: 8px 12px; + width: 40%; + padding: 5px 14px; font-size: 12px; - color: #999; + margin: 5px; + border: none; + background-color: #e0e0e0; + border-radius: 4px; } +.group-title:hover { + color: #8e8e8e; +} + +.group-title-active { + background-color: white; + color: rgb(78, 78, 249); +} + +.fixed-entries { + margin-bottom: 15px; + border-bottom: 1px solid #dcdcdc; +} + +.contactTab { + width: 90%; + margin: 10px auto; + background: #e0e0e0; + display: flex; + align-content: center; + justify-content: center; + border-radius: 4px; +} .list-item { display: flex; padding: 10px 12px; diff --git a/frontend/web/src/views/contact/FriendRequestList.vue b/frontend/web/src/views/contact/FriendRequestList.vue index 7915d83..560641a 100644 --- a/frontend/web/src/views/contact/FriendRequestList.vue +++ b/frontend/web/src/views/contact/FriendRequestList.vue @@ -17,20 +17,20 @@
- @@ -49,6 +59,8 @@ import { friendService } from '@/services/friend'; import { useMessage } from '@/components/messages/useAlert'; import { formatDate } from '@/utils/formatDate'; import { useAuthStore } from '@/stores/auth'; +import { FRIEND_ACTIONS, FRIEND_REQUEST_STATUS } from '@/constants/friendAction'; +import { SYSTEM_BASE_STATUS } from '@/constants/systemBaseStatus'; const message = useMessage(); const authStore = useAuthStore(); @@ -64,6 +76,50 @@ const loadFriendRequests = async () => { requests.value = res.data; } +const showDialog = ref(false); +const remarkName = ref(''); +const activeItem = ref(null); + +const handleOpenDialog = (item) => { + activeItem.value = item; + remarkName.value = item.nickName; // 默认备注为昵称 + showDialog.value = true; +}; + +const confirmAccept = async () => { + if (!activeItem.value) return; + await handleFriendRequest(FRIEND_ACTIONS.Accept) + activeItem.value.state = FRIEND_REQUEST_STATUS.Passed; + showDialog.value = false; +}; + +const confirmReject = async (item) => { + if(!item) return; + activeItem.value = item; + await handleFriendRequest(FRIEND_ACTIONS.Reject); + activeItem.value.state = FRIEND_REQUEST_STATUS.Declined; +} + +const handleFriendRequest = async (action) => { + const res = await friendService.handleFriendRequest(activeItem.value.id,action,activeItem.value.remarkName); + if(res.code == SYSTEM_BASE_STATUS.SUCCESS){ + switch(action){ + case FRIEND_ACTIONS.Accept: + message.show('添加好友成功'); + break; + case FRIEND_ACTIONS.Reject: + message.show('已拒绝'); + break; + default: + message.error('无效的操作'); + break; + } + }else{ + message.error(res.message); + console.log('好友请求处理异常:', res); + } +} + onMounted(async () => { await loadFriendRequests(); }) @@ -79,6 +135,7 @@ onMounted(async () => { display: flex; justify-content: center; overflow-y: auto; + position: relative; } .content-limit { @@ -197,4 +254,70 @@ onMounted(async () => { color: #d2d2d7; padding: 0 12px; } + +/* 弹窗遮罩:毛玻璃效果 */ +.modal-mask { + position: absolute; + inset: 0; + background: rgba(255, 255, 255, 0.4); + backdrop-filter: blur(10px); + display: flex; + align-items: center; + justify-content: center; + z-index: 999; +} + +/* 弹窗主体:延续你的极简白 */ +.modal-box { + background: #fff; + width: 280px; + padding: 24px; + border-radius: 20px; + box-shadow: 0 10px 30px rgba(0,0,0,0.05); + text-align: center; +} + +.modal-header { + font-size: 16px; + font-weight: 600; + margin-bottom: 16px; +} + +/* 输入框:延续你的微灰色调 */ +.modal-input { + width: 100%; + padding: 10px; + border: none; + background: #f5f5f7; + border-radius: 8px; + margin-bottom: 20px; + outline: none; + box-sizing: border-box; +} + +.modal-footer { + display: flex; + gap: 12px; +} + +/* 按钮:完全复用你原本的 btn-text 逻辑 */ +.modal-btn-cancel { + flex: 1; + padding: 10px; + border: none; + background: #f5f5f7; + border-radius: 10px; + color: #86868b; + cursor: pointer; +} + +.modal-btn-confirm { + flex: 1; + padding: 10px; + border: none; + background: #007aff; + color: white; + border-radius: 10px; + cursor: pointer; +} \ No newline at end of file diff --git a/frontend/web/src/views/messages/MessageContent.vue b/frontend/web/src/views/messages/MessageContent.vue index 5bb6cdc..d1632a5 100644 --- a/frontend/web/src/views/messages/MessageContent.vue +++ b/frontend/web/src/views/messages/MessageContent.vue @@ -59,6 +59,7 @@ import { generateSessionId } from '@/utils/sessionIdTools'; import { useSignalRStore } from '@/stores/signalr'; import { useConversationStore } from '@/stores/conversation'; import feather from 'feather-icons'; +import { onBeforeRouteUpdate } from 'vue-router'; const props = defineProps({ id:{ @@ -142,13 +143,22 @@ async function loadConversation(conversationId) { conversationInfo.value = conversationStore.conversations.find(x => x.id == Number(conversationId)); } -// 初始化时滚动到底部 -onMounted(async () => { - await loadConversation(props.id); +const initChat = async (newId) => { + await loadConversation(newId); const sessionid = generateSessionId(conversationInfo.value.userId, conversationInfo.value.targetId) - await chatStore.swtichSession(sessionid,props.id); + await chatStore.swtichSession(sessionid,newId); scrollToBottom(); -}); +} + +// 监听路由参数 +watch( + () => props.id, + async (newId) => { + await initChat(newId) + }, + { immediate: true } // 组件第一次挂载(刷新页面进入)时会立即执行一次 +) + \ No newline at end of file