添加项目文件。

This commit is contained in:
2026-10-12 16:22:04 +08:00
parent 7c68a0ca0a
commit aad11be43d
1000 changed files with 120807 additions and 0 deletions
@@ -0,0 +1,51 @@
using Common;
using Domain.Admin.Admin;
using Domain.Admin.Admin.Param;
using MongoRepository;
using MongoRepository.Config;
namespace AdminService.Admin
{
public class AdminOperationLogService : IAdminOperationLogService
{
private readonly IMongoBaseRepository<AdminOperationLog> _logRepo;
public AdminOperationLogService(IMongoBaseRepository<AdminOperationLog> logRepo)
{
_logRepo = logRepo;
}
public async Task<bool> AddLog(AdminOperationLog log)
{
try
{
await _logRepo.InsertOneAsync(log);
return true;
}
catch
{
return false;
}
}
public async Task<(List<AdminOperationLog> list, long count)> QueryPage(OperationLogQueryParam param)
{
var result = await _logRepo.QueryPageAsync(
filterExpression: x =>
(string.IsNullOrEmpty(param.AdminId) || x.AdminId == param.AdminId) &&
(string.IsNullOrEmpty(param.AdminName) || (x.AdminName != null && x.AdminName.Contains(param.AdminName))) &&
(!param.Module.HasValue || x.Module == param.Module) &&
(!param.StartTime.HasValue || x.OperateTime >= param.StartTime) &&
(!param.EndTime.HasValue || x.OperateTime <= param.EndTime) &&
(!param.Status.HasValue || x.Status == param.Status) &&
x.HttpMethod != "GET",
orderExpression: x => x.OperateTime,
desc: true,
pageSize: param.PageSize,
pageIndex: param.PageIndex
);
return (result.list.ToList(), result.count);
}
}
}
@@ -0,0 +1,68 @@
using System.Security.Claims;
using Domain.Admin.Admin;
using Domain.Admin.Admin.ENum;
using Microsoft.AspNetCore.Http;
namespace AdminService.Admin
{
public static class OperationLogHelper
{
public static AdminOperationLog CreateLog(
EnumOperationModule module,
string actionType,
string description,
string httpMethod,
string requestPath,
string? requestParams = null,
string? response = null,
int status = 1)
{
return new AdminOperationLog
{
Module = (int)module,
ActionType = actionType,
Description = description,
HttpMethod = httpMethod,
RequestPath = requestPath,
RequestParams = requestParams,
Response = response,
Status = status,
OperateTime = DateTime.Now
};
}
public static (string? adminId, string? adminName) GetAdminInfo(ClaimsPrincipal? user)
{
if (user == null) return (null, null);
var adminId = user.FindFirst("uid")?.Value;
var adminName = user.FindFirst("dname")?.Value;
if (string.IsNullOrEmpty(adminName))
{
adminName = user.FindFirst(ClaimTypes.Name)?.Value;
}
return (adminId, adminName);
}
public static void FillAdminInfo(AdminOperationLog log, ClaimsPrincipal? user)
{
var (adminId, adminName) = GetAdminInfo(user);
log.AdminId = adminId;
log.AdminName = adminName;
}
public static string? GetClientIp(HttpContext? context)
{
if (context == null) return null;
var ip = context.Request.Headers["X-Forwarded-For"].FirstOrDefault();
if (string.IsNullOrEmpty(ip))
{
ip = context.Connection.RemoteIpAddress?.ToString();
}
return ip;
}
}
}
+28
View File
@@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AutoMapper" Version="14.0.0" />
<PackageReference Include="DotNetCore.CAP" Version="8.3.4" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\CommonDomain\CommonDomain.csproj" />
<ProjectReference Include="..\..\Infrastructure\CommonInfrastructure\CommonInfrastructure.csproj" />
<ProjectReference Include="..\..\Infrastructure\Common\Common.csproj" />
<ProjectReference Include="..\..\Infrastructure\JWTHelper\JWTHelper.csproj" />
<ProjectReference Include="..\..\Infrastructure\MongoRepository\MongoRepository.csproj" />
<ProjectReference Include="..\..\Infrastructure\RabbitMQHelper\RabbitMQHelper.csproj" />
<ProjectReference Include="..\..\Infrastructure\RedisHelper\RedisHelper.csproj" />
<ProjectReference Include="..\..\RedisService\RedisService.csproj" />
<ProjectReference Include="..\..\UserSrc\UserDomain\UserDomain.csproj" />
<ProjectReference Include="..\..\CustomerSrc\CustomerService.Domain\CustomerService.Domain.csproj" />
<ProjectReference Include="..\AdminDomain\AdminDomain.csproj" />
</ItemGroup>
</Project>
+40
View File
@@ -0,0 +1,40 @@
using AdminService.Notices.Param;
using AdminService.System.Param;
using AdminService.System.Response;
using AdminService.Notices.Param;
using AutoMapper;
using CommonDomain;
using UserDomain.Entity;
using Domain.Admin.Admin;
using UserDomain.Entity;
namespace AdminService
{
public class AutoMapperConfig : Profile
{
public AutoMapperConfig()
{
#region Admin
//param To Entity
CreateMap<AddUserParam, SystemUser>();
CreateMap<AddRoleParam, SystemUserRole>();
CreateMap<AddNavParam, SystemNavigation>();
CreateMap<AddDicParam, SystemDic>();
CreateMap<SystemUser, AdminUserToken>();
CreateMap<SystemNavigation, SystemNavigationResponse>();
CreateMap<SystemDic, SystemDicResponse>();
#endregion
#region Notice
CreateMap<AddNoticeParam, Notice>();
CreateMap<UpdateNoticeParam, Notice>();
#endregion
#region Task
CreateMap<GameTask, UserTask>();
#endregion
}
}
}
@@ -0,0 +1,220 @@
namespace AdminService.BeautifulNumber;
public class BeautifulNumberGenerator
{
public static List<string> GenerateAllPossibleNumbersForRule(string rule)
{
var parts = ParseRuleIntoParts(rule);
int totalLength = parts.Sum(p => p.Length);
if (totalLength != 8)
{
string details = string.Join(", ", parts.Select(p => $"'{p.OriginalString}'({p.Type}, {p.Length}位)"));
throw new ArgumentException($"规则 '{rule}' 解析后的总长度为 {totalLength} 位,不符合要求的8位。详情: [{details}]");
}
var partCombinations = new List<List<string>>();
foreach (var part in parts)
{
partCombinations.Add(GenerateCombinationsForPart(part));
}
var finalNumbers = CartesianProduct(partCombinations);
return finalNumbers.Distinct().ToList();
}
private class RulePart
{
public string OriginalString { get; set; }
public PartType Type { get; set; }
public int Length { get; set; }
public string UniqueLetters { get; set; }
public bool HasPlaceholder { get; set; }
}
private enum PartType
{
Sequential,
FixedGroup, // K, T, KK, TT, KKT, TTKK etc.
Individual, // X
FixedNumber // 8888
}
private static List<RulePart> ParseRuleIntoParts(string rule)
{
var parts = new List<RulePart>();
int i = 0;
while (i < rule.Length)
{
char c = rule[i];
if (char.IsDigit(c))
{
int start = i;
while (i < rule.Length && char.IsDigit(rule[i])) i++;
parts.Add(new RulePart { OriginalString = rule.Substring(start, i - start), Type = PartType.FixedNumber, Length = i - start });
continue;
}
if (c >= 'A' && c <= 'H')
{
int start = i;
int direction = 0;
if (i + 1 < rule.Length && char.IsUpper(rule[i + 1]) && (rule[i + 1] >= 'A' && rule[i + 1] <= 'H'))
{
direction = rule[i + 1] > c ? 1 : -1;
i++;
while (i + 1 < rule.Length && char.IsUpper(rule[i + 1]) && (rule[i + 1] >= 'A' && rule[i + 1] <= 'H') && rule[i + 1] - rule[i] == direction)
{
i++;
}
}
bool hasPlaceholder = (i + 1 < rule.Length && rule[i + 1] == 'X');
if (hasPlaceholder) i++;
int sequenceLength = i - start + 1;
parts.Add(new RulePart { OriginalString = rule.Substring(start, sequenceLength), Type = PartType.Sequential, Length = sequenceLength, HasPlaceholder = hasPlaceholder });
i++;
continue;
}
if (c == 'X')
{
int start = i;
while (i < rule.Length && rule[i] == 'X') i++;
parts.Add(new RulePart { OriginalString = rule.Substring(start, i - start), Type = PartType.Individual, Length = i - start });
continue;
}
if (c == 'K' || c == 'T')
{
int start = i;
while (i < rule.Length && (rule[i] == 'K' || rule[i] == 'T')) i++;
string pattern = rule.Substring(start, i - start);
string uniqueLtrs = new string(pattern.Distinct().OrderBy(ch => ch).ToArray());
parts.Add(new RulePart { OriginalString = pattern, Type = PartType.FixedGroup, Length = pattern.Length, UniqueLetters = uniqueLtrs });
continue;
}
i++;
}
return parts;
}
private static List<string> GenerateCombinationsForPart(RulePart part)
{
switch (part.Type)
{
case PartType.Sequential:
return GenerateSequentialNumbersWithPlaceholder(part.OriginalString, part.HasPlaceholder);
case PartType.FixedGroup:
return GenerateFixedGroupNumbers(part.OriginalString, part.UniqueLetters);
case PartType.Individual:
return GenerateIndividualNumbers(part.OriginalString.Length);
case PartType.FixedNumber:
return new List<string> { part.OriginalString };
default:
return new List<string>();
}
}
private static List<string> GenerateSequentialNumbersWithPlaceholder(string seqRule, bool hasPlaceholder)
{
string sequenceRule = hasPlaceholder ? seqRule.Substring(0, seqRule.Length - 1) : seqRule;
int length = sequenceRule.Length;
if (length == 0) return new List<string> { "" };
var baseSequences = new List<string>();
bool isAscending = length > 1 && sequenceRule[1] > sequenceRule[0];
if (isAscending)
{
int maxStart = 10 - length;
for (int start = 0; start <= maxStart; start++)
{
string num = "";
for (int i = 0; i < length; i++) num += (start + i).ToString();
baseSequences.Add(num);
}
}
else
{
int minStart = length - 1;
for (int start = 9; start >= minStart; start--)
{
string num = "";
for (int i = 0; i < length; i++) num += (start - i).ToString();
baseSequences.Add(num);
}
}
if (!hasPlaceholder)
{
return baseSequences;
}
var finalSequences = new List<string>();
var digits = new List<string> { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" };
foreach (var seq in baseSequences)
{
foreach (var digit in digits)
{
finalSequences.Add(seq + digit);
}
}
return finalSequences;
}
private static List<string> GenerateFixedGroupNumbers(string pattern, string uniqueLetters)
{
var allCombinations = new List<List<string>>();
foreach (char letter in uniqueLetters)
{
var letterValues = new List<string>();
for (int i = 0; i < 10; i++) letterValues.Add(i.ToString());
allCombinations.Add(letterValues);
}
var letterAssignments = CartesianProduct(allCombinations);
var results = new List<string>();
foreach (var assignment in letterAssignments)
{
var map = new Dictionary<char, string>();
for (int i = 0; i < uniqueLetters.Length; i++)
{
map[uniqueLetters[i]] = assignment[i].ToString();
}
string number = "";
foreach (char c in pattern)
{
number += map[c];
}
results.Add(number);
}
return results;
}
private static List<string> GenerateIndividualNumbers(int length)
{
var allCombinations = new List<List<string>>();
for (int i = 0; i < length; i++)
{
var digitList = new List<string>();
for (int j = 0; j < 10; j++) digitList.Add(j.ToString());
allCombinations.Add(digitList);
}
return CartesianProduct(allCombinations);
}
private static List<string> CartesianProduct(List<List<string>> listOfLists)
{
IEnumerable<string> result = new[] { "" };
foreach (var list in listOfLists)
{
result = from r in result
from l in list
select r + l;
}
return result.ToList();
}
}
@@ -0,0 +1,491 @@
using System.Linq.Expressions;
using AdminDomain.BeautifulNumber;
using AdminDomain.Entity;
using Common;
using Common.Utils;
using CommonInfrastructure.Response;
using Microsoft.Extensions.Logging;
using MongoDB.Driver;
using MongoRepository;
using RedisService;
using UserDomain.Entity;
using XAct;
namespace AdminService.BeautifulNumber
{
public class BeautifulNumberService : IBeautifulNumberService
{
private readonly IMongoBaseRepository<BeautifulNumberConfig> _beautifulNumberMongo;
private readonly IMongoBaseRepository<BeautifulNumberRule> _ruleMongo;
private readonly IMongoBaseRepository<UserEntity> _userMongo;
private readonly IRedisService _cache;
public BeautifulNumberService(
IMongoBaseRepository<BeautifulNumberConfig> beautifulNumberMongo,
IMongoBaseRepository<BeautifulNumberRule> ruleMongo,
IMongoBaseRepository<UserEntity> userMongo,
IRedisService cache)
{
_beautifulNumberMongo = beautifulNumberMongo;
_ruleMongo = ruleMongo;
_userMongo = userMongo;
_cache = cache;
}
public async Task<(List<BeautifulNumberConfig> list, long count)> GetList(BeautifulNumberListParam param)
{
Expression<Func<BeautifulNumberConfig, bool>> filter = c => true;
if (!string.IsNullOrEmpty(param.level))
{
filter = filter.And(c => c.Level == param.level);
}
if (param.status.HasValue)
{
filter = filter.And(c => c.Status == (BeautifulNumberStatus)param.status.Value);
}
if (!string.IsNullOrEmpty(param.numberSearch))
{
filter = filter.And(c => c.Number.Contains(param.numberSearch));
}
var (total, list) = await _beautifulNumberMongo.QueryPageAsync(filter, c => c.CreateTime, false, param.size, param.page);
return (list, total);
}
public async Task<(List<BeautifulNumberConfig> list, long count)> GetAvailableList(BeautifulNumberListParam param)
{
param.status = (int)BeautifulNumberStatus.Available;
return await GetList(param);
}
public async Task<(bool isok, string msg)> Purchase(string number, LoginUser loginUser)
{
var config = await _beautifulNumberMongo.FindOneAsync(c => c.Number == number);
if (config == null)
{
return (false, "靓号不存在");
}
if (config.Status != BeautifulNumberStatus.Available)
{
return (false, "靓号不可购买");
}
var user = await _userMongo.FindByIdAsync(loginUser.UserId);
if (user == null)
{
return (false, "用户不存在");
}
if (user.Assets.Gold < config.Price)
{
return (false, "金币不足");
}
user.Assets.Gold -= config.Price;
config.Status = BeautifulNumberStatus.Sold;
config.SaleTime = DateTime.Now.ToTimeStamp();
config.BuyerUserId = loginUser.UserId;
config.UserId = loginUser.UserId;
await _userMongo.ReplaceOneAsync(user);
await _beautifulNumberMongo.ReplaceOneAsync(config);
return (true, "购买成功");
}
public async Task<BeautifulNumberConfig> CheckNumber(string number)
{
var config = await _beautifulNumberMongo.FindOneAsync(c => c.Number == number);
if (config != null)
{
return config;
}
var isUsed = _userMongo.Count(c => c.BaseInfo.Number == number) > 0;
return new BeautifulNumberConfig
{
Number = number,
RuleName = config.RuleName,
Level = config.Level,
Price = config.Price,
Status = isUsed ? BeautifulNumberStatus.Sold : BeautifulNumberStatus.Normal
};
}
public async Task<(bool isok, string msg)> Delete(string id)
{
var config = await _beautifulNumberMongo.FindByIdAsync(id);
if (config == null)
{
return (false, "靓号不存在");
}
if (config.Status == BeautifulNumberStatus.Sold)
{
return (false, "靓号已售出,无法删除");
}
await _beautifulNumberMongo.DeleteByIdAsync(id);
return (true, "删除成功");
}
public async Task<(bool isok, string msg)> SetToNormal(string id)
{
var config = await _beautifulNumberMongo.FindByIdAsync(id);
if (config == null)
{
return (false, "靓号不存在");
}
config.Status = BeautifulNumberStatus.Normal;
await _beautifulNumberMongo.ReplaceOneAsync(config);
return (true, "设置成功");
}
public async Task<(bool isok, string msg)> Freeze(string id)
{
var config = await _beautifulNumberMongo.FindByIdAsync(id);
if (config == null)
{
return (false, "靓号不存在");
}
config.Status = BeautifulNumberStatus.Frozen;
await _beautifulNumberMongo.ReplaceOneAsync(config);
return (true, "冻结成功");
}
public async Task<(bool isok, string msg)> Unfreeze(string id)
{
var config = await _beautifulNumberMongo.FindByIdAsync(id);
if (config == null)
{
return (false, "靓号不存在");
}
config.Status = BeautifulNumberStatus.Available;
await _beautifulNumberMongo.ReplaceOneAsync(config);
return (true, "解冻成功");
}
public async Task<(bool isok, string msg)> UpdatePrice(string id, long price)
{
var config = await _beautifulNumberMongo.FindByIdAsync(id);
if (config == null)
{
return (false, "靓号不存在");
}
config.Price = price;
await _beautifulNumberMongo.ReplaceOneAsync(config);
return (true, "价格修改成功");
}
public async Task<(bool isok, string msg)> UpdateNumber(string id, string level, long price)
{
var config = await _beautifulNumberMongo.FindByIdAsync(id);
if (config == null)
{
return (false, "靓号不存在");
}
config.Level = level;
config.Price = price;
await _beautifulNumberMongo.ReplaceOneAsync(config);
return (true, "修改成功");
}
public async Task<(bool isok, string msg)> BindToUser(string number, string userId)
{
var config = await _beautifulNumberMongo.FindOneAsync(c => c.Number == number);
if (config == null)
{
return (false, "靓号不存在");
}
config.UserId = userId;
await _beautifulNumberMongo.ReplaceOneAsync(config);
return (true, "绑定成功");
}
public async Task<List<BeautifulNumberRule>> GetRules()
{
var filter = Builders<BeautifulNumberRule>.Filter.Empty;
var sort = Builders<BeautifulNumberRule>.Sort.Ascending(c => c.Sort);
var (_, list) = await _ruleMongo.QueryPageAsync(filter, sort, true, 100, 1);
return list;
}
public async Task<(bool isok, string msg)> AddRule(BeautifulNumberRule rule)
{
try
{
await _ruleMongo.InsertOneAsync(rule);
return (true, "添加成功");
}
catch (Exception ex)
{
return (false, "添加失败: " + ex.Message);
}
}
public async Task<(bool isok, string msg)> UpdateRule(BeautifulNumberRule rule)
{
try
{
var existing = await _ruleMongo.FindByIdAsync(rule.Id);
if (existing == null)
return (false, "规则不存在");
existing.RuleName = rule.RuleName;
existing.Level = rule.Level;
existing.Price = rule.Price;
existing.Sort = rule.Sort;
existing.IsEnable = rule.IsEnable;
await _ruleMongo.ReplaceOneAsync(existing);
return (true, "更新成功");
}
catch (Exception ex)
{
return (false, "更新失败: " + ex.Message);
}
}
public async Task<(bool isok, string msg)> DeleteRule(string id)
{
try
{
await _ruleMongo.DeleteByIdAsync(id);
return (true, "删除成功");
}
catch (Exception ex)
{
return (false, "删除失败: " + ex.Message);
}
}
public async Task<(bool isok, string msg)> GiveToUser(string number, string userId)
{
try
{
var user = await _userMongo.FindByIdAsync(userId);
if (user == null)
return (false, "用户不存在");
var config = await _beautifulNumberMongo.FindOneAsync(c => c.Number == number);
if (config != null)
{
if (config.Status == BeautifulNumberStatus.Sold)
return (false, "靓号已售出");
config.Status = BeautifulNumberStatus.Sold;
config.UserId = userId;
config.BuyerUserId = userId;
config.SaleTime = DateTime.Now.ToTimeStamp();
await _beautifulNumberMongo.ReplaceOneAsync(config);
}
user.BaseInfo.Number = number;
await _userMongo.ReplaceOneAsync(user);
return (true, "赠送成功");
}
catch (Exception ex)
{
return (false, "赠送失败: " + ex.Message);
}
}
public async Task<(bool isok, string msg, int count)> InitBeautifulNumbers()
{
try
{
var rules = await GetRules();
if (rules == null || rules.Count == 0)
{
return (false, "请先初始化规则", 0);
}
var soldNumbers = new HashSet<string>();
var allConfigs = await _beautifulNumberMongo.FilterByAsync(c => c.Status == BeautifulNumberStatus.Sold);
foreach (var config in allConfigs)
{
soldNumbers.Add(config.Number);
}
var existingNumbers = new HashSet<string>();
var existingConfigs = await _beautifulNumberMongo.FilterByAsync(c => true);
foreach (var config in existingConfigs)
{
existingNumbers.Add(config.Number);
}
var newNumbers = new List<BeautifulNumberConfig>();
foreach (var rule in rules)
{
if (!rule.IsEnable) continue;
var numbers = BeautifulNumberGenerator.GenerateAllPossibleNumbersForRule(rule.RuleName);
foreach (var num in numbers)
{
if (existingNumbers.Contains(num) || soldNumbers.Contains(num) || newNumbers.Any(c=>c.Number==num))
continue;
newNumbers.Add(new BeautifulNumberConfig() { Level=rule.Level, Number=num, Price=rule.Price, RuleName=rule.RuleName
});
existingNumbers.Add(num);
}
}
if (newNumbers.Count > 0)
{
await _beautifulNumberMongo.InsertManyAsync(newNumbers);
}
return (true, $"初始化成功,新增 {newNumbers.Count} 个靓号", newNumbers.Count);
}
catch (Exception ex)
{
return (false, "初始化失败: " + ex.Message, 0);
}
}
public async Task<(bool isok, string msg)> UpdateRules(List<BeautifulNumberRule> rules)
{
try
{
await _ruleMongo.DeleteManyAsync(c => true);
if (rules.Count > 0)
{
await _ruleMongo.InsertManyAsync(rules);
}
return (true, "规则更新成功");
}
catch (Exception ex)
{
return (false, "规则更新失败: " + ex.Message);
}
}
public async Task<(bool isok, string msg)> InitRules()
{
var existingRules = _ruleMongo.Count(c => true);
if (existingRules > 0)
{
return (true, "规则已初始化");
}
var rules = new List<BeautifulNumberRule>
{
};
await _ruleMongo.InsertManyAsync(rules);
return (true, "规则初始化成功");
}
public async Task<(bool isok, string msg)> AddNumber(string number, string level, long price)
{
try
{
if (string.IsNullOrWhiteSpace(number))
return (false, "靓号不能为空");
number = number.Trim();
var existing = await _beautifulNumberMongo.FindOneAsync(c => c.Number == number);
if (existing != null)
return (false, "靓号已存在: " + number);
var config = new BeautifulNumberConfig
{
Number = number,
Level = level ?? "D",
Price = price,
Status = 0,
CreateTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds()
};
await _beautifulNumberMongo.InsertOneAsync(config);
return (true, "添加成功");
}
catch (Exception ex)
{
return (false, "添加失败: " + ex.Message);
}
}
public async Task<(bool isok, string msg, int successCount, int failCount)> BatchAddNumbers(List<string> numbers, string level, long price)
{
if (numbers == null || numbers.Count == 0)
return (false, "号码列表不能为空", 0, 0);
var successCount = 0;
var failCount = 0;
var failNumbers = new List<string>();
foreach (var number in numbers)
{
if (string.IsNullOrWhiteSpace(number))
{
failCount++;
failNumbers.Add(number);
continue;
}
var num = number.Trim();
try
{
var existing = await _beautifulNumberMongo.FindOneAsync(c => c.Number == num);
if (existing != null)
{
failCount++;
failNumbers.Add(num + "(已存在)");
continue;
}
var config = new BeautifulNumberConfig
{
Number = num,
Level = level ?? "D",
Price = price,
Status = 0,
CreateTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds()
};
await _beautifulNumberMongo.InsertOneAsync(config);
successCount++;
}
catch
{
failCount++;
failNumbers.Add(num);
}
}
var msg = string.IsNullOrEmpty(failNumbers.FirstOrDefault())
? $"成功添加 {successCount} 个靓号"
: $"成功 {successCount} 个,失败 {failCount} 个: {string.Join(", ", failNumbers.Take(10))}";
return (true, msg, successCount, failCount);
}
}
}
@@ -0,0 +1,70 @@
using AdminDomain.Entity;
using Common;
using Common.CommonModel;
using CommonInfrastructure.Response;
using UserDomain.Entity;
using UserDomain.Enum;
namespace AdminService.BeautifulNumber
{
public interface IBeautifulNumberService
{
Task<(List<BeautifulNumberConfig> list, long count)> GetList(BeautifulNumberListParam param);
Task<(List<BeautifulNumberConfig> list, long count)> GetAvailableList(BeautifulNumberListParam param);
Task<(bool isok, string msg)> Purchase(string number, LoginUser loginUser);
Task<BeautifulNumberConfig> CheckNumber(string number);
Task<(bool isok, string msg)> Delete(string id);
Task<(bool isok, string msg)> SetToNormal(string id);
Task<(bool isok, string msg)> Freeze(string id);
Task<(bool isok, string msg)> Unfreeze(string id);
Task<(bool isok, string msg)> UpdatePrice(string id, long price);
Task<(bool isok, string msg)> UpdateNumber(string id, string level, long price);
Task<(bool isok, string msg)> BindToUser(string number, string userId);
Task<List<BeautifulNumberRule>> GetRules();
Task<(bool isok, string msg)> AddRule(BeautifulNumberRule rule);
Task<(bool isok, string msg)> UpdateRule(BeautifulNumberRule rule);
Task<(bool isok, string msg)> DeleteRule(string id);
Task<(bool isok, string msg)> GiveToUser(string number, string userId);
Task<(bool isok, string msg, int count)> InitBeautifulNumbers();
Task<(bool isok, string msg)> UpdateRules(List<BeautifulNumberRule> rules);
Task<(bool isok, string msg)> InitRules();
Task<(bool isok, string msg)> AddNumber(string number, string level, long price);
Task<(bool isok, string msg, int successCount, int failCount)> BatchAddNumbers(List<string> numbers, string level, long price);
}
public class BeautifulNumberListParam
{
public int page { get; set; } = 1;
public int size { get; set; } = 20;
public string? level { get; set; }
public int? status { get; set; }
public string? numberSearch { get; set; }
}
}
@@ -0,0 +1,207 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using AdminDomain.Customer;
using AdminDomain.Customer.Param;
using CustomerService.Domain.Entity;
using MongoDB.Driver;
using MongoRepository;
namespace AdminService.Customer
{
public class CustomerService : ICustomerService
{
private readonly IMongoBaseRepository<CustomerEntity> _customerRepo;
private readonly IMongoBaseRepository<ConversationEntity> _conversationRepo;
private readonly IMongoBaseRepository<MessageEntity> _messageRepo;
public CustomerService(
IMongoBaseRepository<CustomerEntity> customerRepo,
IMongoBaseRepository<ConversationEntity> conversationRepo,
IMongoBaseRepository<MessageEntity> messageRepo)
{
_customerRepo = customerRepo;
_conversationRepo = conversationRepo;
_messageRepo = messageRepo;
}
public async Task<(bool isOk, string msg, CustomerEntity? customer)> AddCustomer(AddCustomerParam param)
{
var allCustomers = await _customerRepo.FilterByAsync(x => true);
var existCustomer = allCustomers.FirstOrDefault(x => x.UserId == param.UserId);
if (existCustomer != null)
{
return (false, "该用户已是客服", null);
}
var customer = new CustomerEntity
{
UserId = param.UserId,
UserName = param.UserName,
Avatar = param.Avatar,
Status = 0,
CreateTime = DateTime.Now
};
await _customerRepo.InsertOneAsync(customer);
return (true, "添加成功", customer);
}
public async Task<(bool isOk, string msg)> UpdateCustomer(UpdateCustomerParam param)
{
var customer = await _customerRepo.FindByIdAsync(param.Id);
if (customer == null)
{
return (false, "客服不存在");
}
var filter = Builders<CustomerEntity>.Filter.Eq(x => x.Id, param.Id);
var update = Builders<CustomerEntity>.Update
.Set(x => x.UserName, param.UserName)
.Set(x => x.Avatar, param.Avatar)
.Set(x => x.Status, param.Status);
await _customerRepo.UpdateByIdAsync(param.Id, update);
return (true, "更新成功");
}
public async Task<(bool isOk, string msg)> DeleteCustomer(string id)
{
var customer = await _customerRepo.FindByIdAsync(id);
if (customer == null)
{
return (false, "客服不存在");
}
await _customerRepo.DeleteByIdAsync(id);
return (true, "删除成功");
}
public async Task<(bool isOk, string msg, List<CustomerEntity>? customers, int total)> GetCustomerList(GetCustomerListParam param)
{
var list = await _customerRepo.FilterByAsync(x => true);
if (!string.IsNullOrEmpty(param.UserName))
{
list = list.Where(x => x.UserName != null && x.UserName.Contains(param.UserName)).ToList();
}
if (param.Status.HasValue)
{
list = list.Where(x => x.Status == param.Status.Value).ToList();
}
var total = list.Count;
var pagedList = list.Skip((param.PageIndex - 1) * param.PageSize).Take(param.PageSize).ToList();
return (true, "获取成功", pagedList, total);
}
public async Task<(bool isOk, string msg, CustomerEntity? customer)> GetCustomerById(string id)
{
var customer = await _customerRepo.FindByIdAsync(id);
if (customer == null)
{
return (false, "客服不存在", null);
}
return (true, "获取成功", customer);
}
public async Task<(bool isOk, string msg, ConversationEntity? conversation)> GetConversationById(string id)
{
var conversation = await _conversationRepo.FindByIdAsync(id);
if (conversation == null)
{
return (false, "会话不存在", null);
}
return (true, "获取成功", conversation);
}
public async Task<(bool isOk, string msg, List<ConversationEntity>? conversations, int total)> GetConversationList(GetConversationListParam param)
{
var list = await _conversationRepo.FilterByAsync(x => true);
if (!string.IsNullOrEmpty(param.UserId))
{
list = list.Where(x => x.UserId == param.UserId).ToList();
}
if (!string.IsNullOrEmpty(param.CustomerId))
{
list = list.Where(x => x.CustomerId == param.CustomerId).ToList();
}
if (param.Status.HasValue)
{
list = list.Where(x => x.Status == param.Status.Value).ToList();
}
var total = list.Count;
var pagedList = list.Skip((param.PageIndex - 1) * param.PageSize).Take(param.PageSize).ToList();
return (true, "获取成功", pagedList, total);
}
public async Task<(bool isOk, string msg)> DeleteConversation(string conversationId)
{
if (string.IsNullOrWhiteSpace(conversationId))
return (false, "会话ID不能为空");
var conversation = await _conversationRepo.FindByIdAsync(conversationId);
if (conversation == null)
return (false, "会话不存在");
var messages = await _messageRepo.FilterByAsync(x => x.ConversationId == conversationId);
foreach (var message in messages)
await _messageRepo.DeleteByIdAsync(message.Id);
await _conversationRepo.DeleteByIdAsync(conversationId);
return (true, "删除成功");
}
public async Task<(bool isOk, string msg, List<MessageEntity>? messages)> GetChatLog(string conversationId)
{
var conversation = await _conversationRepo.FindByIdAsync(conversationId);
if (conversation == null)
{
return (false, "会话不存在", null);
}
var allMessages = await _messageRepo.FilterByAsync(x => true);
var messages = allMessages.Where(x => x.ConversationId == conversationId).ToList();
return (true, "获取成功", messages);
}
public async Task<(bool isOk, string msg)> DeleteMessage(string id)
{
var message = await _messageRepo.FindByIdAsync(id);
if (message == null)
{
return (false, "消息不存在");
}
await _messageRepo.DeleteByIdAsync(id);
return (true, "删除成功");
}
public async Task<(bool isOk, string msg)> DeleteAllMessages(string conversationId)
{
var messages = await _messageRepo.FilterByAsync(x => x.ConversationId == conversationId);
foreach (var msg in messages)
{
await _messageRepo.DeleteByIdAsync(msg.Id);
}
return (true, "删除成功");
}
public async Task<(bool isOk, string msg)> DeleteMessagesBatch(List<string> ids)
{
foreach (var id in ids)
{
await _messageRepo.DeleteByIdAsync(id);
}
return (true, "批量删除成功");
}
}
}
@@ -0,0 +1,101 @@
using System.Linq.Expressions;
using Common;
using CommonDomain;
using CommonDomain.Enum;
using Microsoft.Extensions.Logging;
using MongoRepository;
namespace AdminService.EmptyRoomRate
{
public class EmptyRoomRateConfigService : IEmptyRoomRateConfigService
{
private readonly IMongoBaseRepository<EmptyRoomRateConfig> _emptyRoomRateMongo;
public EmptyRoomRateConfigService(
IMongoBaseRepository<EmptyRoomRateConfig> emptyRoomRateMongo)
{
_emptyRoomRateMongo = emptyRoomRateMongo;
}
/// <inheritdoc/>
public async Task<(List<EmptyRoomRateConfig> list, long count)> GetList(int page, int size, GameTypeEnum? gameType, int? roomLevel)
{
Expression<Func<EmptyRoomRateConfig, bool>> filter = c => true;
if (gameType.HasValue)
{
var type = gameType.Value;
filter = filter.And(c => c.GameType == type);
}
if (roomLevel.HasValue)
{
var level = roomLevel.Value;
filter = filter.And(c => c.RoomLevel == level);
}
var (total, list) = await _emptyRoomRateMongo.QueryPageAsync(filter, c => c.Id, false, size, page);
return (list, total);
}
/// <inheritdoc/>
public async Task<(bool isok, string msg)> Add(GameTypeEnum gameType, int roomLevel, int emptyRoomRate)
{
if (emptyRoomRate < 0 || emptyRoomRate > 100)
return (false, "空房率必须在0到100之间");
if (gameType == GameTypeEnum.中国象棋)
roomLevel = 0;
var existing = gameType == GameTypeEnum.中国象棋
? await _emptyRoomRateMongo.FindOneAsync(c => c.GameType == gameType)
: await _emptyRoomRateMongo.FindOneAsync(c => c.GameType == gameType && c.RoomLevel == roomLevel);
if (existing != null)
{
return (false, "该游戏类型与房间等级的组合已存在");
}
await _emptyRoomRateMongo.InsertOneAsync(new EmptyRoomRateConfig
{
GameType = gameType,
RoomLevel = roomLevel,
EmptyRoomRate = emptyRoomRate
});
return (true, "新增成功");
}
/// <inheritdoc/>
public async Task<(bool isok, string msg)> Update(string id, int emptyRoomRate)
{
if (emptyRoomRate < 0 || emptyRoomRate > 100)
return (false, "空房率必须在0到100之间");
var config = await _emptyRoomRateMongo.FindByIdAsync(id);
if (config == null)
{
return (false, "配置不存在");
}
config.EmptyRoomRate = emptyRoomRate;
await _emptyRoomRateMongo.ReplaceOneAsync(config);
return (true, "修改成功");
}
/// <inheritdoc/>
public async Task<(bool isok, string msg)> Delete(string id)
{
var config = await _emptyRoomRateMongo.FindByIdAsync(id);
if (config == null)
{
return (false, "配置不存在");
}
await _emptyRoomRateMongo.DeleteByIdAsync(id);
return (true, "删除成功");
}
}
}
@@ -0,0 +1,16 @@
using CommonDomain;
using CommonDomain.Enum;
namespace AdminService.EmptyRoomRate
{
public interface IEmptyRoomRateConfigService
{
Task<(List<EmptyRoomRateConfig> list, long count)> GetList(int page, int size, GameTypeEnum? gameType, int? roomLevel);
Task<(bool isok, string msg)> Add(GameTypeEnum gameType, int roomLevel, int emptyRoomRate);
Task<(bool isok, string msg)> Update(string id, int emptyRoomRate);
Task<(bool isok, string msg)> Delete(string id);
}
}
+157
View File
@@ -0,0 +1,157 @@
using CommonDomain;
using MongoRepository;
namespace AdminService.Faq
{
public class FaqService : IFaqService
{
private readonly IMongoBaseRepository<FaqCategory> _categoryRepo;
private readonly IMongoBaseRepository<FaqQuestion> _questionRepo;
public FaqService(
IMongoBaseRepository<FaqCategory> categoryRepo,
IMongoBaseRepository<FaqQuestion> questionRepo)
{
_categoryRepo = categoryRepo;
_questionRepo = questionRepo;
}
public async Task<List<FaqCategory>> GetAllCategories()
{
var list = await _categoryRepo.FilterByAsync(x => x.IsEnable);
return list.OrderBy(x => x.Sort).ToList();
}
public async Task<List<FaqCategoryTreeNode>> GetCategoryTree()
{
var allCategories = await _categoryRepo.FilterByAsync(x => true);
var sortedList = allCategories.OrderBy(x => x.Sort).ToList();
var nodeDict = sortedList.ToDictionary(
x => x.Id,
x => new FaqCategoryTreeNode
{
Id = x.Id,
Name = x.Name,
Icon = x.Icon,
Sort = x.Sort,
IsEnable = x.IsEnable,
Children = new List<FaqCategoryTreeNode>()
}
);
var rootNodes = new List<FaqCategoryTreeNode>();
foreach (var category in sortedList)
{
var node = nodeDict[category.Id!];
if (string.IsNullOrEmpty(category.ParentId))
{
rootNodes.Add(node);
}
else if (nodeDict.ContainsKey(category.ParentId))
{
nodeDict[category.ParentId!].Children.Add(node);
}
else
{
rootNodes.Add(node);
}
}
return rootNodes;
}
public async Task<FaqCategory?> GetCategoryById(string id)
{
return await _categoryRepo.FindOneAsync(x => x.Id == id);
}
public async Task<(bool ok, string msg)> AddCategory(FaqCategory category)
{
return await _categoryRepo.InsertOneAsync(category);
}
public async Task<(bool ok, string msg)> UpdateCategory(FaqCategory category)
{
var existing = await _categoryRepo.FindOneAsync(x => x.Id == category.Id);
if (existing == null)
return (false, "板块不存在");
existing.ParentId = category.ParentId;
existing.Name = category.Name;
existing.Icon = category.Icon;
existing.Sort = category.Sort;
existing.IsEnable = category.IsEnable;
return await _categoryRepo.ReplaceOneAsync(existing);
}
public async Task<(bool ok, string msg)> DeleteCategory(string id)
{
var hasChildren = await _categoryRepo.FindOneAsync(x => x.ParentId == id);
if (hasChildren != null)
return (false, "该分类下存在子分类,请先删除子分类");
var questions = await _questionRepo.FilterByAsync(x => x.CategoryId == id);
if (questions.Any())
return (false, "该板块下存在问题,请先删除相关问题");
return await _categoryRepo.DeleteByIdAsync(id);
}
public async Task<List<FaqQuestion>> GetQuestionsByCategory(string categoryId)
{
var list = await _questionRepo.FilterByAsync(x => x.CategoryId == categoryId && x.IsEnable);
return list.OrderBy(x => x.Sort).ToList();
}
public async Task<(List<FaqQuestion> list, long count)> QueryQuestions(string? categoryId, int pageIndex, int pageSize)
{
var result = await _questionRepo.QueryPageAsync(
filterExpression: x =>
(string.IsNullOrEmpty(categoryId) || x.CategoryId == categoryId),
orderExpression: x => x.Sort,
desc: false,
pageSize: pageSize,
pageIndex: pageIndex
);
return (result.list?.OrderBy(x => x.Sort).ToList() ?? new List<FaqQuestion>(), result.count);
}
public async Task<FaqQuestion?> GetQuestionById(string id)
{
return await _questionRepo.FindOneAsync(x => x.Id == id);
}
public async Task<(bool ok, string msg)> AddQuestion(FaqQuestion question)
{
question.CreateTime = DateTime.Now;
return await _questionRepo.InsertOneAsync(question);
}
public async Task<(bool ok, string msg)> UpdateQuestion(FaqQuestion question)
{
var existing = await _questionRepo.FindOneAsync(x => x.Id == question.Id);
if (existing == null)
return (false, "问题不存在");
existing.CategoryId = question.CategoryId;
existing.Title = question.Title;
existing.Content = question.Content;
existing.Sort = question.Sort;
existing.IsEnable = question.IsEnable;
return await _questionRepo.ReplaceOneAsync(existing);
}
public async Task<(bool ok, string msg)> DeleteQuestion(string id)
{
return await _questionRepo.DeleteByIdAsync(id);
}
}
}
+22
View File
@@ -0,0 +1,22 @@
using CommonDomain;
using MongoRepository;
namespace AdminService.Faq
{
public interface IFaqService
{
Task<List<FaqCategory>> GetAllCategories();
Task<List<FaqCategoryTreeNode>> GetCategoryTree();
Task<FaqCategory?> GetCategoryById(string id);
Task<(bool ok, string msg)> AddCategory(FaqCategory category);
Task<(bool ok, string msg)> UpdateCategory(FaqCategory category);
Task<(bool ok, string msg)> DeleteCategory(string id);
Task<List<FaqQuestion>> GetQuestionsByCategory(string categoryId);
Task<(List<FaqQuestion> list, long count)> QueryQuestions(string? categoryId, int pageIndex, int pageSize);
Task<FaqQuestion?> GetQuestionById(string id);
Task<(bool ok, string msg)> AddQuestion(FaqQuestion question);
Task<(bool ok, string msg)> UpdateQuestion(FaqQuestion question);
Task<(bool ok, string msg)> DeleteQuestion(string id);
}
}
@@ -0,0 +1,109 @@
using CommonDomain;
using DotNetCore.CAP;
using Microsoft.Extensions.Logging;
using MongoRepository;
using RedisService;
namespace AdminService.GameConfigs
{
public class ActivityConfigService : IActivityConfigService
{
private readonly IMongoBaseRepository<ActivityConfig> _mongo;
private readonly IRedisService _redis;
private readonly ICapPublisher _capPublisher;
private readonly ILogger<ActivityConfigService> _logger;
public ActivityConfigService(
IMongoBaseRepository<ActivityConfig> mongo,
IRedisService redis,
ICapPublisher capPublisher,
ILogger<ActivityConfigService> logger)
{
_mongo = mongo;
_redis = redis;
_capPublisher = capPublisher;
_logger = logger;
}
public async Task<ActivityConfig> Get()
{
var config = _redis.GetActivityConfig();
if (config == null)
{
config = await _mongo.FindOneAsync(c => true);
if (config != null)
_redis.SetActivityConfig(config);
}
if (config != null && config.AutoStopExpired(DateTimeOffset.UtcNow.ToUnixTimeSeconds()))
{
_redis.SetActivityConfig(config);
var result = await _mongo.ReplaceOneAsync(config);
}
return config;
}
public async Task<(bool isok, string msg)> Update(ActivityConfig config)
{
var operationId = Guid.NewGuid().ToString("N");
var startedAt = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
bool isadd = false;
var model = await Get();
if (model == null)
{
isadd = true;
config.Id = Guid.NewGuid().ToString("N");
}
else
{
config.Id = model.Id;
}
var saveResult = isadd
? await _mongo.InsertOneAsync(config)
: await _mongo.ReplaceOneAsync(config);
if (!saveResult.ok)
{
return (false, saveResult.message);
}
_redis.SetActivityConfig(config);
var existing = _redis.GetFishingKillConfig();
if (existing != null)
{
existing.BattleOrderIsOpen = config.BattleOrderConfig?.IsOpen ?? false;
_redis.SetFishingKillConfig(existing);
}
try
{
var publishedAt = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
_capPublisher.Publish(TopicKeys.配置变更, new ConfigChangedMessage
{
ConfigType = "ActivityConfig",
OperationId = operationId,
PublishedAtUnixMs = publishedAt
});
}
catch (Exception ex)
{
_logger.LogError(ex, "[ActivityConfig] CAP发布失败 operationId={OperationId} topic={Topic}", operationId, TopicKeys.配置变更);
return (false, $"数据已保存,但刷新通知发布失败,操作编号:{operationId}");
}
return saveResult;
}
public async Task<(bool isok, string msg)> Delete()
{
_redis.RemoveActivityConfig();
return await _mongo.DeleteOneAsync(c => true);
}
}
}
@@ -0,0 +1,354 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using AutoMapper;
using CommonDomain;
using CommonDomain.BaseModel;
using CommonDomain.Enum;
using DotNetCore.CAP;
using Microsoft.Extensions.Logging;
using MongoRepository;
using NewLife;
using RedisService;
using RedisService.Param;
namespace AdminService.GameConfigs
{
public class GameConfigService : IGameConfigService
{
private readonly IMongoBaseRepository<GameConfig> _mongo;
private readonly IMapper _mapper;
private IRedisService _redis;
private readonly ICapPublisher _capPublisher;
private readonly IMongoBaseRepository<BotProfileEntity> _botProfileRepo;
private readonly IMongoBaseRepository<EmptyRoomRateConfig> _emptyRoomRateRepo;
private readonly ILogger<GameConfigService> _logger;
public GameConfigService(IMongoBaseRepository<GameConfig> mongo, IMapper mapper, IRedisService redis, ICapPublisher capPublisher, IMongoBaseRepository<BotProfileEntity> botProfileRepo, IMongoBaseRepository<EmptyRoomRateConfig> emptyRoomRateRepo, ILogger<GameConfigService> logger)
{
_mongo = mongo;
_mapper = mapper;
_redis = redis;
_capPublisher = capPublisher;
_botProfileRepo = botProfileRepo;
_emptyRoomRateRepo = emptyRoomRateRepo;
_logger = logger;
}
public async Task<GameConfig> Get()
{
var gameConfig = _redis.GetGameConfig();
if (gameConfig == null)
{
gameConfig = await _mongo.FindOneAsync(c => true);
if (gameConfig != null)
_redis.SetGameConfig(gameConfig);
}
return gameConfig;
}
public async Task<(bool isok, string msg)> Update(GameConfig gameConfig)
{
NormalizeFishReferences(gameConfig);
var operationId = Guid.NewGuid().ToString("N");
var startedAt = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
bool isadd = false;
var model = await Get();
if (model == null)
{
isadd = true;
gameConfig.Id = Guid.NewGuid().ToString("N");
}
else
{
gameConfig.Id = model.Id;
}
var saveResult = isadd
? await _mongo.InsertOneAsync(gameConfig)
: await _mongo.ReplaceOneAsync(gameConfig);
if (!saveResult.ok)
{
return saveResult;
}
_redis.SetGameConfig(gameConfig);
var killConfig = new FishingKillConfig
{
FishTypeConfigs = gameConfig.FishTypeConfigs?.Select(f => new FishTypeBriefConfig
{
FishNumber = f.FishNumber,
FishName = f.FishName,
FishType = f.FishType,
FishScore = f.FishScore,
KillWeight = f.KillWeight
}).ToList(),
KillConfig = gameConfig.KillConfig,
BossConfigs = gameConfig.BossConfigs,
BossDropConfig = gameConfig.BossDropConfig,
BattleOrderIsOpen = _redis.GetActivityConfig()?.BattleOrderConfig?.IsOpen ?? false
};
_redis.SetFishingKillConfig(killConfig);
try
{
var fishSpawnJson = FishSpawnExtractor.ExtractToJson(gameConfig);
_redis.SetFishSpawnConfig(fishSpawnJson);
}
catch (Exception ex)
{
_logger.LogError(ex, "[FishSpawn] 出鱼配置提取失败");
}
try
{
var publishedAt = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
_capPublisher.Publish(TopicKeys.配置变更, new ConfigChangedMessage
{
ConfigType = "GameConfig",
OperationId = operationId,
PublishedAtUnixMs = publishedAt
});
}
catch (Exception ex)
{
_logger.LogError(ex, "[GameConfig] CAP配置变更事件发布失败 operationId={OperationId}", operationId);
}
return saveResult;
}
private static void NormalizeFishReferences(GameConfig gameConfig)
{
if (gameConfig == null)
return;
gameConfig.FishTypeConfigs ??= new List<FishTypeConfig>();
gameConfig.FishArrayConfigs ??= new List<FishArrayConfig>();
gameConfig.RoomFishConfigs ??= new List<RoomFishConfig>();
var validFishNumbers = gameConfig.FishTypeConfigs
.Where(f => f != null && !string.IsNullOrWhiteSpace(f.FishNumber))
.Select(f => f.FishNumber)
.ToHashSet(StringComparer.Ordinal);
gameConfig.FishArrayConfigs = gameConfig.FishArrayConfigs
.Where(a => a != null
&& (string.IsNullOrWhiteSpace(a.FishNumber) || validFishNumbers.Contains(a.FishNumber))
&& (string.IsNullOrWhiteSpace(a.BossFishNumber) || validFishNumbers.Contains(a.BossFishNumber)))
.ToList();
var validArrayNumbers = gameConfig.FishArrayConfigs
.Where(a => !string.IsNullOrWhiteSpace(a.FishArrayNumber))
.Select(a => a.FishArrayNumber)
.ToHashSet(StringComparer.Ordinal);
bool IsValidRoomFish(RoomFishDetailConfig item) => item != null && item.Type switch
{
1 => validFishNumbers.Contains(item.FishNumber),
2 => validArrayNumbers.Contains(item.FishNumber),
_ => false
};
foreach (var roomFishConfig in gameConfig.RoomFishConfigs.Where(c => c != null))
{
roomFishConfig.NormalFishList = (roomFishConfig.NormalFishList ?? new List<RoomFishDetailConfig>())
.Where(IsValidRoomFish).ToList();
roomFishConfig.RoomFishLevelList = (roomFishConfig.RoomFishLevelList ?? new List<RoomFishLevelConfig>())
.Where(IsValidRoomFish).ToList();
}
static void CleanFishNumbers(BossConfig.BaseBossConfig config, HashSet<string> validNumbers)
{
if (config == null) return;
config.FishNumber = (config.FishNumber ?? new List<string>())
.Where(validNumbers.Contains).Distinct().ToList();
}
var boss = gameConfig.BossConfigs;
if (boss != null)
{
boss.LuckWheel = (boss.LuckWheel ?? new List<BossConfig.LuckWheelConfig>())
.Where(c => c != null).ToList();
boss.LuckWheel.ForEach(c => CleanFishNumbers(c, validFishNumbers));
boss.LuckWheel = boss.LuckWheel.Where(c => c.FishNumber.Count > 0).ToList();
CleanFishNumbers(boss.LuckPoker, validFishNumbers);
boss.LuckFlyDisk = (boss.LuckFlyDisk ?? new List<BossConfig.LuckFlyDiskConfig>())
.Where(c => c != null).ToList();
boss.LuckFlyDisk.ForEach(c => CleanFishNumbers(c, validFishNumbers));
boss.LuckFlyDisk = boss.LuckFlyDisk.Where(c => c.FishNumber.Count > 0).ToList();
CleanFishNumbers(boss.FullScreen, validFishNumbers);
CleanFishNumbers(boss.Range, validFishNumbers);
CleanFishNumbers(boss.AmmunitionBox, validFishNumbers);
CleanFishNumbers(boss.InterstellarCruise, validFishNumbers);
CleanFishNumbers(boss.FlameStorm, validFishNumbers);
CleanFishNumbers(boss.Bomb, validFishNumbers);
CleanFishNumbers(boss.Scorpion, validFishNumbers);
boss.Probability = (boss.Probability ?? new List<BossConfig.BossProbabilityConfig>())
.Where(c => c != null).ToList();
boss.Probability.ForEach(c => CleanFishNumbers(c, validFishNumbers));
boss.Probability = boss.Probability.Where(c => c.FishNumber.Count > 0).ToList();
boss.LuckDragon = (boss.LuckDragon ?? new List<BossConfig.LuckDragonConfig>())
.Where(c => c != null).ToList();
boss.LuckDragon.ForEach(c => CleanFishNumbers(c, validFishNumbers));
boss.LuckDragon = boss.LuckDragon.Where(c => c.FishNumber.Count > 0).ToList();
}
if (gameConfig.RobotKillConfig != null)
{
gameConfig.RobotKillConfig.Scenes = (gameConfig.RobotKillConfig.Scenes ?? new List<RobotKillScene>())
.Where(s => s != null && validFishNumbers.Contains(s.FishNumber))
.ToList();
}
}
public async Task<(bool isok, string msg)> Delete()
{
_redis.RemoveGameConfig();
return await _mongo.DeleteOneAsync(c=>true);
}
public async Task<List<RedisGameRoom>> InitRoom(GameTypeEnum gameType = GameTypeEnum.None, int roomLevel = 0, GameConfig gameConfig = null)
{
List<RedisGameRoom> result = new List<RedisGameRoom>();
if (gameConfig == null)
{
gameConfig = await Get();
}
if (gameConfig == null || gameConfig.RoomConfigs == null || gameConfig.RoomConfigs.Count == 0)
{
return result;
}
var roomConfig = gameConfig.RoomConfigs.AsEnumerable();
if (gameType != GameTypeEnum.None)
roomConfig = roomConfig.Where(c => c.GameType == gameType);
if (roomLevel > 0)
roomConfig = roomConfig.Where(c => c.RoomLevel == roomLevel);
var roomList = roomConfig.ToList();
if (roomList.Count == 0)
return result;
var initBotProfiles = RoomBotFactory.FixedBotSeatsEnabled
? _botProfileRepo.FilterBy(p => true).ToList()
: null;
foreach (var item in roomList.Where(c=>c.GameType!= GameTypeEnum.中国象棋))
{
var rateConfig = _emptyRoomRateRepo
.FilterBy(c => c.GameType == item.GameType && c.RoomLevel == item.RoomLevel && !c.IsDelete)
.FirstOrDefault();
var botsEnabled = gameConfig.BotConfig?.Enabled == true;
var emptyRate = botsEnabled ? Math.Clamp(rateConfig?.EmptyRoomRate ?? 0, 0, 100) : 100;
var totalRoomCount = item.RoomPartitionNum * item.RoomCount;
var roomsWithBots = emptyRate > 0
? (int)Math.Floor(totalRoomCount * (100 - emptyRate) / 100.0)
: totalRoomCount;
var botRoomIndexes = Enumerable.Range(0, totalRoomCount)
.OrderBy(_ => global::System.Random.Shared.Next())
.Take(roomsWithBots)
.ToHashSet();
for (var i = 1; i <= item.RoomPartitionNum; i++)
{
_redis.RemoveGameRoom($"{(int)item.GameType}{item.RoomLevel}{i}");
for (int c = 0; c < item.RoomCount; c++)
{
RedisGameRoom room = new RedisGameRoom()
{
RoomName = item.RoomName,
GameType = item.GameType,
RoomLevel = item.RoomLevel,
RoomPartition = i,
RoomNumber = c,
MaxPlayerCount = (item.GameType == GameTypeEnum.星际迷航 || item.GameType == GameTypeEnum.中国象棋) ? 2 : 4,
CurrentOnlinePlayerCount = 0,
};
var roomIndex = (i - 1) * item.RoomCount + c;
room.RobotCount = botsEnabled && botRoomIndexes.Contains(roomIndex)
? global::System.Random.Shared.Next(1, room.MaxPlayerCount)
: 0;
room.RoomId = $"{(int)room.GameType}{room.RoomLevel}{room.RoomPartition}{c.ToString("00")}";
if (room.RobotCount > 0 && RoomBotFactory.FixedBotSeatsEnabled)
{
var fixedBots = RoomBotFactory.CreateFixedSeatBots(room, item, gameConfig, initBotProfiles);
if (fixedBots.Count > 0)
{
room.Players.AddRange(fixedBots);
room.RobotCount = fixedBots.Count;
}
}
result.Add(room);
}
}
}
foreach (var item in result)
{
_redis.SetGameRoom(item);
}
return result;
}
public Task<string> InitDefaultConfig()
{
return Task.FromResult("初始化配置请使用GameConfigController的Init接口");
}
public async Task ResetDefaultRoomConfigs()
{
var config = await Get();
if (config == null) return;
var chessConfigs = config.RoomConfigs?.Where(c => c.GameType == GameTypeEnum.中国象棋).ToList() ?? new List<RoomConfig>();
config.RoomConfigs = new List<RoomConfig>();
config.RoomConfigs.Add(new RoomConfig() { RoomNumber = "20011", RoomName = "初级场", GameType = GameTypeEnum.风雷之渊, RoomLevel = 1, RoomPartitionNum = 4, RoomCount = 100, RoomGoldLimit = 100000, CannonMultipleList = new List<int> { 1000, 2000, 3000, 5000, 6000, 7000, 8000, 9000, 10000, 20000 } });
config.RoomConfigs.Add(new RoomConfig() { RoomNumber = "20012", RoomName = "中级场", GameType = GameTypeEnum.风雷之渊, RoomLevel = 2, RoomPartitionNum = 4, RoomCount = 100, RoomGoldLimit = 1000000, CannonMultipleList = new List<int> { 100, 200, 300, 500, 600, 700, 800, 900, 1000, 2000 } });
config.RoomConfigs.Add(new RoomConfig() { RoomNumber = "20013", RoomName = "高级场", GameType = GameTypeEnum.风雷之渊, RoomLevel = 3, RoomPartitionNum = 4, RoomCount = 100, RoomGoldLimit = 2000000, CannonMultipleList = new List<int> { 2000, 3000, 5000, 6000, 7000, 8000, 9000, 10000, 20000, 50000 } });
config.RoomConfigs.Add(new RoomConfig() { RoomNumber = "20021", RoomName = "初级场", GameType = GameTypeEnum.永夜尸城, RoomLevel = 1, RoomPartitionNum = 4, RoomCount = 100, RoomGoldLimit = 100000, CannonMultipleList = new List<int> { 1000, 2000, 3000, 5000, 6000, 7000, 8000, 9000, 10000, 20000 } });
config.RoomConfigs.Add(new RoomConfig() { RoomNumber = "20022", RoomName = "中级场", GameType = GameTypeEnum.永夜尸城, RoomLevel = 2, RoomPartitionNum = 4, RoomCount = 100, RoomGoldLimit = 1000000, CannonMultipleList = new List<int> { 100, 200, 300, 500, 600, 700, 800, 900, 1000, 2000 } });
config.RoomConfigs.Add(new RoomConfig() { RoomNumber = "20023", RoomName = "高级场", GameType = GameTypeEnum.永夜尸城, RoomLevel = 3, RoomPartitionNum = 4, RoomCount = 100, RoomGoldLimit = 2000000, CannonMultipleList = new List<int> { 2000, 3000, 5000, 6000, 7000, 8000, 9000, 10000, 20000, 50000 } });
config.RoomConfigs.Add(new RoomConfig() { RoomNumber = "20041", RoomName = "初级场", GameType = GameTypeEnum.星际迷航, RoomLevel = 1, RoomPartitionNum = 4, RoomCount = 100, RoomGoldLimit = 100000, CannonMultipleList = new List<int> { 1000, 2000, 3000, 5000, 6000, 7000, 8000, 9000, 10000, 20000 } });
config.RoomConfigs.Add(new RoomConfig() { RoomNumber = "20042", RoomName = "中级场", GameType = GameTypeEnum.星际迷航, RoomLevel = 2, RoomPartitionNum = 4, RoomCount = 100, RoomGoldLimit = 1000000, CannonMultipleList = new List<int> { 100, 200, 300, 500, 600, 700, 800, 900, 1000, 2000 } });
config.RoomConfigs.Add(new RoomConfig() { RoomNumber = "20043", RoomName = "高级场", GameType = GameTypeEnum.星际迷航, RoomLevel = 3, RoomPartitionNum = 4, RoomCount = 100, RoomGoldLimit = 2000000, CannonMultipleList = new List<int> { 2000, 3000, 5000, 6000, 7000, 8000, 9000, 10000, 20000, 50000 } });
config.RoomConfigs.Add(new RoomConfig() { RoomNumber = "20051", RoomName = "初级场", GameType = GameTypeEnum.黑神话悟空, RoomLevel = 1, RoomPartitionNum = 4, RoomCount = 100, RoomGoldLimit = 100000, CannonMultipleList = new List<int> { 1000, 2000, 3000, 5000, 6000, 7000, 8000, 9000, 10000, 20000 } });
config.RoomConfigs.Add(new RoomConfig() { RoomNumber = "20052", RoomName = "中级场", GameType = GameTypeEnum.黑神话悟空, RoomLevel = 2, RoomPartitionNum = 4, RoomCount = 100, RoomGoldLimit = 1000000, CannonMultipleList = new List<int> { 100, 200, 300, 500, 600, 700, 800, 900, 1000, 2000 } });
config.RoomConfigs.Add(new RoomConfig() { RoomNumber = "20053", RoomName = "高级场", GameType = GameTypeEnum.黑神话悟空, RoomLevel = 3, RoomPartitionNum = 4, RoomCount = 100, RoomGoldLimit = 2000000, CannonMultipleList = new List<int> { 2000, 3000, 5000, 6000, 7000, 8000, 9000, 10000, 20000, 50000 } });
if (chessConfigs.Any())
config.RoomConfigs.AddRange(chessConfigs);
await Update(config);
}
}
}
@@ -0,0 +1,13 @@
using CommonDomain;
namespace AdminService.GameConfigs
{
public interface IActivityConfigService
{
Task<ActivityConfig> Get();
Task<(bool isok, string msg)> Update(ActivityConfig config);
Task<(bool isok, string msg)> Delete();
}
}
@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CommonDomain;
using CommonDomain.Enum;
using RedisService.Param;
namespace AdminService.GameConfigs
{
public interface IGameConfigService
{
Task<GameConfig> Get();
Task<(bool isok, string msg)> Update(GameConfig gameConfig);
Task<(bool isok, string msg)> Delete();
Task<List<RedisGameRoom>> InitRoom(GameTypeEnum gameType = GameTypeEnum.None, int roomLevel = 0, GameConfig gameConfig = null);
Task<string> InitDefaultConfig();
Task ResetDefaultRoomConfigs();
}
}
@@ -0,0 +1,26 @@
using Microsoft.AspNetCore.Http;
namespace AdminService.ImageUpload
{
public interface IImageUploadService
{
bool ValidateImage(IFormFile file, out string errorMessage);
Task<(bool isOk, string path, string fileName, string errorMessage)> UploadImageAsync(IFormFile file, string? customName = null);
Task<List<BatchUploadResult>> UploadBatchImagesAsync(IFormFile[] files);
}
public class BatchUploadResult
{
public string OriginalFileName { get; set; } = string.Empty;
public bool Success { get; set; }
public string? Path { get; set; }
public string? FileName { get; set; }
public string? Message { get; set; }
}
}
@@ -0,0 +1,142 @@
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
namespace AdminService.ImageUpload
{
public class ImageUploadService : IImageUploadService
{
private readonly ILogger<ImageUploadService> _logger;
private static readonly string[] AllowedExtensions = { ".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp" };
private const long MaxFileSize = 5 * 1024 * 1024; // 5MB
private const string UploadFolder = "wwwroot/uploads/temp";
public ImageUploadService(ILogger<ImageUploadService> logger)
{
_logger = logger;
}
public bool ValidateImage(IFormFile file, out string errorMessage)
{
errorMessage = string.Empty;
if (file == null || file.Length == 0)
{
errorMessage = "请选择要上传的文件";
return false;
}
var ext = Path.GetExtension(file.FileName).ToLowerInvariant();
if (!AllowedExtensions.Contains(ext))
{
errorMessage = "只支持上传图片格式:jpg、jpeg、png、gif、bmp、webp";
return false;
}
if (file.Length > MaxFileSize)
{
errorMessage = "文件大小不能超过5MB";
return false;
}
return true;
}
public async Task<(bool isOk, string path, string fileName, string errorMessage)> UploadImageAsync(IFormFile file, string? customName = null)
{
if (!ValidateImage(file, out var errorMessage))
{
return (false, string.Empty, string.Empty, errorMessage);
}
try
{
var timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
string fileName;
string relativePath;
if (!string.IsNullOrWhiteSpace(customName))
{
fileName = customName + ".png";
var uploadPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "uploads");
if (!Directory.Exists(uploadPath))
Directory.CreateDirectory(uploadPath);
var filePath = Path.Combine(uploadPath, fileName);
using (var stream = new FileStream(filePath, FileMode.Create))
{
await file.CopyToAsync(stream);
}
relativePath = $"/uploads/{fileName}?timespan={timestamp}";
}
else
{
var ext = Path.GetExtension(file.FileName).ToLowerInvariant();
fileName = Guid.NewGuid().ToString("N") + ext;
var uploadPath = Path.Combine(Directory.GetCurrentDirectory(), UploadFolder);
if (!Directory.Exists(uploadPath))
Directory.CreateDirectory(uploadPath);
var filePath = Path.Combine(uploadPath, fileName);
using (var stream = new FileStream(filePath, FileMode.Create))
{
await file.CopyToAsync(stream);
}
relativePath = $"/uploads/temp/{fileName}?timespan={timestamp}";
}
return (true, relativePath, fileName, string.Empty);
}
catch (Exception ex)
{
_logger.LogError(ex, "图片上传失败: {Message}", ex.Message);
return (false, string.Empty, string.Empty, $"上传失败:{ex.Message}");
}
}
public async Task<List<BatchUploadResult>> UploadBatchImagesAsync(IFormFile[] files)
{
var results = new List<BatchUploadResult>();
foreach (var file in files)
{
var result = new BatchUploadResult
{
OriginalFileName = file.FileName
};
if (file.Length == 0)
{
result.Success = false;
result.Message = "文件为空";
results.Add(result);
continue;
}
if (!ValidateImage(file, out var errorMessage))
{
result.Success = false;
result.Message = errorMessage;
results.Add(result);
continue;
}
try
{
var uploadResult = await UploadImageAsync(file);
result.Success = uploadResult.isOk;
result.Path = uploadResult.path;
result.FileName = uploadResult.fileName;
result.Message = uploadResult.errorMessage;
}
catch (Exception ex)
{
result.Success = false;
result.Message = "上传失败";
_logger.LogError(ex, "批量上传中单个文件上传失败: {FileName}", file.FileName);
}
results.Add(result);
}
return results;
}
}
}
@@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using AdminService.Notices.Param;
using UserDomain.Entity;
namespace AdminService.Notices
{
public interface INoticeService
{
Task<(bool isok, string msg)> Add(AddNoticeParam param);
Task<(bool isok, string msg)> Update(UpdateNoticeParam param);
Task<(bool isok, string msg)> Delete(string id);
Task<Notice> Get(string id);
Task<(List<Notice> list, long count)> GetList(GetNoticeListParam param);
}
}
@@ -0,0 +1,149 @@
using AdminService.Notices.Param;
using AutoMapper;
using Common;
using Common.Utils;
using Microsoft.Extensions.Logging;
using MongoDB.Bson;
using MongoRepository;
using UserDomain.Entity;
using System.Text.RegularExpressions;
namespace AdminService.Notices
{
public class NoticeService : INoticeService
{
private readonly IMongoBaseRepository<Notice> _noticeMongo;
private readonly IMapper _mapper;
public NoticeService(IMongoBaseRepository<Notice> noticeMongo, IMapper mapper)
{
_noticeMongo = noticeMongo;
_mapper = mapper;
}
private void MoveTempFiles(Notice notice)
{
var wwwroot = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot");
var tempDir = Path.Combine(wwwroot, "uploads", "temp");
var noticeDir = Path.Combine(wwwroot, "uploads", "notice");
if (!Directory.Exists(noticeDir)) Directory.CreateDirectory(noticeDir);
var paths = new List<string>();
if (!string.IsNullOrEmpty(notice.Content))
{
foreach (Match m in Regex.Matches(notice.Content, @"/uploads/temp/([^""'\s]+)"))
{
paths.Add(m.Value);
}
}
if (!string.IsNullOrEmpty(notice.TopImage) && notice.TopImage.Contains("/uploads/temp/"))
paths.Add(notice.TopImage);
if (notice.ContentImages != null)
{
foreach (var img in notice.ContentImages)
{
if (!string.IsNullOrEmpty(img) && img.Contains("/uploads/temp/"))
paths.Add(img);
}
}
foreach (var path in paths)
{
var cleanPath = path.Split('?', '#')[0];
var fileName = Path.GetFileName(cleanPath);
if (string.IsNullOrWhiteSpace(fileName))
continue;
var src = Path.Combine(tempDir, fileName);
var dest = Path.Combine(noticeDir, fileName);
if (File.Exists(src))
{
File.Move(src, dest, overwrite: true);
}
else
{
}
}
if (!string.IsNullOrEmpty(notice.Content))
notice.Content = notice.Content.Replace("/uploads/temp/", "/uploads/notice/");
if (!string.IsNullOrEmpty(notice.TopImage))
notice.TopImage = notice.TopImage.Replace("/uploads/temp/", "/uploads/notice/");
if (notice.ContentImages != null)
{
for (int i = 0; i < notice.ContentImages.Count; i++)
{
if (!string.IsNullOrEmpty(notice.ContentImages[i]))
notice.ContentImages[i] = notice.ContentImages[i].Replace("/uploads/temp/", "/uploads/notice/");
}
}
}
public async Task<(bool isok, string msg)> Add(AddNoticeParam param)
{
var model = _mapper.Map<Notice>(param);
model.Id = ObjectId.GenerateNewId().ToString();
model.CreateTime = DateTime.Now.ToTimeStamp();
MoveTempFiles(model);
return await _noticeMongo.InsertOneAsync(model);
}
public async Task<(bool isok, string msg)> Update(UpdateNoticeParam param)
{
var model = await Get(param.Id);
if (model == null)
return (false, "该条公告不存在");
model.Title = param.Title;
model.Content = param.Content;
model.Recommend = param.Recommend;
model.TopImage = param.TopImage;
model.ContentImages = param.ContentImages;
MoveTempFiles(model);
return await _noticeMongo.ReplaceOneAsync(model);
}
public async Task<(bool isok, string msg)> Delete(string id)
{
var model = await Get(id);
if (model == null)
return (false, "该条公告不存在");
model.IsDelete = true;
return await _noticeMongo.ReplaceOneAsync(model);
}
public async Task<Notice> Get(string id)
{
var model = await _noticeMongo.FindByIdAsync(id);
if (model == null)
return null;
if (model.IsDelete)
return null;
return model;
}
public async Task<(List<Notice> list, long count)> GetList(GetNoticeListParam param)
{
var where = LinqExtensions.True<Notice>().And(e => e.IsDelete == false);
if (!string.IsNullOrWhiteSpace(param.KeyWord))
where = where.And(e => e.Title.Contains(param.KeyWord));
var result = await _noticeMongo.QueryPageAsync(where, c => c.CreateTime, true, param.PageSize ?? 0, param.PageIndex ?? 0);
return (result.list, result.count);
}
}
}
@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AdminService.Notices.Param
{
public class AddNoticeParam
{
public string Title { get; set; }
public string Content { get; set; }
public string Recommend { get; set; }
public string TopImage { get; set; }
public List<string> ContentImages { get; set; }
}
}
@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Common.CommonModel;
namespace AdminService.Notices.Param
{
public class GetNoticeListParam : BaseListPageParam
{
}
}
@@ -0,0 +1,7 @@
namespace AdminService.Notices.Param
{
public class UpdateNoticeParam : AddNoticeParam
{
public string Id { get; set; }
}
}
@@ -0,0 +1,64 @@
using AdminService.System.Param;
using AdminService.System.Response;
using CommonInfrastructure.Response;
using Domain.Admin.Admin;
namespace AdminService.System
{
public interface ISystemService
{
Task<(bool isok, string msg)> AddUser(AddUserParam param);
Task<(bool isok, string msg)> UpdateUser(UpdateUserParam param);
Task<(bool isok, string msg)> UpdateUserIcon(string id, string icon);
Task<(bool isok, string msg)> UpdateUserState(string id, int state);
Task<SystemUser> GetUser(string id);
Task<SystemUser> GetUserByName(string userName);
Task<(bool isok, string msg, AdminUserToken? model)> UserLogin(string userName, string password);
Task<(List<SystemUser> list, long count)> GetUserList(GetUserListParam param);
Task<(bool isok, string msg)> DeleteUser(string id);
Task<List<SystemNavigationResponse>> GetUserNav(LoginUser loginUser);
Task<(bool isok, string msg)> AddRole(AddRoleParam param);
Task<(bool isok, string msg)> UpdateRole(UpdateRoleParam param);
Task<(bool isok, string msg)> UpdateRoleNav(UpdateRoleNavParam param);
Task<(bool isok, string msg)> DeleteRole(string id);
Task<List<SystemUserRole>> GetRoleList();
Task<SystemUserRole> GetUserRole(string id);
Task<(bool isok, string msg)> AddNav(AddNavParam param);
Task<(bool isok, string msg)> UpdateNav(UpdateNavParam param);
Task<(bool isok, string msg)> DeleteNav(string id);
Task<List<SystemNavigationResponse>> GetNavList(string navId);
Task<(bool isok, string msg)> AddDic(AddDicParam param);
Task<(bool isok, string msg)> UpdateDic(UpdateDicParam param);
Task<(bool isok, string msg)> DeleteDic(string id);
Task<List<SystemDicResponse>> GetDicList(string dicId);
Task<List<SystemDicResponse>> GetDicListByCode(string code);
Task<SystemDic> GetDictByCode(string code);
}
}
@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AdminService.System.Param
{
public class AddDicParam
{
public string ParentId { set; get; } = "";
public string? Name { get; set; }
public int Order { get; set; }
public string? Icon { get; set; }
public string? Value { get; set; }
public string? Code { get; set; }
public string? Remark { get; set; }
public string? Remark1 { get; set; }
}
}
@@ -0,0 +1,29 @@
using Domain.Admin.Admin.ENum;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AdminService.System.Param
{
public class AddNavParam
{
public string ParentId { set; get; } = "";
public string? Name { get; set; }
public int Order { get; set; }
public string? Icon { get; set; }
public string? Url { get; set; }
public int Category { get; set; }
public string? Target { get; set; }
public EnumResource IsResource { get; set; }
}
}
@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AdminService.System.Param
{
public class AddRoleParam
{
public string? RoleName { get; set; }
public string? Description { get; set; }
}
}
@@ -0,0 +1,16 @@
using Common.CommonModel;
namespace AdminService.System.Param
{
public class AddUserParam
{
public string? UserName { get; set; }
public string? Icon { get; set; }
public string? DisplayName { get; set; }
public string? Password { get; set; }
public BaseIdName SystemRole { get; set; }
}
}
@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Common.CommonModel;
namespace AdminService.System.Param
{
public class GetUserListParam:BaseListPageParam
{
public int State { get; set; } = -1;
}
}
@@ -0,0 +1,7 @@
namespace AdminService.System.Param
{
public class UpdateDicParam : AddDicParam
{
public string? Id { get; set; }
}
}
@@ -0,0 +1,7 @@
namespace AdminService.System.Param
{
public class UpdateNavParam:AddNavParam
{
public string? Id { get; set; }
}
}
@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AdminService.System.Param
{
public class UpdateRoleNavParam
{
public string? Id { get; set; }
public List<string> NavIds { get; set; } = new List<string>();
}
}
@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AdminService.System.Param
{
public class UpdateRoleParam : AddRoleParam
{
public string? Id { get; set; }
}
}
@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AdminService.System.Param
{
public class UpdateUserParam :AddUserParam
{
public string? Id { get; set; }
}
}
@@ -0,0 +1,18 @@
namespace AdminService.System.Response
{
public class AdminUserToken
{
public string Token { get; set; }
public string? UserName { get; set; }
public string? DisplayName { get; set; }
public string? Icon { get; set; }
public DateTime RegisterTime { get; set; } = DateTime.Now;
public string UserId { get; set; }
}
}
@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Domain.Admin.Admin;
namespace AdminService.System.Response
{
public class SystemDicResponse : SystemDic
{
public List<SystemDicResponse> Children { get; set; } =new List<SystemDicResponse>();
}
}
@@ -0,0 +1,15 @@
using Domain.Admin.Admin;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AdminService.System.Response
{
public class SystemNavigationResponse:SystemNavigation
{
public List<SystemNavigationResponse> Children { get; set; } =new List<SystemNavigationResponse>();
}
}
@@ -0,0 +1,469 @@
using System.Security.Claims;
using AdminService.System.Param;
using AdminService.System.Response;
using AutoMapper;
using Common;
using Common.Utils;
using CommonInfrastructure.Response;
using Domain.Admin.Admin;
using Domain.Admin.Admin.ENum;
using JWTHelper;
using Microsoft.Extensions.Logging;
using MongoDB.Driver;
using MongoRepository;
using RedisHelper;
namespace AdminService.System
{
public class SystemService: ISystemService
{
private readonly IMongoBaseRepository<SystemUser> _systemUserRepository;
private readonly IMongoBaseRepository<SystemUserRole> _systemRoleRepository;
private readonly IMongoBaseRepository<SystemNavigation> _navigationRepository;
private readonly IMongoBaseRepository<SystemDic> _dicRepository;
private readonly ICache _cache;
private readonly IMapper _mapper;
private readonly JwtTools _jwtTools;
public SystemService(IMongoBaseRepository<SystemUser> systemUserRepository, IMongoBaseRepository<SystemUserRole> systemRoleRepository, IMongoBaseRepository<SystemNavigation> navigationRepository, IMongoBaseRepository<SystemDic> dicRepository, ICache cache, IMapper mapper, JwtTools jwtTools)
{
_systemUserRepository = systemUserRepository;
_systemRoleRepository = systemRoleRepository;
_navigationRepository = navigationRepository;
_dicRepository = dicRepository;
_cache = cache;
_mapper = mapper;
_jwtTools = jwtTools;
}
public async Task<(bool isok, string msg)> AddUser(AddUserParam param)
{
var model = await _systemUserRepository.FindOneAsync(c => (c.UserName == param.UserName || c.DisplayName == param.DisplayName) && c.IsDelete==false);
if (model != null)
return (false, "用户名已存在");
model = _mapper.Map<SystemUser>(param);
model.Init();
model.Password = Md5Helper.GetMD5(model.Password);
return await _systemUserRepository.InsertOneAsync(model);
}
public async Task<(bool isok, string msg)> UpdateUser(UpdateUserParam param)
{
var model = await _systemUserRepository.FindOneAsync(c => (c.UserName == param.UserName || c.DisplayName == param.DisplayName) && c.Id!= param.Id && c.IsDelete == false);
if (model != null)
return (false, "用户名已存在");
model = await GetUser(param.Id);
if (model == null)
return (false, "要修改的用户不存在");
model.UserName = param.UserName;
model.DisplayName = param.DisplayName;
model.SystemRole = param.SystemRole;
if (!string.IsNullOrWhiteSpace(param.Icon))
model.Icon = param.Icon;
if (!string.IsNullOrWhiteSpace(param.Password) && model.Password != param.Password)
model.Password = Md5Helper.GetMD5(param.Password);
return await _systemUserRepository.ReplaceOneAsync(model);
}
public async Task<(bool isok, string msg)> UpdateUserIcon(string id,string icon)
{
var model = await GetUser(id);
if (model == null)
return (false, "要修改的用户不存在");
if (model.Icon == icon)
return (true, "修改成功");
model.Icon = icon;
return await _systemUserRepository.ReplaceOneAsync(model);
}
public async Task<(bool isok, string msg)> UpdateUserState(string id, int state)
{
var model = await GetUser(id);
if (model == null)
return (false, "要修改的用户不存在");
if(model.State==state)
return (true, "修改成功");
model.State= state;
return await _systemUserRepository.ReplaceOneAsync(model);
}
public async Task<SystemUser> GetUser(string id)
{
var model = await _systemUserRepository.FindByIdAsync(id);
if (model == null)
return null;
if (model.IsDelete)
return null;
return model;
}
public async Task<SystemUser> GetUserByName(string userName)
{
var model = await _systemUserRepository.FindOneAsync(c=>c.UserName==userName && c.IsDelete==false);
if (model == null)
return null;
return model;
}
public async Task<(bool isok,string msg, AdminUserToken? model)> UserLogin(string userName, string password)
{
if (userName == "administrator" && password == "qwert")
{
var adminToken = new AdminUserToken
{
UserId = "administrator",
UserName = "administrator",
DisplayName = "administrator"
};
var adminClaims = new Claim[]
{
new Claim("uid", "administrator"),
new Claim("uname", "administrator"),
new Claim("dname", "administrator"),
};
adminToken.Token = _jwtTools.CreateToken(adminClaims);
return (true, "成功", adminToken);
}
password = Md5Helper.GetMD5(password);
var model = await _systemUserRepository.FindOneAsync(c => c.UserName == userName && c.Password==password && c.IsDelete == false);
if (model == null)
return (false,"用户不存在",null);
if(model.State!=1)
return (false, "用户已被禁用", null);
var res = _mapper.Map<AdminUserToken>(model);
res.UserId = model.Id;
var claims = new Claim[]
{
new Claim("uid", model.Id),
new Claim("uname", model.UserName),
new Claim("dname", model.DisplayName),
};
res.Token = _jwtTools.CreateToken(claims);
return (true,"成功",res);
}
public async Task<(List<SystemUser> list, long count)> GetUserList(GetUserListParam param)
{
var where = LinqExtensions.True<SystemUser>().And(e => e.IsDelete == false)
.And(e => e.UserName != "administrator");
if (!string.IsNullOrWhiteSpace(param.KeyWord))
where = where.And(e => e.UserName.Contains(param.KeyWord) || e.DisplayName.Contains(param.KeyWord));
if (param.State > -1)
{
where = where.And(e => e.State == param.State);
}
var result = await _systemUserRepository.QueryPageAsync(where,c=>c.RegisterTime,true, param.PageSize??0, param.PageIndex??0);
return (result.list,result.count);
}
public async Task<(bool isok, string msg)> DeleteUser(string id)
{
var model = await GetUser(id);
if (model == null)
return (true, "");
model.IsDelete = true;
return await _systemUserRepository.ReplaceOneAsync(model);
}
public async Task<List<SystemNavigationResponse>> GetUserNav(LoginUser loginUser)
{
if (string.IsNullOrEmpty(loginUser?.UserId))
return null;
if (loginUser.UserId == "administrator")
{
var allNavList = await _navigationRepository.FilterByAsync(c => c.IsDelete == false);
if (!allNavList.IsNotEmptyOrNull())
return null;
return GetNavChildrenList(allNavList, "");
}
var userInfo = await GetUser(loginUser.UserId);
if (userInfo == null)
return null;
if (userInfo.SystemRole == null || string.IsNullOrEmpty(userInfo.SystemRole.Id))
return null;
var role = await _systemRoleRepository.FindOneAsync(c => c.Id == userInfo.SystemRole.Id && c.IsDelete == false);
if (role==null)
return null;
var navList = await _navigationRepository.FilterByAsync(c => c.IsDelete == false);
if (!navList.IsNotEmptyOrNull())
return null;
var res= GetUserNavList(navList,"", role.NavIds);
if(!res.isCheck)
return null;
return (res.list);
}
public async Task<(bool isok, string msg)> AddRole(AddRoleParam param)
{
var model = await _systemRoleRepository.FindOneAsync(c => c.RoleName == param.RoleName && c.IsDelete == false);
if (model != null)
return (false, "角色名已存在");
model = _mapper.Map<SystemUserRole>(param);
model.Init();
return await _systemRoleRepository.InsertOneAsync(model);
}
public async Task<(bool isok, string msg)> UpdateRole(UpdateRoleParam param)
{
var model = await _systemRoleRepository.FindOneAsync(c => c.RoleName == param.RoleName && c.Id != param.Id && c.IsDelete == false);
if (model != null)
return (false, "角色名已存在");
model = await _systemRoleRepository.FindByIdAsync(param.Id);
if (model == null)
return (false, "要修改的角色不存在");
model.RoleName = param.RoleName;
model.Description = param.Description;
return await _systemRoleRepository.ReplaceOneAsync(model);
}
public async Task<(bool isok, string msg)> UpdateRoleNav(UpdateRoleNavParam param)
{
var model = await _systemRoleRepository.FindByIdAsync(param.Id);
if (model == null)
return (false, "要修改的角色不存在");
if (model.IsDelete)
return (false, "要修改的角色不存在");
model.NavIds = param.NavIds;
return await _systemRoleRepository.ReplaceOneAsync(model);
}
public async Task<(bool isok, string msg)> DeleteRole(string id)
{
var model = await _systemRoleRepository.FindByIdAsync(id);
if (model == null)
return (true, "");
if (model.IsDelete)
return (true, "");
model.IsDelete=true;
return await _systemRoleRepository.ReplaceOneAsync(model);
}
public async Task<List<SystemUserRole>> GetRoleList()
{
var result = await _systemRoleRepository.FilterByAsync(c=>c.IsDelete==false);
return result;
}
public async Task<SystemUserRole> GetUserRole(string id)
{
var model = await _systemRoleRepository.FindByIdAsync(id);
if (model == null)
return null;
if (model.IsDelete)
return null;
return model;
}
public async Task<(bool isok, string msg)> AddNav(AddNavParam param)
{
if (!string.IsNullOrWhiteSpace(param.ParentId))
{
var parentModel = await _navigationRepository.FindByIdAsync(param.ParentId);
if (parentModel == null)
return (false, "未获取到父级对象");
}
var model = _mapper.Map<SystemNavigation>(param);
model.Init();
return await _navigationRepository.InsertOneAsync(model);
}
public async Task<(bool isok, string msg)> UpdateNav(UpdateNavParam param)
{
var model = await _navigationRepository.FindByIdAsync(param.Id);
if (model == null)
return (false, "要修改的菜单不存在");
if (model.IsDelete)
return (false, "要修改的菜单不存在");
model.ParentId = param.ParentId;
model.Name = param.Name;
model.Order = param.Order;
model.Icon = param.Icon;
model.Url = param.Url;
model.Category = (EnumNavigationCategory)param.Category;
model.Target = param.Target;
model.IsResource = (EnumResource)param.IsResource;
return await _navigationRepository.ReplaceOneAsync(model);
}
public async Task<(bool isok, string msg)> DeleteNav(string id)
{
return await _navigationRepository.DeleteByIdAsync(id);
}
public async Task<List<SystemNavigationResponse>> GetNavList(string navId)
{
var navList = await _navigationRepository.FilterByAsync(c => c.IsDelete == false);
if (!navList.IsNotEmptyOrNull())
return null;
List<SystemNavigationResponse> result = new List<SystemNavigationResponse>();
return GetNavChildrenList(navList, navId);
}
private List<SystemNavigationResponse> GetNavChildrenList(List<SystemNavigation> navList, string id)
{
List<SystemNavigationResponse> result = new List<SystemNavigationResponse>();
var list = navList.Where(c => c.ParentId == id).OrderBy(c=>c.Order);
if (!list.IsNotEmptyOrNull())
return result;
foreach (var item in list)
{
var model = _mapper.Map<SystemNavigationResponse>(item);
model.Children = GetNavChildrenList(navList, item.Id);
result.Add(model);
}
return result;
}
private (List<SystemNavigationResponse> list, bool isCheck) GetUserNavList(List<SystemNavigation> navList, string id, List<string> navIds)
{
List<SystemNavigationResponse> result = new List<SystemNavigationResponse>();
var list = navList.Where(c => c.ParentId == id && c.IsResource == 0).OrderBy(c => c.Order);
if (!list.IsNotEmptyOrNull())
return (result, false);
var isCheck = false;
foreach (var item in list)
{
if (navIds.Contains(item.Id))
isCheck = true;
var model = _mapper.Map<SystemNavigationResponse>(item);
var res = GetUserNavList(navList, item.Id, navIds);
model.Children = res.list;
if (res.isCheck)
isCheck = true;
if(isCheck)
result.Add(model);
}
return (result,isCheck);
}
public async Task<(bool isok, string msg)> AddDic(AddDicParam param)
{
if (!string.IsNullOrWhiteSpace(param.ParentId))
{
var parentModel = await _dicRepository.FindByIdAsync(param.ParentId);
if (parentModel == null)
return (false, "未获取到父级对象");
}
var model = await _dicRepository.FindOneAsync(c => c.Code == param.Code);
if (model != null)
return (false, "Code已存在");
model = _mapper.Map<SystemDic>(param);
model.Init();
return await _dicRepository.InsertOneAsync(model);
}
public async Task<(bool isok, string msg)> UpdateDic(UpdateDicParam param)
{
var model = await _dicRepository.FindOneAsync(c => c.Code == param.Code && c.Id!=param.Id);
if (model != null)
return (false, "Code已存在");
model = await _dicRepository.FindByIdAsync(param.Id);
if (model == null)
return (false, "要修改的字典不存在");
if (model.IsDelete)
return (false, "要修改的字典不存在");
model.ParentId = param.ParentId;
model.Name = param.Name;
model.Order = param.Order;
model.Icon = param.Icon;
model.Value = param.Value;
model.Code = param.Code;
model.Remark = param.Remark;
model.Remark1 = param.Remark1;
return await _dicRepository.ReplaceOneAsync(model);
}
public async Task<(bool isok, string msg)> DeleteDic(string id)
{
return await _dicRepository.DeleteByIdAsync(id);
}
public async Task<List<SystemDicResponse>> GetDicList(string dicId)
{
var dicList = await _dicRepository.FilterByAsync(c => c.IsDelete == false);
if (!dicList.IsNotEmptyOrNull())
return null;
List<SystemDicResponse> result = new List<SystemDicResponse>();
return GetDicChildrenList(dicList, dicId);
}
public async Task<List<SystemDicResponse>> GetDicListByCode(string code)
{
var dicList = await _dicRepository.FilterByAsync(c => c.IsDelete == false);
if (!dicList.IsNotEmptyOrNull())
return null;
List<SystemDicResponse> result = new List<SystemDicResponse>();
var model = dicList.FirstOrDefault(c => c.Code == code);
if (model == null)
return null;
return GetDicChildrenList(dicList, model.Id);
}
public async Task<SystemDic> GetDictByCode(string code)
{
var model = await _dicRepository.FindOneAsync(c => c.IsDelete == false&&c.Code==code);
return model;
}
private List<SystemDicResponse> GetDicChildrenList(List<SystemDic> navList, string id)
{
List<SystemDicResponse> result = new List<SystemDicResponse>();
var list = navList.Where(c => c.ParentId == id).OrderBy(c => c.Order);
if (!list.IsNotEmptyOrNull())
return result;
foreach (var item in list)
{
var model = _mapper.Map<SystemDicResponse>(item);
model.Children = GetDicChildrenList(navList, item.Id);
result.Add(model);
}
return result;
}
}
}
@@ -0,0 +1,29 @@
using AdminService.Tasks.Param;
using CommonDomain;
using UserDomain.Entity;
namespace AdminService.Tasks
{
public interface ITaskService
{
Task<(bool isok, string msg)> AddTask(GameTask param);
Task<(bool isok, string msg)> DeleteTask(string id);
Task<(bool isok, string msg)> UpdateTaskState(string id, int state);
Task<GameTask> GetTask(string id);
Task<(List<GameTask> list, int count)> GetTaskList(GetTaskListParam param);
Task<(bool isok, string msg)> UpdateTask(GameTask param);
Task<string> DayTaskReward();
void HourTaskReward();
void RegisterTaskReward(string userId);
Task<List<UserTask>> GetUserTaskList(GetUserTaskListParam param, string userId);
Task<string> InitDefaultTasks();
}
}
@@ -0,0 +1,17 @@
using CommonDomain.Enum;
using UserDomain.Enum;
namespace AdminService.Tasks.Param
{
public class CompletedTaskParam
{
public TaskEvent taskEvent { get; set; }
public string userId { get; set; }=string.Empty;
public double count { get; set; } = 1;
public string otherId { get; set; } = "";
}
}
@@ -0,0 +1,11 @@
using Common.CommonModel;
namespace AdminService.Tasks.Param
{
public class GetTaskListParam:BaseListPageParam
{
public int State { get; set; } = -1;
public int TaskType { get; set; } = -1;
public int TaskEvent { get; set; } = -1;
}
}
@@ -0,0 +1,7 @@
namespace AdminService.Tasks.Param
{
public class GetUserTaskListParam
{
public int TaskType { get; set; } = -1;
}
}
@@ -0,0 +1,11 @@
using CommonDomain.BaseModel;
namespace AdminService.Tasks.Param
{
public class GiftResponse
{
public long Gold { get; set; } = 0;
public int Diamond { get; set; }
public List<BaseProductCount> Product { get; set; } = new List<BaseProductCount>();
}
}
+257
View File
@@ -0,0 +1,257 @@
using AdminService.Tasks.Param;
using AutoMapper;
using Common;
using Common.Utils;
using CommonDomain;
using CommonDomain.BaseModel;
using CommonDomain.Enum;
using DotNetCore.CAP;
using MongoRepository;
using RedisService;
using UserDomain.Entity;
using UserDomain.Enum;
namespace AdminService.Tasks
{
public class TaskService : ITaskService
{
private readonly IMongoBaseRepository<GameTask> _gameTaskMongo;
private readonly IMongoBaseRepository<UserTask> _userTaskMongo;
private readonly IMongoBaseRepository<UserEntity> _userEntityMongo;
private readonly IMapper _mapper;
private readonly IRedisService _redisService;
private readonly ICapPublisher _capPublisher;
public TaskService(IMongoBaseRepository<GameTask> gameTaskMongo, IMongoBaseRepository<UserTask> userTaskMongo, IMongoBaseRepository<UserEntity> userEntityMongo, IMapper mapper, IRedisService redisService, ICapPublisher capPublisher)
{
_gameTaskMongo = gameTaskMongo;
_userTaskMongo = userTaskMongo;
_userEntityMongo = userEntityMongo;
_mapper = mapper;
_redisService = redisService;
_capPublisher = capPublisher;
}
public async Task<(bool isok, string msg)> AddTask(GameTask param)
{
if (string.IsNullOrWhiteSpace(param.Id))
{
param.Id = Guid.NewGuid().ToString("N");
return await _gameTaskMongo.InsertOneAsync(param);
}
else
return await _gameTaskMongo.ReplaceOneAsync(param);
}
public async Task<(bool isok, string msg)> DeleteTask(string id)
{
return await _gameTaskMongo.DeleteByIdAsync(id);
}
public async Task<(bool isok, string msg)> UpdateTaskState(string id, int state)
{
var model = await _gameTaskMongo.FindByIdAsync(id);
if (model == null)
return (false, "任务不存在");
model.State = state;
return await _gameTaskMongo.ReplaceOneAsync(model);
}
public async Task<GameTask> GetTask(string id)
{
return await _gameTaskMongo.FindByIdAsync(id);
}
public async Task<(List<GameTask> list, int count)> GetTaskList(GetTaskListParam param)
{
var where = LinqExtensions.True<GameTask>().And(e => e.IsDelete == false);
if (!string.IsNullOrWhiteSpace(param.KeyWord))
where = where.And(e => e.Remark.Contains(param.KeyWord));
if (param.State > -1)
{
where = where.And(e => e.State == param.State);
}
if (param.TaskType > -1)
{
where = where.And(e => (int)e.TaskType == param.TaskType);
}
if (param.TaskEvent > -1)
{
where = where.And(e => (int)e.TaskEvent == param.TaskEvent);
}
var result = await _gameTaskMongo.QueryPageAsync(where, c => c.Number, false, param.PageSize ?? 0, param.PageIndex ?? 0);
return (result.list, (int)result.count);
}
public async Task<(bool isok, string msg)> UpdateTask(GameTask param)
{
if (string.IsNullOrWhiteSpace(param.Id))
return (false, "任务ID不能为空");
var existing = await _gameTaskMongo.FindByIdAsync(param.Id);
if (existing == null)
return (false, "任务不存在");
param.CreateInfo = existing.CreateInfo;
return await _gameTaskMongo.ReplaceOneAsync(param);
}
public async Task<string> DayTaskReward()
{
var tasks = _gameTaskMongo.FilterBy(c => (c.TaskType == TaskType.每日任务 || c.TaskType == TaskType.在线时长任务) && c.State == 1)?.ToList();
if (tasks == null || tasks.Count == 0)
return "没有已启用的每日任务配置,请在任务管理中添加任务";
var deleteTask = _userTaskMongo.DeleteManyAsync(c => c.TaskType == TaskType.每日任务 || c.TaskType == TaskType.在线时长任务);
var totalUsers = await _userEntityMongo.CountAsync(LinqExtensions.True<UserEntity>());
await deleteTask;
var now = DateTime.Now.Date.ToTimeStamp();
var tomorrow = now + 86400;
var pageSize = 500;
var pageCount = (int)Math.Ceiling((double)totalUsers / pageSize);
var totalCreated = 0;
var lockObj = new object();
await Parallel.ForEachAsync(Enumerable.Range(0, pageCount), new ParallelOptions { MaxDegreeOfParallelism = 4 }, async (page, ct) =>
{
var list = await _userEntityMongo.QueryPageAsync(LinqExtensions.True<UserEntity>(), c => c.Id, false, pageSize, page + 1);
if (list.list.Count == 0) return;
var batch = new List<UserTask>(list.list.Count * tasks.Count);
foreach (var user in list.list)
{
foreach (var task in tasks)
{
batch.Add(new UserTask
{
Id = Guid.NewGuid().ToString("N"),
UserId = user.Id,
Number = task.Number,
Remark = task.Remark,
TaskType = task.TaskType,
TaskEvent = task.TaskEvent,
TaskCount = task.TaskCount,
Exp = task.Exp,
Count = 0,
IsFinish = false,
IsGet = false,
TaskAddTime = now,
TaskEndTime = tomorrow
});
}
}
if (batch.Count > 0)
{
await _userTaskMongo.InsertManyAsync(batch);
lock (lockObj) { totalCreated += batch.Count; }
}
});
return $"发放完成:找到 {tasks.Count} 个任务,处理 {totalUsers} 个用户,共创建 {totalCreated} 条记录";
}
public async void HourTaskReward()
{
var userIds = _redisService.GetAllLoginUserIds();
if (userIds == null || userIds.Count() == 0)
return;
userIds = userIds.Distinct().ToArray();
var task = await _gameTaskMongo.FindOneAsync(c => (c.TaskEvent == TaskEvent.整点在线奖励) && c.State == 1);
if (task == null)
return;
List<UserTask> res = new List<UserTask>();
var now = DateTime.Now.Hour;
foreach (var id in userIds)
{
var model = _mapper.Map<UserTask>(task);
model.Id = Guid.NewGuid().ToString("N");
model.UserId = id;
model.Remark = $"{now}点在线奖励";
model.IsGet = false;
model.Count = 0;
model.IsFinish = true;
model.TaskAddTime = DateTime.Now.Date.ToTimeStamp();
model.TaskEndTime = DateTime.Now.Date.AddDays(1).ToTimeStamp();
res.Add(model);
}
if (res.Count > 0)
await _userTaskMongo.InsertManyAsync(res);
}
public async void RegisterTaskReward(string userId)
{
var tasks = _gameTaskMongo.FilterBy(c => c.State == 1);
List<UserTask> res = new List<UserTask>();
foreach (var task in tasks)
{
var model = _mapper.Map<UserTask>(task);
model.Id = Guid.NewGuid().ToString("N");
model.UserId = userId;
model.IsGet = false;
model.Count = 0;
model.IsFinish = false;
model.TaskAddTime = DateTime.Now.Date.ToTimeStamp();
if (model.TaskType == TaskType.每日任务 || model.TaskType == TaskType.在线时长任务)
model.TaskEndTime = DateTime.Now.Date.AddDays(1).ToTimeStamp();
else
model.TaskEndTime = DateTime.Now.Date.AddYears(100).ToTimeStamp();
res.Add(model);
}
if (res.Count > 0)
await _userTaskMongo.InsertManyAsync(res);
}
public async Task<List<UserTask>> GetUserTaskList(GetUserTaskListParam param, string userId)
{
var where = LinqExtensions.True<UserTask>().And(e => e.IsDelete == false);
if (param.TaskType > -1)
{
where = where.And(e => (int)e.TaskType == param.TaskType);
}
if (!string.IsNullOrEmpty(userId))
where = where.And(e => e.UserId == userId);
var result = _userTaskMongo.FilterBy(where).ToList();
if (result != null && result.Count() > 0)
result = result.OrderByDescending(c => c.IsFinish ? 1 : 0).ThenByDescending(c => c.IsGet ? 1 : 0).ToList();
return result;
}
public async Task<string> InitDefaultTasks()
{
var defaultTasks = new List<GameTask>
{
new GameTask { Id = "", Number = "90001", Remark = "领取整点在线奖励", State = 1, TaskCount = 1, TaskEvent = TaskEvent.整点在线奖励, TaskType = TaskType.每日任务 },
new GameTask { Id = "", Number = "90002", Remark = "单次在线30分钟", State = 1, TaskCount = 30, TaskEvent = TaskEvent.在线时长, TaskType = TaskType.在线时长任务 },
new GameTask { Id = "", Number = "90003", Remark = "捕获任意鱼100只", State = 1, TaskCount = 100, TaskEvent = TaskEvent.捕获任意鱼, TaskType = TaskType.每日任务 },
new GameTask { Id = "", Number = "90004", Remark = "捕获黄金鱼5只", State = 1, TaskCount = 5, TaskEvent = TaskEvent.捕获黄金鱼, TaskType = TaskType.每日任务 },
new GameTask { Id = "", Number = "90005", Remark = "捕获BOSS鱼1只", State = 1, TaskCount = 1, TaskEvent = TaskEvent.捕获Boss鱼, TaskType = TaskType.每日任务 },
new GameTask { Id = "", Number = "90006", Remark = "在游戏内赢取20万金币", State = 1, TaskCount = 200000, TaskEvent = TaskEvent.赢取金币, TaskType = TaskType.每日任务 },
new GameTask { Id = "", Number = "90007", Remark = "使用技能狂暴5次", State = 1, TaskCount = 5, TaskEvent = TaskEvent.使用狂暴, TaskType = TaskType.每日任务 },
new GameTask { Id = "", Number = "90008", Remark = "游戏内发射炮弹1000发", State = 1, TaskCount = 1000, TaskEvent = TaskEvent.开炮任务, TaskType = TaskType.每日任务 },
new GameTask { Id = "", Number = "90009", Remark = "每日登陆游戏一次", State = 1, TaskCount = 1, TaskEvent = TaskEvent.登录任务, TaskType = TaskType.每日任务 },
new GameTask { Id = "", Number = "90010", Remark = "游戏充值100元", State = 1, TaskCount = 100, TaskEvent = TaskEvent.充值任务, TaskType = TaskType.每日任务 }
};
foreach (var task in defaultTasks)
{
await AddTask(task);
}
return "初始化成功";
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,96 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using AdminService.System.Response;
using AdminService.User.Param;
using AdminService.User.Response;
using Common;
using Common.Utils;
using CommonDomain.BaseModel;
using CommonInfrastructure.Response;
using MongoRepository;
using UserDomain.Entity;
namespace AdminService.User
{
public interface IAdminUserService
{
Task<(bool isok, string msg)> CancelUser(LoginUser loginUser);
Task<(bool isok, string msg)> BanUser(string userId, string reason, int days);
Task<(bool isok, string msg)> DeleteUser(string id);
Task<(bool isok, string msg)> UpdateUser(UserEntity user);
Task<(bool isok, string msg)> UpdateUser(UpdateUserDataParam param);
Task<(bool isok, string msg)> SendEmail(SendEmailParam param, LoginUser loginUser);
Task<UserEntity> GetUser(string id);
Task<(List<UserEntity> list, int count)> GetAdminUserList(AdminUserListParam param);
Task<(bool isok, string msg)> UpdateUserAssets(UpdateUserAssetsParam param);
Task<UserStatistics> GetUserStatistics();
Task<EmailStatistics> GetEmailStatistics();
Task<(bool isok, string msg)> BatchBanUser(BatchBanUserParam param);
Task<(bool isok, string msg)> UnbanUser(string userId);
Task<(bool isok, string msg)> BatchSendEmail(BatchSendEmailParam param);
Task<(bool isok, string msg)> SetUserType(string userId, int userType);
Task<(bool isok, string msg)> BatchSetUserType(List<string> userIds, int userType);
Task<(bool isok, string msg)> SetBlackUser(string userId, bool isBlack);
Task<(bool isok, string msg)> BatchSetBlackUser(List<string> userIds, bool isBlack);
Task<(bool isok, string msg)> BatchDeleteUser(List<string> userIds);
Task<(bool isok, string msg)> UpdateUserFactor(string userId, PlayerFactor factor);
Task<PlayerFactor> GetBlackFactorConfig();
Task<(bool isok, string msg)> ResetUserFactor(string userId);
Task<(bool isok, string msg)> InitUserPassword(string userId);
Task<(bool isok, string msg)> InitSafeBoxPassword(string userId);
Task<(List<UserEntity> list, int count)> GetUserFactorList(UserFactorListParam param);
Task<List<UserEntity>> GetUsersByRegisterIps(List<string> registerIps);
Task<List<BaseProductCount>> GetUserProps(string userId);
Task<(bool isok, string msg)> AddUserProp(string userId, string propNumber, int count);
Task<(bool isok, string msg)> UpdateUserPropCount(string userId, string propNumber, int count);
Task<(bool isok, string msg)> DeleteUserProp(string userId, string propNumber);
Task<(int total, int updated, string msg)> SyncAllUserLevel();
Task<(List<UserEmail> list, long count)> GetEmailList(Param.EmailListQueryParam param);
Task<Dictionary<string, (string UserName, string UserNumber)>> GetEmailUserNamesAsync(List<string> userIds);
Task<(bool ok, string message)> DeleteEmail(string id);
Task<(int success, int fail)> BatchDeleteEmail(List<string> ids);
}
}
@@ -0,0 +1,62 @@
using Common.CommonModel;
namespace AdminService.User.Param
{
public class AdminUserListParam : BaseListPageParam
{
public string KeyWord { get; set; } = "";
public int? IsBan { get; set; }
public int? VipLevel { get; set; }
public int? VipLevelMin { get; set; }
public int? VipLevelMax { get; set; }
public long? CreateTimeStart { get; set; }
public long? CreateTimeEnd { get; set; }
public long? LastLoginTimeStart { get; set; }
public long? LastLoginTimeEnd { get; set; }
public string Channel { get; set; } = "";
public string LoginIp { get; set; } = "";
public string RegisterIp { get; set; } = "";
public bool? IsBlack { get; set; }
public string IpCountOp { get; set; } = "";
public int? IpCount { get; set; }
public string IpType { get; set; } = "register";
public int? UserType { get; set; }
public bool? IsOnline { get; set; }
public string OrderBy { get; set; } = "createTime";
}
public class UserFactorListParam : BaseListPageParam
{
public string KeyWord { get; set; } = "";
public string RegisterIp { get; set; } = "";
public string LoginIp { get; set; } = "";
public bool? IsBlack { get; set; }
public bool? IsOnline { get; set; }
public string IpCountOp { get; set; } = "";
public int? IpCount { get; set; }
}
}
@@ -0,0 +1,11 @@
namespace AdminService.User.Param
{
public class BatchBanUserParam
{
public List<string> UserIds { get; set; } = new();
public string Reason { get; set; } = "";
public int Days { get; set; } = 0;
}
}
@@ -0,0 +1,21 @@
using CommonDomain.BaseModel;
namespace AdminService.User.Param
{
public class BatchSendEmailParam
{
public List<string>? UserIds { get; set; }
public string Title { get; set; } = "";
public string Content { get; set; } = "";
public long Gold { get; set; } = 0;
public int Diamond { get; set; } = 0;
public List<BaseProductCount> Props { get; set; }=new List<BaseProductCount>();
public int ExpireDays { get; set; } = 30;
}
}
@@ -0,0 +1,14 @@
namespace AdminService.User.Param
{
public class EmailListQueryParam
{
public int? PageIndex { get; set; } = 1;
public int? PageSize { get; set; } = 10;
public string? UserNumber { get; set; }
public string? UserId { get; set; }
public int? ReadStatus { get; set; }
public int? RewardStatus { get; set; }
public long? StartTime { get; set; }
public long? EndTime { get; set; }
}
}
@@ -0,0 +1,17 @@
using CommonDomain.BaseModel;
namespace AdminService.User.Param
{
public class SendEmailParam
{
public string? UserId { get; set; }
public string? Title { get; set; }
public string? Content { get; set; }
public int RewardDiamond { get; set; } = 0;
public long RewardGold { get; set; } = 0;
public List<BaseProductCount>? RewardItems { get; set; } = null;
}
}
@@ -0,0 +1,23 @@
namespace AdminService.User.Param
{
public class UpdateUserAssetsParam
{
public string UserId { get; set; } = "";
public int Diamond { get; set; } = 0;
public long Gold { get; set; } = 0;
public long SafeBoxGold { get; set; } = 0;
public int? Level { get; set; }
public long? Exp { get; set; }
public int? VipLevel { get; set; }
public int OperationType { get; set; } = 3;
public string Remark { get; set; } = "";
}
}
@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AdminService.User.Param
{
public class UpdateUserDataParam
{
public string Id { get; set; }
public int Diamond { get; set; } = 0;
public long Gold { get; set; } = 0;
public int Level { get; set; } = 1;
public long Exp { get; set; } = 0;
}
}
@@ -0,0 +1,26 @@
using Common.CommonModel;
using System.Collections.Generic;
namespace AdminService.User.Param
{
public class UserAssetChangeQueryParam : BaseListPageParam
{
public string UserId { get; set; } = "";
public string KeyWord { get; set; } = "";
public List<int> CurrencyTypes { get; set; }
public List<int> ChangeTypes { get; set; }
public List<string> Sources { get; set; }
public List<string> ExcludeSources { get; set; }
public int? CurrencyType { get; set; }
public long? TimeStart { get; set; }
public long? TimeEnd { get; set; }
}
}
@@ -0,0 +1,59 @@
using System.Collections.Generic;
using CommonDomain.BaseModel;
using UserDomain.Entity;
namespace AdminService.User.Param
{
public class BatchSetUserTypeParam
{
public List<string> UserIds { get; set; } = new List<string>();
public int UserType { get; set; } = 1;
}
public class BatchSetBlackUserParam
{
public List<string> UserIds { get; set; } = new List<string>();
public bool IsBlack { get; set; }
}
public class UpdateUserFactorParam
{
public string UserId { get; set; } = "";
public PlayerFactor Factor { get; set; } = new PlayerFactor();
}
public class UpdateUserFactorFlatParam
{
public string UserId { get; set; } = "";
public double ViolateValue { get; set; } = 0.2;
public double MissRate { get; set; } = 0;
public int CompensateQuota { get; set; } = 0;
public int PenaltyQuota { get; set; } = 0;
public CompensateRateParam CompensateRate { get; set; } = new CompensateRateParam();
public CompensateRateParam PenaltyRate { get; set; } = new CompensateRateParam();
public double AdditionalFactor { get; set; } = 0;
}
public class BatchDeleteUserParam
{
public List<string> UserIds { get; set; } = new List<string>();
}
public class UpdateUserFactorPartialParam
{
public string UserId { get; set; } = "";
public double? ViolateValue { get; set; }
public double? MissRate { get; set; }
public int? CompensateQuota { get; set; }
public int? PenaltyQuota { get; set; }
public CompensateRateParam CompensateRate { get; set; }
public CompensateRateParam PenaltyRate { get; set; }
public bool? IsBlack { get; set; }
public bool? IsIgnore { get; set; }
public long? StartingBonus { get; set; }
}
}
@@ -0,0 +1,11 @@
namespace AdminService.User.Response
{
public class EmailStatistics
{
public long TotalEmails { get; set; }
public long RewardEmails { get; set; }
public long UnclaimedEmails { get; set; }
public long TodayEmails { get; set; }
public long MonthEmails { get; set; }
}
}
@@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AdminService.User.Response
{
public class UserStatistics
{
public long TotalUsers { get; set; }
public long TodayRegisteredUsers { get; set; }
public long MonthRegisteredUsers { get; set; }
public long OnlineUsers { get; set; }
public long BannedUsers { get; set; }
public long VipUsers { get; set; }
public Dictionary<int, long> VipLevelCount { get; set; } = new();
public long TotalGold { get; set; }
public long TotalDiamond { get; set; }
}
}