From 786dbe506f29034484420aebe7f3d72fc9770709 Mon Sep 17 00:00:00 2001 From: zpc Date: Wed, 24 Jul 2024 04:58:00 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E8=AE=B0=E5=BF=86=E5=8D=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Cache/Special/CharacterEntityCache.cs | 9 +- .../Character/CharacterBLL.cs | 6 + .../HuanMeng.MiaoYu.Code/Chat/ChatBLL.cs | 170 ++++++++--- .../Chat/Claude/ClaudeChat.cs | 7 +- .../Chat/Claude/ClaudeChatMessage.cs | 11 +- .../HuanMeng.MiaoYu.Code/GlobalUsings.cs | 6 +- .../HuanMeng.MiaoYu.Code/Users/UserBLL.cs | 46 +-- .../Users/UserCurrencyBLL.cs | 220 +++++++++++++- .../HuanMeng.MiaoYu.Code/Users/UserInfoBLL.cs | 270 +++++++++++++++++- .../DbSqlServer/Db_MiaoYu/MiaoYuContext.cs | 39 +++ .../DbSqlServer/Db_MiaoYu/T_Character.cs | 5 + .../DbSqlServer/Db_MiaoYu/T_Chat.cs | 5 + .../DbSqlServer/Db_MiaoYu/T_User_Chat.cs | 5 + .../Db_MiaoYu/T_User_MemoryCard.cs | 59 ++++ .../Dto/Chat/CharacterInfoDto.cs | 5 + .../Dto/ResponseUserInfo.cs | 8 +- .../EnumModel/User/UserCurrencyType.cs | 11 +- .../EnumModel/UserMemoryCardType.cs | 32 +++ 18 files changed, 839 insertions(+), 75 deletions(-) create mode 100644 src/0-core/HuanMeng.MiaoYu.Model/DbSqlServer/Db_MiaoYu/T_User_MemoryCard.cs create mode 100644 src/0-core/HuanMeng.MiaoYu.Model/EnumModel/UserMemoryCardType.cs diff --git a/src/0-core/HuanMeng.MiaoYu.Code/Cache/Special/CharacterEntityCache.cs b/src/0-core/HuanMeng.MiaoYu.Code/Cache/Special/CharacterEntityCache.cs index c009346..80a174d 100644 --- a/src/0-core/HuanMeng.MiaoYu.Code/Cache/Special/CharacterEntityCache.cs +++ b/src/0-core/HuanMeng.MiaoYu.Code/Cache/Special/CharacterEntityCache.cs @@ -1,17 +1,10 @@ using AutoMapper; - -using HuanMeng.MiaoYu.Code.DataAccess; -using HuanMeng.MiaoYu.Code.Other; using HuanMeng.MiaoYu.Model.Dto; using HuanMeng.MiaoYu.Model.Dto.Character; using HuanMeng.MiaoYu.Model.Dto.Label; using HuanMeng.MiaoYu.Model.Dto.Personality; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; + namespace HuanMeng.MiaoYu.Code.Cache.Special { diff --git a/src/0-core/HuanMeng.MiaoYu.Code/Character/CharacterBLL.cs b/src/0-core/HuanMeng.MiaoYu.Code/Character/CharacterBLL.cs index c64735f..ff3f1c8 100644 --- a/src/0-core/HuanMeng.MiaoYu.Code/Character/CharacterBLL.cs +++ b/src/0-core/HuanMeng.MiaoYu.Code/Character/CharacterBLL.cs @@ -3,8 +3,10 @@ using AutoMapper; using HuanMeng.DotNetCore.Base; +using HuanMeng.MiaoYu.Code.Users; using HuanMeng.MiaoYu.Model.Dto.Character; using HuanMeng.MiaoYu.Model.Dto.Chat; +using HuanMeng.MiaoYu.Model.EnumModel.User; using Microsoft.EntityFrameworkCore; @@ -70,6 +72,7 @@ namespace HuanMeng.MiaoYu.Code.Character return new BaseResponse(); } var info = Mapper.Map(charactersinfo); + info.MemoryCardState = false; //不是游客 if (_UserId != 0) { @@ -77,6 +80,9 @@ namespace HuanMeng.MiaoYu.Code.Character var intimacys = await Dao.daoDbMiaoYu.context.T_Character_User_Intimacy.Where(it => it.UserId == _UserId && it.CharacterId == info.CharacterId).FirstOrDefaultAsync(); info.Intimacy = intimacys?.IntimacyValue ?? 0; + UserInfoBLL userInfoBLL = new UserInfoBLL(Dao, _UserId); + (var maxtoken, var memoryCardState) = userInfoBLL.GetMemoryCardMaxToken(requestCharacterInfo.CharacterId); + info.MemoryCardState = memoryCardState; } return new BaseResponse(ResonseCode.Success, "", info); } diff --git a/src/0-core/HuanMeng.MiaoYu.Code/Chat/ChatBLL.cs b/src/0-core/HuanMeng.MiaoYu.Code/Chat/ChatBLL.cs index 6dc0992..e24ca55 100644 --- a/src/0-core/HuanMeng.MiaoYu.Code/Chat/ChatBLL.cs +++ b/src/0-core/HuanMeng.MiaoYu.Code/Chat/ChatBLL.cs @@ -3,13 +3,16 @@ using HuanMeng.MiaoYu.Code.Cache; using HuanMeng.MiaoYu.Code.Chat.Claude; using HuanMeng.MiaoYu.Code.Chat.Claude.Model; using HuanMeng.MiaoYu.Code.Chat.Contract; +using HuanMeng.MiaoYu.Code.Users; using HuanMeng.MiaoYu.Model.DbSqlServer.Db_MiaoYu; using HuanMeng.MiaoYu.Model.Dto.Chat; using HuanMeng.MiaoYu.Model.EnumModel.Chat; +using HuanMeng.MiaoYu.Model.EnumModel.User; using HuanMeng.Utility; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Storage; using Microsoft.Extensions.DependencyInjection; using System; @@ -178,6 +181,12 @@ namespace HuanMeng.MiaoYu.Code.Chat return new BaseResponse(ResonseCode.Success, "", chatListDto); } //if(timeStamp.) + UserInfoBLL userInfoBLL = new UserInfoBLL(Dao, _UserId); + if (!userInfoBLL.IsCheckingSufficient(UserCurrencyType.聊天次数)) + { + throw new Exception("聊天次数不足"); + } + //userInfoBLL.User.GetUserCurrency(Model.EnumModel.User.UserCurrencyType.聊天次数); var userChatSession = await Dao.daoDbMiaoYu.context.T_User_Chat.Where(it => it.CharacterId == characterId && it.UserId == _UserId && !it.IsDelete).FirstOrDefaultAsync(); if (userChatSession == null) { @@ -187,33 +196,26 @@ namespace HuanMeng.MiaoYu.Code.Chat CharacterId = characterId, CreateAt = DateTime.Now, IsDelete = false, - SessionName = "新会话", + SessionName = $"{charact.Name}-{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}", UpdateAt = DateTime.Now, ModelConfigId = charact.ModelConfigId, TenantId = charact.TenantId, UserId = _UserId, + TotalToken = 0, }; Dao.daoDbMiaoYu.context.T_User_Chat.Add(userChatSession); Dao.daoDbMiaoYu.context.SaveChanges(); } - var role = new List(); - role.Add(ChatRole.assistant.ToString()); - role.Add(ChatRole.user.ToString()); - - var chatList = await Dao.daoDbMiaoYu.context.T_Chat.Where(it => it.SessionId == userChatSession.SessionId && it.UserId == _UserId && role.Contains(it.Role)).OrderBy(it => it.SendMessageDay).ToListAsync(); - if (chatList == null) - { - chatList = new List(); - } List mess = new List(); - for (int i = 0; i < chatList.Count; i++) - { - mess.Add(new ClaudeChatMessage - { - Role = chatList[i].Role, - Content = chatList[i].Content, - }); - } + int maxToken = 7000; + // var _memoryCardType = userInfoBLL[Model.EnumModel.UserMemoryCardType.记忆卡, charact.Id]; + (maxToken, var isMemoryCard) = userInfoBLL.GetMemoryCardMaxToken(charact.Id); + //await AddMessage(userChatSession, mess); + //递归获取聊天记录 + var _token = await AddMessage(userChatSession.SessionId, _UserId, 1, 0, maxToken, mess); + //将聊天数据反转 + // mess.Reverse(); + mess = mess.OrderBy(it => it.MessageDay).ToList(); mess.Add(new ClaudeChatMessage { Role = ChatRole.user.ToString(), @@ -256,7 +258,12 @@ namespace HuanMeng.MiaoYu.Code.Chat throw new Exception("ai出现错误"); } var claudeChatResponse = response as ClaudeChatResponse; + if (claudeChatResponse == null) + { + throw new Exception("ai返回出现错误"); + } #endregion + T_Chat t_Chat = new T_Chat { CharacterId = charact.Id, @@ -276,11 +283,55 @@ namespace HuanMeng.MiaoYu.Code.Chat Type = 0, UpdateTime = DateTime.Now, UserId = _UserId, + Tokens = claudeChatResponse.Usage.OutputTokens }; - Dao.daoDbMiaoYu.context.Add(t_Chat1); - Dao.daoDbMiaoYu.context.Add(t_Chat); - userChatSession.UpdateAt = DateTime.Now; - await Dao.daoDbMiaoYu.context.SaveChangesAsync(); + + #region 开启事务 + using (IDbContextTransaction transaction = Dao.daoDbMiaoYu.context.Database.BeginTransaction()) + { + try + { + if (charact.Token == null || charact.Token == 0) + { + charact.Token = (int)(claudeChatResponse.Usage.InputTokens - (message.Length * 2)); + var t_Character = await Dao.daoDbMiaoYu.context.T_Character.FirstOrDefaultAsync(it => it.Id == charact.Id); + if (t_Character != null) + { + t_Character.Token = charact.Token; + } + } + t_Chat1.Tokens = claudeChatResponse.Usage.InputTokens - charact.Token; + t_Chat1.Input_tokens = claudeChatResponse.Usage.InputTokens; + //设置消耗的总token + userChatSession.TotalToken += claudeChatResponse.Usage.InputTokens + claudeChatResponse.Usage.OutputTokens; + Dao.daoDbMiaoYu.context.Add(t_Chat1); + Dao.daoDbMiaoYu.context.Add(t_Chat); + //最后一次聊天时间 + userChatSession.UpdateAt = DateTime.Now; + //最后一次聊天记录 + userChatSession.LastMessage = t_Chat.Content; + if (userChatSession.LastMessage.Length > 10) + { + userChatSession.LastMessage = userChatSession.LastMessage.Substring(0, 10) + "..."; + } + await Dao.daoDbMiaoYu.context.SaveChangesAsync(); + //扣除货币 + userInfoBLL[UserCurrencyType.聊天次数].ConsumeMoneyNoWork(-1, Dao); + if (isMemoryCard) + { + //扣除记忆卡 + userInfoBLL[Model.EnumModel.UserMemoryCardType.记忆卡, charact.Id].ConsumeMemoryCard(-1, Dao); + } + + transaction.Commit(); + } + catch (Exception ex) + { + transaction.Rollback(); + throw ex; + } + } + #endregion //claudeChatResponse. ChatMessageDto chatMessageDto = new ChatMessageDto() { @@ -292,22 +343,77 @@ namespace HuanMeng.MiaoYu.Code.Chat Id = t_Chat.Id }; chatMessageDtos.Add(chatMessageDto); - ChatMessageDto chatMessageDto1 = new ChatMessageDto() + #region 添加返回次数 + var cishu = userInfoBLL[UserCurrencyType.聊天次数].CurrencyMoney; + if (cishu <= 3) { - ClaudeType = ChatMessageType.text.ToString(), - Content = "您还剩余1次聊天次数", - Role = ChatRole.tips.ToString(), - Timestamp = DateTime.Now, - UserIcon = "", - Id = t_Chat.Id - }; - chatMessageDtos.Add(chatMessageDto1); + ChatMessageDto chatMessageDto1 = new ChatMessageDto() + { + ClaudeType = ChatMessageType.text.ToString(), + Content = $"您还剩余{cishu:F0}次聊天次数", + Role = ChatRole.tips.ToString(), + Timestamp = DateTime.Now, + Id = 0 + }; + chatMessageDtos.Add(chatMessageDto1); + } + #endregion chatListDto.ChatList = chatMessageDtos; chatListDto.LastMessageId = t_Chat1.Id; - chatListDto.RemainingChatCount = 1; + chatListDto.RemainingChatCount = (int)cishu; return new BaseResponse(ResonseCode.Success, "", chatListDto); } + /// + /// 通过递归判断聊天是否满足 + /// + /// + /// + /// + /// + /// + /// + /// + private async Task AddMessage(Guid sessionId, int userId, int index, int tokens, int maxTokens, List mess) + { + int size = 50; + var role = new List(); + role.Add(ChatRole.assistant.ToString()); + role.Add(ChatRole.user.ToString()); + + var chatList = await Dao.daoDbMiaoYu.context.T_Chat.AsNoTracking().Where(it => it.SessionId == sessionId && it.UserId == userId && role.Contains(it.Role)).OrderByDescending(it => it.SendMessageDay).Skip((index - 1) * size).Take(size) + .Select(it => new { it.Role, it.Content, it.Tokens, it.SendMessageDay }).ToListAsync(); + if (chatList != null || chatList.Count == 0) + { + for (int i = 0; i < chatList.Count; i++) + { + var chat = chatList[i]; + if ((tokens + chat.Tokens) > maxTokens) + { + return tokens; + } + mess.Add(new ClaudeChatMessage + { + Role = chat.Role, + Content = chat.Content, + MessageDay = chat.SendMessageDay + }); + tokens += chat.Tokens ?? 0; + } + //数据不够 + if (chatList.Count < size) + { + return tokens; + } + if (tokens < maxTokens) + { + index++; + await AddMessage(sessionId, userId, index, tokens, maxTokens, mess); + } + } + return tokens; + } + /// /// 删除聊天记录 /// diff --git a/src/0-core/HuanMeng.MiaoYu.Code/Chat/Claude/ClaudeChat.cs b/src/0-core/HuanMeng.MiaoYu.Code/Chat/Claude/ClaudeChat.cs index 30b6081..8ea867c 100644 --- a/src/0-core/HuanMeng.MiaoYu.Code/Chat/Claude/ClaudeChat.cs +++ b/src/0-core/HuanMeng.MiaoYu.Code/Chat/Claude/ClaudeChat.cs @@ -63,10 +63,13 @@ namespace HuanMeng.MiaoYu.Code.Chat.Claude var response = await httpClient.PostAsync(claudeChatConfig.RequestUrl, content); if (response.IsSuccessStatusCode) { - var chatInfo = await response.Content.ReadFromJsonAsync(); + //var chatInfo1 = await response.Content.ReadFromJsonAsync(); + string responseBody = await response.Content.ReadAsStringAsync(); + var chatInfo = JsonConvert.DeserializeObject(responseBody); return chatInfo; } - else { + else + { // 读取并打印返回消息 string responseBody = await response.Content.ReadAsStringAsync(); Console.WriteLine($"返回消息: {responseBody}"); diff --git a/src/0-core/HuanMeng.MiaoYu.Code/Chat/Claude/ClaudeChatMessage.cs b/src/0-core/HuanMeng.MiaoYu.Code/Chat/Claude/ClaudeChatMessage.cs index ebb1247..c7a2abf 100644 --- a/src/0-core/HuanMeng.MiaoYu.Code/Chat/Claude/ClaudeChatMessage.cs +++ b/src/0-core/HuanMeng.MiaoYu.Code/Chat/Claude/ClaudeChatMessage.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; + namespace HuanMeng.MiaoYu.Code.Chat.Claude { @@ -19,5 +15,10 @@ namespace HuanMeng.MiaoYu.Code.Chat.Claude /// 内容 /// public string Content { get; set; } + /// + /// + /// + [JsonIgnore] + public long MessageDay { get; set; } } } diff --git a/src/0-core/HuanMeng.MiaoYu.Code/GlobalUsings.cs b/src/0-core/HuanMeng.MiaoYu.Code/GlobalUsings.cs index bdecd20..09519e3 100644 --- a/src/0-core/HuanMeng.MiaoYu.Code/GlobalUsings.cs +++ b/src/0-core/HuanMeng.MiaoYu.Code/GlobalUsings.cs @@ -1,4 +1,8 @@ global using HuanMeng.MiaoYu.Code.Base; global using HuanMeng.MiaoYu.Code.DataAccess; global using HuanMeng.MiaoYu.Model.DbSqlServer.Db_MiaoYu; -global using HuanMeng.MiaoYu.Code.Other; \ No newline at end of file +global using HuanMeng.MiaoYu.Code.Other; +global using System.Text.Json; +global using System.Text.Json.Serialization; +global using HuanMeng.MiaoYu.Model.EnumModel; +global using HuanMeng.MiaoYu.Model.EnumModel.User; diff --git a/src/0-core/HuanMeng.MiaoYu.Code/Users/UserBLL.cs b/src/0-core/HuanMeng.MiaoYu.Code/Users/UserBLL.cs index 26e18c0..98c313f 100644 --- a/src/0-core/HuanMeng.MiaoYu.Code/Users/UserBLL.cs +++ b/src/0-core/HuanMeng.MiaoYu.Code/Users/UserBLL.cs @@ -6,6 +6,7 @@ using HuanMeng.MiaoYu.Code.Users.UserAccount.PhoneAccount; using HuanMeng.MiaoYu.Model.Dto; using HuanMeng.MiaoYu.Model.Dto.Account; using HuanMeng.MiaoYu.Model.Dto.Character; + using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; using Microsoft.IdentityModel.Tokens; @@ -16,6 +17,7 @@ using System.Linq; using System.Security.Claims; using System.Text; using System.Threading.Tasks; + using static System.Net.Mime.MediaTypeNames; namespace HuanMeng.MiaoYu.Code.Users @@ -108,40 +110,44 @@ namespace HuanMeng.MiaoYu.Code.Users var user = await Dao.daoDbMiaoYu.context.T_User.FirstOrDefaultAsync(it => it.Id == _UserId); var userData = await Dao.daoDbMiaoYu.context.T_User_Data.FirstOrDefaultAsync(it => it.Id == _UserId); //获取用户余额 - var Currency = user.GetUserCurrency(Model.EnumModel.User.UserCurrencyType.付费币, Dao); - var RemainingChatCount = user.GetUserCurrency(Model.EnumModel.User.UserCurrencyType.聊天次数, Dao); + var Currency = user.GetUserCurrencyMoney(Model.EnumModel.User.UserCurrencyType.付费币, Dao); + var RemainingChatCount = user.GetUserCurrencyMoney(Model.EnumModel.User.UserCurrencyType.聊天次数, Dao); + var memoryCard = user.GetUserCurrencyMoney(Model.EnumModel.User.UserCurrencyType.记忆卡, Dao); + //获取聊天次数 + var hasTalked = Dao.daoDbMiaoYu.context.T_User_Chat.Where(it => it.UserId == _UserId && !it.IsDelete && it.TotalToken > 0).Count(); List characters = new List(); return new BaseResponse(ResonseCode.Success, "请求成功", new ResponseUserInfo { NickName = user.NickName, UserId = user.Id, - Currency = userData.Currency, + Currency = (int)Currency, UserIconUrl = userData.UserIconUrl, - RemainingChatCount = 1,//这里先写1,我不会decimal转int - HasTalked=1, - Photographs=1, + RemainingChatCount = (int)RemainingChatCount,//这里先写1,我不会decimal转int + HasTalked = hasTalked, + Photographs = 0, + MemoryCard = (int)memoryCard, CharacterInfo = new List { - new CreateCharacterInfo - { - CharacterId = 1, - CharacterName = "林婉儿", - BgImage = "https://cos.shhuanmeng.com/image/icon/%E6%9E%97%E5%A9%89%E5%84%BF.png" - }, - new CreateCharacterInfo - { - CharacterId = 2, - CharacterName = "赵灵儿", - BgImage = "https://cos.shhuanmeng.com/image/icon/%E6%9E%97%E5%A9%89%E5%84%BF.png" - } - // 可以继续添加更多的 CreateCharacterInfo 对象 + //new CreateCharacterInfo + //{ + // CharacterId = 1, + // CharacterName = "林婉儿", + // BgImage = "https://cos.shhuanmeng.com/image/icon/%E6%9E%97%E5%A9%89%E5%84%BF.png" + //}, + //new CreateCharacterInfo + //{ + // CharacterId = 2, + // CharacterName = "赵灵儿", + // BgImage = "https://cos.shhuanmeng.com/image/icon/%E6%9E%97%E5%A9%89%E5%84%BF.png" + //} + //// 可以继续添加更多的 CreateCharacterInfo 对象 }, InviteNewUser = new InviteNewUserDto { ImgUrl = "https://cos.shhuanmeng.com/image/icon/20240720205857.png", Type = 0 } - }); + }); } } } diff --git a/src/0-core/HuanMeng.MiaoYu.Code/Users/UserCurrencyBLL.cs b/src/0-core/HuanMeng.MiaoYu.Code/Users/UserCurrencyBLL.cs index a2fbae5..6bb5bf2 100644 --- a/src/0-core/HuanMeng.MiaoYu.Code/Users/UserCurrencyBLL.cs +++ b/src/0-core/HuanMeng.MiaoYu.Code/Users/UserCurrencyBLL.cs @@ -41,6 +41,34 @@ namespace HuanMeng.MiaoYu.Code.Users /// public static class UserCurrencyExtend { + /// + /// 获取用户货币余额,没有货币的时候添加 + /// + /// + /// + /// + /// + public static decimal GetUserCurrencyMoney(this T_User user, UserCurrencyType userCurrencyType, DAO dao) + { + var userCurrency = dao.daoDbMiaoYu.context.T_User_Currency.FirstOrDefault(it => it.UserId == user.Id && it.CurrencyType == (int)userCurrencyType); + if (userCurrency == null) + { + userCurrency = new T_User_Currency() + { + CurrencyMoney = 0, + CreateAt = DateTime.Now, + CurrencyName = userCurrencyType.ToString(), + CurrencyType = (int)userCurrencyType, + TenantId = dao.daoDbMiaoYu.context.TenantInfo.TenantId, + UpdateAt = DateTime.Now, + UserId = user.Id, + }; + dao.daoDbMiaoYu.context.Add(userCurrency); + dao.daoDbMiaoYu.context.SaveChanges(); + } + return userCurrency?.CurrencyMoney ?? 0; + } + /// /// 获取用户货币余额 /// @@ -48,12 +76,178 @@ namespace HuanMeng.MiaoYu.Code.Users /// /// /// - public static decimal GetUserCurrency(this T_User user, UserCurrencyType userCurrencyType, DAO dao) + public static T_User_Currency GetUserCurrency(this T_User user, UserCurrencyType userCurrencyType, DAO dao) { var userCurrency = dao.daoDbMiaoYu.context.T_User_Currency.FirstOrDefault(it => it.UserId == user.Id && it.CurrencyType == (int)userCurrencyType); - return userCurrency?.CurrencyMoney ?? 0; + return userCurrency; } + /// + /// 获取用户所有货币信息 + /// + /// + /// + /// + /// + public static List GetUserCurrencys(this T_User user, DAO dao) + { + var userCurrency = dao.daoDbMiaoYu.context.T_User_Currency.Where(it => it.UserId == user.Id).ToList(); + return userCurrency; + } + + #region 不带事务 + + /// + /// 扣除或者充值货币 + /// + /// + /// 货币类型 + /// 扣除金额(负数扣除,正数添加) + /// 数据库 + /// + /// + /// + /// + public static bool ConsumeMoneyNoWork(this T_User user, UserCurrencyType userCurrencyType, decimal money, DAO dao, T_User_Currency? _currency = null) + { + if (user == null || user.Id == 0) + { + throw new ArgumentNullException("用户不能为空"); + } + int userId = user.Id; + return ConsumeMoneyNoWork(userId, userCurrencyType, money, dao, _currency); + } + /// + /// 扣除或者充值货币 + /// + /// + /// 货币类型 + /// 扣除金额(负数扣除,正数添加) + /// 数据库 + /// + /// + /// + /// + public static bool ConsumeMoneyNoWork(this T_User_Data user, UserCurrencyType userCurrencyType, decimal money, DAO dao, T_User_Currency? _currency = null) + { + if (user == null || user.UserId == 0) + { + throw new ArgumentNullException("用户不能为空"); + } + int userId = user.UserId; + return ConsumeMoneyNoWork(userId, userCurrencyType, money, dao, _currency); + + } + + /// + /// 扣除或者充值货币 + /// + /// + /// 货币类型 + /// 扣除金额(负数扣除,正数添加) + /// 数据库 + /// + /// + /// + /// + public static bool ConsumeMoneyNoWork(this T_User_Currency user, decimal money, DAO dao) + { + if (user == null || user.UserId == 0) + { + throw new ArgumentNullException("用户不能为空"); + } + int userId = user.UserId; + return ConsumeMoneyNoWork(userId, (UserCurrencyType)user.CurrencyType, money, dao, user); + + } + + /// + /// 扣除或者充值货币,没有事务 + /// > + /// 用户Id + /// 货币类型 + /// 扣除金额(负数扣除,正数添加) + /// 数据库 + /// + /// + public static bool ConsumeMoneyNoWork(int userId, UserCurrencyType userCurrencyType, decimal money, DAO dao, T_User_Currency? _currency = null) + { + var userCurrency = dao.daoDbMiaoYu.context.T_User_Currency.FirstOrDefault(it => it.UserId == userId && it.CurrencyType == (int)userCurrencyType); + if (userCurrency == null) + { + userCurrency = new T_User_Currency() + { + CreateAt = DateTime.Now, + CurrencyMoney = 0, + CurrencyName = userCurrencyType.ToString(), + CurrencyType = (int)userCurrencyType, + TenantId = dao.daoDbMiaoYu.context.TenantInfo.TenantId, + UpdateAt = DateTime.Now, + UserId = userId + }; + dao.daoDbMiaoYu.context.Add(userCurrency); + dao.daoDbMiaoYu.context.SaveChanges(); + } + userCurrency.CurrencyMoney += money; + if (userCurrency.CurrencyMoney < 0) + { + //余额不足 + throw new Exception("余额不足"); + } + T_User_Currency_Log? log = null; + UserCurrencyConsumeType userCurrencyConsumeType = UserCurrencyConsumeType.消耗; + if (money >= 0) + { + userCurrencyConsumeType = UserCurrencyConsumeType.充值; + } + + if (userCurrencyType == UserCurrencyType.聊天次数) + { + var mintes = DateTime.Now.AddMinutes(-5); + log = dao.daoDbMiaoYu.context.T_User_Currency_Log.Where(it => it.CreateTime > mintes).OrderByDescending(it => it.CreateTime).FirstOrDefault(); + } + var tempMoney = Math.Abs(money); + //消费 + if (log == null) + { + log = new T_User_Currency_Log() + { + Consume = tempMoney, + ConsumeType = (int)userCurrencyConsumeType, + CreateTime = DateTime.Now, + CurrencyType = (int)userCurrencyType, + TenantId = dao.daoDbMiaoYu.context.TenantInfo.TenantId, + UpdateTime = DateTime.Now, + UserId = userId, + Remarks = "" + }; + dao.daoDbMiaoYu.context.T_User_Currency_Log.Add(log); + } + log.Remarks += $"于{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}{userCurrencyConsumeType.ToString()}[{tempMoney}]{userCurrencyType.ToString()};"; + if (log.Remarks.Length > 200) + { + log.Remarks = log.Remarks.Substring(log.Remarks.Length - 200); + } + dao.daoDbMiaoYu.context.SaveChanges(); + if (_currency != null) + { + _currency.CurrencyMoney = userCurrency.CurrencyMoney; + _currency.UpdateAt = userCurrency.UpdateAt; + _currency.Id = userCurrency.Id; + _currency.CurrencyName = userCurrency.CurrencyName; + _currency.CreateAt = userCurrency.CreateAt; + _currency.CurrencyType = userCurrency.CurrencyType; + } + else + { + _currency = userCurrency; + } + + return true; + } + + #endregion + #region 自带事务 /// /// 扣除或者充值货币 /// @@ -90,6 +284,25 @@ namespace HuanMeng.MiaoYu.Code.Users int userId = user.UserId; return ConsumeMoney(userId, userCurrencyType, money, dao); + } + /// + /// 扣除或者充值货币 + /// + /// + /// 货币类型 + /// 扣除金额(负数扣除,正数添加) + /// 数据库 + /// + /// + public static bool ConsumeMoney(this T_User_Currency user, UserCurrencyType userCurrencyType, decimal money, DAO dao) + { + if (user == null || user.UserId == 0) + { + throw new ArgumentNullException("用户不能为空"); + } + int userId = user.UserId; + return ConsumeMoney(userId, userCurrencyType, money, dao); + } /// @@ -183,5 +396,8 @@ namespace HuanMeng.MiaoYu.Code.Users } return true; } + #endregion + + } } diff --git a/src/0-core/HuanMeng.MiaoYu.Code/Users/UserInfoBLL.cs b/src/0-core/HuanMeng.MiaoYu.Code/Users/UserInfoBLL.cs index 29b4a9c..43a1b1f 100644 --- a/src/0-core/HuanMeng.MiaoYu.Code/Users/UserInfoBLL.cs +++ b/src/0-core/HuanMeng.MiaoYu.Code/Users/UserInfoBLL.cs @@ -1,12 +1,21 @@ using HuanMeng.MiaoYu.Code.DataAccess; using HuanMeng.MiaoYu.Model.DbSqlServer.Db_MiaoYu; +using HuanMeng.MiaoYu.Model.EnumModel; +using HuanMeng.MiaoYu.Model.EnumModel.User; + +using Microsoft.EntityFrameworkCore; using System; using System.Collections.Generic; +using System.Diagnostics.Eventing.Reader; using System.Linq; +using System.Numerics; +using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks; +using XLib.DotNetCore.CacheHelper; + namespace HuanMeng.MiaoYu.Code.Users { /// @@ -40,11 +49,270 @@ namespace HuanMeng.MiaoYu.Code.Users } } + /// + /// 货币 + /// + public Dictionary UserCurrencys { get; set; } + + + /// + /// 获取用户货币信息 + /// + /// + /// + [JsonIgnore] + public T_User_Currency this[UserCurrencyType userCurrencyType] + { + get + { + if (UserCurrencys == null) + { + UserCurrencys = new Dictionary(); + var _currencuys = User.GetUserCurrencys(dao); + if (_currencuys != null) + { + foreach (var item in _currencuys) + { + UserCurrencys.Add((UserCurrencyType)item.CurrencyType, item); + } + } + } + + if (!UserCurrencys.TryGetValue(userCurrencyType, out var t)) + { + t = User.GetUserCurrency(userCurrencyType, dao); + if (t == null) + { + t = new T_User_Currency() + { + CurrencyType = (int)userCurrencyType, + CurrencyMoney = 0, + }; + } + UserCurrencys.Add(userCurrencyType, t); + } + return t; + } + } + + /// + /// 记忆卡 + /// + public Dictionary>? UserMemoryCard { get; set; } + /// + /// 获取记忆卡 + /// + /// + /// + /// + [JsonIgnore] + public T_User_MemoryCard? this[UserMemoryCardType userMemoryCardType, int characterId] + { + get + { + if (UserMemoryCard == null) + { + string key = $"{dao.daoDbMiaoYu.context.TenantInfo.TenantId}:User:{userId}:MemoryCard"; + UserMemoryCard = MemoryCacheHelper.GetCache>>(key); + if (UserMemoryCard == null) + { + UserMemoryCard = new Dictionary>(); + //获取当前用户所有的记忆卡 + var _currencuys = dao.daoDbMiaoYu.context.T_User_MemoryCard.AsNoTracking().Where(it => it.UserId == userId && it.RemainingCount > 0).ToList(); + if (_currencuys != null) + { + var characterIds = _currencuys.Select(it => it.CharacterId).Distinct().ToList(); + foreach (var _characterId in characterIds) + { + var _temp = new Dictionary(); + foreach (var item in _currencuys) + { + _temp.Add((UserMemoryCardType)item.MemoryCardType, item); + } + UserMemoryCard.Add(_characterId, _temp); + } + } + MemoryCacheHelper.SetCache(key, UserMemoryCard, 60 * 5); + } + } + T_User_MemoryCard? _memoryCard = null; + if (UserMemoryCard.TryGetValue(characterId, out var _m)) + { + if (userMemoryCardType == UserMemoryCardType.记忆卡) + { + + if (_m.TryGetValue(UserMemoryCardType.高级记忆卡, out _memoryCard)) + { + return _memoryCard; + } + + if (_m.TryGetValue(UserMemoryCardType.中级记忆卡, out _memoryCard)) + { + return _memoryCard; + } + + if (_m.TryGetValue(UserMemoryCardType.初级记忆卡, out _memoryCard)) + { + return _memoryCard; + } + } + else + { + if (_m.TryGetValue(userMemoryCardType, out _memoryCard)) + { + return _memoryCard; + } + } + } + return _memoryCard; + } + } + + + /// + /// 验证余额是否大于0 + /// + /// + /// + /// + public bool IsCheckingSufficient(UserCurrencyType userCurrencyType, decimal money = 0) + { + if (this[userCurrencyType].CurrencyMoney > money) + { + return true; + } + return false; + } + + /// + /// 获取当前角色记忆卡最大的token + /// + /// + /// + public (int maxToken, bool isMemoryCard) GetMemoryCardMaxToken(int characterId) + { + var _m = this[UserMemoryCardType.记忆卡, characterId]; + if (_m != null && _m.RemainingCount > 0) + { + return (_m.MemoryCardToken, true); + } + return (UserMemoryCardType.记忆卡.GetUserMemoryCardTypeToken(), false); + } } public static class UserInfoExtend { - + /// + /// 记忆卡使用次数 + /// + /// + /// + /// + /// + /// + public static T_User_MemoryCard ConsumeMemoryCard(this T_User_MemoryCard _memoryCard, int useCount, DAO dao) + { + if (_memoryCard == null) + { + throw new ArgumentNullException("未找到记忆卡"); + } + var memoryCard = ConsumeMemoryCard(_memoryCard.UserId, _memoryCard.CharacterId, (UserMemoryCardType)_memoryCard.MemoryCardType, useCount, dao, _memoryCard.Id); + _memoryCard.Id = memoryCard.Id; + _memoryCard.UserId = memoryCard.UserId; + _memoryCard.CharacterId = memoryCard.CharacterId; + _memoryCard.RemainingCount = memoryCard.RemainingCount; + _memoryCard.UseCount = useCount; + _memoryCard.Remark = memoryCard.Remark; + return memoryCard; + } + + /// + /// 记忆卡使用次数 + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static T_User_MemoryCard ConsumeMemoryCard(int userId, int characterId, UserMemoryCardType userMemoryCardType, int useCount, DAO dao, int id = 0) + { + + T_User_MemoryCard? memoryCard = null; + if (id > 0) + { + memoryCard = dao.daoDbMiaoYu.context.T_User_MemoryCard.FirstOrDefault(it => it.Id == id); + } + if (memoryCard == null) + { + memoryCard = dao.daoDbMiaoYu.context.T_User_MemoryCard.FirstOrDefault(it => it.UserId == userId && it.CharacterId == characterId && it.MemoryCardType == (int)userMemoryCardType); + } + if (memoryCard == null) + { + memoryCard = new T_User_MemoryCard() + { + CharacterId = characterId, + CreateTime = DateTime.Now, + MemoryCardToken = userMemoryCardType.GetUserMemoryCardTypeToken(), + MemoryCardType = (int)userMemoryCardType, + RemainingCount = 0, + UseCount = 0, + Name = userMemoryCardType.ToString(), + TenantId = dao.daoDbMiaoYu.context.TenantInfo.TenantId, + Remark = "", + UpdateTime = DateTime.Now, + UserId = userId, + }; + dao.daoDbMiaoYu.context.Add(memoryCard); + } + memoryCard.RemainingCount += useCount; + if (memoryCard.RemainingCount < 0) + { + throw new Exception("记忆卡使用次数不足"); + } + if (useCount < 0) + { + //添加使用次数 + var _useCount = Math.Abs(useCount); + memoryCard.UseCount += _useCount; + memoryCard.Remark += $"记忆卡使用{useCount}次;"; + } + else + { + memoryCard.Remark += $"记忆卡添加{useCount};"; + } + memoryCard.UpdateTime = DateTime.Now; + if (memoryCard.Remark.Length > 200) + { + memoryCard.Remark = memoryCard.Remark.Substring(memoryCard.Remark.Length - 200); + } + dao.daoDbMiaoYu.context.SaveChanges(); + return memoryCard; + } + + /// + /// 获取记忆卡的token数量 + /// + /// + /// + public static int GetUserMemoryCardTypeToken(this UserMemoryCardType userMemoryCardType) + { + if (userMemoryCardType == UserMemoryCardType.高级记忆卡) + { + return 20000; + } + if (userMemoryCardType == UserMemoryCardType.中级记忆卡) + { + return 15000; + } + if (userMemoryCardType == UserMemoryCardType.初级记忆卡) + { + return 10000; + } + return 7000; + } } } diff --git a/src/0-core/HuanMeng.MiaoYu.Model/DbSqlServer/Db_MiaoYu/MiaoYuContext.cs b/src/0-core/HuanMeng.MiaoYu.Model/DbSqlServer/Db_MiaoYu/MiaoYuContext.cs index dae14a9..0778289 100644 --- a/src/0-core/HuanMeng.MiaoYu.Model/DbSqlServer/Db_MiaoYu/MiaoYuContext.cs +++ b/src/0-core/HuanMeng.MiaoYu.Model/DbSqlServer/Db_MiaoYu/MiaoYuContext.cs @@ -138,6 +138,11 @@ public partial class MiaoYuContext : MultiTenantDbContext//DbContext /// public virtual DbSet T_User_Data { get; set; } + /// + /// 用户记忆卡 + /// + public virtual DbSet T_User_MemoryCard { get; set; } + /// /// 手机号登录表 /// @@ -185,6 +190,7 @@ public partial class MiaoYuContext : MultiTenantDbContext//DbContext .HasMaxLength(1000) .HasComment("人物初始设定"); entity.Property(e => e.TenantId).HasComment("租户Id"); + entity.Property(e => e.Token).HasComment("system最大的token数"); entity.Property(e => e.UpdateTime) .HasComment("更新时间") .HasColumnType("datetime"); @@ -410,6 +416,7 @@ public partial class MiaoYuContext : MultiTenantDbContext//DbContext entity.Property(e => e.TimeStamp) .HasComment("发送时间") .HasColumnType("datetime"); + entity.Property(e => e.Tokens).HasComment("消耗的token"); entity.Property(e => e.Type).HasComment("0正常,1重新生成,2 删除"); entity.Property(e => e.UpdateTime) .HasComment("更新时间") @@ -691,6 +698,7 @@ public partial class MiaoYuContext : MultiTenantDbContext//DbContext .HasMaxLength(50) .HasComment("会话名称"); entity.Property(e => e.TenantId).HasComment("租户"); + entity.Property(e => e.TotalToken).HasComment("消耗的总token"); entity.Property(e => e.UpdateAt) .HasComment("修改时间") .HasColumnType("datetime"); @@ -791,6 +799,37 @@ public partial class MiaoYuContext : MultiTenantDbContext//DbContext } }); + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.Id).HasName("PK__T_User_M__3214EC0706BA6604"); + + entity.ToTable(tb => tb.HasComment("用户记忆卡")); + + entity.Property(e => e.CharacterId).HasComment("角色Id"); + entity.Property(e => e.CreateTime) + .HasComment("创建时间") + .HasColumnType("datetime"); + entity.Property(e => e.MemoryCardType).HasComment("记忆卡类型,0初级,1中级,2高级"); + entity.Property(e => e.Name) + .HasMaxLength(1) + .HasComment("记忆卡名称"); + entity.Property(e => e.RemainingCount).HasComment("剩余次数"); + entity.Property(e => e.Remark) + .HasMaxLength(1) + .HasComment("备注"); + entity.Property(e => e.TenantId).HasComment("租户"); + entity.Property(e => e.UpdateTime) + .HasComment("修改时间") + .HasColumnType("datetime"); + entity.Property(e => e.UseCount).HasComment("已使用次数"); + entity.Property(e => e.UserId).HasComment("用户Id"); + //添加全局筛选器 + if (this.TenantInfo != null) + { + entity.HasQueryFilter(it => it.TenantId == this.TenantInfo.TenantId); + } + }); + modelBuilder.Entity(entity => { entity.HasKey(e => e.Id).HasName("PK__T_User_P__3214EC07987BDDB2"); diff --git a/src/0-core/HuanMeng.MiaoYu.Model/DbSqlServer/Db_MiaoYu/T_Character.cs b/src/0-core/HuanMeng.MiaoYu.Model/DbSqlServer/Db_MiaoYu/T_Character.cs index ddd0a31..4a354f6 100644 --- a/src/0-core/HuanMeng.MiaoYu.Model/DbSqlServer/Db_MiaoYu/T_Character.cs +++ b/src/0-core/HuanMeng.MiaoYu.Model/DbSqlServer/Db_MiaoYu/T_Character.cs @@ -77,4 +77,9 @@ public partial class T_Character: MultiTenantEntity /// 对话性别 /// public string? UserSex { get; set; } + + /// + /// system最大的token数 + /// + public int? Token { get; set; } } diff --git a/src/0-core/HuanMeng.MiaoYu.Model/DbSqlServer/Db_MiaoYu/T_Chat.cs b/src/0-core/HuanMeng.MiaoYu.Model/DbSqlServer/Db_MiaoYu/T_Chat.cs index 78dc785..63ccf5e 100644 --- a/src/0-core/HuanMeng.MiaoYu.Model/DbSqlServer/Db_MiaoYu/T_Chat.cs +++ b/src/0-core/HuanMeng.MiaoYu.Model/DbSqlServer/Db_MiaoYu/T_Chat.cs @@ -89,4 +89,9 @@ public partial class T_Chat: MultiTenantEntity /// 人物模型,聊天返回的模型 /// public string? ClaudeModel { get; set; } + + /// + /// 消耗的token + /// + public int? Tokens { get; set; } } diff --git a/src/0-core/HuanMeng.MiaoYu.Model/DbSqlServer/Db_MiaoYu/T_User_Chat.cs b/src/0-core/HuanMeng.MiaoYu.Model/DbSqlServer/Db_MiaoYu/T_User_Chat.cs index 6b82a36..43669c9 100644 --- a/src/0-core/HuanMeng.MiaoYu.Model/DbSqlServer/Db_MiaoYu/T_User_Chat.cs +++ b/src/0-core/HuanMeng.MiaoYu.Model/DbSqlServer/Db_MiaoYu/T_User_Chat.cs @@ -54,4 +54,9 @@ public partial class T_User_Chat: MultiTenantEntity /// 最后一条消息 /// public string? LastMessage { get; set; } + + /// + /// 消耗的总token + /// + public int? TotalToken { get; set; } } diff --git a/src/0-core/HuanMeng.MiaoYu.Model/DbSqlServer/Db_MiaoYu/T_User_MemoryCard.cs b/src/0-core/HuanMeng.MiaoYu.Model/DbSqlServer/Db_MiaoYu/T_User_MemoryCard.cs new file mode 100644 index 0000000..907cdaa --- /dev/null +++ b/src/0-core/HuanMeng.MiaoYu.Model/DbSqlServer/Db_MiaoYu/T_User_MemoryCard.cs @@ -0,0 +1,59 @@ +using System; + +namespace HuanMeng.MiaoYu.Model.DbSqlServer.Db_MiaoYu; + +/// +/// 用户记忆卡 +/// +public partial class T_User_MemoryCard: MultiTenantEntity +{ + public int Id { get; set; } + + + /// + /// 记忆卡名称 + /// + public string Name { get; set; } = null!; + + /// + /// 用户Id + /// + public int UserId { get; set; } + + /// + /// 角色Id + /// + public int CharacterId { get; set; } + + /// + /// 剩余次数 + /// + public int RemainingCount { get; set; } + + /// + /// 已使用次数 + /// + public int UseCount { get; set; } + + /// + /// 创建时间 + /// + public DateTime CreateTime { get; set; } + + /// + /// 修改时间 + /// + public DateTime UpdateTime { get; set; } + + /// + /// 备注 + /// + public string? Remark { get; set; } + + /// + /// 记忆卡类型,0初级,1中级,2高级 + /// + public int MemoryCardType { get; set; } + + public int MemoryCardToken { get; set; } +} diff --git a/src/0-core/HuanMeng.MiaoYu.Model/Dto/Chat/CharacterInfoDto.cs b/src/0-core/HuanMeng.MiaoYu.Model/Dto/Chat/CharacterInfoDto.cs index be6b663..8be6059 100644 --- a/src/0-core/HuanMeng.MiaoYu.Model/Dto/Chat/CharacterInfoDto.cs +++ b/src/0-core/HuanMeng.MiaoYu.Model/Dto/Chat/CharacterInfoDto.cs @@ -79,6 +79,11 @@ namespace HuanMeng.MiaoYu.Model.Dto.Chat [SourceMember(nameof(CharacterCache.Label))] public List Label { get; set; } + /// + /// 记忆卡状态 + /// + public bool MemoryCardState { get; set; } + /// /// 余下聊天次数 /// diff --git a/src/0-core/HuanMeng.MiaoYu.Model/Dto/ResponseUserInfo.cs b/src/0-core/HuanMeng.MiaoYu.Model/Dto/ResponseUserInfo.cs index bfb37d9..dd2f641 100644 --- a/src/0-core/HuanMeng.MiaoYu.Model/Dto/ResponseUserInfo.cs +++ b/src/0-core/HuanMeng.MiaoYu.Model/Dto/ResponseUserInfo.cs @@ -1,4 +1,5 @@ using HuanMeng.MiaoYu.Model.Dto.Character; + using System; using System.Collections.Generic; using System.Linq; @@ -61,7 +62,12 @@ namespace HuanMeng.MiaoYu.Model.Dto /// /// 相册 /// - public int Photographs { get; set; } + public int Photographs { get; set; } + + /// + /// 记忆卡数量 + /// + public int MemoryCard { get; set; } } /// diff --git a/src/0-core/HuanMeng.MiaoYu.Model/EnumModel/User/UserCurrencyType.cs b/src/0-core/HuanMeng.MiaoYu.Model/EnumModel/User/UserCurrencyType.cs index 9daede9..e0a8972 100644 --- a/src/0-core/HuanMeng.MiaoYu.Model/EnumModel/User/UserCurrencyType.cs +++ b/src/0-core/HuanMeng.MiaoYu.Model/EnumModel/User/UserCurrencyType.cs @@ -7,7 +7,7 @@ using System.Threading.Tasks; namespace HuanMeng.MiaoYu.Model.EnumModel.User { /// - /// 用户货币类型 + /// 用户货币类型 /// public enum UserCurrencyType { @@ -20,8 +20,13 @@ namespace HuanMeng.MiaoYu.Model.EnumModel.User /// 付费币 = 1, /// - /// + /// 聊天次数 /// - 聊天次数 = 2 + 聊天次数 = 2, + + /// + /// 记忆卡数量 + /// + 记忆卡 = 3 } } diff --git a/src/0-core/HuanMeng.MiaoYu.Model/EnumModel/UserMemoryCardType.cs b/src/0-core/HuanMeng.MiaoYu.Model/EnumModel/UserMemoryCardType.cs new file mode 100644 index 0000000..7eddc04 --- /dev/null +++ b/src/0-core/HuanMeng.MiaoYu.Model/EnumModel/UserMemoryCardType.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace HuanMeng.MiaoYu.Model.EnumModel +{ + /// + /// 用户会员卡类型 + /// + public enum UserMemoryCardType + { + /// + /// 记忆卡 + /// + 记忆卡 = 0, + /// + /// + /// + 初级记忆卡 = 1, + /// + /// + /// + 中级记忆卡 = 2, + /// + /// + /// + 高级记忆卡 = 3 + + } +}