using Medallion.Threading;
namespace Dpz.Core.Service.RepositoryServiceImpl;
public class AiChatService(
IRepository<AiChatRecord> chatRecordRepository,
IRepository<AiChatSession> sessionRepository,
IMapper mapper,
IConfiguration configuration,
IDistributedLockProvider distributedLockProvider
) : IAiChatService
{
public async Task<VmAiChatSession> CreateSessionAsync(
string userId,
string sessionId,
string sessionName,
string modelType
)
{
var lockKey = $"CreateSessionAsync.Chat.Lock:{userId}_{sessionId}";
await using (await distributedLockProvider.AcquireLockAsync(lockKey))
{
var defaultSession = sessionRepository
.SearchFor(x =>
x.UserId == userId && x.SessionId == sessionId && x.IsDeleted == false
)
.SingleOrDefault();
if (defaultSession != null)
{
return mapper.Map<VmAiChatSession>(defaultSession);
}
var session = new AiChatSession
{
UserId = userId,
SessionId = sessionId,
SessionName = sessionName,
ModelType = modelType,
CreateTime = DateTime.Now,
LastUpdateTime = DateTime.Now,
IsDeleted = false,
};
await sessionRepository.InsertAsync(session);
return mapper.Map<VmAiChatSession>(session);
}
}
public async Task<List<VmAiChatSession>> GetSessionListAsync(string userId)
{
var sessions = await sessionRepository
.SearchFor(x => x.UserId == userId && !x.IsDeleted)
.OrderByDescending(x => x.LastUpdateTime)
.ToListAsync();
var vmSessions = mapper.Map<List<VmAiChatSession>>(sessions);
// 获取每个会话的消息数量
foreach (var session in vmSessions)
{
var count = await chatRecordRepository
.SearchFor(x => x.SessionId == session.SessionId && !x.IsDeleted)
.CountAsync();
session.MessageCount = count;
}
return vmSessions;
}
public async Task<bool> DeleteSessionAsync(string userId, string sessionId)
{
var updateDef = Builders<AiChatSession>
.Update.Set(x => x.IsDeleted, true)
.Set(x => x.LastUpdateTime, DateTime.Now);
var result = await sessionRepository.UpdateAsync(
x => x.UserId == userId && x.SessionId == sessionId && !x.IsDeleted,
updateDef
);
// 同时软删除该会话的所有聊天记录
if (result.ModifiedCount > 0)
{
var chatUpdateDef = Builders<AiChatRecord>
.Update.Set(x => x.IsDeleted, true)
.Set(x => x.DeletedTime, DateTime.Now);
await chatRecordRepository.UpdateAsync(
x => x.SessionId == sessionId && !x.IsDeleted,
chatUpdateDef
);
}
return result.ModifiedCount > 0;
}
public async Task<VmAiChatSession?> GetSessionAsync(string userId, string sessionId)
{
var session = await sessionRepository
.SearchFor(x => x.UserId == userId && x.SessionId == sessionId && !x.IsDeleted)
.FirstOrDefaultAsync();
if (session == null)
{
return null;
}
var vmSession = mapper.Map<VmAiChatSession>(session);
// 获取消息数量
var count = await chatRecordRepository
.SearchFor(x => x.SessionId == sessionId && !x.IsDeleted)
.CountAsync();
vmSession.MessageCount = count;
return vmSession;
}
public async Task SaveUserMessageAsync(
string userId,
string sessionId,
string message,
VmUserInfo sender
)
{
// 使用会话ID作为AI用户的ID
var receiver = CreateAiUserInfo(sessionId);
var record = new AiChatRecord
{
SessionId = sessionId,
Message = message,
Sender = mapper.Map<UserInfo>(sender),
Receiver = mapper.Map<UserInfo>(receiver),
Type = "ai",
SendTime = DateTime.Now,
IsDeleted = false,
};
await chatRecordRepository.InsertAsync(record);
// 更新会话的最后更新时间
await UpdateSessionLastUpdateTime(userId, sessionId);
}
public async Task SaveAiMessageAsync(
string userId,
string sessionId,
string message,
VmUserInfo aiUser
)
{
var userInfo = await GetUserInfoFromSession(userId, sessionId);
if (userInfo == null)
{
return;
}
// 确保AI用户的ID使用会话ID
aiUser.Id = sessionId;
var record = new AiChatRecord
{
SessionId = sessionId,
Message = message,
Sender = mapper.Map<UserInfo>(aiUser),
Receiver = mapper.Map<UserInfo>(userInfo),
Type = "ai",
SendTime = DateTime.Now,
IsDeleted = false,
};
await chatRecordRepository.InsertAsync(record);
// 更新会话的最后更新时间
await UpdateSessionLastUpdateTime(userId, sessionId);
}
public async Task<IPagedList<VmAiChatRecord>> GetSessionMessagesAsync(
string sessionId,
int pageIndex,
int pageSize
)
{
return await chatRecordRepository
.SearchFor(x => x.SessionId == sessionId && !x.IsDeleted)
.OrderByDescending(x => x.SendTime)
.ToPagedListAsync<AiChatRecord, VmAiChatRecord>(pageIndex, pageSize);
}
public async Task<bool> UpdateSessionNameAsync(string userId, string sessionId, string newName)
{
var updateDef = Builders<AiChatSession>
.Update.Set(x => x.SessionName, newName)
.Set(x => x.LastUpdateTime, DateTime.Now);
var result = await sessionRepository.UpdateAsync(
x => x.UserId == userId && x.SessionId == sessionId && !x.IsDeleted,
updateDef
);
return result.ModifiedCount > 0;
}
public async Task<bool> UpdateSessionModelTypeAsync(
string userId,
string sessionId,
string modelType
)
{
var updateDef = Builders<AiChatSession>
.Update.Set(x => x.ModelType, modelType)
.Set(x => x.LastUpdateTime, DateTime.Now);
var result = await sessionRepository.UpdateAsync(
x => x.UserId == userId && x.SessionId == sessionId && !x.IsDeleted,
updateDef
);
return result.ModifiedCount > 0;
}
public async Task<ResponseResult> DeleteMessageAsync(string messageId, string account)
{
var record = await chatRecordRepository.TryGetAsync(messageId);
if (record == null)
{
return ResponseResult.ToFail("聊天记录不存在");
}
if (record.Sender?.Id != account && record.Receiver?.Id != account)
{
return ResponseResult.ToFail("无权限删除该聊天记录");
}
var update = Builders<AiChatRecord>
.Update.Set(x => x.IsDeleted, true)
.Set(x => x.DeletedTime, DateTime.Now);
await chatRecordRepository.UpdateAsync(x => x.Id == record.Id, update);
return ResponseResult.ToSuccess();
}
private async Task UpdateSessionLastUpdateTime(string userId, string sessionId)
{
var updateDef = Builders<AiChatSession>.Update.Set(x => x.LastUpdateTime, DateTime.Now);
await sessionRepository.UpdateAsync(
x => x.UserId == userId && x.SessionId == sessionId,
updateDef
);
}
private async Task<VmUserInfo?> GetUserInfoFromSession(string userId, string sessionId)
{
// 从会话的聊天记录中获取用户信息
var record = await chatRecordRepository
.SearchFor(x => x.SessionId == sessionId && x.Sender != null && x.Sender.Id == userId)
.FirstOrDefaultAsync();
return record?.Sender != null ? mapper.Map<VmUserInfo>(record.Sender) : null;
}
private VmUserInfo CreateAiUserInfo(string sessionId)
{
return new VmUserInfo
{
// 使用会话ID作为AI用户的ID,确保一致性
Avatar = $"{configuration["CDNBaseAddress"]}/images/ChatGPT.ico",
Id = sessionId,
Name = "AI Assistant",
Enable = true,
Key = string.Empty,
LastAccessTime = DateTime.Now,
};
}
}