# 客服模块(CustomerSrc)—— 功能与代码目录文档 > 本文档为「客服模块(CustomerSrc)」的完整上下文说明,供其他对话 / 开发者理解本模块时使用。所有内容均基于 `d:\BUYUHouDuanNet\BuYu\CustomerSrc\` 下的实际源代码整理,路径统一以解决方案根目录 `d:\BUYUHouDuanNet\BuYu` 为基准使用相对路径。 --- ## 一、平台背景与模块位置 本模块是「捕鱼 + 棋牌游戏运营平台后端」的一部分。平台整体技术栈与运行形态如下: * **解决方案**:`Template0524.sln` * **技术栈**:.NET 8 + MongoDB + Redis + RabbitMQ(通过 DotNetCore.CAP 集成)+ SignalR + Ocelot 网关 + NLog + Autofac + AutoMapper * **网关(Ocelot)**:监听 `http://localhost:8001`,按前缀将请求反向代理到各业务进程: * `/adminapi` → AdminHost(5241) * `/api`、`/loginhub` → UserHost(5245) * `/customerapi`、`/customerHub` → **CustomerHost(5247,本模块)** * `/chess`、`/chessgamehub` → ChessGameHost(5242) * `/fishingroomhub` → FishRoomHost(5244) * `/gamehub` → FishGameHost(5248) **本模块(客服 / 客服 IM)在解决方案中的位置:** * 源码根目录:`CustomerSrc/`,解决方案中作为独立解决方案文件夹 `CustomerSrc` 存在(`Template0524.sln` 第 68~80 行)。 * 进程入口:`CustomerSrc/CustomerHost`,对外监听端口在 `Gateway/ocelot.json` 中被指向 **5247**(`DownstreamPathTemplate` 为 `/customerapi/{everything}` 与 `/customerHub/{everything}`,下游 Host 均为 `localhost:5247`)。 * 对外路由前缀:**`/customerapi`**(HTTP 接口)与 **`/customerHub`**(SignalR 长连接 Hub)。 > 补充说明(与代码核对):`CustomerSrc/CustomerHost/docker-compose.yaml` 将容器端口 `5247:80` 映射,即容器内在 80 端口提供服务、宿主机映射为 5247;`CustomerSrc/CustomerHost/Config/system.json` 中 `urls` 为 `http://*:8006`;`CustomerSrc/CustomerHost/Properties/launchSettings.json` 中开发调试地址为 `https://localhost:38200;http://localhost:38201`。三者为不同运行方式下的监听配置,网关实际指向的是 5247。 --- ## 二、模块简介 ### 2.1 定位与职责 CustomerSrc 是一个**客服 IM(即时会话)模块**,为游戏平台的用户与人工客服之间提供一对一的在线聊天能力。核心职责: 1. **客服在线状态管理**:客服上线 / 下线,并把在线客服写入 Redis 列表,供用户发起会话时选择。 2. **会话(Conversation)生命周期管理**:用户发起会话、按在线客服负载分配客服、结束会话。 3. **消息(Message)收发与存储**:以 MongoDB 持久化每条消息,并通过 SignalR 实时推送给会话组。 4. **客服工作台支持**:客服查看自己的会话列表、历史消息、未读数、标记已读。 5. **图片上传**:为聊天消息提供图片上传能力。 ### 2.2 业务角色与关键约定 * **用户(玩家)**:会话发起方,`SenderType = 1`。 * **客服**:会话接待方,`SenderType = 2`(由后台 Admin 模块创建客服账号并维护,见第七节)。 * **会话状态 `Status`**:`1` = 进行中,`2` = 已结束(`EndConversation` 会置为 2 并写入 `EndTime`)。 * **消息内容类型 `ContentType`**:整型标识(1 文本 / 2 图片等由前端约定,代码中仅透传存储,不做枚举约束)。 * **在线标记**:在线客服 ID 列表存放于 Redis 列表键 `customer-service:online`(由 `RedisKey.CUSTOMER_ONLINE_KEY` → `RedisKeyGenerator.CustomerKey("online")` 生成,前缀映射 `customer` → `customer-service`)。 ### 2.3 核心业务流程(以代码为准) **(一)客服上线** 1. 后台把某用户的 `CustomerEntity` 加入客服集合(Admin 模块负责,见第七节)。 2. 客服端调用 `POST /customerapi/customer/status`,`status = 1`;服务端 `UpdateCustomerStatus` 更新 / 新建 `customer` 记录,并将该用户 ID 加入 Redis 在线列表 `customer-service:online`;`status != 1` 时从列表移除。 3. 前端建立 SignalR 连接 `/customerHub`,`OnConnectedAsync` 会依据 JWT 中的 `uid` 声明把连接加入分组 `customer_{uid}`,客服由此接收分配给自己的新会话事件。 **(二)用户发起会话** 1. 用户(或前端)调用 `POST /customerapi/customer/start`,或通过 Hub 方法 `StartConversation` / `SendMessage`(当 `conversationId` 为空且 `SenderType == 1` 时自动发起)。 2. 服务端 `StartConversation` 读取 Redis 在线客服列表 → 对每位在线客服统计其「进行中会话数」(`Status == 1`)→ 选取会话数最少的客服(负载均衡)→ 生成 `ConversationEntity` 并落库;若无客服在线则返回「暂无客服在线」。 3. Hub 侧在会话创建成功后,向该客服所在分组 `customer_{CustomerId}` 广播事件 `OnNewConversation`,并把发起者连接加入会话分组。 **(三)收发消息** 1. 客户端调用 `POST /customerapi/customer/message`,或通过 Hub 方法 `SendMessage(...)`。 2. 服务端 `SendMessage` 生成 `MessageEntity`(含会话 ID、发送者类型 / ID / 名称 / 头像、内容类型、内容、时间)写入 MongoDB。 3. Hub 侧写入成功后,向会话分组 `conversationId` 广播 `OnMessageReceived` 事件(携带 `GuId`、`ConversationId`、`SenderType`、`SenderId`、`SenderName`、`SenderAvatar`、`ContentType`、`Content`、`CreateTime`)。 **(四)客服接待与收尾** 1. 客服调用 `GET /customerapi/customer/conversations?customerId=xxx` 获取自己的会话列表(含最后一条消息、未读数、消息明细)。 2. 进入会话后调用 `GET /customerapi/customer/messages/{conversationId}` 拉取历史消息。 3. 客服调用 `POST /customerapi/customer/mark-read` 标记已读(更新该用户会话的 `LastReadTime`)。 4. 会话结束后调用 `POST /customerapi/customer/end`,`Status` 置 2、写入 `EndTime`。 **(五)用户侧查询** * `GET /customerapi/customer/my-conversations`:从 JWT 取当前登录用户 `uid`,返回该用户最近 1 条会话(含消息与未读数)。 --- ## 三、业务功能清单 ### 3.1 HTTP 接口 —— CustomerController 路由前缀:`customerapi/[controller]`,即实际路径 **`/customerapi/customer/...`**。 控制器继承 `BaseController`(`CommonInfrastructure`),返回统一响应体(`BaseResponse` / `ListResponse`)。 | 方法 | 路由(相对 `/customerapi/customer`) | 请求参数 | 用途 | | --- | --- | --- | --- | | POST | `/start` | `StartConversationParam { UserId, UserName }` | 用户发起会话,返回 `ConversationEntity`;无在线客服时返回失败 | | POST | `/message` | `SendMessageParam { ConversationId, SenderType, SenderId, SenderName, SenderAvatar, ContentType, Content }` | 发送一条消息并落库 | | GET | `/messages/{conversationId}` | 路径参数 `conversationId` | 获取某会话的全部历史消息(按时间升序) | | GET | `/conversations` | 查询参数 `customerId`(可空) | 客服获取会话列表;为空时返回全部会话 | | GET | `/my-conversations` | 无(取 JWT `uid`) | 当前用户最近一条会话(含 `Messages`、`LastMessage`、`UnreadCount`) | | POST | `/end` | `EndConversationParam { ConversationId }` | 结束会话(Status=2,写 EndTime) | | GET | `/online` | 无 | 获取当前在线客服列表(`CustomerEntity`) | | POST | `/status` | `UpdateStatusParam { UserId, Status }` | 客服上线 / 下线,维护 Redis 在线列表 | | POST | `/mark-read` | `MarkReadParam { CustomerId, UserId }` | 客服标记会话已读(更新 `LastReadTime`) | | GET | `/chatlog/{conversationId}` | 路径参数 `conversationId` | 获取会话聊天记录(内部复用 `GetMessages`) | ### 3.2 HTTP 接口 —— ImageUploadController 路由前缀:`customerapi/[controller]`,实际路径 **`/customerapi/imageupload/upload`**。 | 方法 | 路由 | 用途 | | --- | --- | --- | | POST | `/customerapi/imageupload/upload` | 接收 `IFormFile file`,以 GUID 重命名后保存到 `wwwroot/uploads/`,返回 `{ Path = "uploads/{guid}{ext}" }` | ### 3.3 SignalR Hub —— CustomerHub 连接地址:**`/customerHub`**(仅允许 WebSockets 传输,见 `Program.cs` 的 `MapHub` 配置)。 **客户端 → 服务端可调用方法:** | 方法 | 参数 | 作用 | | --- | --- | --- | | `JoinConversation` | `conversationId` | 把当前连接加入会话分组 `conversationId` | | `LeaveConversation` | `conversationId` | 退出会话分组 | | `JoinCustomerQueue` | `customerId` | 加入分组 `customer_{customerId}`(客服队列) | | `LeaveCustomerQueue` | `customerId` | 退出分组 `customer_{customerId}` | | `SendMessage` | `conversationId, senderType, senderId, senderName, senderAvatar, contentType, content, guId=""` | 发送消息;当 `conversationId` 为空且 `senderType==1` 时先自动发起会话(并广播 `OnNewConversation`),随后广播 `OnMessageReceived` | | `StartConversation` | `userId, userName` | 发起会话,向调用方回发 `OnConversationStarted`,并向对应客服分组广播 `OnNewConversation` | **服务端 → 客户端推送事件:** | 事件名 | 触发时机 | 附带数据 | | --- | --- | --- | | `OnMessageReceived` | 消息写入成功后,向会话分组广播 | `GuId, ConversationId, SenderType, SenderId, SenderName, SenderAvatar, ContentType, Content, CreateTime` | | `OnConversationStarted` | `StartConversation` 成功,回发给发起连接 | `ConversationEntity` | | `OnNewConversation` | 新会话创建成功,向客服分组 `customer_{CustomerId}` 广播 | `conversationId, customerId, customerName, userId, userName, status, createTime` | | `ErrorMessage` | 参数错误 / 会话创建失败 / 异常 | 错误字符串 | **连接生命周期:** * `OnConnectedAsync`:读取 `Context.User` 的 `uid` 声明,非空则加入分组 `customer_{uid}`。 * `OnDisconnectedAsync`:仅调用基类实现(不做额外清理)。 --- ## 四、项目结构与技术要点 CustomerSrc 由 4 个项目组成,依赖关系如下(`→` 表示引用): ``` CustomerHost (可执行进程, Web SDK, net8.0) → CustomerApi → RedisService → SharpAbp.Infrastructure.Api → (间接)CustomerService → CustomerService.Domain CustomerApi (Web SDK, net8.0, 实为类库,含 AssemblyMarker) → CustomerService → CommonDomain / CommonInfrastructure / Common / JWTHelper → MongoRepository / RabbitMQHelper / RedisHelper / SharpAbp.Infrastructure.Api → RedisService / CommonService CustomerService (类库, net8.0) → CustomerService.Domain → CommonDomain / CommonInfrastructure / Common / JWTHelper → MongoRepository / RabbitMQHelper / RedisHelper / RedisService CustomerService.Domain (类库, net8.0) → CommonDomain → MongoRepository ``` ### 4.1 各 csproj 框架与关键依赖 | 项目 | SDK / 目标框架 | 关键 NuGet 包 | 关键项目引用 | | --- | --- | --- | --- | | `CustomerSrc/CustomerApi/CustomerApi.csproj` | `Microsoft.NET.Sdk.Web`,`net8.0` | 无自有 PackageReference(依赖由引用项目传递) | CommonDomain、CommonInfrastructure、Common、JWTHelper、MongoRepository、RabbitMQHelper、RedisHelper、SharpAbp.Infrastructure.Api、RedisService、CommonService、**CustomerService** | | `CustomerSrc/CustomerHost/CustomerHost.csproj` | `Microsoft.NET.Sdk.Web`,`net8.0` | Microsoft.AspNetCore.Mvc.NewtonsoftJson 8.0.24、Microsoft.AspNetCore.SignalR.StackExchangeRedis 8.0.0、NLog.Web.AspNetCore 6.1.2、Autofac 8.3.0、Autofac.Extensions.DependencyInjection 10.0.0、AutoMapper 14.0.0、DotNetCore.CAP 8.3.4、DotNetCore.CAP.Dashboard 8.3.4、DotNetCore.CAP.PostgreSql 8.3.4、DotNetCore.CAP.RabbitMQ 8.3.4 | RedisService、**CustomerApi**、SharpAbp.Infrastructure.Api | | `CustomerSrc/CustomerService/CustomerService.csproj` | `Microsoft.NET.Sdk`,`net8.0` | AutoMapper 14.0.0 | CommonDomain、CommonInfrastructure、Common、JWTHelper、MongoRepository、RabbitMQHelper、RedisHelper、RedisService、**CustomerService.Domain** | | `CustomerSrc/CustomerService.Domain/CustomerService.Domain.csproj` | `Microsoft.NET.Sdk`,`net8.0` | MongoDB.Bson 3.6.0 | CommonDomain、MongoRepository | > CustomerHost.csproj 另外通过 `` 引入解决方案根的 `nlog.config`(`CopyToOutputDirectory=PreserveNewest`),并声明把 `Config/system.json`、`docker-compose.yaml`、`Dockerfile` 复制到输出目录;项目内含 `wwwroot\uploads\` 文件夹占位。 ### 4.2 进程入口要点(CustomerHost/Program.cs) * **环境变量**:启动时设置 `SERVICE_NAME`(默认取入口程序集名,即 CustomerHost)与 `INSTANCE_ID`(默认 `default`)。 * **控制器装配**:`AddControllers().AddApplicationPart(typeof(AssemblyMarker).Assembly)` 显式加载 `CustomerApi` 程序集中的控制器;`AddControllersAsServices()` 使控制器走 DI 容器。 * **JSON 序列化**:追加 Newtonsoft.Json 配置,日期格式 `yyyy-MM-dd HH:mm:ss`,`DateTimeZoneHandling = Local`。 * **配置加载**:`AddJsonFile(Path.Combine("Config","system.json"))`,绑定 `MongoDbSettings` 与 `IMongoDbSettings`。 * **AutoMapper**:`AddAutoMapper(typeof(CustomerService.AutoMapperConfig))`。 * **健康检查**:`AddHealthChecks().AddCheck("self", ...)`,映射 `/health` 返回 JSON 明细。 * **SignalR 配置**:`AddSignalR` 设置 `MaximumReceiveMessageSize=64KB`、`StreamBufferCapacity=512`、`ClientTimeoutInterval=70s`、`KeepAliveInterval=15s`;启用 `AddJsonProtocol()` 与 `AddStackExchangeRedis(RedisConfig:ConnectionString)`,Redis 频道前缀 `RedisChannel.Literal("CustomerHub")`(多实例横向扩展时的背板)。 * **CORS**:名为 `Cors` 的策略,允许任意 Origin / Header / Method,且 `AllowCredentials()`。 * **鉴权 / 缓存 / 消息**:`AddCustomJwt`(JWTHelper 扩展)、`AddRedisCache`(RedisHelper 扩展)、`AddCap`(自定义 CAP 扩展,见 4.4)。 * **DI 容器**:`UseServiceProviderFactory(new AutofacServiceProviderFactory())` + `RegisterModule()`。 * **中间件管线(顺序)**:开发环境异常页 → `UseCors("Cors")` → 静态文件(`RequestPath = "/uploads"`,物理目录 `wwwroot`,启动时自动创建 `wwwroot/uploads`)→ `UseRouting` → `UseAuthentication` → `UseAuthorization` → `UseApiLogging()` → `MapHub("/customerHub", WebSockets)` → `MapControllers()` → `UseGlobalExceptionHandler()` → `MapHealthChecks("/health")` → `Run`。 ### 4.3 DI 注册位置(CustomerHost/Middleware/AutofacModule.cs) `AutofacModule` 继承 `SharpAbp.Infrastructure.Api.Autofac.ApplicationModule`: * `CurrentAssembly` = CustomerHost 程序集。 * `GetServiceAssemblies()` 返回 `ICustomerService` 所在程序集(CustomerService)、`CustomerService.Customer.CustomerService` 所在程序集、`ApiLogService` 所在程序集(CommonService)。 * 基类 `ApplicationModule.Load` 统一注册: * `MongoBaseRepository<>` → `IMongoBaseRepository<>`(InstancePerLifetimeScope) * `MongoBaseService<>` → `IMongoBaseService<>`(InstancePerLifetimeScope) * `RedisService` → `IRedisService` * 各程序集中类名以 `Service` 结尾的非抽象类型按接口注册;类名以 `Repository` 结尾的同样按接口注册。 因此 `ICustomerService` / `CustomerService` 以及三个 `IMongoBaseRepository` 均通过 Autofac 自动注入。 ### 4.4 CAP / RabbitMQ 配置(CustomerHost/Extension/CapExtension.cs) `AddCap` 扩展注册 DotNetCore.CAP: * 启用 Dashboard(`UseDashboard()`)。 * 传输层用 RabbitMQ:Host/UserName/Password/VirtualHost 取自 `CapConfig:*`,交换机名 `cap.exchange`。 * 存储层用 PostgreSQL:连接串 `CapConfig:PGLogHost`,Schema `public`。 * 成功消息保留 7 天;失败重试 5 次、间隔 10(秒);提供 `FailedThresholdCallback` 回调(当前回调体为空实现)。 > 说明:本模块代码中**未发现**发布 / 订阅任何 CAP 消息或 `[CapSubscribe]` 处理器;CAP 仅完成框架级接入与 Dashboard 可用。 ### 4.5 SignalR 分组模型 | 分组名 | 含义 | 加入方式 | | --- | --- | --- | | `{conversationId}` | 单个会话的参与者 | `JoinConversation`,或发送 / 发起消息时自动加入 | | `customer_{customerId}` | 某客服的所有连接(客服队列) | `JoinCustomerQueue`,客服连接建立时按 `uid` 自动加入 | ### 4.6 API 日志中间件(CustomerHost/Middleware/ApiLoggingMiddleware.cs) * 仅当请求路径 **以 `/api` 段开头**(`StartsWithSegments("/api")`)且非 `OPTIONS` 时记录日志;记录前读取请求体(POST)、包裹响应流以捕获响应体,并对响应体中的 `BaseResponse.Data` 做脱敏(置空后再序列化)。 * 采集 Header:`Token、Version、User-Agent、Source、DeviceId、Device、Ip、Sign`,组装 `ApiLog` 后经 `IApiLogService.InsertOneAsync` 落库。 > 注意(实测代码行为):本模块对外路由前缀为 `/customerapi`,其首段为 `customerapi`,**不等于** `/api` 段,因此 `ApiLoggingMiddleware` 的判断条件不会命中的 `/customerapi/*` 请求,即本模块 HTTP 接口目前不会被该中间件记录(仅经典的 `/api` 前缀才记录)。 --- ## 五、代码目录说明 > 以下目录树基于 `CustomerSrc/` 实际文件(已排除 `bin/`、`obj/` 编译产物)完整列出。 ``` CustomerSrc/ ├── CustomerApi/ # HTTP 接口 + SignalR Hub 定义(类库工程,含 AssemblyMarker) │ ├── Controllers/ │ │ ├── CustomerController.cs # 客服会话/消息相关接口(start/message/messages/conversations/end/online/status/mark-read/chatlog) │ │ └── ImageUploadController.cs # 聊天图片上传接口(/customerapi/imageupload/upload) │ ├── Hubs/ │ │ └── CustomerHub.cs # SignalR Hub:加入分组、发送消息、发起会话及事件广播 │ ├── Properties/ │ │ └── launchSettings.json # CustomerApi 调试配置(applicationUrl https://localhost:8007/) │ ├── wwwroot/ │ │ └── uploads/ # 图片上传落盘目录 │ │ └── e6f31921718c4a10aed90f483f005130.png # 已上传的示例图片文件 │ ├── .dockerignore # Docker 构建忽略规则 │ ├── CustomerApi.csproj # Web SDK / net8.0,引用 CustomerService 等 │ └── Program.cs # 占位入口:提示使用 CustomerHost 运行;定义 AssemblyMarker │ ├── CustomerHost/ # 进程入口(可执行 Web 工程) │ ├── Config/ │ │ └── system.json # 运行配置:urls、JWT、PG、CAP、Redis、Mongo、CustomerService 段 │ ├── Extension/ │ │ └── CapExtension.cs # AddCap 扩展:CAP + RabbitMQ + PostgreSQL 存储 + Dashboard │ ├── Middleware/ │ │ ├── ApiLoggingMiddleware.cs # /api 请求日志采集中间件(含响应脱敏) │ │ └── AutofacModule.cs # Autofac 模块:注册 Service/Repository 程序集 │ ├── Properties/ │ │ └── launchSettings.json # 调试端口配置(https://localhost:38200;http://localhost:38201) │ ├── CustomerHost.csproj # Web SDK / net8.0,含 SignalR/CAP/NLog/Autofac 等包 │ ├── docker-compose.yaml # 容器编排:0524customer 镜像,端口 5247:80 │ └── Program.cs # 进程入口:DI、SignalR、CORS、JWT、Redis、CAP、中间件与 Hub 映射 │ ├── CustomerService/ # 业务服务层 │ ├── Customer/ │ │ ├── ICustomerService.cs # 客服服务接口(会话/消息/在线状态/已读) │ │ └── CustomerService.cs # 客服服务实现(Mongo + Redis) │ ├── AutoMapperConfig.cs # AutoMapper Profile(当前为空配置) │ └── CustomerService.csproj # 类库 / net8.0,AutoMapper 14.0.0 │ └── CustomerService.Domain/ # 领域模型层 ├── Entity/ │ ├── ConversationEntity.cs # 会话实体(集合 conversation) │ ├── CustomerEntity.cs # 客服实体(集合 customer) │ └── MessageEntity.cs # 消息实体(集合 message) ├── Response/ │ └── ConversationResponse.cs # 会话响应模型(继承会话实体 + 最后消息/未读数/消息列表) └── CustomerService.Domain.csproj # 类库 / net8.0,MongoDB.Bson 3.6.0 ``` --- ## 六、关键文件索引 | 文件相对路径 | 职责 | | --- | --- | | `CustomerSrc/CustomerHost/Program.cs` | 进程入口:服务注册(控制器 / SignalR / CORS / JWT / Redis / CAP / AutoMapper / 健康检查)、Autofac 容器、中间件管线、`/customerHub` 与 `/health` 映射 | | `CustomerSrc/CustomerHost/Config/system.json` | 运行配置:`urls=http://*:8006`、`JwtConfig`、`ConnectionStrings:PgSqlConnection`、`CapConfig`、`RedisConfig`、`MongoDbSettings`、`CustomerService` 段 | | `CustomerSrc/CustomerHost/Middleware/AutofacModule.cs` | Autofac 模块,声明需要扫描注册 Service/Repository 的程序集 | | `CustomerSrc/CustomerHost/Middleware/ApiLoggingMiddleware.cs` | `/api` 请求日志中间件(请求/响应采集、响应脱敏、落库 ApiLog) | | `CustomerSrc/CustomerHost/Extension/CapExtension.cs` | `AddCap` 扩展:CAP + RabbitMQ + PostgreSQL + Dashboard | | `CustomerSrc/CustomerHost/docker-compose.yaml` | 容器编排,端口映射 `5247:80` | | `CustomerSrc/CustomerHost/Properties/launchSettings.json` | 本地调试端口配置 | | `CustomerSrc/CustomerApi/Program.cs` | 占位入口(提示由 CustomerHost 运行),并定义 `AssemblyMarker` 供 Host 加载程序集 | | `CustomerSrc/CustomerApi/Controllers/CustomerController.cs` | 客服 HTTP 接口:发起/结束会话、收发消息、会话与消息查询、在线客服、状态更新、标记已读;含各请求参数类 | | `CustomerSrc/CustomerApi/Controllers/ImageUploadController.cs` | 图片上传接口,保存至 `wwwroot/uploads` 并返回相对路径 | | `CustomerSrc/CustomerApi/Hubs/CustomerHub.cs` | SignalR Hub:分组加入/退出、发送消息、发起会话、连接生命周期、事件广播(OnMessageReceived / OnConversationStarted / OnNewConversation / ErrorMessage) | | `CustomerSrc/CustomerService/Customer/ICustomerService.cs` | 客服服务接口定义 | | `CustomerSrc/CustomerService/Customer/CustomerService.cs` | 客服服务实现:会话负载分配、消息落库、会话/消息查询、在线状态(Redis)、标记已读、客服查询 | | `CustomerSrc/CustomerService/AutoMapperConfig.cs` | AutoMapper Profile(当前无映射配置) | | `CustomerSrc/CustomerService.Domain/Entity/ConversationEntity.cs` | 会话实体,`BsonCollection("conversation", data_0524_customer)` | | `CustomerSrc/CustomerService.Domain/Entity/MessageEntity.cs` | 消息实体,`BsonCollection("message", data_0524_customer)` | | `CustomerSrc/CustomerService.Domain/Entity/CustomerEntity.cs` | 客服实体,`BsonCollection("customer", data_0524_customer)` | | `CustomerSrc/CustomerService.Domain/Response/ConversationResponse.cs` | 会话响应模型(继承 ConversationEntity,增加 LastMessage / UnreadCount / Messages) | | `CustomerSrc/CustomerHost/CustomerHost.csproj` | Host 工程文件(包与项目引用、静态文件复制) | | `CustomerSrc/CustomerApi/CustomerApi.csproj` | API 工程文件(项目引用) | | `CustomerSrc/CustomerService/CustomerService.csproj` | 服务层工程文件 | | `CustomerSrc/CustomerService.Domain/CustomerService.Domain.csproj` | 领域层工程文件 | --- ## 七、与其他模块的交互 ### 7.1 MongoDB 集合 所有实体继承 `MongoRepository.MongoBaseEntity`(含 `_id`(GUID 字符串,默认 `Guid.NewGuid().ToString("N")`)、`isdelete`、`CreateInfo`),并通过 `[BsonCollection(集合名, ServiceDatabases.Customer)]` 指定数据库。`ServiceDatabases.Customer = "data_0524_customer"`。 | 实体 | 集合名 | 数据库 | 主要字段(Bson 名) | | --- | --- | --- | --- | | `ConversationEntity` | `conversation` | `data_0524_customer` | userid、username、useravatar、customerid、customername、customeravatar、status、createtime、endtime、lastreadtime | | `MessageEntity` | `message` | `data_0524_customer` | conversationid、sendertype、senderid、sendername、senderavatar、contenttype、content、createtime | | `CustomerEntity` | `customer` | `data_0524_customer` | userid、username、status、avatar、createtime | 访问方式:`IMongoBaseRepository`(`FindOneAsync` / `FilterByAsync` / `InsertOneAsync` / `ReplaceOneAsync` / `UpdateManyAsync` 等)。 ### 7.2 Redis * **在线客服列表**:键 `customer-service:online`(`RedisKey.CUSTOMER_ONLINE_KEY` → `RedisKeyGenerator.CustomerKey("online")`,前缀映射 `customer` → `customer-service`)。通过 `ICache`(`Common.Redis`,来自 RedisHelper)的 `AddToList` / `RemoveFromList` / `GetList` 维护,元素为客服的用户 ID 字符串。 * 上线(`UpdateCustomerStatus(status==1)`):先从列表移除再追加,写入该客服 ID。 * 下线:从列表移除。 * 分配客服(`StartConversation` / `GetOnlineCustomerList`):读取列表 → 逐个查 `customer` 集合校验 `Status==1` → 返回有效在线客服。 * 附:`RedisKeyConstants.CustomerOnlineKey`(`"game0524:customer:online"`)为另一套常量定义,但本模块实际使用的是 `RedisKey.CUSTOMER_ONLINE_KEY`。 * **SignalR 背板**:`AddStackExchangeRedis(RedisConfig:ConnectionString)`,频道前缀 `CustomerHub`,用于多实例间广播。`system.json` 中 `RedisConfig.ConnectionString = "localhost:6379,password=123456,defaultDatabase=2,abortConnect=false"`,另 `Database=1`。 ### 7.3 CAP / RabbitMQ * 通过 `AddCap` 接入,交换机 `cap.exchange`,存储为 PostgreSQL(`public` Schema,连接串 `CapConfig:PGLogHost`)。**本模块代码未定义任何 CAP 发布/订阅消息**,仅框架级接入与 Dashboard。 ### 7.4 HTTP / 项目引用依赖 * 通过 `CommonInfrastructure`(`BaseController`、`ControllerExtension.ToResult/ToListResult/...`、`BaseResponse`/`ListResponse`)、`Common`(`BaseResponse`、`CommonResponseCodeEnum`、`RedisKey`、`ICache`)、`JWTHelper`(`AddCustomJwt`)、`RedisHelper`(`AddRedisCache`)、`MongoRepository`(`IMongoBaseRepository`)、`SharpAbp.Infrastructure.Api`(`ApplicationModule`、`UseGlobalExceptionHandler`)、`CommonService`(`ApiLogService` / `IApiLogService`)等基础设施项目协作。 * 未发现本模块主动发起对其他业务进程的 HTTP 调用。 ### 7.5 与 Admin 模块(客服管理)的关系 * 后台客服账号与「客服管理」属于 **Admin 模块**:`AdminSrc/AdminService/Customer/CustomerService.cs` 提供 `AddCustomer` 等能力(新增客服、校验「该用户已是客服」),并在 `AdminSrc/AdminDomain/Admin/ENum/EnumOperationModule.cs` 中以 `Customer = 8`「客服管理」登记操作模块。 * 两侧共用同一套 `CustomerEntity` 集合(`data_0524_customer.customer`)与前端页面 `AdminSrc/AdminHost/wwwroot/admin/dist/views/customer/service.html`;CustomerSrc 负责在线会话与消息,Admin 负责客服的创建与运营管理。 ### 7.6 网关映射 `Gateway/ocelot.json` 中: * `/customerapi/{everything}` → `localhost:5247` * `/customerHub/{everything}` → `localhost:5247` 即外部统一经网关 `http://localhost:8001` 访问本模块的 HTTP 接口与 SignalR Hub。