添加项目文件。
@@ -0,0 +1,28 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AutoMapper" Version="14.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.3.9" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\ChessGameSrc\ChessGameDomain\ChessGameDomain.csproj" />
|
||||
<ProjectReference Include="..\..\ChessGameSrc\ChessGameService\ChessGameService.csproj" />
|
||||
<ProjectReference Include="..\..\FishRoomService\FishRoomService.csproj" />
|
||||
<ProjectReference Include="..\..\CustomerSrc\CustomerService.Domain\CustomerService.Domain.csproj" />
|
||||
<ProjectReference Include="..\AdminService\AdminService.csproj" />
|
||||
<ProjectReference Include="..\AdminApplication\AdminApplication.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Lottery\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,665 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using AdminService.GameConfigs;
|
||||
using AdminService.User;
|
||||
using AdminService.User.Param;
|
||||
using AdminService.User.Response;
|
||||
using Common;
|
||||
using Common.Utils;
|
||||
using CommonDomain;
|
||||
using CommonDomain.BaseModel;
|
||||
using CommonDomain.Enum;
|
||||
using CommonInfrastructure;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using RedisService;
|
||||
using UserDomain.Entity;
|
||||
|
||||
namespace AdminApi.AdminUser
|
||||
{
|
||||
[ApiController, Route("adminapi/[controller]")]
|
||||
[Authorize]
|
||||
public class AdminUserController : BaseController
|
||||
{
|
||||
private readonly IAdminUserService _userService;
|
||||
private readonly IRedisService _redisService;
|
||||
|
||||
public AdminUserController(IAdminUserService userService, IRedisService redisService)
|
||||
{
|
||||
_userService = userService;
|
||||
_redisService = redisService;
|
||||
}
|
||||
|
||||
[HttpPost("GetUserList")]
|
||||
public async Task<PageResponse<UserListItemDto>> GetUserList(AdminUserListParam param)
|
||||
{
|
||||
var result = await _userService.GetAdminUserList(param);
|
||||
var onlineUserIds = _redisService.GetAllLoginUserIds().ToHashSet(StringComparer.Ordinal);
|
||||
var dtos = (result.list ?? new List<UserEntity>()).Select(u =>
|
||||
{
|
||||
var dto = MapToUserListItemDto(u);
|
||||
dto.IsOnline = onlineUserIds.Contains(u.Id);
|
||||
if (dto.CurrentGameType != 3)
|
||||
{
|
||||
var session = _redisService.GetRedisUserCId(u.Id);
|
||||
if (session == null || string.IsNullOrWhiteSpace(session.RoomId))
|
||||
{
|
||||
dto.CurrentGameType = 0;
|
||||
dto.RoomNumber = "";
|
||||
}
|
||||
}
|
||||
return dto;
|
||||
}).ToList();
|
||||
return this.ToPageResult(dtos, result.count);
|
||||
}
|
||||
|
||||
[HttpGet("GetUserDetail")]
|
||||
public async Task<BaseResponse<UserEntity>> GetUserDetail(string userId)
|
||||
{
|
||||
var result = await _userService.GetUser(userId);
|
||||
if (result == null)
|
||||
return this.ToFailResult<UserEntity>("用户不存在");
|
||||
|
||||
return this.ToResult(result);
|
||||
}
|
||||
|
||||
[HttpPost("UpdateUserAssets")]
|
||||
public async Task<BaseResponse<string>> UpdateUserAssets(UpdateUserAssetsParam param)
|
||||
{
|
||||
var result = await _userService.UpdateUserAssets(param);
|
||||
if (result.isok)
|
||||
return this.ToResult(result.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
|
||||
[HttpGet("GetUserStatistics")]
|
||||
public async Task<BaseResponse<UserStatisticsDto>> GetUserStatistics()
|
||||
{
|
||||
var result = await _userService.GetUserStatistics();
|
||||
return this.ToResult(MapToUserStatisticsDto(result));
|
||||
}
|
||||
|
||||
[HttpGet("GetEmailStatistics")]
|
||||
public async Task<BaseResponse<EmailStatisticsDto>> GetEmailStatistics()
|
||||
{
|
||||
var result = await _userService.GetEmailStatistics();
|
||||
return this.ToResult(MapToEmailStatisticsDto(result));
|
||||
}
|
||||
|
||||
[HttpPost("BatchBanUser")]
|
||||
public async Task<BaseResponse<string>> BatchBanUser(BatchBanUserParam param)
|
||||
{
|
||||
var result = await _userService.BatchBanUser(param);
|
||||
if (result.isok)
|
||||
return this.ToResult(result.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
|
||||
[HttpGet("UnbanUser")]
|
||||
public async Task<BaseResponse<string>> UnbanUser(string userId)
|
||||
{
|
||||
var result = await _userService.UnbanUser(userId);
|
||||
if (result.isok)
|
||||
return this.ToResult(result.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
|
||||
[HttpPost("BatchSendEmail")]
|
||||
public async Task<BaseResponse<string>> BatchSendEmail(BatchSendEmailParam param)
|
||||
{
|
||||
var result = await _userService.BatchSendEmail(param);
|
||||
if (result.isok)
|
||||
return this.ToResult(result.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
|
||||
[HttpGet("CancelUser")]
|
||||
[Authorize]
|
||||
public async Task<BaseResponse<string>> CancelUser()
|
||||
{
|
||||
var result = await _userService.CancelUser(loginUser);
|
||||
if (result.isok)
|
||||
return this.ToResult(result.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
|
||||
[HttpGet("DeleteUser")]
|
||||
[Authorize]
|
||||
public async Task<BaseResponse<string>> DeleteUser(string id)
|
||||
{
|
||||
var result = await _userService.DeleteUser(id);
|
||||
if (result.isok)
|
||||
return this.ToResult(result.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
|
||||
[HttpPost("BatchDeleteUser")]
|
||||
[Authorize]
|
||||
public async Task<BaseResponse<string>> BatchDeleteUser([FromBody] BatchDeleteUserParam param)
|
||||
{
|
||||
var result = await _userService.BatchDeleteUser(param.UserIds);
|
||||
if (result.isok)
|
||||
return this.ToResult(result.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
|
||||
[HttpPost("BanUser")]
|
||||
[Authorize]
|
||||
public async Task<BaseResponse<string>> BanUser([FromBody] BanUserParam param)
|
||||
{
|
||||
var result = await _userService.BanUser(param.UserId, param.Reason, param.Days);
|
||||
if (result.isok)
|
||||
return this.ToResult(result.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
|
||||
[HttpPost("UpdateUser")]
|
||||
[Authorize]
|
||||
public async Task<BaseResponse<string>> UpdateUser(UpdateUserDataParam param)
|
||||
{
|
||||
var result = await _userService.UpdateUser(param);
|
||||
if (result.isok)
|
||||
return this.ToResult(result.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
|
||||
[HttpPost("SendEmail")]
|
||||
[Authorize]
|
||||
public async Task<BaseResponse<string>> SendEmail(SendEmailParam param)
|
||||
{
|
||||
var result = await _userService.SendEmail(param, loginUser);
|
||||
if (result.isok)
|
||||
return this.ToResult(result.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
|
||||
[HttpPost("GetEmailList")]
|
||||
[Authorize]
|
||||
public async Task<PageResponse<EmailListItemDto>> GetEmailList([FromBody] EmailListQueryParam param)
|
||||
{
|
||||
var (list, count) = await _userService.GetEmailList(param);
|
||||
var userIds = list.Where(e => !string.IsNullOrWhiteSpace(e.UserId))
|
||||
.Select(e => e.UserId).Distinct().ToList();
|
||||
var userNames = await _userService.GetEmailUserNamesAsync(userIds);
|
||||
var dtos = list.Select(e =>
|
||||
{
|
||||
userNames.TryGetValue(e.UserId ?? "", out var info);
|
||||
return new EmailListItemDto
|
||||
{
|
||||
Id = e.Id,
|
||||
UserId = e.UserId ?? "",
|
||||
UserName = info.UserName,
|
||||
UserNumber = info.UserNumber,
|
||||
Title = e.Title ?? "",
|
||||
IsRead = e.IsRead,
|
||||
IsReward = e.IsReward,
|
||||
RewardGold = e.RewardGold,
|
||||
RewardDiamond = e.RewardDiamond,
|
||||
SendTime = e.SendTime,
|
||||
ReadTime = e.ReadTime,
|
||||
ExpireTime = e.ExpireTime,
|
||||
RewardText = BuildRewardText(e)
|
||||
};
|
||||
}).ToList();
|
||||
return this.ToPageResult(dtos, (int)count);
|
||||
}
|
||||
|
||||
[HttpGet("DeleteEmail")]
|
||||
[Authorize]
|
||||
public async Task<BaseResponse<string>> DeleteEmail(string id)
|
||||
{
|
||||
var result = await _userService.DeleteEmail(id);
|
||||
if (result.ok)
|
||||
return this.ToResult("删除成功");
|
||||
else
|
||||
return this.ToFailResult<string>(result.message ?? "删除失败");
|
||||
}
|
||||
|
||||
[HttpPost("BatchDeleteEmail")]
|
||||
[Authorize]
|
||||
public async Task<BaseResponse<string>> BatchDeleteEmail([FromBody] List<string> ids)
|
||||
{
|
||||
var (success, fail) = await _userService.BatchDeleteEmail(ids);
|
||||
if (fail == 0)
|
||||
return this.ToResult($"成功删除 {success} 条");
|
||||
return this.ToResult($"删除完成:成功 {success} 条,失败 {fail} 条");
|
||||
}
|
||||
|
||||
[HttpPost("SetUserType")]
|
||||
public async Task<BaseResponse<string>> SetUserType(string userId, int userType)
|
||||
{
|
||||
var result = await _userService.SetUserType(userId, userType);
|
||||
if (result.isok)
|
||||
return this.ToResult(result.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
|
||||
[HttpPost("BatchSetUserType")]
|
||||
public async Task<BaseResponse<string>> BatchSetUserType([FromBody] BatchSetUserTypeParam param)
|
||||
{
|
||||
var result = await _userService.BatchSetUserType(param.UserIds, param.UserType);
|
||||
if (result.isok)
|
||||
return this.ToResult(result.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
|
||||
[HttpPost("SetBlackUser")]
|
||||
public async Task<BaseResponse<string>> SetBlackUser(string userId, bool isBlack)
|
||||
{
|
||||
var result = await _userService.SetBlackUser(userId, isBlack);
|
||||
if (result.isok)
|
||||
return this.ToResult(result.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
|
||||
[HttpPost("BatchSetBlackUser")]
|
||||
public async Task<BaseResponse<string>> BatchSetBlackUser([FromBody] BatchSetBlackUserParam param)
|
||||
{
|
||||
var result = await _userService.BatchSetBlackUser(param.UserIds, param.IsBlack);
|
||||
if (result.isok)
|
||||
return this.ToResult(result.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
|
||||
[HttpPost("UpdateUserFactor")]
|
||||
public async Task<BaseResponse<string>> UpdateUserFactor([FromBody] UpdateUserFactorFlatParam param)
|
||||
{
|
||||
var factor = new PlayerFactor
|
||||
{
|
||||
ViolateValue = param.ViolateValue,
|
||||
MissRate = param.MissRate,
|
||||
CompensateQuota = param.CompensateQuota,
|
||||
PenaltyQuota = param.PenaltyQuota,
|
||||
CompensateRate = param.CompensateRate,
|
||||
PenaltyRate = param.PenaltyRate,
|
||||
AdditionalFactor = param.AdditionalFactor
|
||||
};
|
||||
var result = await _userService.UpdateUserFactor(param.UserId, factor);
|
||||
if (result.isok)
|
||||
return this.ToResult(result.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
|
||||
[HttpGet("GetBlackFactorConfig")]
|
||||
public async Task<BaseResponse<PlayerFactorDto>> GetBlackFactorConfig()
|
||||
{
|
||||
var result = await _userService.GetBlackFactorConfig();
|
||||
return this.ToResult(MapToPlayerFactorDto(result));
|
||||
}
|
||||
|
||||
[HttpPost("ResetUserFactor")]
|
||||
public async Task<BaseResponse<string>> ResetUserFactor(string userId)
|
||||
{
|
||||
var result = await _userService.ResetUserFactor(userId);
|
||||
if (result.isok)
|
||||
return this.ToResult(result.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
|
||||
[HttpGet("InitUserPassword")]
|
||||
public async Task<BaseResponse<string>> InitUserPassword([FromQuery] string userId)
|
||||
{
|
||||
var result = await _userService.InitUserPassword(userId);
|
||||
if (result.isok)
|
||||
return this.ToResult(result.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
|
||||
[HttpGet("InitSafeBoxPassword")]
|
||||
public async Task<BaseResponse<string>> InitSafeBoxPassword([FromQuery] string userId)
|
||||
{
|
||||
var result = await _userService.InitSafeBoxPassword(userId);
|
||||
if (result.isok)
|
||||
return this.ToResult(result.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
|
||||
[HttpPost("GetUserFactorList")]
|
||||
public async Task<PageResponse<UserFactorListItemDto>> GetUserFactorList([FromBody] UserFactorListParam param)
|
||||
{
|
||||
var result = await _userService.GetUserFactorList(param);
|
||||
|
||||
var pageIps = (result.list ?? new List<UserEntity>())
|
||||
.Where(u => !string.IsNullOrEmpty(u.RegisterIp))
|
||||
.Select(u => u.RegisterIp!)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
|
||||
Dictionary<string, int> ipCounts = new Dictionary<string, int>();
|
||||
if (pageIps.Count > 0)
|
||||
{
|
||||
var allUsersWithSameIp = await _userService.GetUsersByRegisterIps(pageIps);
|
||||
ipCounts = allUsersWithSameIp
|
||||
.GroupBy(u => u.RegisterIp)
|
||||
.ToDictionary(g => g.Key, g => g.Count());
|
||||
}
|
||||
|
||||
var dtos = (result.list ?? new List<UserEntity>()).Select(u => new UserFactorListItemDto
|
||||
{
|
||||
Id = u.Id,
|
||||
Number = u.BaseInfo?.Number ?? "",
|
||||
UserName = u.BaseInfo?.UserName ?? "",
|
||||
Mobile = u.BaseInfo?.Mobile ?? "",
|
||||
UserType = u.UserType,
|
||||
RegisterIp = u.RegisterIp ?? "",
|
||||
LoginIp = u.Static?.LastLoginIp ?? "",
|
||||
IpCount = !string.IsNullOrEmpty(u.RegisterIp) && ipCounts.ContainsKey(u.RegisterIp) ? ipCounts[u.RegisterIp] : 1,
|
||||
IsBlack = u.PlayerFactor?.IsBlack ?? false,
|
||||
ViolateValue = u.PlayerFactor?.ViolateValue ?? 0.2,
|
||||
MissRate = u.PlayerFactor?.MissRate ?? 0,
|
||||
CompensateQuota = u.PlayerFactor?.CompensateQuota ?? 0,
|
||||
PenaltyQuota = u.PlayerFactor?.PenaltyQuota ?? 0,
|
||||
CompensateRate = u.PlayerFactor?.CompensateRate != null ? new CompensateRateDto
|
||||
{
|
||||
BossRate = u.PlayerFactor.CompensateRate.BossRate,
|
||||
GoldRate = u.PlayerFactor.CompensateRate.GoldRate,
|
||||
SpecialRate = u.PlayerFactor.CompensateRate.SpecialRate,
|
||||
Ordinary = u.PlayerFactor.CompensateRate.Ordinary
|
||||
} : new CompensateRateDto(),
|
||||
PenaltyRate = u.PlayerFactor?.PenaltyRate != null ? new PenaltyRateDto
|
||||
{
|
||||
BossRate = u.PlayerFactor.PenaltyRate.BossRate,
|
||||
GoldRate = u.PlayerFactor.PenaltyRate.GoldRate,
|
||||
SpecialRate = u.PlayerFactor.PenaltyRate.SpecialRate,
|
||||
Ordinary = u.PlayerFactor.PenaltyRate.Ordinary
|
||||
} : new PenaltyRateDto(),
|
||||
CreateTime = u.CreateTime,
|
||||
TotalRecharge = u.Static?.TotalRecharge ?? 0,
|
||||
TotalRechargeGold = u.Static?.TotalRechargeGold ?? 0,
|
||||
TotalConsumeGold = u.Static?.TotalConsumeGold ?? 0,
|
||||
CurrentGold = u.Assets?.Gold ?? 0,
|
||||
Profit = (u.Assets?.Gold ?? 0) - ((u.Static?.TotalRechargeGold ?? 0) + (u.PlayerFactor?.StartingBonus ?? 0)),
|
||||
AdditionalFactor = u.PlayerFactor?.AdditionalFactor ?? 0,
|
||||
StartingBonus = u.PlayerFactor?.StartingBonus ?? 0
|
||||
});
|
||||
return this.ToPageResult(dtos, result.count);
|
||||
}
|
||||
|
||||
|
||||
private static UserListItemDto MapToUserListItemDto(UserEntity entity)
|
||||
{
|
||||
return new UserListItemDto
|
||||
{
|
||||
Id = entity.Id,
|
||||
Number = entity.BaseInfo?.Number ?? "",
|
||||
NickName = entity.BaseInfo?.UserName ?? "",
|
||||
Avatar = entity.BaseInfo?.AvatarUrl ?? "",
|
||||
Mobile = entity.BaseInfo?.Mobile ?? "",
|
||||
Diamond = entity.Assets?.Diamond ?? 0,
|
||||
Gold = entity.Assets?.Gold ?? 0,
|
||||
SafeBoxGold = entity.SafeBox?.Amount ?? 0,
|
||||
VipLevel = entity.Assets?.VipLevel ?? 0,
|
||||
UserType = entity.UserType,
|
||||
IsBan = entity.BanInfo?.IsBan ?? false,
|
||||
BanReason = entity.BanInfo?.BanReason,
|
||||
BanEndTime = entity.BanInfo?.BanEndTime ?? 0,
|
||||
IsBlack = entity.PlayerFactor?.IsBlack ?? false,
|
||||
RegisterTime = entity.CreateTime,
|
||||
RegisterIp = entity.RegisterIp ?? "",
|
||||
LastLoginIp = entity.Static?.LastLoginIp ?? "",
|
||||
LastLoginTime = entity.Static?.LastLoginTime ?? 0,
|
||||
RoomNumber = entity.Static?.RoomNumber ?? "",
|
||||
CurrentGameType = entity.Static?.CurrentGameType ?? 0,
|
||||
Channel = entity.BaseInfo?.Channel ?? "",
|
||||
TotalRecharge = (long)(entity.Static?.TotalRecharge ?? 0)
|
||||
};
|
||||
}
|
||||
|
||||
private static UserStatisticsDto MapToUserStatisticsDto(UserStatistics stat)
|
||||
{
|
||||
return new UserStatisticsDto
|
||||
{
|
||||
TotalUsers = stat.TotalUsers,
|
||||
TodayRegisteredUsers = stat.TodayRegisteredUsers,
|
||||
MonthRegisteredUsers = stat.MonthRegisteredUsers,
|
||||
OnlineUsers = stat.OnlineUsers,
|
||||
BannedUsers = stat.BannedUsers,
|
||||
VipUsers = stat.VipUsers,
|
||||
VipLevelCount = stat.VipLevelCount ?? new Dictionary<int, long>(),
|
||||
TotalGold = stat.TotalGold,
|
||||
TotalDiamond = stat.TotalDiamond
|
||||
};
|
||||
}
|
||||
|
||||
private static EmailStatisticsDto MapToEmailStatisticsDto(EmailStatistics stat)
|
||||
{
|
||||
return new EmailStatisticsDto
|
||||
{
|
||||
TotalEmails = stat.TotalEmails,
|
||||
RewardEmails = stat.RewardEmails,
|
||||
UnclaimedEmails = stat.UnclaimedEmails,
|
||||
TodayEmails = stat.TodayEmails,
|
||||
MonthEmails = stat.MonthEmails
|
||||
};
|
||||
}
|
||||
|
||||
private static PlayerFactorDto MapToPlayerFactorDto(PlayerFactor factor)
|
||||
{
|
||||
if (factor == null)
|
||||
return new PlayerFactorDto();
|
||||
|
||||
return new PlayerFactorDto
|
||||
{
|
||||
IsBlack = factor.IsBlack,
|
||||
IsIgnore = factor.IsIgnore,
|
||||
ViolateValue = factor.ViolateValue,
|
||||
CompensateRate = factor.CompensateRate?.BossRate ?? 1,
|
||||
PenaltyRate = factor.PenaltyRate?.BossRate ?? 0,
|
||||
CompensateQuota = factor.CompensateQuota,
|
||||
PenaltyQuota = factor.PenaltyQuota,
|
||||
StartingBonus = factor.StartingBonus
|
||||
};
|
||||
}
|
||||
|
||||
private static string BuildRewardText(UserEmail email)
|
||||
{
|
||||
var parts = new List<string>();
|
||||
if (email.RewardGold > 0)
|
||||
parts.Add($"金币{email.RewardGold:N0}");
|
||||
if (email.RewardDiamond > 0)
|
||||
parts.Add($"钻石{email.RewardDiamond}");
|
||||
if (email.RewardItems != null)
|
||||
{
|
||||
foreach (var item in email.RewardItems)
|
||||
{
|
||||
if (item != null && !string.IsNullOrWhiteSpace(item.Name))
|
||||
parts.Add($"{item.Name}*{item.Count}");
|
||||
}
|
||||
}
|
||||
return parts.Count > 0 ? string.Join(" ", parts) : "无";
|
||||
}
|
||||
|
||||
|
||||
|
||||
[HttpGet("GetUserProps")]
|
||||
public async Task<BaseResponse<List<BaseProductCount>>> GetUserProps(string userId)
|
||||
{
|
||||
var result = await _userService.GetUserProps(userId);
|
||||
return this.ToResult(result);
|
||||
}
|
||||
|
||||
[HttpPost("AddUserProp")]
|
||||
public async Task<BaseResponse<string>> AddUserProp(string userId, string propNumber, int count)
|
||||
{
|
||||
var res = await _userService.AddUserProp(userId, propNumber, count);
|
||||
if (res.isok) return this.ToResult(res.msg);
|
||||
return this.ToFailResult<string>(res.msg);
|
||||
}
|
||||
|
||||
[HttpPost("UpdateUserPropCount")]
|
||||
public async Task<BaseResponse<string>> UpdateUserPropCount(string userId, string propNumber, int count)
|
||||
{
|
||||
var res = await _userService.UpdateUserPropCount(userId, propNumber, count);
|
||||
if (res.isok) return this.ToResult(res.msg);
|
||||
return this.ToFailResult<string>(res.msg);
|
||||
}
|
||||
|
||||
[HttpPost("DeleteUserProp")]
|
||||
public async Task<BaseResponse<string>> DeleteUserProp(string userId, string propNumber)
|
||||
{
|
||||
var res = await _userService.DeleteUserProp(userId, propNumber);
|
||||
if (res.isok) return this.ToResult(res.msg);
|
||||
return this.ToFailResult<string>(res.msg);
|
||||
}
|
||||
|
||||
[HttpGet("SyncAllUserLevel")]
|
||||
public async Task<BaseResponse<string>> SyncAllUserLevel()
|
||||
{
|
||||
var (total, updated, msg) = await _userService.SyncAllUserLevel();
|
||||
return this.ToResult(msg);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public class UserListItemDto
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string Number { get; set; }
|
||||
public string NickName { get; set; }
|
||||
public string Avatar { get; set; }
|
||||
public string Mobile { get; set; }
|
||||
public int Diamond { get; set; }
|
||||
public long Gold { get; set; }
|
||||
public long SafeBoxGold { get; set; }
|
||||
public int VipLevel { get; set; }
|
||||
public int UserType { get; set; }
|
||||
public bool IsBan { get; set; }
|
||||
public string BanReason { get; set; }
|
||||
public long BanEndTime { get; set; }
|
||||
public bool IsBlack { get; set; }
|
||||
public long RegisterTime { get; set; }
|
||||
public string RegisterIp { get; set; }
|
||||
public string LastLoginIp { get; set; }
|
||||
public long LastLoginTime { get; set; }
|
||||
public string RoomNumber { get; set; }
|
||||
public int CurrentGameType { get; set; }
|
||||
public bool IsOnline { get; set; }
|
||||
public string Channel { get; set; }
|
||||
public long TotalRecharge { get; set; }
|
||||
}
|
||||
|
||||
public class UserStatisticsDto
|
||||
{
|
||||
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; }
|
||||
public long TotalGold { get; set; }
|
||||
public long TotalDiamond { get; set; }
|
||||
}
|
||||
|
||||
public class EmailStatisticsDto
|
||||
{
|
||||
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; }
|
||||
}
|
||||
|
||||
public class PlayerFactorDto
|
||||
{
|
||||
public bool IsBlack { get; set; }
|
||||
public bool IsIgnore { get; set; }
|
||||
public double ViolateValue { get; set; }
|
||||
public double CompensateRate { get; set; }
|
||||
public double PenaltyRate { get; set; }
|
||||
public int CompensateQuota { get; set; }
|
||||
public int PenaltyQuota { get; set; }
|
||||
public long StartingBonus { get; set; }
|
||||
}
|
||||
|
||||
public class UserFactorListItemDto
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string Number { get; set; }
|
||||
public string UserName { get; set; }
|
||||
public string Mobile { get; set; }
|
||||
public int UserType { get; set; }
|
||||
public string RegisterIp { get; set; }
|
||||
public string LoginIp { get; set; }
|
||||
public int IpCount { get; set; }
|
||||
public bool IsBlack { get; set; }
|
||||
public double ViolateValue { get; set; }
|
||||
public double MissRate { get; set; }
|
||||
public int CompensateQuota { get; set; }
|
||||
public int PenaltyQuota { get; set; }
|
||||
public CompensateRateDto CompensateRate { get; set; }
|
||||
public PenaltyRateDto PenaltyRate { get; set; }
|
||||
public long CreateTime { get; set; }
|
||||
public double TotalRecharge { get; set; }
|
||||
public long TotalRechargeGold { get; set; }
|
||||
public long TotalConsumeGold { get; set; }
|
||||
public long CurrentGold { get; set; }
|
||||
public long Profit { get; set; }
|
||||
public double AdditionalFactor { get; set; }
|
||||
public long StartingBonus { get; set; }
|
||||
}
|
||||
|
||||
public class CompensateRateDto
|
||||
{
|
||||
public double BossRate { get; set; } = 1;
|
||||
public double GoldRate { get; set; } = 1;
|
||||
public double SpecialRate { get; set; } = 1;
|
||||
public double Ordinary { get; set; } = 1;
|
||||
}
|
||||
|
||||
public class PenaltyRateDto
|
||||
{
|
||||
public double BossRate { get; set; } = 1;
|
||||
public double GoldRate { get; set; } = 1;
|
||||
public double SpecialRate { get; set; } = 1;
|
||||
public double Ordinary { get; set; } = 1;
|
||||
}
|
||||
|
||||
public class BanUserParam
|
||||
{
|
||||
public string UserId { get; set; } = "";
|
||||
|
||||
public string Reason { get; set; } = "";
|
||||
|
||||
public int Days { get; set; } = 0;
|
||||
}
|
||||
|
||||
public class EmailListItemDto
|
||||
{
|
||||
public string Id { get; set; } = "";
|
||||
public string UserId { get; set; } = "";
|
||||
public string UserName { get; set; } = "";
|
||||
public string UserNumber { get; set; } = "";
|
||||
public string Title { get; set; } = "";
|
||||
public bool IsRead { get; set; }
|
||||
public bool IsReward { get; set; }
|
||||
public long RewardGold { get; set; }
|
||||
public int RewardDiamond { get; set; }
|
||||
public long SendTime { get; set; }
|
||||
public long ReadTime { get; set; }
|
||||
public long ExpireTime { get; set; }
|
||||
public string RewardText { get; set; } = "";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace AdminApi
|
||||
{
|
||||
public static class AssemblyMarker
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
using System.Threading.Tasks;
|
||||
using Common;
|
||||
using CommonDomain;
|
||||
using CommonInfrastructure;
|
||||
using CommonInfrastructure.Response;
|
||||
using CommonService;
|
||||
using CommonService.BackgroundOperation;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using UserDomain.Entity;
|
||||
|
||||
namespace AdminApi.BackgroundOperation
|
||||
{
|
||||
[Authorize]
|
||||
[Route("adminapi/[controller]")]
|
||||
[ApiController]
|
||||
public class BackgroundOperationController : BaseController
|
||||
{
|
||||
private readonly IBackgroundOperationService _operationService;
|
||||
|
||||
public BackgroundOperationController(IBackgroundOperationService operationService)
|
||||
{
|
||||
_operationService = operationService;
|
||||
}
|
||||
|
||||
[HttpPost("charge")]
|
||||
public async Task<BaseResponse<string>> Charge([FromBody] BackgroundOperationRequest request)
|
||||
{
|
||||
if (request == null || string.IsNullOrWhiteSpace(request.TargetUserId))
|
||||
return this.ToFailResult<string>("目标用户ID不能为空");
|
||||
|
||||
if (request.Amount <= 0)
|
||||
return this.ToFailResult<string>("操作数量必须大于0");
|
||||
|
||||
var (success, message) = await _operationService.Charge(
|
||||
loginUser.UserId, request.TargetUserId, request.CurrencyType, request.Amount, request.Remark);
|
||||
|
||||
return success
|
||||
? this.ToResult("充值成功")
|
||||
: this.ToFailResult<string>(message);
|
||||
}
|
||||
|
||||
[HttpPost("deduct")]
|
||||
public async Task<BaseResponse<string>> Deduct([FromBody] BackgroundOperationRequest request)
|
||||
{
|
||||
if (request == null || string.IsNullOrWhiteSpace(request.TargetUserId))
|
||||
return this.ToFailResult<string>("目标用户ID不能为空");
|
||||
|
||||
if (request.Amount <= 0)
|
||||
return this.ToFailResult<string>("操作数量必须大于0");
|
||||
|
||||
var (success, message) = await _operationService.Deduct(
|
||||
loginUser.UserId, request.TargetUserId, request.CurrencyType, request.Amount, request.Remark);
|
||||
|
||||
return success
|
||||
? this.ToResult("扣除成功")
|
||||
: this.ToFailResult<string>(message);
|
||||
}
|
||||
|
||||
[HttpGet("logs")]
|
||||
public async Task<PageResponse<BackgroundOperationLog>> GetLogs(
|
||||
string? adminUserId = null,
|
||||
string? targetUserId = null,
|
||||
int? operationType = null,
|
||||
int pageIndex = 1,
|
||||
int pageSize = 20)
|
||||
{
|
||||
var (data, total) = await _operationService.QueryLogs(
|
||||
adminUserId, targetUserId, operationType, pageIndex, pageSize);
|
||||
|
||||
return this.ToPageResult(data, total);
|
||||
}
|
||||
|
||||
[HttpGet("search-user")]
|
||||
public async Task<BaseResponse<SearchUserResponse>> SearchUser(string keyword)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(keyword))
|
||||
{
|
||||
|
||||
return this.ToFailResult<SearchUserResponse>("搜索关键词不能为空");
|
||||
}
|
||||
|
||||
var user = await _operationService.SearchUser(keyword);
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
return this.ToFailResult<SearchUserResponse>("用户不存在");
|
||||
}
|
||||
|
||||
var dto = new SearchUserResponse
|
||||
{
|
||||
UserId = user.Id,
|
||||
UserName = user.BaseInfo?.UserName ?? "",
|
||||
UserPhone = user.BaseInfo?.Mobile ?? "",
|
||||
Gold = user.Assets?.Gold ?? 0,
|
||||
Diamond = user.Assets?.Diamond ?? 0,
|
||||
RegisterTime = user.CreateTime / 1000
|
||||
};
|
||||
|
||||
return this.ToResult(dto);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class SearchUserResponse
|
||||
{
|
||||
public string UserId { get; set; }
|
||||
public string UserName { get; set; }
|
||||
public string UserPhone { get; set; }
|
||||
public long Gold { get; set; }
|
||||
public int Diamond { get; set; }
|
||||
public long RegisterTime { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,384 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AdminDomain.BeautifulNumber;
|
||||
using AdminDomain.Entity;
|
||||
using AdminService.BeautifulNumber;
|
||||
using Common;
|
||||
using CommonInfrastructure;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using UserDomain.Entity;
|
||||
|
||||
namespace AdminApi.BeautifulNumber
|
||||
{
|
||||
[ApiController, Route("adminapi/[controller]")]
|
||||
[Authorize]
|
||||
|
||||
public class BeautifulNumberController : BaseController
|
||||
{
|
||||
private readonly IBeautifulNumberService _beautifulNumberService;
|
||||
|
||||
public BeautifulNumberController(IBeautifulNumberService beautifulNumberService)
|
||||
{
|
||||
_beautifulNumberService = beautifulNumberService;
|
||||
}
|
||||
|
||||
[HttpGet("list")]
|
||||
public async Task<PageResponse<BeautifulNumberDto>> GetList([FromQuery] BeautifulNumberListParam param)
|
||||
{
|
||||
param.page = param.page > 0 ? param.page : 1;
|
||||
param.size = param.size > 0 ? param.size : 20;
|
||||
var result = await _beautifulNumberService.GetList(param);
|
||||
var dtos = result.list.Select(MapToDto).ToList();
|
||||
return this.ToPageResult(dtos, (int)result.count);
|
||||
}
|
||||
|
||||
[HttpGet("available")]
|
||||
public async Task<PageResponse<BeautifulNumberDto>> GetAvailableList([FromQuery] BeautifulNumberListParam param)
|
||||
{
|
||||
param.page = param.page > 0 ? param.page : 1;
|
||||
param.size = param.size > 0 ? param.size : 20;
|
||||
param.status = (int)BeautifulNumberStatus.Available;
|
||||
var result = await _beautifulNumberService.GetList(param);
|
||||
var dtos = result.list.Select(MapToDto).ToList();
|
||||
return this.ToPageResult(dtos, (int)result.count);
|
||||
}
|
||||
|
||||
[HttpGet("check/{number}")]
|
||||
public async Task<BaseResponse<BeautifulNumberDto>> CheckNumber(string number)
|
||||
{
|
||||
var result = await _beautifulNumberService.CheckNumber(number);
|
||||
return this.ToResult(MapToDto(result));
|
||||
}
|
||||
|
||||
private static BeautifulNumberDto MapToDto(BeautifulNumberConfig entity)
|
||||
{
|
||||
if (entity == null) return new BeautifulNumberDto();
|
||||
return new BeautifulNumberDto
|
||||
{
|
||||
Id = entity.Id ?? "",
|
||||
Number = entity.Number ?? "",
|
||||
RuleName = entity.RuleName ?? "",
|
||||
Level = entity.Level ?? "",
|
||||
Price = entity.Price,
|
||||
Status = (int)entity.Status,
|
||||
SaleTime = entity.SaleTime,
|
||||
BuyerUserId = entity.BuyerUserId ?? "",
|
||||
UserId = entity.UserId ?? "",
|
||||
Remark = entity.Remark ?? "",
|
||||
CreateTime = entity.CreateTime
|
||||
};
|
||||
}
|
||||
|
||||
[HttpPost("delete")]
|
||||
public async Task<BaseResponse<string>> Delete([FromBody] DeleteBeautifulNumberParam param)
|
||||
{
|
||||
var result = await _beautifulNumberService.Delete(param.Id);
|
||||
if (result.isok)
|
||||
return this.ToResult<string>(result.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
|
||||
[HttpPost("set-normal")]
|
||||
public async Task<BaseResponse<string>> SetToNormal([FromBody] DeleteBeautifulNumberParam param)
|
||||
{
|
||||
var result = await _beautifulNumberService.SetToNormal(param.Id);
|
||||
if (result.isok)
|
||||
return this.ToResult<string>(result.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
|
||||
[HttpPost("freeze")]
|
||||
public async Task<BaseResponse<string>> Freeze([FromBody] DeleteBeautifulNumberParam param)
|
||||
{
|
||||
var result = await _beautifulNumberService.Freeze(param.Id);
|
||||
if (result.isok)
|
||||
return this.ToResult<string>(result.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
|
||||
[HttpPost("unfreeze")]
|
||||
public async Task<BaseResponse<string>> Unfreeze([FromBody] DeleteBeautifulNumberParam param)
|
||||
{
|
||||
var result = await _beautifulNumberService.Unfreeze(param.Id);
|
||||
if (result.isok)
|
||||
return this.ToResult<string>(result.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
|
||||
[HttpPost("update-price")]
|
||||
public async Task<BaseResponse<string>> UpdatePrice([FromBody] UpdateBeautifulNumberPriceParam param)
|
||||
{
|
||||
var result = await _beautifulNumberService.UpdatePrice(param.Id, param.Price);
|
||||
if (result.isok)
|
||||
return this.ToResult<string>(result.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
|
||||
[HttpPost("update-number")]
|
||||
public async Task<BaseResponse<string>> UpdateNumber([FromBody] UpdateBeautifulNumberParam param)
|
||||
{
|
||||
var result = await _beautifulNumberService.UpdateNumber(param.Id, param.Level, param.Price);
|
||||
if (result.isok)
|
||||
return this.ToResult<string>(result.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
|
||||
[HttpGet("rules")]
|
||||
public async Task<BaseResponse<List<BeautifulNumberRuleDto>>> GetRules()
|
||||
{
|
||||
var result = await _beautifulNumberService.GetRules();
|
||||
var dtos = result.Select(MapRuleToDto).ToList();
|
||||
return this.ToResult(dtos);
|
||||
}
|
||||
|
||||
private static BeautifulNumberRuleDto MapRuleToDto(BeautifulNumberRule entity)
|
||||
{
|
||||
if (entity == null) return new BeautifulNumberRuleDto();
|
||||
return new BeautifulNumberRuleDto
|
||||
{
|
||||
Id = entity.Id ?? "",
|
||||
RuleName = entity.RuleName ?? "",
|
||||
Level = entity.Level ?? "",
|
||||
Price = entity.Price,
|
||||
Sort = entity.Sort,
|
||||
IsEnable = entity.IsEnable
|
||||
};
|
||||
}
|
||||
|
||||
[HttpPost("update-rules")]
|
||||
public async Task<BaseResponse<string>> UpdateRules([FromBody] List<UpdateBeautifulNumberRuleParam> paramList)
|
||||
{
|
||||
var rules = new List<BeautifulNumberRule>();
|
||||
foreach (var param in paramList)
|
||||
{
|
||||
rules.Add(new BeautifulNumberRule
|
||||
{
|
||||
Id = param.Id,
|
||||
RuleName = param.RuleName,
|
||||
Level = param.Level,
|
||||
Price = param.Price,
|
||||
Sort = param.Sort,
|
||||
IsEnable = param.IsEnable
|
||||
});
|
||||
}
|
||||
var result = await _beautifulNumberService.UpdateRules(rules);
|
||||
if (result.isok)
|
||||
return this.ToResult<string>(result.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
|
||||
[HttpPost("init-rules")]
|
||||
public async Task<BaseResponse<string>> InitRules()
|
||||
{
|
||||
var result = await _beautifulNumberService.InitRules();
|
||||
if (result.isok)
|
||||
return this.ToResult<string>(result.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
|
||||
[HttpPost("add-rule")]
|
||||
public async Task<BaseResponse<string>> AddRule([FromBody] BeautifulNumberRule rule)
|
||||
{
|
||||
var result = await _beautifulNumberService.AddRule(rule);
|
||||
if (result.isok)
|
||||
return this.ToResult<string>(result.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
|
||||
[HttpPost("update-rule")]
|
||||
public async Task<BaseResponse<string>> UpdateRule([FromBody] BeautifulNumberRule rule)
|
||||
{
|
||||
var result = await _beautifulNumberService.UpdateRule(rule);
|
||||
if (result.isok)
|
||||
return this.ToResult<string>(result.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
|
||||
[HttpPost("delete-rule")]
|
||||
public async Task<BaseResponse<string>> DeleteRule([FromBody] DeleteBeautifulNumberRuleParam param)
|
||||
{
|
||||
var result = await _beautifulNumberService.DeleteRule(param.Id);
|
||||
if (result.isok)
|
||||
return this.ToResult<string>(result.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
|
||||
[HttpPost("give-to-user")]
|
||||
public async Task<BaseResponse<string>> GiveToUser([FromBody] GiveBeautifulNumberParam param)
|
||||
{
|
||||
var result = await _beautifulNumberService.GiveToUser(param.Number, param.UserId);
|
||||
if (result.isok)
|
||||
return this.ToResult<string>(result.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
|
||||
[HttpPost("init-numbers")]
|
||||
public async Task<BaseResponse<string>> InitNumbers()
|
||||
{
|
||||
var result = await _beautifulNumberService.InitBeautifulNumbers();
|
||||
if (result.isok)
|
||||
return this.ToResult<string>(result.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
|
||||
[HttpPost("add-number")]
|
||||
public async Task<BaseResponse<string>> AddNumber([FromBody] AddBeautifulNumberParam param)
|
||||
{
|
||||
var result = await _beautifulNumberService.AddNumber(param.Number, param.Level, param.Price);
|
||||
if (result.isok)
|
||||
return this.ToResult<string>(result.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
|
||||
[HttpPost("batch-add-numbers")]
|
||||
public async Task<BaseResponse<BatchAddResult>> BatchAddNumbers([FromBody] BatchAddBeautifulNumbersParam param)
|
||||
{
|
||||
var result = await _beautifulNumberService.BatchAddNumbers(param.Numbers, param.Level, param.Price);
|
||||
return this.ToResult(new BatchAddResult { SuccessCount = result.successCount, FailCount = result.failCount, Message = result.msg });
|
||||
}
|
||||
}
|
||||
|
||||
public class BatchAddResult
|
||||
{
|
||||
public int SuccessCount { get; set; }
|
||||
public int FailCount { get; set; }
|
||||
public string Message { get; set; }
|
||||
}
|
||||
|
||||
public class BeautifulNumberDto
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string Number { get; set; }
|
||||
public string RuleName { get; set; }
|
||||
public string Level { get; set; }
|
||||
public long Price { get; set; }
|
||||
public int Status { get; set; }
|
||||
public long SaleTime { get; set; }
|
||||
public string BuyerUserId { get; set; }
|
||||
public string UserId { get; set; }
|
||||
public string Remark { get; set; }
|
||||
public long CreateTime { get; set; }
|
||||
}
|
||||
|
||||
public class BeautifulNumberRuleDto
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string RuleName { get; set; }
|
||||
public string Level { get; set; }
|
||||
public long Price { get; set; }
|
||||
public int Sort { get; set; }
|
||||
public bool IsEnable { get; set; }
|
||||
}
|
||||
|
||||
public class BatchAddBeautifulNumbersParam
|
||||
{
|
||||
public List<string> Numbers { get; set; }
|
||||
|
||||
public string Level { get; set; }
|
||||
|
||||
public long Price { get; set; }
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
public class GiveBeautifulNumberParam
|
||||
{
|
||||
public string Number { get; set; }
|
||||
|
||||
public string UserId { get; set; }
|
||||
}
|
||||
|
||||
public class AddBeautifulNumberParam
|
||||
{
|
||||
public string Number { get; set; }
|
||||
|
||||
public string Level { get; set; }
|
||||
|
||||
public long Price { get; set; }
|
||||
|
||||
public int Sort { get; set; }
|
||||
|
||||
public string Remark { get; set; }
|
||||
}
|
||||
public class UpdateBeautifulNumberParam
|
||||
{
|
||||
public string Id { get; set; }
|
||||
|
||||
public string Level { get; set; }
|
||||
|
||||
public long Price { get; set; }
|
||||
|
||||
}
|
||||
public class DeleteBeautifulNumberParam
|
||||
{
|
||||
public string Id { get; set; }
|
||||
}
|
||||
|
||||
public class UpdateBeautifulNumberPriceParam
|
||||
{
|
||||
public string Id { get; set; }
|
||||
|
||||
public long Price { get; set; }
|
||||
}
|
||||
|
||||
|
||||
|
||||
public class UpdateBeautifulNumberRuleParam
|
||||
{
|
||||
public string Id { get; set; }
|
||||
|
||||
|
||||
|
||||
public string RuleName { get; set; }
|
||||
|
||||
|
||||
|
||||
public string Level { get; set; }
|
||||
|
||||
public long Price { get; set; }
|
||||
|
||||
public int Sort { get; set; }
|
||||
|
||||
public bool IsEnable { get; set; }
|
||||
}
|
||||
|
||||
public class AddBeautifulNumberRuleParam
|
||||
{
|
||||
public string RuleName { get; set; }
|
||||
public string Level { get; set; }
|
||||
public long Price { get; set; }
|
||||
public int Sort { get; set; }
|
||||
public bool IsEnable { get; set; }
|
||||
}
|
||||
|
||||
public class UpdateSingleBeautifulNumberRuleParam
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string RuleName { get; set; }
|
||||
public string Level { get; set; }
|
||||
public long Price { get; set; }
|
||||
public int Sort { get; set; }
|
||||
public bool IsEnable { get; set; }
|
||||
}
|
||||
|
||||
public class DeleteBeautifulNumberRuleParam
|
||||
{
|
||||
public string Id { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
using AdminService.ImageUpload;
|
||||
using Common;
|
||||
using CommonInfrastructure;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace AdminApi.Controllers
|
||||
{
|
||||
[Route("adminapi/[controller]")]
|
||||
[ApiController]
|
||||
public class ImageUploadController : BaseController
|
||||
{
|
||||
private readonly IImageUploadService _imageUploadService;
|
||||
|
||||
public ImageUploadController(IImageUploadService imageUploadService)
|
||||
{
|
||||
_imageUploadService = imageUploadService;
|
||||
}
|
||||
|
||||
[HttpPost("upload")]
|
||||
public async Task<IActionResult> UploadFile(IFormFile file, [FromQuery] string? name = null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
name = Request.Form["name"].FirstOrDefault();
|
||||
|
||||
var result = await _imageUploadService.UploadImageAsync(file, name);
|
||||
return result.isOk
|
||||
? Json(this.ToResult(new { path = result.path, fileName = result.fileName }))
|
||||
: Json(this.ToFailResult<object>(result.errorMessage));
|
||||
}
|
||||
|
||||
[HttpPost("upload/temp")]
|
||||
public async Task<IActionResult> UploadTempFile(IFormFile file)
|
||||
{
|
||||
var result = await _imageUploadService.UploadImageAsync(file);
|
||||
return result.isOk
|
||||
? Json(this.ToResult(new { path = result.path, fileName = result.fileName }))
|
||||
: Json(this.ToFailResult<object>(result.errorMessage));
|
||||
}
|
||||
|
||||
[HttpPost("upload/config")]
|
||||
public async Task<IActionResult> UploadConfigFile(IFormFile file, [FromQuery] string? number = null)
|
||||
{
|
||||
if (!string.Equals(Path.GetExtension(file?.FileName), ".png", StringComparison.OrdinalIgnoreCase))
|
||||
return Json(this.ToFailResult<object>("配置图片必须是 PNG 格式"));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(number))
|
||||
number = Request.Form["number"].FirstOrDefault();
|
||||
|
||||
number = number?.Trim();
|
||||
if (string.IsNullOrWhiteSpace(number)
|
||||
|| number != Path.GetFileName(number)
|
||||
|| number.Any(c => !char.IsLetterOrDigit(c) && c != '_' && c != '-'))
|
||||
{
|
||||
return Json(this.ToFailResult<object>("图片编号无效"));
|
||||
}
|
||||
|
||||
var result = await _imageUploadService.UploadImageAsync(file, number);
|
||||
return result.isOk
|
||||
? Json(this.ToResult(new { path = result.path, fileName = result.fileName, number }))
|
||||
: Json(this.ToFailResult<object>(result.errorMessage));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using AdminService.Admin;
|
||||
using Common;
|
||||
using CommonInfrastructure;
|
||||
using Domain.Admin.Admin;
|
||||
using Domain.Admin.Admin.ENum;
|
||||
using Domain.Admin.Admin.Param;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using MongoRepository;
|
||||
|
||||
namespace AdminApi.Controllers
|
||||
{
|
||||
[Route("adminapi/[controller]")]
|
||||
[ApiController]
|
||||
public class OperationLogController : BaseController
|
||||
{
|
||||
private readonly IAdminOperationLogService _operationLogService;
|
||||
private readonly IMongoBaseRepository<SystemUser> _systemUserRepo;
|
||||
private readonly IMongoBaseRepository<AdminOperationLog> _logRepo;
|
||||
|
||||
public OperationLogController(IAdminOperationLogService operationLogService, IMongoBaseRepository<SystemUser> systemUserRepo, IMongoBaseRepository<AdminOperationLog> logRepo)
|
||||
{
|
||||
_operationLogService = operationLogService;
|
||||
_systemUserRepo = systemUserRepo;
|
||||
_logRepo = logRepo;
|
||||
}
|
||||
|
||||
[HttpPost("list")]
|
||||
public async Task<PageResponse<OperationLogDto>> GetList([FromBody] OperationLogQueryParam param)
|
||||
{
|
||||
var (list, count) = await _operationLogService.QueryPage(param);
|
||||
if (list == null)
|
||||
return this.ToPageResult(new List<OperationLogDto>(), 0);
|
||||
var dtos = list.Select(MapToDto);
|
||||
return this.ToPageResult(dtos, (int)count);
|
||||
}
|
||||
|
||||
[HttpGet("admins")]
|
||||
public async Task<BaseResponse<List<AdminOptionDto>>> GetAdmins()
|
||||
{
|
||||
var users = await _systemUserRepo.FilterByAsync(x => x.State == 1);
|
||||
var result = users.Select(x => new AdminOptionDto { Value = x.Id, Label = x.DisplayName }).ToList();
|
||||
return this.ToResult(result);
|
||||
}
|
||||
|
||||
[HttpGet("modules")]
|
||||
public BaseResponse<List<ModuleOptionDto>> GetModules()
|
||||
{
|
||||
var modules = Enum.GetValues<EnumOperationModule>()
|
||||
.Select(x => new ModuleOptionDto { Value = (int)x, Label = ((int)x).ToString() })
|
||||
.ToList();
|
||||
return this.ToResult(modules);
|
||||
}
|
||||
|
||||
[HttpPost("Delete")]
|
||||
public async Task<BaseResponse<string>> Delete(string id)
|
||||
{
|
||||
var result = await _logRepo.DeleteByIdAsync(id);
|
||||
if (result.ok)
|
||||
return this.ToResult("删除成功");
|
||||
else
|
||||
return this.ToFailResult<string>("删除失败");
|
||||
}
|
||||
|
||||
[HttpPost("BatchDelete")]
|
||||
public async Task<BaseResponse<string>> BatchDelete([FromBody] List<string> ids)
|
||||
{
|
||||
int success = 0, fail = 0;
|
||||
foreach (var id in ids)
|
||||
{
|
||||
var result = await _logRepo.DeleteByIdAsync(id);
|
||||
if (result.ok)
|
||||
success++;
|
||||
else
|
||||
fail++;
|
||||
}
|
||||
if (fail == 0)
|
||||
return this.ToResult($"成功删除 {success} 条");
|
||||
return this.ToResult($"删除完成:成功 {success} 条,失败 {fail} 条");
|
||||
}
|
||||
|
||||
private static OperationLogDto MapToDto(AdminOperationLog entity)
|
||||
{
|
||||
return new OperationLogDto
|
||||
{
|
||||
Id = entity.Id,
|
||||
AdminId = entity.AdminId ?? "",
|
||||
AdminName = entity.AdminName ?? "",
|
||||
Module = entity.Module,
|
||||
ActionType = entity.ActionType ?? "",
|
||||
Description = entity.Description ?? "",
|
||||
HttpMethod = entity.HttpMethod ?? "",
|
||||
RequestPath = entity.RequestPath ?? "",
|
||||
Ip = entity.Ip ?? "",
|
||||
Status = entity.Status,
|
||||
OperateTime = entity.OperateTime
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class OperationLogDto
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string AdminId { get; set; }
|
||||
public string AdminName { get; set; }
|
||||
public int Module { get; set; }
|
||||
public string ActionType { get; set; }
|
||||
public string Description { get; set; }
|
||||
public string HttpMethod { get; set; }
|
||||
public string RequestPath { get; set; }
|
||||
public string Ip { get; set; }
|
||||
public int Status { get; set; }
|
||||
public DateTime OperateTime { get; set; }
|
||||
}
|
||||
|
||||
public class AdminOptionDto
|
||||
{
|
||||
public string Value { get; set; }
|
||||
public string Label { get; set; }
|
||||
}
|
||||
|
||||
public class ModuleOptionDto
|
||||
{
|
||||
public int Value { get; set; }
|
||||
public string Label { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,244 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using AdminApplication.DTOs;
|
||||
using AdminDomain.Customer;
|
||||
using AdminDomain.Customer.Param;
|
||||
using Common;
|
||||
using CommonInfrastructure;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace AdminApi.Customer
|
||||
{
|
||||
[ApiController]
|
||||
[Route("adminapi/[controller]")]
|
||||
[Authorize]
|
||||
public class CustomerController : BaseController
|
||||
{
|
||||
private readonly ICustomerService _customerService;
|
||||
|
||||
public CustomerController(ICustomerService customerService)
|
||||
{
|
||||
_customerService = customerService;
|
||||
}
|
||||
|
||||
[HttpGet("list")]
|
||||
public async Task<PageResponse<CustomerDto>> GetCustomerList([FromQuery] GetCustomerListParam param)
|
||||
{
|
||||
var result = await _customerService.GetCustomerList(param);
|
||||
var dtos = MapToCustomerDtos(result.customers);
|
||||
return this.ToPageResult(dtos, result.total);
|
||||
}
|
||||
|
||||
[HttpGet("{id}")]
|
||||
public async Task<BaseResponse<CustomerDto>> GetCustomerById(string id)
|
||||
{
|
||||
var result = await _customerService.GetCustomerById(id);
|
||||
var dto = MapToCustomerDto(result.customer);
|
||||
return this.ToResult(dto);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<BaseResponse<CustomerDto>> AddCustomer([FromBody] AddCustomerParam param)
|
||||
{
|
||||
var result = await _customerService.AddCustomer(param);
|
||||
if (result.isOk)
|
||||
{
|
||||
var dto = MapToCustomerDto(result.customer);
|
||||
return this.ToResult(dto);
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.ToFailResult<CustomerDto>(result.msg);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPut]
|
||||
public async Task<BaseResponse<string>> UpdateCustomer([FromBody] UpdateCustomerParam param)
|
||||
{
|
||||
var result = await _customerService.UpdateCustomer(param);
|
||||
if (result.isOk)
|
||||
{
|
||||
return this.ToResult(result.msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpDelete("{id}")]
|
||||
public async Task<BaseResponse<string>> DeleteCustomer(string id)
|
||||
{
|
||||
var result = await _customerService.DeleteCustomer(id);
|
||||
if (result.isOk)
|
||||
{
|
||||
return this.ToResult(result.msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("conversations")]
|
||||
public async Task<PageResponse<ConversationDto>> GetConversationList([FromQuery] GetConversationListParam param)
|
||||
{
|
||||
var result = await _customerService.GetConversationList(param);
|
||||
var dtos = MapToConversationDtos(result.conversations);
|
||||
return this.ToPageResult(dtos, result.total);
|
||||
}
|
||||
|
||||
[HttpGet("conversation/{id}")]
|
||||
public async Task<BaseResponse<ConversationDto>> GetConversationById(string id)
|
||||
{
|
||||
var result = await _customerService.GetConversationById(id);
|
||||
var dto = MapToConversationDto(result.conversation);
|
||||
return this.ToResult(dto);
|
||||
}
|
||||
|
||||
[HttpDelete("conversation/{id}")]
|
||||
public async Task<BaseResponse<string>> DeleteConversation(string id)
|
||||
{
|
||||
var result = await _customerService.DeleteConversation(id);
|
||||
return result.isOk
|
||||
? this.ToResult(result.msg)
|
||||
: this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
|
||||
[HttpGet("chatlog/{conversationId}")]
|
||||
public async Task<BaseResponse<List<MessageDto>>> GetChatLog(string conversationId)
|
||||
{
|
||||
var result = await _customerService.GetChatLog(conversationId);
|
||||
if (result.isOk)
|
||||
{
|
||||
var dtos = MapToMessageDtos(result.messages);
|
||||
return this.ToResult(dtos);
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.ToFailResult<List<MessageDto>>(result.msg);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpDelete("message/{id}")]
|
||||
public async Task<BaseResponse<string>> DeleteMessage(string id)
|
||||
{
|
||||
var result = await _customerService.DeleteMessage(id);
|
||||
if (result.isOk)
|
||||
{
|
||||
return this.ToResult(result.msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpDelete("messages/{conversationId}")]
|
||||
public async Task<BaseResponse<string>> DeleteAllMessages(string conversationId)
|
||||
{
|
||||
var result = await _customerService.DeleteAllMessages(conversationId);
|
||||
if (result.isOk)
|
||||
{
|
||||
return this.ToResult(result.msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("messages/delete-batch")]
|
||||
public async Task<BaseResponse<string>> DeleteMessagesBatch([FromBody] DeleteMessagesBatchRequest param)
|
||||
{
|
||||
var result = await _customerService.DeleteMessagesBatch(param.Ids);
|
||||
if (result.isOk)
|
||||
{
|
||||
return this.ToResult(result.msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
}
|
||||
|
||||
private static CustomerDto MapToCustomerDto(CustomerService.Domain.Entity.CustomerEntity? entity)
|
||||
{
|
||||
if (entity == null) return null!;
|
||||
return new CustomerDto
|
||||
{
|
||||
Id = entity.Id,
|
||||
UserId = entity.UserId,
|
||||
UserName = entity.UserName,
|
||||
Status = entity.Status,
|
||||
Avatar = entity.Avatar,
|
||||
CreateTime = entity.CreateTime
|
||||
};
|
||||
}
|
||||
|
||||
private static List<CustomerDto> MapToCustomerDtos(IEnumerable<CustomerService.Domain.Entity.CustomerEntity> entities)
|
||||
{
|
||||
var dtos = new List<CustomerDto>();
|
||||
foreach (var entity in entities)
|
||||
{
|
||||
dtos.Add(MapToCustomerDto(entity));
|
||||
}
|
||||
return dtos;
|
||||
}
|
||||
|
||||
private static ConversationDto MapToConversationDto(CustomerService.Domain.Entity.ConversationEntity? entity)
|
||||
{
|
||||
if (entity == null) return null!;
|
||||
return new ConversationDto
|
||||
{
|
||||
Id = entity.Id,
|
||||
UserId = entity.UserId,
|
||||
UserName = entity.UserName,
|
||||
UserAvatar = entity.UserAvatar,
|
||||
CustomerId = entity.CustomerId,
|
||||
CustomerName = entity.CustomerName,
|
||||
CustomerAvatar = entity.CustomerAvatar,
|
||||
Status = entity.Status,
|
||||
CreateTime = entity.CreateTime,
|
||||
EndTime = entity.EndTime
|
||||
};
|
||||
}
|
||||
|
||||
private static List<ConversationDto> MapToConversationDtos(IEnumerable<CustomerService.Domain.Entity.ConversationEntity> entities)
|
||||
{
|
||||
var dtos = new List<ConversationDto>();
|
||||
foreach (var entity in entities)
|
||||
{
|
||||
dtos.Add(MapToConversationDto(entity));
|
||||
}
|
||||
return dtos;
|
||||
}
|
||||
|
||||
private static MessageDto MapToMessageDto(CustomerService.Domain.Entity.MessageEntity entity)
|
||||
{
|
||||
return new MessageDto
|
||||
{
|
||||
Id = entity.Id,
|
||||
ConversationId = entity.ConversationId,
|
||||
SenderType = entity.SenderType,
|
||||
SenderId = entity.SenderId,
|
||||
SenderName = entity.SenderName,
|
||||
SenderAvatar = entity.SenderAvatar,
|
||||
ContentType = entity.ContentType,
|
||||
Content = entity.Content,
|
||||
CreateTime = entity.CreateTime
|
||||
};
|
||||
}
|
||||
|
||||
private static List<MessageDto> MapToMessageDtos(IEnumerable<CustomerService.Domain.Entity.MessageEntity> entities)
|
||||
{
|
||||
var dtos = new List<MessageDto>();
|
||||
foreach (var entity in entities)
|
||||
{
|
||||
dtos.Add(MapToMessageDto(entity));
|
||||
}
|
||||
return dtos;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
using AdminService.EmptyRoomRate;
|
||||
using Common;
|
||||
using CommonDomain;
|
||||
using CommonDomain.Enum;
|
||||
using CommonInfrastructure;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace AdminApi.EmptyRoomRate
|
||||
{
|
||||
[ApiController, Route("adminapi/[controller]")]
|
||||
[Authorize]
|
||||
public class EmptyRoomRateConfigController : BaseController
|
||||
{
|
||||
private readonly IEmptyRoomRateConfigService _emptyRoomRateConfigService;
|
||||
|
||||
public EmptyRoomRateConfigController(IEmptyRoomRateConfigService emptyRoomRateConfigService)
|
||||
{
|
||||
_emptyRoomRateConfigService = emptyRoomRateConfigService;
|
||||
}
|
||||
|
||||
[HttpGet("list")]
|
||||
public async Task<PageResponse<EmptyRoomRateDto>> GetList(
|
||||
[FromQuery] int page = 1,
|
||||
[FromQuery] int size = 20,
|
||||
[FromQuery] GameTypeEnum? gameType = null,
|
||||
[FromQuery] int? roomLevel = null)
|
||||
{
|
||||
page = page > 0 ? page : 1;
|
||||
size = size > 0 ? size : 20;
|
||||
var result = await _emptyRoomRateConfigService.GetList(page, size, gameType, roomLevel);
|
||||
var dtos = result.list.Select(MapToDto).ToList();
|
||||
return this.ToPageResult(dtos, (int)result.count);
|
||||
}
|
||||
|
||||
[HttpPost("add")]
|
||||
public async Task<BaseResponse<string>> Add([FromBody] AddEmptyRoomRateParam param)
|
||||
{
|
||||
var result = await _emptyRoomRateConfigService.Add(param.GameType, param.RoomLevel, param.EmptyRoomRate);
|
||||
if (result.isok)
|
||||
return this.ToResult<string>(result.msg);
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
|
||||
[HttpPost("update")]
|
||||
public async Task<BaseResponse<string>> Update([FromBody] UpdateEmptyRoomRateParam param)
|
||||
{
|
||||
var result = await _emptyRoomRateConfigService.Update(param.Id, param.EmptyRoomRate);
|
||||
if (result.isok)
|
||||
return this.ToResult<string>(result.msg);
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
|
||||
[HttpPost("delete")]
|
||||
public async Task<BaseResponse<string>> Delete([FromBody] DeleteEmptyRoomRateParam param)
|
||||
{
|
||||
var result = await _emptyRoomRateConfigService.Delete(param.Id);
|
||||
if (result.isok)
|
||||
return this.ToResult<string>(result.msg);
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
|
||||
private static EmptyRoomRateDto MapToDto(EmptyRoomRateConfig entity)
|
||||
{
|
||||
if (entity == null) return new EmptyRoomRateDto();
|
||||
return new EmptyRoomRateDto
|
||||
{
|
||||
Id = entity.Id ?? "",
|
||||
GameType = (int)entity.GameType,
|
||||
GameTypeName = entity.GameType.ToString(),
|
||||
RoomLevel = entity.RoomLevel,
|
||||
EmptyRoomRate = entity.EmptyRoomRate
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class EmptyRoomRateDto
|
||||
{
|
||||
public string Id { get; set; }
|
||||
|
||||
public int GameType { get; set; }
|
||||
|
||||
public string GameTypeName { get; set; }
|
||||
|
||||
public int RoomLevel { get; set; }
|
||||
|
||||
public int EmptyRoomRate { get; set; }
|
||||
}
|
||||
|
||||
public class AddEmptyRoomRateParam
|
||||
{
|
||||
public GameTypeEnum GameType { get; set; }
|
||||
|
||||
public int RoomLevel { get; set; }
|
||||
|
||||
public int EmptyRoomRate { get; set; } = 30;
|
||||
}
|
||||
|
||||
public class UpdateEmptyRoomRateParam
|
||||
{
|
||||
public string Id { get; set; }
|
||||
|
||||
public int EmptyRoomRate { get; set; }
|
||||
}
|
||||
|
||||
public class DeleteEmptyRoomRateParam
|
||||
{
|
||||
public string Id { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,246 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Common;
|
||||
using CommonDomain;
|
||||
using CommonInfrastructure;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using AdminService.Faq;
|
||||
|
||||
namespace AdminApi.Faq
|
||||
{
|
||||
[ApiController, Route("adminapi/[controller]")]
|
||||
public class FaqController : BaseController
|
||||
{
|
||||
private readonly IFaqService _faqService;
|
||||
|
||||
public FaqController(IFaqService faqService)
|
||||
{
|
||||
_faqService = faqService;
|
||||
}
|
||||
|
||||
|
||||
[HttpGet("Categories")]
|
||||
public async Task<BaseCommonResponse> GetCategories()
|
||||
{
|
||||
var list = await _faqService.GetAllCategories();
|
||||
if (list == null)
|
||||
return new ListResponse<FaqCategoryDto> { Code = CommonResponseCodeEnum.Success, Data = new List<FaqCategoryDto>() };
|
||||
var dtos = list.Select(MapToCategoryDto).ToList();
|
||||
return new ListResponse<FaqCategoryDto>
|
||||
{
|
||||
Code = CommonResponseCodeEnum.Success,
|
||||
Info = "获取成功",
|
||||
Data = dtos
|
||||
};
|
||||
}
|
||||
|
||||
[HttpGet("CategoryTree")]
|
||||
public async Task<BaseCommonResponse> GetCategoryTree()
|
||||
{
|
||||
var tree = await _faqService.GetCategoryTree();
|
||||
var dtos = tree.Select(MapToTreeNodeDto).ToList();
|
||||
return new ListResponse<FaqCategoryTreeNodeDto>
|
||||
{
|
||||
Code = CommonResponseCodeEnum.Success,
|
||||
Info = "获取成功",
|
||||
Data = dtos
|
||||
};
|
||||
}
|
||||
|
||||
[HttpGet("CategoryDetail")]
|
||||
public async Task<BaseResponse<FaqCategoryDto>> GetCategoryDetail(string id)
|
||||
{
|
||||
var category = await _faqService.GetCategoryById(id);
|
||||
if (category == null)
|
||||
return this.ToFailResult<FaqCategoryDto>("板块不存在");
|
||||
|
||||
return this.ToResult(MapToCategoryDto(category));
|
||||
}
|
||||
|
||||
[HttpPost("AddCategory")]
|
||||
public async Task<BaseResponse<string>> AddCategory([FromBody] FaqCategory category)
|
||||
{
|
||||
var result = await _faqService.AddCategory(category);
|
||||
if (result.ok)
|
||||
return this.ToResult(result.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
|
||||
[HttpPost("UpdateCategory")]
|
||||
public async Task<BaseResponse<string>> UpdateCategory([FromBody] FaqCategory category)
|
||||
{
|
||||
var result = await _faqService.UpdateCategory(category);
|
||||
if (result.ok)
|
||||
return this.ToResult(result.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
|
||||
[HttpPost("DeleteCategory")]
|
||||
public async Task<BaseResponse<string>> DeleteCategory(string id)
|
||||
{
|
||||
var result = await _faqService.DeleteCategory(id);
|
||||
if (result.ok)
|
||||
return this.ToResult(result.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
|
||||
|
||||
[HttpGet("Questions")]
|
||||
public async Task<BaseCommonResponse> GetQuestions(string categoryId)
|
||||
{
|
||||
var list = await _faqService.GetQuestionsByCategory(categoryId);
|
||||
if (list == null)
|
||||
return new ListResponse<FaqQuestionDto> { Code = CommonResponseCodeEnum.Success, Data = new List<FaqQuestionDto>() };
|
||||
var dtos = list.Select(MapToQuestionDto).ToList();
|
||||
return new ListResponse<FaqQuestionDto>
|
||||
{
|
||||
Code = CommonResponseCodeEnum.Success,
|
||||
Info = "获取成功",
|
||||
Data = dtos
|
||||
};
|
||||
}
|
||||
|
||||
[HttpPost("QuestionList")]
|
||||
public async Task<BaseCommonResponse> GetQuestionList([FromBody] FaqQueryParam param)
|
||||
{
|
||||
var result = await _faqService.QueryQuestions(param.CategoryId, param.PageIndex, param.PageSize);
|
||||
var dtos = result.list.Select(MapToQuestionDto).ToList();
|
||||
return new PageDataResponse<FaqQuestionDto>
|
||||
{
|
||||
Code = CommonResponseCodeEnum.Success,
|
||||
Info = "获取成功",
|
||||
Data = dtos,
|
||||
Count = (int)result.count
|
||||
};
|
||||
}
|
||||
|
||||
[HttpGet("QuestionDetail")]
|
||||
public async Task<BaseResponse<FaqQuestionDto>> GetQuestionDetail(string id)
|
||||
{
|
||||
var question = await _faqService.GetQuestionById(id);
|
||||
if (question == null)
|
||||
return this.ToFailResult<FaqQuestionDto>("问题不存在");
|
||||
|
||||
return this.ToResult(MapToQuestionDto(question));
|
||||
}
|
||||
|
||||
[HttpPost("AddQuestion")]
|
||||
public async Task<BaseResponse<string>> AddQuestion([FromBody] FaqQuestion question)
|
||||
{
|
||||
var result = await _faqService.AddQuestion(question);
|
||||
if (result.ok)
|
||||
return this.ToResult(result.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
|
||||
[HttpPost("UpdateQuestion")]
|
||||
public async Task<BaseResponse<string>> UpdateQuestion([FromBody] FaqQuestion question)
|
||||
{
|
||||
var result = await _faqService.UpdateQuestion(question);
|
||||
if (result.ok)
|
||||
return this.ToResult(result.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
|
||||
[HttpPost("DeleteQuestion")]
|
||||
public async Task<BaseResponse<string>> DeleteQuestion(string id)
|
||||
{
|
||||
var result = await _faqService.DeleteQuestion(id);
|
||||
if (result.ok)
|
||||
return this.ToResult(result.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
|
||||
|
||||
private static FaqCategoryDto MapToCategoryDto(FaqCategory entity)
|
||||
{
|
||||
if (entity == null) return new FaqCategoryDto();
|
||||
return new FaqCategoryDto
|
||||
{
|
||||
Id = entity.Id ?? "",
|
||||
ParentId = entity.ParentId ?? "",
|
||||
Name = entity.Name ?? "",
|
||||
Icon = entity.Icon ?? "",
|
||||
Sort = entity.Sort,
|
||||
IsEnable = entity.IsEnable
|
||||
};
|
||||
}
|
||||
|
||||
private static FaqCategoryTreeNodeDto MapToTreeNodeDto(FaqCategoryTreeNode entity)
|
||||
{
|
||||
if (entity == null) return new FaqCategoryTreeNodeDto();
|
||||
return new FaqCategoryTreeNodeDto
|
||||
{
|
||||
Id = entity.Id ?? "",
|
||||
Name = entity.Name ?? "",
|
||||
Icon = entity.Icon ?? "",
|
||||
Sort = entity.Sort,
|
||||
IsEnable = entity.IsEnable,
|
||||
Children = entity.Children?.Select(MapToTreeNodeDto).ToList() ?? new List<FaqCategoryTreeNodeDto>(),
|
||||
QuestionCount = entity.QuestionCount
|
||||
};
|
||||
}
|
||||
|
||||
private static FaqQuestionDto MapToQuestionDto(FaqQuestion entity)
|
||||
{
|
||||
if (entity == null) return new FaqQuestionDto();
|
||||
return new FaqQuestionDto
|
||||
{
|
||||
Id = entity.Id ?? "",
|
||||
CategoryId = entity.CategoryId ?? "",
|
||||
Title = entity.Title ?? "",
|
||||
Content = entity.Content ?? "",
|
||||
Sort = entity.Sort,
|
||||
IsEnable = entity.IsEnable,
|
||||
CreateTime = entity.CreateTime
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class FaqCategoryDto
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string ParentId { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Icon { get; set; }
|
||||
public int Sort { get; set; }
|
||||
public bool IsEnable { get; set; }
|
||||
}
|
||||
|
||||
public class FaqCategoryTreeNodeDto
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Icon { get; set; }
|
||||
public int Sort { get; set; }
|
||||
public bool IsEnable { get; set; }
|
||||
public List<FaqCategoryTreeNodeDto> Children { get; set; } = new();
|
||||
public int QuestionCount { get; set; }
|
||||
}
|
||||
|
||||
public class FaqQuestionDto
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string CategoryId { get; set; }
|
||||
public string Title { get; set; }
|
||||
public string Content { get; set; }
|
||||
public int Sort { get; set; }
|
||||
public bool IsEnable { get; set; }
|
||||
public DateTime CreateTime { get; set; }
|
||||
}
|
||||
|
||||
public class FaqQueryParam
|
||||
{
|
||||
public string? CategoryId { get; set; }
|
||||
public int PageIndex { get; set; } = 1;
|
||||
public int PageSize { get; set; } = 10;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Common;
|
||||
using CommonInfrastructure;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using MongoRepository;
|
||||
using UserDomain;
|
||||
using UserDomain.Entity;
|
||||
using UserDomain.Param;
|
||||
|
||||
namespace AdminApi.Feedbacks
|
||||
{
|
||||
[ApiController, Route("adminapi/[controller]")]
|
||||
public class FeedbackManageController : BaseController
|
||||
{
|
||||
private readonly IUserFeedbackService _feedbackService;
|
||||
private readonly IMongoBaseRepository<UserFeedback> _feedbackRepo;
|
||||
|
||||
public FeedbackManageController(IUserFeedbackService feedbackService, IMongoBaseRepository<UserFeedback> feedbackRepo)
|
||||
{
|
||||
_feedbackService = feedbackService;
|
||||
_feedbackRepo = feedbackRepo;
|
||||
}
|
||||
|
||||
[HttpPost("List")]
|
||||
public async Task<PageResponse<FeedbackDto>> GetList([FromBody] FeedbackQueryParam param)
|
||||
{
|
||||
var result = await _feedbackService.QueryPage(param);
|
||||
var dtos = result.list.Select(MapToDto);
|
||||
return this.ToPageResult(dtos, (int)result.count);
|
||||
}
|
||||
|
||||
[HttpGet("Detail")]
|
||||
public async Task<BaseResponse<FeedbackDetailDto>> GetDetail(string id)
|
||||
{
|
||||
var feedback = await _feedbackRepo.FindOneAsync(x => x.Id == id);
|
||||
if (feedback == null)
|
||||
return this.ToFailResult<FeedbackDetailDto>("反馈不存在");
|
||||
|
||||
return this.ToResult(MapToDetailDto(feedback));
|
||||
}
|
||||
|
||||
[HttpPost("Reply")]
|
||||
public async Task<BaseResponse<string>> Reply([FromBody] ReplyFeedbackParam param)
|
||||
{
|
||||
var feedback = await _feedbackRepo.FindOneAsync(x => x.Id == param.Id);
|
||||
if (feedback == null)
|
||||
return this.ToFailResult<string>("反馈不存在");
|
||||
|
||||
feedback.Reply = param.Reply;
|
||||
feedback.Status = param.Status;
|
||||
feedback.ReplyTime = DateTime.Now;
|
||||
feedback.ReplyAdminId = loginUser.UserId;
|
||||
feedback.ReplyAdminName = loginUser.UserName;
|
||||
|
||||
var result = await _feedbackRepo.ReplaceOneAsync(feedback);
|
||||
if (result.ok)
|
||||
return this.ToResult("回复成功");
|
||||
else
|
||||
return this.ToFailResult<string>("回复失败");
|
||||
}
|
||||
|
||||
[HttpPost("Delete")]
|
||||
public async Task<BaseResponse<string>> Delete(string id)
|
||||
{
|
||||
var result = await _feedbackRepo.DeleteByIdAsync(id);
|
||||
if (result.ok)
|
||||
return this.ToResult("删除成功");
|
||||
else
|
||||
return this.ToFailResult<string>("删除失败");
|
||||
}
|
||||
|
||||
private static FeedbackDto MapToDto(UserFeedback entity)
|
||||
{
|
||||
return new FeedbackDto
|
||||
{
|
||||
Id = entity.Id,
|
||||
UserId = entity.UserId ?? "",
|
||||
UserNumber = entity.UserNumber ?? "",
|
||||
UserName = entity.UserName ?? "",
|
||||
Type = entity.Type,
|
||||
Title = entity.Title ?? "",
|
||||
Content = entity.Content ?? "",
|
||||
Images = string.IsNullOrEmpty(entity.Images) ? new List<string>() : entity.Images.Split(',').ToList(),
|
||||
QQ = entity.QQ ?? "",
|
||||
Contact = entity.Contact ?? "",
|
||||
Status = entity.Status,
|
||||
Reply = entity.Reply ?? "",
|
||||
ReplyTime = entity.ReplyTime,
|
||||
SubmitTime = entity.SubmitTime
|
||||
};
|
||||
}
|
||||
|
||||
private static FeedbackDetailDto MapToDetailDto(UserFeedback entity)
|
||||
{
|
||||
return new FeedbackDetailDto
|
||||
{
|
||||
Id = entity.Id,
|
||||
UserId = entity.UserId ?? "",
|
||||
UserNumber = entity.UserNumber ?? "",
|
||||
UserName = entity.UserName ?? "",
|
||||
Type = entity.Type,
|
||||
Title = entity.Title ?? "",
|
||||
Content = entity.Content ?? "",
|
||||
Images = string.IsNullOrEmpty(entity.Images) ? new List<string>() : entity.Images.Split(',').ToList(),
|
||||
QQ = entity.QQ ?? "",
|
||||
Contact = entity.Contact ?? "",
|
||||
Status = entity.Status,
|
||||
Reply = entity.Reply ?? "",
|
||||
ReplyTime = entity.ReplyTime,
|
||||
ReplyAdminId = entity.ReplyAdminId ?? "",
|
||||
ReplyAdminName = entity.ReplyAdminName ?? "",
|
||||
SubmitTime = entity.SubmitTime
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class FeedbackDto
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string UserId { get; set; }
|
||||
public string UserNumber { get; set; }
|
||||
public string UserName { get; set; }
|
||||
public int Type { get; set; }
|
||||
public string Title { get; set; }
|
||||
public string Content { get; set; }
|
||||
public List<string> Images { get; set; }
|
||||
/// <summary>
|
||||
/// QQ
|
||||
/// </summary>
|
||||
public string QQ { get; set; }
|
||||
public string Contact { get; set; }
|
||||
public int Status { get; set; }
|
||||
public string Reply { get; set; }
|
||||
public DateTime? ReplyTime { get; set; }
|
||||
public DateTime SubmitTime { get; set; }
|
||||
}
|
||||
|
||||
public class FeedbackDetailDto
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string UserId { get; set; }
|
||||
public string UserNumber { get; set; }
|
||||
public string UserName { get; set; }
|
||||
public int Type { get; set; }
|
||||
public string Title { get; set; }
|
||||
public string Content { get; set; }
|
||||
public List<string> Images { get; set; }
|
||||
/// <summary>
|
||||
/// QQ
|
||||
/// </summary>
|
||||
public string QQ { get; set; }
|
||||
public string Contact { get; set; }
|
||||
public int Status { get; set; }
|
||||
public string Reply { get; set; }
|
||||
public DateTime? ReplyTime { get; set; }
|
||||
public string ReplyAdminId { get; set; }
|
||||
public string ReplyAdminName { get; set; }
|
||||
public DateTime SubmitTime { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,224 @@
|
||||
using System.Text;
|
||||
using AdminService.GameConfigs;
|
||||
using Common;
|
||||
using CommonDomain;
|
||||
using CommonDomain.BaseModel;
|
||||
using CommonInfrastructure;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NewLife;
|
||||
using RedisService;
|
||||
using XAct;
|
||||
|
||||
namespace AdminApi.GameConfigs
|
||||
{
|
||||
[ApiController, Route("adminapi/[controller]")]
|
||||
public class ActivityConfigController : BaseController
|
||||
{
|
||||
private readonly IActivityConfigService _activityConfigService;
|
||||
private readonly IRedisService _redisService;
|
||||
|
||||
public ActivityConfigController(IActivityConfigService activityConfigService, IRedisService redisService)
|
||||
{
|
||||
_activityConfigService = activityConfigService;
|
||||
_redisService = redisService;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[HttpGet("Get")]
|
||||
public async Task<BaseResponse<ActivityConfig>> Get()
|
||||
{
|
||||
var config = await _activityConfigService.Get();
|
||||
if (config != null)
|
||||
return this.ToResult(config);
|
||||
else
|
||||
return this.ToFailResult<ActivityConfig>("配置不存在");
|
||||
}
|
||||
|
||||
[HttpPost("Update")]
|
||||
public async Task<BaseResponse<string>> Update(ActivityConfig config)
|
||||
{
|
||||
var res = await _activityConfigService.Update(config);
|
||||
if (res.isok)
|
||||
return this.ToResult(res.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(res.msg);
|
||||
}
|
||||
|
||||
[HttpPost("Delete")]
|
||||
public async Task<BaseResponse<string>> Delete()
|
||||
{
|
||||
var res = await _activityConfigService.Delete();
|
||||
if (res.isok)
|
||||
return this.ToResult(res.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(res.msg);
|
||||
}
|
||||
|
||||
[HttpPost("UpdateBattleOrder")]
|
||||
public async Task<BaseResponse<string>> UpdateBattleOrder(BattleOrderConfig config)
|
||||
{
|
||||
var activityConfig = await _activityConfigService.Get() ?? new ActivityConfig();
|
||||
activityConfig.BattleOrderConfig = config;
|
||||
var res = await _activityConfigService.Update(activityConfig);
|
||||
if (res.isok)
|
||||
return this.ToResult(res.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(res.msg);
|
||||
}
|
||||
|
||||
[HttpPost("UpdateRelief")]
|
||||
public async Task<BaseResponse<string>> UpdateRelief(ReliefConfig config)
|
||||
{
|
||||
var activityConfig = await _activityConfigService.Get() ?? new ActivityConfig();
|
||||
activityConfig.ReliefConfig = config;
|
||||
var res = await _activityConfigService.Update(activityConfig);
|
||||
if (res.isok)
|
||||
return this.ToResult(res.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(res.msg);
|
||||
}
|
||||
|
||||
[HttpPost("UpdateSign")]
|
||||
public async Task<BaseResponse<string>> UpdateSign(List<SignConfig> configs)
|
||||
{
|
||||
var activityConfig = await _activityConfigService.Get() ?? new ActivityConfig();
|
||||
activityConfig.SignConfigs = configs;
|
||||
var res = await _activityConfigService.Update(activityConfig);
|
||||
if (res.isok)
|
||||
return this.ToResult(res.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(res.msg);
|
||||
}
|
||||
|
||||
[HttpPost("UpdateGoldShop")]
|
||||
public async Task<BaseResponse<string>> UpdateGoldShop(List<GoldShopConfig> configs)
|
||||
{
|
||||
var activityConfig = await _activityConfigService.Get() ?? new ActivityConfig();
|
||||
activityConfig.GoldShopConfigs = configs;
|
||||
var res = await _activityConfigService.Update(activityConfig);
|
||||
if (res.isok)
|
||||
return this.ToResult(res.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(res.msg);
|
||||
}
|
||||
|
||||
[HttpPost("UpdateDiamondShop")]
|
||||
public async Task<BaseResponse<string>> UpdateDiamondShop(List<DiamondShopConfig> configs)
|
||||
{
|
||||
var activityConfig = await _activityConfigService.Get() ?? new ActivityConfig();
|
||||
activityConfig.DiamondShopConfigs = configs;
|
||||
var res = await _activityConfigService.Update(activityConfig);
|
||||
if (res.isok)
|
||||
return this.ToResult(res.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(res.msg);
|
||||
}
|
||||
|
||||
[HttpPost("UpdateRegister")]
|
||||
public async Task<BaseResponse<string>> UpdateRegister(RegisterConfig config)
|
||||
{
|
||||
var activityConfig = await _activityConfigService.Get() ?? new ActivityConfig();
|
||||
activityConfig.RegisterConfig = config;
|
||||
var res = await _activityConfigService.Update(activityConfig);
|
||||
if (res.isok)
|
||||
return this.ToResult(res.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(res.msg);
|
||||
}
|
||||
|
||||
[HttpPost("UpdateFishingReturn")]
|
||||
public async Task<BaseResponse<string>> UpdateFishingReturn(FishingReturnConfig config)
|
||||
{
|
||||
var activityConfig = await _activityConfigService.Get() ?? new ActivityConfig();
|
||||
activityConfig.FishingReturnConfig = config;
|
||||
var res = await _activityConfigService.Update(activityConfig);
|
||||
if (res.isok)
|
||||
return this.ToResult(res.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(res.msg);
|
||||
}
|
||||
|
||||
[HttpGet("GetFishingReturn")]
|
||||
public async Task<BaseResponse<FishingReturnConfig>> GetFishingReturn()
|
||||
{
|
||||
var config = await _activityConfigService.Get();
|
||||
if (config?.FishingReturnConfig != null)
|
||||
return this.ToResult(config.FishingReturnConfig);
|
||||
else
|
||||
return this.ToFailResult<FishingReturnConfig>("配置不存在");
|
||||
}
|
||||
|
||||
[HttpGet("SaveSection/{sectionName}")]
|
||||
public async Task<BaseResponse<string>> SaveSection(string sectionName)
|
||||
{
|
||||
var prop = typeof(ActivityConfig).GetProperty(sectionName);
|
||||
if (prop == null)
|
||||
return this.ToFailResult<string>($"板块 {sectionName} 不存在");
|
||||
|
||||
var config = await _activityConfigService.Get();
|
||||
if (config == null)
|
||||
return this.ToFailResult<string>("配置不存在");
|
||||
|
||||
var sectionValue = prop.GetValue(config);
|
||||
if (sectionValue == null)
|
||||
return this.ToFailResult<string>($"板块 {sectionName} 数据为空");
|
||||
|
||||
var jsonPath = global::System.IO.Path.Combine(AppContext.BaseDirectory, "Config", "activityconfig.json");
|
||||
|
||||
try
|
||||
{
|
||||
await _activityConfigService.Update(config);
|
||||
|
||||
ActivityConfig fileConfig;
|
||||
if (global::System.IO.File.Exists(jsonPath))
|
||||
{
|
||||
var json = await global::System.IO.File.ReadAllTextAsync(jsonPath, global::System.Text.Encoding.UTF8);
|
||||
fileConfig = global::System.Text.Json.JsonSerializer.Deserialize<ActivityConfig>(json, new global::System.Text.Json.JsonSerializerOptions { PropertyNameCaseInsensitive = true, ReadCommentHandling = global::System.Text.Json.JsonCommentHandling.Skip }) ?? new ActivityConfig();
|
||||
}
|
||||
else
|
||||
{
|
||||
fileConfig = new ActivityConfig();
|
||||
}
|
||||
|
||||
prop.SetValue(fileConfig, sectionValue);
|
||||
|
||||
var outJson = global::System.Text.Json.JsonSerializer.Serialize(fileConfig, new global::System.Text.Json.JsonSerializerOptions
|
||||
{
|
||||
WriteIndented = true,
|
||||
Encoder = global::System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
|
||||
});
|
||||
await global::System.IO.File.WriteAllTextAsync(jsonPath, outJson, global::System.Text.Encoding.UTF8);
|
||||
|
||||
return this.ToResult($"{sectionName} 已保存到 activityconfig.json");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return this.ToFailResult<string>($"保存失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("ExportSection/{sectionName}")]
|
||||
public async Task<IActionResult> ExportSection(string sectionName)
|
||||
{
|
||||
var config = await _activityConfigService.Get();
|
||||
if (config == null)
|
||||
return NotFound("配置不存在");
|
||||
|
||||
var prop = typeof(ActivityConfig).GetProperty(sectionName);
|
||||
if (prop == null)
|
||||
return NotFound($"板块 {sectionName} 不存在");
|
||||
|
||||
var data = prop.GetValue(config);
|
||||
if (data == null)
|
||||
return NotFound($"板块 {sectionName} 数据为空");
|
||||
|
||||
var json = global::System.Text.Json.JsonSerializer.Serialize(data, new global::System.Text.Json.JsonSerializerOptions
|
||||
{
|
||||
WriteIndented = true,
|
||||
Encoder = global::System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
|
||||
});
|
||||
return File(global::System.Text.Encoding.UTF8.GetBytes(json), "application/json", $"{sectionName}.json");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,828 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using AdminService.GameConfigs;
|
||||
using ChessGameDomain;
|
||||
using Common;
|
||||
using CommonDomain;
|
||||
using CommonDomain.Enum;
|
||||
using CommonInfrastructure;
|
||||
using DotNetCore.CAP;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MongoRepository;
|
||||
using RedisService;
|
||||
using RedisService.Param;
|
||||
using UserDomain.Entity;
|
||||
|
||||
namespace AdminApi.GameConfigs
|
||||
{
|
||||
[ApiController, Route("adminapi/Admin/[controller]")]
|
||||
[Authorize]
|
||||
public class RoomController : BaseController
|
||||
{
|
||||
private readonly IRedisService _redisService;
|
||||
private readonly IGameConfigService _gameConfigService;
|
||||
private readonly ICapPublisher _capPublisher;
|
||||
private readonly IMongoBaseRepository<UserEntity> _userMongo;
|
||||
private readonly ILogger<RoomController> _logger;
|
||||
|
||||
public RoomController(IRedisService redisService, IGameConfigService gameConfigService, ICapPublisher capPublisher, IMongoBaseRepository<UserEntity> userMongo, ILogger<RoomController> logger)
|
||||
{
|
||||
_redisService = redisService;
|
||||
_gameConfigService = gameConfigService;
|
||||
_capPublisher = capPublisher;
|
||||
_userMongo = userMongo;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
[HttpGet("GetAllRooms")]
|
||||
public async Task<PageResponse<RedisGameRoom>> GetAllRooms(
|
||||
int pageIndex = 1,
|
||||
int pageSize = 20,
|
||||
int? roomLevel = null,
|
||||
string? gameType = null,
|
||||
string? sortField = null,
|
||||
string? sortOrder = "desc")
|
||||
{
|
||||
var rooms = GetAllRoomsFromRedis()?.ToList() ?? new List<RedisGameRoom>();
|
||||
|
||||
foreach (var room in rooms)
|
||||
{
|
||||
if (room?.Players != null && room.Players.Count > 1)
|
||||
room.Players = room.Players.OrderBy(p => p.Position).ToList();
|
||||
}
|
||||
|
||||
if (roomLevel.HasValue)
|
||||
{
|
||||
rooms = rooms.Where(r => r != null && r.RoomLevel == roomLevel.Value).ToList();
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(gameType) && int.TryParse(gameType, out var gameTypeValue))
|
||||
{
|
||||
rooms = rooms.Where(r => r != null && (int)r.GameType == gameTypeValue).ToList();
|
||||
}
|
||||
|
||||
var total = rooms.Count;
|
||||
|
||||
if (!string.IsNullOrEmpty(sortField))
|
||||
{
|
||||
switch (sortField.ToLower())
|
||||
{
|
||||
case "currentonline":
|
||||
rooms = sortOrder?.ToLower() == "asc"
|
||||
? rooms.OrderBy(r => r.CurrentOnlinePlayerCount).ToList()
|
||||
: rooms.OrderByDescending(r => r.CurrentOnlinePlayerCount).ToList();
|
||||
break;
|
||||
case "robotcount":
|
||||
rooms = sortOrder?.ToLower() == "asc"
|
||||
? rooms.OrderBy(r => r.RobotCount).ToList()
|
||||
: rooms.OrderByDescending(r => r.RobotCount).ToList();
|
||||
break;
|
||||
case "roomlevel":
|
||||
rooms = sortOrder?.ToLower() == "asc"
|
||||
? rooms.OrderBy(r => r.RoomLevel).ToList()
|
||||
: rooms.OrderByDescending(r => r.RoomLevel).ToList();
|
||||
break;
|
||||
default:
|
||||
rooms = rooms.OrderByDescending(r => r.RoomId).ToList();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var pagedRooms = rooms.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();
|
||||
|
||||
return this.ToPageResult(pagedRooms, total);
|
||||
}
|
||||
|
||||
[HttpGet("GetAllChessRooms")]
|
||||
public async Task<PageResponse<ChessGameRoom>> GetAllChessRooms(
|
||||
int pageIndex = 1,
|
||||
int pageSize = 20,
|
||||
int? state = null,
|
||||
int? gameType = null)
|
||||
{
|
||||
var rooms = _redisService.GetAllChessRooms()?.ToList();
|
||||
if (state.HasValue)
|
||||
{
|
||||
rooms = rooms.Where(r => r != null && r.state == state.Value).ToList();
|
||||
}
|
||||
|
||||
if (gameType.HasValue)
|
||||
{
|
||||
rooms = rooms.Where(r => r != null && r.gameType == gameType.Value).ToList();
|
||||
}
|
||||
|
||||
var total = rooms.Count;
|
||||
|
||||
rooms = rooms.OrderByDescending(r => r.CreateTime).ToList();
|
||||
|
||||
var pagedRooms = rooms.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();
|
||||
|
||||
return this.ToPageResult(pagedRooms, total);
|
||||
}
|
||||
|
||||
[HttpGet("GetChessRoomDetail")]
|
||||
public async Task<BaseResponse<ChessGameRoom>> GetChessRoomDetail(string roomId)
|
||||
{
|
||||
var room = _redisService.GetChessRoom(roomId);
|
||||
if (room == null)
|
||||
return this.ToFailResult<ChessGameRoom>("房间不存在");
|
||||
|
||||
return this.ToResult(room);
|
||||
}
|
||||
|
||||
[HttpGet("ClearChessRoom")]
|
||||
public async Task<BaseResponse<string>> ClearChessRoom(string roomId)
|
||||
{
|
||||
var room = _redisService.GetChessRoom(roomId);
|
||||
if (room == null)
|
||||
return this.ToFailResult<string>("房间不存在");
|
||||
|
||||
int playerCount = 0;
|
||||
foreach (var user in room.users.ToList())
|
||||
{
|
||||
try
|
||||
{
|
||||
var userRedis = _redisService.GetRedisUserCId(user.userId);
|
||||
if (userRedis != null)
|
||||
{
|
||||
userRedis.CId = "";
|
||||
_redisService.SetRedisUserCId(userRedis);
|
||||
}
|
||||
playerCount++;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "管理员清空象棋房间 清理userRedis失败 roomId={RoomId} userId={UserId}", roomId, user.userId);
|
||||
}
|
||||
}
|
||||
|
||||
room.users.Clear();
|
||||
room.state = 0;
|
||||
room.robotCount = 0;
|
||||
room.gold = 0;
|
||||
room.gameType = 0;
|
||||
room.password = "";
|
||||
room.IsGameEnded = false;
|
||||
room.EndGameRequests?.Clear();
|
||||
room.PendingWinUserId = null;
|
||||
room.TimeoutLoserUserId = "";
|
||||
room.LastActiveTime = 0;
|
||||
_redisService.SetChessRoom(room);
|
||||
|
||||
return this.ToResult($"已清空房间 {roomId} 的所有玩家({playerCount} 人)");
|
||||
}
|
||||
|
||||
[HttpGet("KickChessPlayer")]
|
||||
public async Task<BaseResponse<string>> KickChessPlayer(string roomId, string userId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(roomId) || string.IsNullOrWhiteSpace(userId))
|
||||
return this.ToFailResult<string>("房间ID和玩家ID不能为空");
|
||||
|
||||
var room = _redisService.GetChessRoom(roomId);
|
||||
if (room == null)
|
||||
return this.ToFailResult<string>("房间不存在");
|
||||
if (room.state == 2)
|
||||
return this.ToFailResult<string>("游戏进行中不能直接踢人,请先结束对局");
|
||||
if (room.users?.Any(u => u.userId == userId) != true)
|
||||
return this.ToFailResult<string>("玩家不在该房间中");
|
||||
|
||||
await _capPublisher.PublishAsync(TopicKeys.后台踢出象棋玩家, new AdminKickChessPlayerMessage
|
||||
{
|
||||
RoomId = roomId,
|
||||
UserId = userId,
|
||||
Reason = "被管理员移出房间"
|
||||
});
|
||||
|
||||
return this.ToResult("踢人指令已提交");
|
||||
}
|
||||
|
||||
[HttpGet("GetChessRoomStatistics")]
|
||||
public async Task<BaseResponse<ChessRoomStatistics>> GetChessRoomStatistics()
|
||||
{
|
||||
var rooms = _redisService.GetAllChessRooms() ?? new List<ChessGameRoom>();
|
||||
|
||||
var stats = new ChessRoomStatistics
|
||||
{
|
||||
TotalRooms = rooms.Count,
|
||||
EmptyRooms = rooms.Count(r => r.state == 0 &&
|
||||
(r.users == null || !r.users.Any(u => u != null && !u.IsLeave)) &&
|
||||
r.robotCount == 0),
|
||||
WaitingRooms = rooms.Count(r => r.state == 1),
|
||||
PlayingRooms = rooms.Count(r => r.state == 2),
|
||||
FinishedRooms = rooms.Count(r => r.state == 3),
|
||||
TotalPlayers = rooms.SelectMany(r => r.users ?? new List<RedisChessGameUser>()).Count(),
|
||||
BlackSidePlayers = rooms.SelectMany(r => r.users ?? new List<RedisChessGameUser>()).Count(u => u.type == 1),
|
||||
RedSidePlayers = rooms.SelectMany(r => r.users ?? new List<RedisChessGameUser>()).Count(u => u.type == 2)
|
||||
};
|
||||
|
||||
stats.EmptyRoomRate = stats.TotalRooms == 0
|
||||
? 0
|
||||
: Math.Round(stats.EmptyRooms * 100.0 / stats.TotalRooms, 2);
|
||||
|
||||
return this.ToResult(stats);
|
||||
}
|
||||
|
||||
[HttpGet("DeleteChessRoom")]
|
||||
public async Task<BaseResponse<string>> DeleteChessRoom(string roomId)
|
||||
{
|
||||
var room = _redisService.GetChessRoom(roomId);
|
||||
if (room == null)
|
||||
return this.ToFailResult<string>("房间不存在");
|
||||
|
||||
_redisService.RemoveChessRoom(roomId);
|
||||
|
||||
return this.ToResult("已删除房间 " + roomId);
|
||||
}
|
||||
|
||||
[HttpGet("AddChessRobot")]
|
||||
public async Task<BaseResponse<string>> AddChessRobot(string roomId, int count)
|
||||
{
|
||||
var room = _redisService.GetChessRoom(roomId);
|
||||
if (room == null)
|
||||
return this.ToFailResult<string>("房间不存在");
|
||||
|
||||
var realPlayers = room.users?.Where(u => u != null && !u.IsLeave).ToList();
|
||||
if (realPlayers != null && realPlayers.Count > 0)
|
||||
return this.ToFailResult<string>("房间有真人玩家,不能修改机器人数量");
|
||||
|
||||
room.robotCount += count;
|
||||
ApplyRobotStateRule(room);
|
||||
_redisService.SetChessRoom(room);
|
||||
|
||||
return this.ToResult("添加成功");
|
||||
}
|
||||
|
||||
[HttpGet("SetChessRobot")]
|
||||
public async Task<BaseResponse<string>> SetChessRobot(string roomId, int count)
|
||||
{
|
||||
var room = _redisService.GetChessRoom(roomId);
|
||||
if (room == null)
|
||||
return this.ToFailResult<string>("房间不存在");
|
||||
|
||||
var realPlayers = room.users?.Where(u => u != null && !u.IsLeave).ToList();
|
||||
if (realPlayers != null && realPlayers.Count > 0)
|
||||
return this.ToFailResult<string>("房间有真人玩家,不能修改机器人数量");
|
||||
|
||||
room.robotCount = Math.Clamp(count, 0, 2);
|
||||
ApplyRobotStateRule(room);
|
||||
_redisService.SetChessRoom(room);
|
||||
|
||||
return this.ToResult("设置成功");
|
||||
}
|
||||
|
||||
[HttpGet("GetRoomDetail")]
|
||||
public async Task<BaseResponse<RedisGameRoom>> GetRoomDetail(string roomId)
|
||||
{
|
||||
var room = _redisService.GetGameRoom(roomId);
|
||||
if (room == null)
|
||||
return this.ToFailResult<RedisGameRoom>("房间不存在");
|
||||
|
||||
return this.ToResult(room);
|
||||
}
|
||||
|
||||
[HttpGet("GetUserRoomRealtime")]
|
||||
[ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)]
|
||||
public async Task<BaseResponse<UserRoomRealtimeDto>> GetUserRoomRealtime(string userId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(userId))
|
||||
return this.ToFailResult<UserRoomRealtimeDto>("用户ID不能为空");
|
||||
|
||||
var dto = new UserRoomRealtimeDto { Found = true };
|
||||
|
||||
var user = await _userMongo.FindByIdAsync(userId);
|
||||
var factor = user?.PlayerFactor ?? new PlayerFactor();
|
||||
|
||||
dto.Gold = user?.Assets?.Gold ?? 0;
|
||||
dto.TotalRechargeGold = user?.Static?.TotalRechargeGold ?? 0;
|
||||
|
||||
RedisGameRoom? matchedRoom = null;
|
||||
RedisGamePlayer? matchedPlayer = null;
|
||||
var session = _redisService.GetRedisUserCId(userId);
|
||||
if (!string.IsNullOrWhiteSpace(session?.RoomId))
|
||||
{
|
||||
matchedRoom = _redisService.GetGameRoom(session.RoomId);
|
||||
matchedPlayer = matchedRoom?.Players?
|
||||
.FirstOrDefault(p => p != null && !p.IsBot && p.UserId == userId);
|
||||
}
|
||||
|
||||
if (matchedPlayer == null)
|
||||
{
|
||||
var fallback = _redisService.GetGameRoomAllList()
|
||||
.Where(r => r != null && r.Players != null)
|
||||
.SelectMany(r => r.Players.Select(p => new { Room = r, Player = p }))
|
||||
.FirstOrDefault(x => x.Player != null && !x.Player.IsBot && x.Player.UserId == userId);
|
||||
matchedRoom = fallback?.Room;
|
||||
matchedPlayer = fallback?.Player;
|
||||
}
|
||||
|
||||
if (matchedRoom != null && matchedPlayer != null)
|
||||
{
|
||||
var room = matchedRoom;
|
||||
var player = matchedPlayer;
|
||||
dto.InRoom = true;
|
||||
dto.RoomId = room.RoomId;
|
||||
dto.RoomName = room.RoomName;
|
||||
dto.GameType = (int)room.GameType;
|
||||
dto.GameTypeName = room.GameType.ToString();
|
||||
dto.RoomLevel = room.RoomLevel;
|
||||
dto.RoomPartition = room.RoomPartition;
|
||||
dto.RoomNumber = room.RoomNumber;
|
||||
dto.SystemFactor = room.SystemFactor;
|
||||
dto.CurrentOnlinePlayerCount = room.CurrentOnlinePlayerCount;
|
||||
dto.RobotCount = room.RobotCount;
|
||||
dto.Position = player.Position;
|
||||
dto.UserName = player.UserName;
|
||||
dto.UserNumber = player.UserNumber;
|
||||
dto.Gold = player.Gold;
|
||||
dto.MaxGold = player.MaxGold;
|
||||
dto.BeginCoin = player.BeginCoin;
|
||||
dto.CannonMultiple = player.CannonMultiple;
|
||||
dto.CannonId = player.CannonId;
|
||||
dto.State = player.State;
|
||||
dto.IsAfk = player.IsAfk;
|
||||
dto.TotalRechargeGold = player.TotalRechargeGold;
|
||||
|
||||
var delta = player.Gold - player.BeginCoin;
|
||||
dto.WinGold = delta > 0 ? delta : 0;
|
||||
dto.LoseGold = delta < 0 ? -delta : 0;
|
||||
|
||||
var realtimeFactor = player.PlayerFactor ?? new PlayerFactor();
|
||||
if (realtimeFactor.ViolateValue > 0)
|
||||
factor.ViolateValue = realtimeFactor.ViolateValue;
|
||||
}
|
||||
|
||||
dto.IsBlack = factor.IsBlack;
|
||||
dto.IsIgnore = factor.IsIgnore;
|
||||
dto.ViolateValue = factor.ViolateValue;
|
||||
dto.AdditionalFactor = factor.AdditionalFactor;
|
||||
dto.MissRate = factor.MissRate;
|
||||
dto.CompensateQuota = factor.CompensateQuota;
|
||||
dto.CompensateStartGold = factor.CompensateStartGold;
|
||||
dto.CompensateTargetGold = factor.CompensateTargetGold;
|
||||
if (factor.CompensateQuota <= 0)
|
||||
dto.CompensateRemainingQuota = 0;
|
||||
else
|
||||
dto.CompensateRemainingQuota = factor.CompensateTargetGold > 0
|
||||
? Math.Max(0, factor.CompensateTargetGold - dto.Gold)
|
||||
: Math.Max(0, factor.CompensateQuota);
|
||||
dto.PenaltyQuota = factor.PenaltyQuota;
|
||||
dto.StartingBonus = factor.StartingBonus;
|
||||
dto.CompensateRateBoss = factor.CompensateRate?.BossRate ?? 0;
|
||||
dto.CompensateRateGold = factor.CompensateRate?.GoldRate ?? 0;
|
||||
dto.CompensateRateSpecial = factor.CompensateRate?.SpecialRate ?? 0;
|
||||
dto.CompensateRateOrdinary = factor.CompensateRate?.Ordinary ?? 0;
|
||||
dto.PenaltyRateBoss = factor.PenaltyRate?.BossRate ?? 0;
|
||||
dto.PenaltyRateGold = factor.PenaltyRate?.GoldRate ?? 0;
|
||||
dto.PenaltyRateSpecial = factor.PenaltyRate?.SpecialRate ?? 0;
|
||||
dto.PenaltyRateOrdinary = factor.PenaltyRate?.Ordinary ?? 0;
|
||||
|
||||
return this.ToResult(dto);
|
||||
}
|
||||
|
||||
[HttpGet("AddRobot")]
|
||||
public async Task<BaseResponse<string>> AddRobot(string roomId, int count)
|
||||
{
|
||||
var room = _redisService.GetGameRoom(roomId);
|
||||
if (room == null)
|
||||
return this.ToFailResult<string>("房间不存在");
|
||||
|
||||
if (count < 0)
|
||||
return this.ToFailResult<string>("机器人增加数量不能小于0");
|
||||
|
||||
var realPlayerCount = room.Players?.Count(p => !p.IsBot) ?? room.CurrentOnlinePlayerCount;
|
||||
room.CurrentOnlinePlayerCount = realPlayerCount;
|
||||
room.RobotCount = Math.Clamp(room.RobotCount + count, 0, Math.Max(0, room.MaxPlayerCount - realPlayerCount));
|
||||
if (room.Players != null)
|
||||
{
|
||||
var excessBots = room.Players.Where(p => p.IsBot).Skip(room.RobotCount).ToList();
|
||||
foreach (var bot in excessBots)
|
||||
{
|
||||
_redisService.ClearBulletIds(roomId, bot.UserId);
|
||||
room.Players.Remove(bot);
|
||||
}
|
||||
room.RobotCount = Math.Max(room.RobotCount, room.Players.Count(p => p.IsBot));
|
||||
}
|
||||
_redisService.SetGameRoom(room);
|
||||
|
||||
return this.ToResult($"已为房间 {roomId} 添加 {count} 个机器人");
|
||||
}
|
||||
|
||||
[HttpGet("SetRobotCount")]
|
||||
public async Task<BaseResponse<string>> SetRobotCount(string roomId, int count)
|
||||
{
|
||||
var room = _redisService.GetGameRoom(roomId);
|
||||
if (room == null)
|
||||
return this.ToFailResult<string>("房间不存在");
|
||||
|
||||
if (count < 0)
|
||||
return this.ToFailResult<string>("机器人数量不能小于0");
|
||||
|
||||
var realPlayerCount = room.Players?.Count(p => !p.IsBot) ?? room.CurrentOnlinePlayerCount;
|
||||
room.CurrentOnlinePlayerCount = realPlayerCount;
|
||||
room.RobotCount = Math.Clamp(count, 0, Math.Max(0, room.MaxPlayerCount - realPlayerCount));
|
||||
if (room.Players != null)
|
||||
{
|
||||
var excessBots = room.Players.Where(p => p.IsBot).Skip(room.RobotCount).ToList();
|
||||
foreach (var bot in excessBots)
|
||||
{
|
||||
_redisService.ClearBulletIds(roomId, bot.UserId);
|
||||
room.Players.Remove(bot);
|
||||
}
|
||||
room.RobotCount = Math.Max(room.RobotCount, room.Players.Count(p => p.IsBot));
|
||||
}
|
||||
_redisService.SetGameRoom(room);
|
||||
|
||||
return this.ToResult($"已将房间 {roomId} 的机器人数量设置为 {count}");
|
||||
}
|
||||
|
||||
[HttpGet("RegenerateAllBotIds")]
|
||||
public async Task<BaseResponse<string>> RegenerateAllBotIds()
|
||||
{
|
||||
var rooms = GetAllRoomsFromRedis()?.ToList() ?? new List<RedisGameRoom>();
|
||||
int totalBots = 0;
|
||||
|
||||
foreach (var room in rooms)
|
||||
{
|
||||
if (room?.Players == null || room.Players.Count == 0)
|
||||
continue;
|
||||
|
||||
var bots = room.Players.Where(p => p.IsBot).ToList();
|
||||
if (bots.Count == 0)
|
||||
continue;
|
||||
|
||||
foreach (var bot in bots)
|
||||
{
|
||||
var oldUserId = bot.UserId;
|
||||
bot.UserId = Guid.NewGuid().ToString("N");
|
||||
bot.UserNumber = Common.Utils.RandomHelper.RandomNumbers(8);
|
||||
totalBots++;
|
||||
}
|
||||
|
||||
_redisService.SetGameRoom(room);
|
||||
}
|
||||
|
||||
|
||||
return this.ToResult($"已刷新 {totalBots} 个机器人编号(涉及 {rooms.Count(r => r?.Players?.Any(p => p.IsBot) == true)} 个房间)");
|
||||
}
|
||||
|
||||
[HttpGet("ClearRoom")]
|
||||
public async Task<BaseResponse<string>> ClearRoom(string roomId)
|
||||
{
|
||||
var room = _redisService.GetGameRoom(roomId);
|
||||
if (room == null)
|
||||
return this.ToFailResult<string>("房间不存在");
|
||||
|
||||
int success = 0, fail = 0;
|
||||
|
||||
foreach (var player in room.Players.Where(p => !p.IsBot).ToList())
|
||||
{
|
||||
try
|
||||
{
|
||||
await _capPublisher.PublishAsync(TopicKeys.游戏金币结算, new
|
||||
{
|
||||
UserId = player.UserId,
|
||||
BeforeGold = player.BeginCoin,
|
||||
AfterGold = player.Gold,
|
||||
Remark = "管理员清空房间结算",
|
||||
RoomName = room.RoomName ?? "",
|
||||
RoomInfo = room.RoomId ?? ""
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "管理员清空房间 CAP 结算失败 roomId={RoomId} userId={UserId}", roomId, player.UserId);
|
||||
}
|
||||
|
||||
try { _redisService.ClearBulletIds(roomId, player.UserId); }
|
||||
catch (Exception ex) { _logger.LogError(ex, "管理员清空房间 清理子弹失败 roomId={RoomId} userId={UserId}", roomId, player.UserId); }
|
||||
|
||||
try
|
||||
{
|
||||
var userRedis = _redisService.GetRedisUserCId(player.UserId);
|
||||
if (userRedis != null && userRedis.RoomId == roomId)
|
||||
{
|
||||
userRedis.RoomId = "";
|
||||
userRedis.CId = "";
|
||||
_redisService.SetRedisUserCId(userRedis);
|
||||
}
|
||||
success++;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "管理员清空房间 清理userRedis失败 roomId={RoomId} userId={UserId}", roomId, player.UserId);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var player in room.Players.Where(p => p.IsBot).ToList())
|
||||
{
|
||||
try { _redisService.ClearBulletIds(roomId, player.UserId); }
|
||||
catch { }
|
||||
}
|
||||
|
||||
var robotCount = room.RobotCount;
|
||||
room.Players.Clear();
|
||||
room.CurrentOnlinePlayerCount = 0;
|
||||
room.RobotCount = Math.Clamp(robotCount, 0, room.MaxPlayerCount);
|
||||
_redisService.SetGameRoom(room);
|
||||
|
||||
var msg = fail > 0
|
||||
? $"已清空房间 {roomId}:成功 {success} 人,失败 {fail} 人"
|
||||
: $"已清空房间 {roomId} 的所有玩家({success} 人)";
|
||||
return this.ToResult(msg);
|
||||
}
|
||||
|
||||
[HttpGet("GetRoomStatistics")]
|
||||
public async Task<BaseResponse<RoomStatistics>> GetRoomStatistics()
|
||||
{
|
||||
var rooms = GetAllRoomsFromRedis();
|
||||
if (rooms == null || rooms.Count() == 0)
|
||||
{
|
||||
return this.ToResult(new RoomStatistics());
|
||||
}
|
||||
var stats = new RoomStatistics
|
||||
{
|
||||
TotalRooms = rooms?.Count()?? 0,
|
||||
TotalPlayers = rooms?.Where(_=>_!=null).Sum(r => r.CurrentOnlinePlayerCount) ?? 0,
|
||||
TotalRobots = rooms?.Where(_ => _ != null).Sum(r => r.RobotCount) ?? 0
|
||||
};
|
||||
|
||||
if (rooms != null && rooms.Count() > 0)
|
||||
{
|
||||
stats.RoomsByLevel = rooms.Where(_ => _ != null).GroupBy(r => r.RoomLevel)
|
||||
.ToDictionary(g => g.Key, g => g.Count());
|
||||
|
||||
stats.RoomsByType = rooms.Where(_ => _ != null).GroupBy(r => r.GameType.ToString())
|
||||
.ToDictionary(g => g.Key, g => g.Count());
|
||||
}
|
||||
|
||||
return this.ToResult(stats);
|
||||
}
|
||||
|
||||
[HttpGet("RepairChessRooms")]
|
||||
public async Task<BaseResponse<string>> RepairChessRooms()
|
||||
{
|
||||
var rooms = _redisService.GetAllChessRooms();
|
||||
if (rooms == null || rooms.Count == 0)
|
||||
return this.ToFailResult<string>("没有象棋房间");
|
||||
|
||||
int fixedCount = 0;
|
||||
foreach (var room in rooms)
|
||||
{
|
||||
if (room.users != null && room.users.Any(u => !u.IsLeave))
|
||||
continue;
|
||||
|
||||
ApplyRobotStateRule(room);
|
||||
_redisService.SetChessRoom(room);
|
||||
fixedCount++;
|
||||
}
|
||||
|
||||
return this.ToResult($"已修复 {fixedCount} 个象棋房间");
|
||||
}
|
||||
|
||||
[HttpGet("InitRooms")]
|
||||
public async Task<BaseResponse<string>> InitRooms()
|
||||
{
|
||||
_redisService.RemoveGameRoomAll();
|
||||
_redisService.ClearAllChessRooms();
|
||||
await _gameConfigService.InitRoom();
|
||||
|
||||
var config = await _gameConfigService.Get();
|
||||
var chessRoomConfigs = config?.RoomConfigs.Where(c => c.GameType == GameTypeEnum.中国象棋).ToList();
|
||||
if (chessRoomConfigs != null)
|
||||
{
|
||||
foreach (var roomConfig in chessRoomConfigs)
|
||||
{
|
||||
for (int y = 1; y <= roomConfig.RoomPartitionNum; y++)
|
||||
{
|
||||
for (int i = 0; i < roomConfig.RoomCount; i++)
|
||||
{
|
||||
var room = new ChessGameRoom
|
||||
{
|
||||
RoomId = $"{(int)GameTypeEnum.中国象棋}{y}{i.ToString("00")}",
|
||||
CreateTime = DateTime.Now,
|
||||
createUserId = "",
|
||||
gameType = 0,
|
||||
gold = roomConfig.RoomGoldLimit,
|
||||
Id = Guid.NewGuid().ToString(),
|
||||
users = new List<RedisChessGameUser>(),
|
||||
EndGameRequests = new Dictionary<string, long>(),
|
||||
LastActiveTime = 0,
|
||||
TimeoutSeconds = 60,
|
||||
robotCount = 0
|
||||
};
|
||||
ApplyRobotStateRule(room);
|
||||
_redisService.SetChessRoom(room);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this.ToResult("房间初始化成功");
|
||||
}
|
||||
|
||||
[HttpGet("ReInitChessRooms")]
|
||||
public async Task<BaseResponse<string>> ReInitChessRooms()
|
||||
{
|
||||
var config = await _gameConfigService.Get();
|
||||
var chessConfig = config?.ChessConfig;
|
||||
if (chessConfig == null)
|
||||
return this.ToFailResult<string>("象棋配置不存在");
|
||||
|
||||
_redisService.ClearAllChessRooms();
|
||||
|
||||
var chessRoomConfigs = config.RoomConfigs.Where(c => c.GameType == GameTypeEnum.中国象棋).ToList();
|
||||
if (chessRoomConfigs.Count == 0)
|
||||
return this.ToFailResult<string>("未找到象棋房间配置");
|
||||
|
||||
int count = 0;
|
||||
foreach (var roomConfig in chessRoomConfigs)
|
||||
{
|
||||
for (int y = 1; y <= roomConfig.RoomPartitionNum; y++)
|
||||
{
|
||||
for (int i = 0; i < roomConfig.RoomCount; i++)
|
||||
{
|
||||
var room = new ChessGameRoom
|
||||
{
|
||||
RoomId = $"{(int)GameTypeEnum.中国象棋}{y}{i.ToString("00")}",
|
||||
CreateTime = DateTime.Now,
|
||||
createUserId = "",
|
||||
gameType = 0,
|
||||
gold = roomConfig.RoomGoldLimit,
|
||||
Id = Guid.NewGuid().ToString(),
|
||||
users = new List<RedisChessGameUser>(),
|
||||
EndGameRequests = new Dictionary<string, long>(),
|
||||
LastActiveTime = 0,
|
||||
TimeoutSeconds = 60,
|
||||
robotCount = 0
|
||||
};
|
||||
ApplyRobotStateRule(room);
|
||||
_redisService.SetChessRoom(room);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this.ToResult($"象棋房间已重新初始化,共 {count} 个房间");
|
||||
}
|
||||
|
||||
private IEnumerable<RedisGameRoom> GetAllRoomsFromRedis()
|
||||
{
|
||||
|
||||
return _redisService.GetGameRoomAllList();
|
||||
}
|
||||
|
||||
private static void ApplyRobotStateRule(ChessGameRoom room)
|
||||
{
|
||||
var userCount = room.users?.Count ?? 0;
|
||||
var totalCount = userCount + room.robotCount;
|
||||
|
||||
if (totalCount == 0)
|
||||
{
|
||||
room.state = 0;
|
||||
room.password = "";
|
||||
}
|
||||
else if (totalCount == 1)
|
||||
{
|
||||
room.state = 1;
|
||||
if (string.IsNullOrEmpty(room.password))
|
||||
room.password = Random.Shared.Next(100000, 999999).ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
room.state = 2;
|
||||
if (string.IsNullOrEmpty(room.password))
|
||||
room.password = Random.Shared.Next(100000, 999999).ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class RoomStatistics
|
||||
{
|
||||
public int TotalRooms { get; set; } = 0;
|
||||
|
||||
public int TotalPlayers { get; set; } = 0;
|
||||
|
||||
public int TotalRobots { get; set; } = 0;
|
||||
|
||||
public Dictionary<int, int> RoomsByLevel { get; set; } = new();
|
||||
|
||||
public Dictionary<string, int> RoomsByType { get; set; } = new();
|
||||
}
|
||||
|
||||
public class ChessRoomStatistics
|
||||
{
|
||||
public int TotalRooms { get; set; } = 0;
|
||||
|
||||
public int EmptyRooms { get; set; } = 0;
|
||||
|
||||
public double EmptyRoomRate { get; set; } = 0;
|
||||
|
||||
public int WaitingRooms { get; set; } = 0;
|
||||
|
||||
public int PlayingRooms { get; set; } = 0;
|
||||
|
||||
public int FinishedRooms { get; set; } = 0;
|
||||
|
||||
public int TotalPlayers { get; set; } = 0;
|
||||
|
||||
public int BlackSidePlayers { get; set; } = 0;
|
||||
|
||||
public int RedSidePlayers { get; set; } = 0;
|
||||
}
|
||||
|
||||
public class UserRoomRealtimeDto
|
||||
{
|
||||
public bool Found { get; set; } = false;
|
||||
|
||||
public bool InRoom { get; set; } = false;
|
||||
|
||||
public long WinGold { get; set; } = 0;
|
||||
|
||||
public long LoseGold { get; set; } = 0;
|
||||
|
||||
public string RoomId { get; set; } = string.Empty;
|
||||
|
||||
public string RoomName { get; set; } = string.Empty;
|
||||
|
||||
public int GameType { get; set; }
|
||||
|
||||
public string GameTypeName { get; set; } = string.Empty;
|
||||
|
||||
public int RoomLevel { get; set; }
|
||||
|
||||
public int RoomPartition { get; set; }
|
||||
|
||||
public int RoomNumber { get; set; }
|
||||
|
||||
public double SystemFactor { get; set; } = 1.0;
|
||||
|
||||
public int CurrentOnlinePlayerCount { get; set; }
|
||||
|
||||
public int RobotCount { get; set; }
|
||||
|
||||
public int Position { get; set; }
|
||||
|
||||
public string UserName { get; set; } = string.Empty;
|
||||
|
||||
public string UserNumber { get; set; } = string.Empty;
|
||||
|
||||
public long Gold { get; set; }
|
||||
|
||||
public long BeginCoin { get; set; }
|
||||
|
||||
public long MaxGold { get; set; }
|
||||
|
||||
public int CannonMultiple { get; set; }
|
||||
|
||||
public string CannonId { get; set; } = string.Empty;
|
||||
|
||||
public int State { get; set; }
|
||||
|
||||
public bool IsAfk { get; set; }
|
||||
|
||||
public long TotalRechargeGold { get; set; }
|
||||
|
||||
public bool IsBlack { get; set; }
|
||||
|
||||
public bool IsIgnore { get; set; }
|
||||
|
||||
public double ViolateValue { get; set; }
|
||||
|
||||
public double AdditionalFactor { get; set; }
|
||||
|
||||
public double MissRate { get; set; }
|
||||
|
||||
public int CompensateQuota { get; set; }
|
||||
|
||||
public long CompensateStartGold { get; set; }
|
||||
|
||||
public long CompensateTargetGold { get; set; }
|
||||
|
||||
public long CompensateRemainingQuota { get; set; }
|
||||
|
||||
public int PenaltyQuota { get; set; }
|
||||
|
||||
public long StartingBonus { get; set; }
|
||||
|
||||
public double CompensateRateBoss { get; set; }
|
||||
|
||||
public double CompensateRateGold { get; set; }
|
||||
|
||||
public double CompensateRateSpecial { get; set; }
|
||||
|
||||
public double CompensateRateOrdinary { get; set; }
|
||||
|
||||
public double PenaltyRateBoss { get; set; }
|
||||
|
||||
public double PenaltyRateGold { get; set; }
|
||||
|
||||
public double PenaltyRateSpecial { get; set; }
|
||||
|
||||
public double PenaltyRateOrdinary { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
using Common;
|
||||
using CommonDomain;
|
||||
using CommonInfrastructure;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using MongoDB.Driver;
|
||||
using MongoRepository;
|
||||
|
||||
namespace AdminApi.GameConfigs
|
||||
{
|
||||
[ApiController, Route("adminapi/[controller]")]
|
||||
public class BotConfigController : BaseController
|
||||
{
|
||||
private readonly IMongoBaseRepository<BotProfileEntity> _botRepo;
|
||||
|
||||
public BotConfigController(IMongoBaseRepository<BotProfileEntity> botRepo)
|
||||
{
|
||||
_botRepo = botRepo;
|
||||
}
|
||||
|
||||
[HttpGet("GetAll")]
|
||||
public async Task<BaseResponse<List<BotProfileEntity>>> GetAll()
|
||||
{
|
||||
var list = await _botRepo.FilterByAsync(_ => true);
|
||||
return this.ToResult(list.ToList());
|
||||
}
|
||||
|
||||
[HttpPost("Add")]
|
||||
public async Task<BaseResponse<string>> Add(BotProfileEntity entity)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(entity.Name))
|
||||
return this.ToFailResult<string>("名称不能为空");
|
||||
|
||||
await _botRepo.InsertOneAsync(entity);
|
||||
return this.ToResult("添加成功");
|
||||
}
|
||||
|
||||
[HttpPost("Update")]
|
||||
public async Task<BaseResponse<string>> Update(BotProfileEntity entity)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(entity.Id))
|
||||
return this.ToFailResult<string>("缺少Id");
|
||||
|
||||
if (string.IsNullOrWhiteSpace(entity.Name))
|
||||
return this.ToFailResult<string>("名称不能为空");
|
||||
|
||||
var update = Builders<BotProfileEntity>.Update
|
||||
.Set(x => x.Name, entity.Name)
|
||||
.Set(x => x.Avatar, entity.Avatar ?? string.Empty);
|
||||
await _botRepo.UpdateByIdAsync(entity.Id, update);
|
||||
return this.ToResult("更新成功");
|
||||
}
|
||||
|
||||
[HttpPost("Delete")]
|
||||
public async Task<BaseResponse<string>> Delete([FromQuery] string id)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(id))
|
||||
return this.ToFailResult<string>("缺少Id");
|
||||
|
||||
await _botRepo.DeleteByIdAsync(id);
|
||||
return this.ToResult("删除成功");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
using System;
|
||||
using Common;
|
||||
using CommonDomain;
|
||||
using CommonInfrastructure;
|
||||
using DotNetCore.CAP;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using RedisService;
|
||||
|
||||
namespace AdminApi.GameConfigs
|
||||
{
|
||||
[ApiController, Route("adminapi/Admin/[controller]")]
|
||||
[AllowAnonymous]
|
||||
public class TempSpawnFishController : BaseController
|
||||
{
|
||||
private readonly ICapPublisher _capPublisher;
|
||||
private readonly IRedisService _redisService;
|
||||
private readonly ILogger<TempSpawnFishController> _logger;
|
||||
|
||||
public TempSpawnFishController(ICapPublisher capPublisher, IRedisService redisService, ILogger<TempSpawnFishController> logger)
|
||||
{
|
||||
_capPublisher = capPublisher;
|
||||
_redisService = redisService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
[HttpGet("SpawnFish")]
|
||||
public async Task<BaseResponse<string>> SpawnFish(
|
||||
string roomId,
|
||||
string fishNumber,
|
||||
int count = 1,
|
||||
int? seconds = null,
|
||||
int? routeId = null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(roomId) || string.IsNullOrWhiteSpace(fishNumber))
|
||||
{
|
||||
|
||||
return new BaseResponse<string>
|
||||
{
|
||||
Code = CommonResponseCodeEnum.ParamError,
|
||||
Info = "roomId / fishNumber 不能为空"
|
||||
};
|
||||
}
|
||||
|
||||
var room = _redisService.GetGameRoom(roomId);
|
||||
if (room == null)
|
||||
{
|
||||
|
||||
return new BaseResponse<string>
|
||||
{
|
||||
Code = CommonResponseCodeEnum.ParamError,
|
||||
Info = $"房间 {roomId} 不存在或不在线"
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
count = Math.Clamp(count <= 0 ? 1 : count, 1, 2000);
|
||||
var effectiveSeconds = (seconds.HasValue && seconds.Value > 0) ? seconds.Value : 30;
|
||||
|
||||
var operationId = Guid.NewGuid().ToString("N");
|
||||
var publishedAt = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||||
var msg = new TempSpawnFishMessage
|
||||
{
|
||||
RoomId = roomId,
|
||||
FishNumber = fishNumber,
|
||||
Count = count,
|
||||
RouteId = routeId,
|
||||
Seconds = effectiveSeconds,
|
||||
OperationId = operationId,
|
||||
PublishedAtUnixMs = publishedAt
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
await _capPublisher.PublishAsync(TopicKeys.临时强制出鱼, msg);
|
||||
|
||||
return new BaseResponse<string>
|
||||
{
|
||||
Code = CommonResponseCodeEnum.Success,
|
||||
Info = "已推送临时出鱼指令(异步),operationId=" + operationId,
|
||||
Data = operationId
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex,
|
||||
"[TempSpawn] CAP发布失败 operationId={OperationId} roomId={RoomId} fishNumber={FishNumber} count={Count}",
|
||||
operationId, roomId, fishNumber, count);
|
||||
return new BaseResponse<string>
|
||||
{
|
||||
Code = CommonResponseCodeEnum.Error,
|
||||
Info = "发布失败:" + ex.Message
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Common;
|
||||
using CommonInfrastructure;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using MongoRepository;
|
||||
using UserDomain;
|
||||
using UserDomain.Entity;
|
||||
using UserDomain.Param;
|
||||
|
||||
namespace AdminApi.LoginLogs
|
||||
{
|
||||
[ApiController, Route("adminapi/[controller]")]
|
||||
public class UserLoginLogController : BaseController
|
||||
{
|
||||
private readonly IMongoBaseRepository<UserLoginLog> _logRepo;
|
||||
|
||||
public UserLoginLogController(IMongoBaseRepository<UserLoginLog> logRepo)
|
||||
{
|
||||
_logRepo = logRepo;
|
||||
}
|
||||
|
||||
[HttpPost("List")]
|
||||
public async Task<PageResponse<UserLoginLogDto>> GetList([FromBody] UserLoginLogQueryParam param)
|
||||
{
|
||||
var result = await _logRepo.QueryPageAsync(
|
||||
filterExpression: x =>
|
||||
(string.IsNullOrEmpty(param.UserId) || x.UserId == param.UserId) &&
|
||||
(string.IsNullOrEmpty(param.UserNumber) || x.UserNumber == param.UserNumber) &&
|
||||
(string.IsNullOrEmpty(param.UserName) || (x.UserName != null && x.UserName.Contains(param.UserName))) &&
|
||||
(!param.LoginType.HasValue || x.LoginType == param.LoginType) &&
|
||||
(!param.Status.HasValue || x.Status == param.Status) &&
|
||||
(!param.Platform.HasValue || x.Platform == param.Platform) &&
|
||||
(!param.StartTime.HasValue || x.LoginTime >= param.StartTime) &&
|
||||
(!param.EndTime.HasValue || x.LoginTime <= param.EndTime),
|
||||
orderExpression: x => x.LoginTime,
|
||||
desc: true,
|
||||
pageSize: param.PageSize,
|
||||
pageIndex: param.PageIndex
|
||||
);
|
||||
|
||||
var dtos = result.list.Select(MapToDto);
|
||||
return this.ToPageResult(dtos, (int)result.count);
|
||||
}
|
||||
|
||||
[HttpGet("Detail")]
|
||||
public async Task<BaseResponse<UserLoginLogDto>> GetDetail(string id)
|
||||
{
|
||||
var log = await _logRepo.FindOneAsync(x => x.Id == id);
|
||||
if (log == null)
|
||||
return this.ToFailResult<UserLoginLogDto>("日志不存在");
|
||||
|
||||
return this.ToResult(MapToDto(log));
|
||||
}
|
||||
|
||||
[HttpPost("Delete")]
|
||||
public async Task<BaseResponse<string>> Delete(string id)
|
||||
{
|
||||
var result = await _logRepo.DeleteByIdAsync(id);
|
||||
if (result.ok)
|
||||
return this.ToResult("删除成功");
|
||||
else
|
||||
return this.ToFailResult<string>("删除失败");
|
||||
}
|
||||
|
||||
[HttpPost("BatchDelete")]
|
||||
public async Task<BaseResponse<string>> BatchDelete([FromBody] List<string> ids)
|
||||
{
|
||||
int success = 0, fail = 0;
|
||||
foreach (var id in ids)
|
||||
{
|
||||
var result = await _logRepo.DeleteByIdAsync(id);
|
||||
if (result.ok)
|
||||
success++;
|
||||
else
|
||||
fail++;
|
||||
}
|
||||
if (fail == 0)
|
||||
return this.ToResult($"成功删除 {success} 条");
|
||||
return this.ToResult($"删除完成:成功 {success} 条,失败 {fail} 条");
|
||||
}
|
||||
|
||||
private static UserLoginLogDto MapToDto(UserLoginLog entity)
|
||||
{
|
||||
return new UserLoginLogDto
|
||||
{
|
||||
Id = entity.Id,
|
||||
UserId = entity.UserId ?? "",
|
||||
UserNumber = entity.UserNumber ?? "",
|
||||
UserName = entity.UserName ?? "",
|
||||
LoginType = entity.LoginType,
|
||||
Status = entity.Status,
|
||||
Ip = entity.Ip ?? "",
|
||||
IpLocation = entity.IpLocation ?? "",
|
||||
DeviceType = entity.DeviceType ?? "",
|
||||
DeviceId = entity.DeviceId ?? "",
|
||||
Platform = entity.Platform,
|
||||
ClientVersion = entity.ClientVersion ?? "",
|
||||
FailReason = entity.FailReason ?? "",
|
||||
LoginTime = entity.LoginTime
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class UserLoginLogDto
|
||||
{
|
||||
public string Id { get; set; }
|
||||
|
||||
public string UserId { get; set; }
|
||||
|
||||
public string UserNumber { get; set; }
|
||||
|
||||
public string UserName { get; set; }
|
||||
|
||||
public int LoginType { get; set; }
|
||||
|
||||
public int Status { get; set; }
|
||||
|
||||
public string Ip { get; set; }
|
||||
|
||||
public string IpLocation { get; set; }
|
||||
|
||||
public string DeviceType { get; set; }
|
||||
|
||||
public string DeviceId { get; set; }
|
||||
|
||||
public int Platform { get; set; }
|
||||
|
||||
public string ClientVersion { get; set; }
|
||||
|
||||
public string FailReason { get; set; }
|
||||
|
||||
public DateTime LoginTime { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,236 @@
|
||||
using AdminService.GameConfigs;
|
||||
using Common;
|
||||
using CommonDomain;
|
||||
using CommonDomain.BaseModel;
|
||||
using CommonInfrastructure;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace AdminApi.Lottery
|
||||
{
|
||||
[ApiController, Route("adminapi/[controller]")]
|
||||
public class LotteryController : BaseController
|
||||
{
|
||||
private readonly IActivityConfigService _activityConfigService;
|
||||
private readonly IGameConfigService _gameConfigService;
|
||||
|
||||
public LotteryController(IActivityConfigService activityConfigService, IGameConfigService gameConfigService)
|
||||
{
|
||||
_activityConfigService = activityConfigService;
|
||||
_gameConfigService = gameConfigService;
|
||||
}
|
||||
|
||||
[HttpGet("list")]
|
||||
public async Task<BaseResponse<List<LotteryActivityVm>>> List()
|
||||
{
|
||||
var config = await _activityConfigService.Get();
|
||||
if (config == null)
|
||||
return this.ToResult(new List<LotteryActivityVm>());
|
||||
|
||||
var lotto = config.LotteryActivityConfig;
|
||||
if (lotto == null || string.IsNullOrWhiteSpace(lotto.ActivityName))
|
||||
return this.ToResult(new List<LotteryActivityVm>());
|
||||
|
||||
LotteryPrizeProductHelper.Enrich(lotto, await _gameConfigService.Get());
|
||||
|
||||
var vm = new LotteryActivityVm
|
||||
{
|
||||
ActivityId = config.Id,
|
||||
ActivityName = lotto.ActivityName,
|
||||
IsOpen = lotto.IsOpen,
|
||||
StartTime = lotto.StartTime,
|
||||
EndTime = lotto.EndTime,
|
||||
CostsByPool = lotto.CostsByPool,
|
||||
UserMaxDraw = lotto.UserMaxDraw,
|
||||
Pools = lotto.Pools
|
||||
};
|
||||
|
||||
return this.ToResult(new List<LotteryActivityVm> { vm });
|
||||
}
|
||||
|
||||
[HttpGet("detail")]
|
||||
public async Task<BaseResponse<LotteryActivityVm>> Detail([FromQuery] string activityId)
|
||||
{
|
||||
var config = await _activityConfigService.Get();
|
||||
if (config == null)
|
||||
return this.ToFailResult<LotteryActivityVm>("活动配置不存在");
|
||||
|
||||
LotteryPrizeProductHelper.Enrich(config.LotteryActivityConfig, await _gameConfigService.Get());
|
||||
|
||||
var vm = new LotteryActivityVm
|
||||
{
|
||||
ActivityId = config.Id,
|
||||
ActivityName = config.LotteryActivityConfig.ActivityName,
|
||||
IsOpen = config.LotteryActivityConfig.IsOpen,
|
||||
StartTime = config.LotteryActivityConfig.StartTime,
|
||||
EndTime = config.LotteryActivityConfig.EndTime,
|
||||
CostsByPool = config.LotteryActivityConfig.CostsByPool,
|
||||
UserMaxDraw = config.LotteryActivityConfig.UserMaxDraw,
|
||||
Pools = config.LotteryActivityConfig.Pools
|
||||
};
|
||||
|
||||
return this.ToResult(vm);
|
||||
}
|
||||
|
||||
[HttpPost("save")]
|
||||
public async Task<BaseResponse<string>> Save([FromBody] LotteryActivityVm param)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(param.ActivityName))
|
||||
return this.ToFailResult<string>("活动名称不能为空");
|
||||
|
||||
var pools = param.Pools ?? new List<LotteryPoolLevel>();
|
||||
if (pools.SelectMany(pool => pool.PrizeList ?? new List<LotteryPrize>())
|
||||
.Any(prize => !string.IsNullOrWhiteSpace(prize.OtherId)))
|
||||
{
|
||||
var gameConfig = await _gameConfigService.Get();
|
||||
if (gameConfig == null)
|
||||
return this.ToFailResult<string>("道具配置不存在,无法补全奖项信息");
|
||||
EnrichPrizeProducts(pools, gameConfig);
|
||||
}
|
||||
|
||||
var activityConfig = await _activityConfigService.Get() ?? new ActivityConfig();
|
||||
var previousLottery = activityConfig.LotteryActivityConfig;
|
||||
var cycleChanged = previousLottery == null
|
||||
|| previousLottery.StartTime != param.StartTime
|
||||
|| previousLottery.EndTime != param.EndTime;
|
||||
|
||||
if (cycleChanged)
|
||||
{
|
||||
foreach (var prize in pools.SelectMany(pool => pool.PrizeList ?? new List<LotteryPrize>()))
|
||||
prize.DrawnCount = 0;
|
||||
}
|
||||
|
||||
var normalizedCostsByPool = new Dictionary<int, List<long>>();
|
||||
var poolNames = new Dictionary<int, string> { [1] = "炮台", [2] = "战神", [3] = "翅膀" };
|
||||
for (var poolType = 1; poolType <= 3; poolType++)
|
||||
{
|
||||
List<long>? poolCosts = null;
|
||||
param.CostsByPool?.TryGetValue(poolType, out poolCosts);
|
||||
var validPoolCosts = poolCosts?.Where(cost => cost > 0).ToList();
|
||||
if (validPoolCosts == null || validPoolCosts.Count == 0)
|
||||
return this.ToFailResult<string>($"请配置{poolNames[poolType]}奖池消耗金币");
|
||||
|
||||
normalizedCostsByPool[poolType] = validPoolCosts;
|
||||
}
|
||||
|
||||
activityConfig.LotteryActivityConfig = new LotteryActivityConfig
|
||||
{
|
||||
ActivityName = param.ActivityName,
|
||||
IsOpen = param.IsOpen,
|
||||
StartTime = param.StartTime,
|
||||
EndTime = param.EndTime,
|
||||
CostsByPool = normalizedCostsByPool,
|
||||
UserMaxDraw = param.UserMaxDraw,
|
||||
Pools = pools
|
||||
};
|
||||
|
||||
var res = await _activityConfigService.Update(activityConfig);
|
||||
if (res.isok)
|
||||
return this.ToResult("保存成功");
|
||||
else
|
||||
return this.ToFailResult<string>(res.msg);
|
||||
}
|
||||
|
||||
private static void EnrichPrizeProducts(List<LotteryPoolLevel> pools, GameConfig gameConfig)
|
||||
{
|
||||
LotteryPrizeProductHelper.Enrich(new LotteryActivityConfig { Pools = pools }, gameConfig);
|
||||
}
|
||||
|
||||
[HttpPost("toggle")]
|
||||
public async Task<BaseResponse<string>> Toggle([FromBody] LotteryToggleParam param)
|
||||
{
|
||||
var activityConfig = await _activityConfigService.Get();
|
||||
if (activityConfig == null)
|
||||
return this.ToFailResult<string>("活动配置不存在");
|
||||
|
||||
activityConfig.LotteryActivityConfig.IsOpen = param.IsOpen;
|
||||
|
||||
var res = await _activityConfigService.Update(activityConfig);
|
||||
if (res.isok)
|
||||
return this.ToResult(param.IsOpen ? "活动已开启" : "活动已关闭");
|
||||
else
|
||||
return this.ToFailResult<string>(res.msg);
|
||||
}
|
||||
|
||||
[HttpPost("initialize")]
|
||||
public async Task<BaseResponse<string>> Initialize()
|
||||
{
|
||||
var path = Path.Combine(AppContext.BaseDirectory, "Config", "lottery-init.json");
|
||||
if (!global::System.IO.File.Exists(path))
|
||||
return this.ToFailResult<string>("奖池初始化模板不存在");
|
||||
|
||||
LotteryActivityConfig? template;
|
||||
try
|
||||
{
|
||||
var json = await global::System.IO.File.ReadAllTextAsync(path);
|
||||
template = global::System.Text.Json.JsonSerializer.Deserialize<LotteryActivityConfig>(json,
|
||||
new global::System.Text.Json.JsonSerializerOptions { PropertyNameCaseInsensitive = true });
|
||||
}
|
||||
catch (global::System.Text.Json.JsonException)
|
||||
{
|
||||
return this.ToFailResult<string>("奖池初始化模板格式错误");
|
||||
}
|
||||
|
||||
var pools = template?.Pools;
|
||||
var prizes = pools?.SelectMany(pool => pool.PrizeList ?? new List<LotteryPrize>()).ToList();
|
||||
if (template == null || string.IsNullOrWhiteSpace(template.ActivityName)
|
||||
|| template.CostsByPool == null
|
||||
|| new[] { 1, 2, 3 }.Any(type => !template.CostsByPool.TryGetValue(type, out var costs) || costs == null || costs.Count == 0)
|
||||
|| pools == null || pools.Count == 0 || prizes == null || prizes.Count == 0
|
||||
|| new[] { 1, 2, 3 }.Any(type => !pools.Any(pool => pool.PoolType == type && pool.PrizeList?.Count > 0))
|
||||
|| prizes.Any(prize => string.IsNullOrWhiteSpace(prize.PrizeId))
|
||||
|| prizes.Select(prize => prize.PrizeId).Distinct().Count() != prizes.Count)
|
||||
return this.ToFailResult<string>("奖池初始化模板不完整,未修改现有配置");
|
||||
|
||||
template.IsOpen = false;
|
||||
foreach (var prize in prizes) prize.DrawnCount = 0;
|
||||
var config = await _activityConfigService.Get() ?? new ActivityConfig();
|
||||
config.LotteryActivityConfig = template;
|
||||
var result = await _activityConfigService.Update(config);
|
||||
return result.isok
|
||||
? this.ToResult("奖池配置已初始化,活动保持关闭")
|
||||
: this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
|
||||
[HttpDelete("delete")]
|
||||
public async Task<BaseResponse<string>> Delete()
|
||||
{
|
||||
var activityConfig = await _activityConfigService.Get();
|
||||
if (activityConfig == null)
|
||||
return this.ToFailResult<string>("活动配置不存在");
|
||||
|
||||
activityConfig.LotteryActivityConfig = new LotteryActivityConfig();
|
||||
|
||||
var res = await _activityConfigService.Update(activityConfig);
|
||||
if (res.isok)
|
||||
return this.ToResult("删除成功");
|
||||
else
|
||||
return this.ToFailResult<string>(res.msg);
|
||||
}
|
||||
}
|
||||
|
||||
public class LotteryActivityVm
|
||||
{
|
||||
public string ActivityId { get; set; } = string.Empty;
|
||||
|
||||
public string ActivityName { get; set; } = string.Empty;
|
||||
|
||||
public bool IsOpen { get; set; } = false;
|
||||
|
||||
public long StartTime { get; set; } = 0;
|
||||
|
||||
public long EndTime { get; set; } = 0;
|
||||
|
||||
public Dictionary<int, List<long>> CostsByPool { get; set; } = new Dictionary<int, List<long>>();
|
||||
|
||||
public int UserMaxDraw { get; set; } = 100;
|
||||
|
||||
public List<LotteryPoolLevel> Pools { get; set; } = new List<LotteryPoolLevel>();
|
||||
}
|
||||
|
||||
public class LotteryToggleParam
|
||||
{
|
||||
public string ActivityId { get; set; } = string.Empty;
|
||||
|
||||
public bool IsOpen { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
using Domain.Admin.Notices;
|
||||
using AdminService.Notices;
|
||||
using AdminService.Notices.Param;
|
||||
using Common;
|
||||
using CommonInfrastructure;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace AdminApi.Notices;
|
||||
|
||||
[ApiController, Route("adminapi/[controller]")]
|
||||
[Authorize]
|
||||
public class NoticeController : BaseController
|
||||
{
|
||||
private readonly INoticeService _noticeService;
|
||||
|
||||
public NoticeController(INoticeService noticeService)
|
||||
{
|
||||
_noticeService = noticeService;
|
||||
}
|
||||
|
||||
[HttpPost("add")]
|
||||
public async Task<BaseResponse<string>> AddNotice([FromBody] AddNoticeParam param)
|
||||
{
|
||||
var result = await _noticeService.Add(param);
|
||||
return result.isok
|
||||
? this.ToResult(result.msg)
|
||||
: this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
|
||||
[HttpPost("update")]
|
||||
public async Task<BaseResponse<string>> UpdateNotice([FromBody] UpdateNoticeParam param)
|
||||
{
|
||||
var result = await _noticeService.Update(param);
|
||||
return result.isok
|
||||
? this.ToResult(result.msg)
|
||||
: this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
|
||||
[HttpGet("delete")]
|
||||
public async Task<BaseResponse<string>> DeleteNotice([FromQuery] string id)
|
||||
{
|
||||
var result = await _noticeService.Delete(id);
|
||||
return result.isok
|
||||
? this.ToResult(result.msg)
|
||||
: this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
|
||||
[HttpPost("list")]
|
||||
public async Task<PageResponse<NoticeListItemDto>> GetNoticeList([FromBody] GetNoticeListParam param)
|
||||
{
|
||||
var (list, count) = await _noticeService.GetList(param);
|
||||
if (list == null)
|
||||
return this.ToPageResult(new List<NoticeListItemDto>(), 0);
|
||||
var dtos = list.Select(n => new NoticeListItemDto
|
||||
{
|
||||
Id = n.Id,
|
||||
Title = n.Title,
|
||||
Recommend = n.Recommend,
|
||||
TopImage = n.TopImage,
|
||||
CreateTime = n.CreateTime
|
||||
});
|
||||
return this.ToPageResult(dtos, (int)count);
|
||||
}
|
||||
|
||||
[HttpGet("get")]
|
||||
[AllowAnonymous]
|
||||
public async Task<BaseResponse<NoticeDto>> GetNotice([FromQuery] string id)
|
||||
{
|
||||
var notice = await _noticeService.Get(id);
|
||||
if (notice == null)
|
||||
{
|
||||
return this.ToFailResult<NoticeDto>("公告不存在");
|
||||
}
|
||||
|
||||
var dto = new NoticeDto
|
||||
{
|
||||
Id = notice.Id,
|
||||
Title = notice.Title,
|
||||
Content = notice.Content,
|
||||
Recommend = notice.Recommend,
|
||||
TopImage = notice.TopImage,
|
||||
ContentImages = notice.ContentImages,
|
||||
CreateTime = notice.CreateTime
|
||||
};
|
||||
return this.ToResult(dto);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
// See https://aka.ms/new-console-template for more information
|
||||
Console.WriteLine("Hello, World!");
|
||||
@@ -0,0 +1,330 @@
|
||||
using System.Linq;
|
||||
using AdminService.System;
|
||||
using AdminService.System.Param;
|
||||
using AdminService.System.Response;
|
||||
using Common;
|
||||
using CommonInfrastructure ;
|
||||
using Domain.Admin.Admin;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
|
||||
|
||||
namespace AdminApi.System
|
||||
{
|
||||
|
||||
[ApiController, Route("adminapi/[controller]")]
|
||||
[Authorize]
|
||||
public class SystemController : BaseController
|
||||
{
|
||||
private readonly ISystemService _systemService;
|
||||
|
||||
public SystemController(ISystemService systemService)
|
||||
{
|
||||
_systemService = systemService;
|
||||
}
|
||||
|
||||
|
||||
[HttpPost("user/add")]
|
||||
|
||||
public async Task<BaseResponse<string>> AddUser(AddUserParam param)
|
||||
{
|
||||
var result = await _systemService.AddUser(param);
|
||||
if (result.isok)
|
||||
return this.ToResult(result.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
[HttpPost("user/update")]
|
||||
|
||||
public async Task<BaseResponse<string>> UpdateUser(UpdateUserParam param)
|
||||
{
|
||||
var result = await _systemService.UpdateUser(param);
|
||||
if (result.isok)
|
||||
return this.ToResult(result.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
[HttpGet("user/update/icon")]
|
||||
|
||||
public async Task<BaseResponse<string>> UpdateUserIcon(string id, string icon)
|
||||
{
|
||||
var result = await _systemService.UpdateUserIcon(id,icon);
|
||||
if (result.isok)
|
||||
return this.ToResult(result.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
[HttpGet("user/update/state")]
|
||||
|
||||
public async Task<BaseResponse<string>> UpdateUserState(string id, int state)
|
||||
{
|
||||
var result = await _systemService.UpdateUserState(id, state);
|
||||
if (result.isok)
|
||||
return this.ToResult(result.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
[HttpGet("user/delete")]
|
||||
|
||||
public async Task<BaseResponse<string>> DeleteUser(string id)
|
||||
{
|
||||
var result = await _systemService.DeleteUser(id);
|
||||
if (result.isok)
|
||||
return this.ToResult(result.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
[HttpGet("user/login")]
|
||||
[AllowAnonymous]
|
||||
public async Task<BaseResponse<AdminUserToken>> UserLogin(string userName, string password)
|
||||
{
|
||||
var result = await _systemService.UserLogin(userName, password);
|
||||
if (result.isok)
|
||||
return this.ToResult(result.model);
|
||||
else
|
||||
return this.ToFailResult<AdminUserToken>(result.msg);
|
||||
}
|
||||
[HttpPost("user/list")]
|
||||
|
||||
public async Task<PageResponse<SystemUserDto>> GetUserList(GetUserListParam param)
|
||||
{
|
||||
var result = await _systemService.GetUserList(param);
|
||||
var dtos = result.list.Select(MapToSystemUserDto).ToList();
|
||||
return this.ToPageResult(dtos, (int)result.count);
|
||||
}
|
||||
|
||||
[HttpGet("user/getuser")]
|
||||
|
||||
public async Task<BaseResponse<SystemUserDto>> GetUser()
|
||||
{
|
||||
if (string.IsNullOrEmpty(loginUser.UserId))
|
||||
return this.ToFailResult<SystemUserDto>("用户未登录");
|
||||
var result = await _systemService.GetUser(loginUser.UserId);
|
||||
if (result == null)
|
||||
return this.ToFailResult<SystemUserDto>("用户不存在");
|
||||
return this.ToResult(MapToSystemUserDto(result));
|
||||
}
|
||||
|
||||
private static SystemUserDto MapToSystemUserDto(SystemUser entity)
|
||||
{
|
||||
if (entity == null) return new SystemUserDto();
|
||||
return new SystemUserDto
|
||||
{
|
||||
Id = entity.Id ?? "",
|
||||
UserName = entity.UserName ?? "",
|
||||
DisplayName = entity.DisplayName ?? "",
|
||||
Icon = entity.Icon ?? "",
|
||||
RegisterTime = entity.RegisterTime,
|
||||
State = entity.State,
|
||||
RoleId = entity.SystemRole?.Id ?? "",
|
||||
RoleName = entity.SystemRole?.Name ?? ""
|
||||
};
|
||||
}
|
||||
|
||||
[HttpGet("user/nav")]
|
||||
|
||||
public async Task<ListResponse<SystemNavigationResponse>> GetUserNav()
|
||||
{
|
||||
if (string.IsNullOrEmpty(loginUser.UserId))
|
||||
return this.ToListFailResult<SystemNavigationResponse>("用户未登录");
|
||||
var result = await _systemService.GetUserNav(this.loginUser);
|
||||
if (result == null)
|
||||
return this.ToListFailResult<SystemNavigationResponse>("获取菜单失败");
|
||||
return this.ToListResult(result);
|
||||
}
|
||||
|
||||
|
||||
|
||||
[HttpPost("role/add")]
|
||||
|
||||
public async Task<BaseResponse<string>> AddRole(AddRoleParam param)
|
||||
{
|
||||
var result = await _systemService.AddRole(param);
|
||||
if (result.isok)
|
||||
return this.ToResult(result.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
[HttpPost("role/update")]
|
||||
|
||||
public async Task<BaseResponse<string>> UpdateRole(UpdateRoleParam param)
|
||||
{
|
||||
var result = await _systemService.UpdateRole(param);
|
||||
if (result.isok)
|
||||
return this.ToResult(result.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
[HttpPost("role/updatenav")]
|
||||
|
||||
public async Task<BaseResponse<string>> UpdateRoleNav(UpdateRoleNavParam param)
|
||||
{
|
||||
var result = await _systemService.UpdateRoleNav(param);
|
||||
if (result.isok)
|
||||
return this.ToResult(result.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
|
||||
[HttpGet("role/delete")]
|
||||
|
||||
public async Task<BaseResponse<string>> DeleteRole(string id)
|
||||
{
|
||||
var result = await _systemService.DeleteRole(id);
|
||||
if (result.isok)
|
||||
return this.ToResult(result.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
|
||||
[HttpGet("role/list")]
|
||||
public async Task<ListResponse<SystemUserRoleDto>> GetRoleList(int page, int limit)
|
||||
{
|
||||
var result = await _systemService.GetRoleList();
|
||||
var dtos = result.Select(MapToSystemUserRoleDto).ToList();
|
||||
return this.ToListResult(dtos);
|
||||
}
|
||||
|
||||
private static SystemUserRoleDto MapToSystemUserRoleDto(SystemUserRole entity)
|
||||
{
|
||||
if (entity == null) return new SystemUserRoleDto();
|
||||
return new SystemUserRoleDto
|
||||
{
|
||||
Id = entity.Id ?? "",
|
||||
RoleName = entity.RoleName ?? "",
|
||||
Description = entity.Description ?? "",
|
||||
NavIds = entity.NavIds ?? new List<string>()
|
||||
};
|
||||
}
|
||||
[HttpGet("role/get")]
|
||||
|
||||
public async Task<BaseResponse<SystemUserRoleDto>> GetUserRole(string id)
|
||||
{
|
||||
var result = await _systemService.GetUserRole(id);
|
||||
return this.ToResult(MapToSystemUserRoleDto(result));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
[HttpPost("nav/add")]
|
||||
|
||||
public async Task<BaseResponse<string>> AddNav(AddNavParam param)
|
||||
{
|
||||
var result = await _systemService.AddNav(param);
|
||||
if (result.isok)
|
||||
return this.ToResult(result.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
[HttpPost("nav/update")]
|
||||
|
||||
public async Task<BaseResponse<string>> UpdateNav(UpdateNavParam param)
|
||||
{
|
||||
var result = await _systemService.UpdateNav(param);
|
||||
if (result.isok)
|
||||
return this.ToResult(result.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
|
||||
[HttpGet("nav/delete")]
|
||||
|
||||
public async Task<BaseResponse<string>> DeleteNav(string id)
|
||||
{
|
||||
var result = await _systemService.DeleteNav(id);
|
||||
if (result.isok)
|
||||
return this.ToResult(result.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
|
||||
|
||||
[HttpGet("nav/list")]
|
||||
|
||||
public async Task<ListResponse<SystemNavigationResponse>> GetNavList(string? id="")
|
||||
{
|
||||
var result = await _systemService.GetNavList(id);
|
||||
return this.ToListResult(result);
|
||||
}
|
||||
|
||||
|
||||
public class SystemUserDto
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string UserName { get; set; }
|
||||
public string DisplayName { get; set; }
|
||||
public string Icon { get; set; }
|
||||
public DateTime RegisterTime { get; set; }
|
||||
public int State { get; set; }
|
||||
public string RoleId { get; set; }
|
||||
public string RoleName { get; set; }
|
||||
}
|
||||
|
||||
public class SystemUserRoleDto
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string RoleName { get; set; }
|
||||
public string Description { get; set; }
|
||||
public List<string> NavIds { get; set; } = new List<string>();
|
||||
}
|
||||
|
||||
|
||||
|
||||
[HttpPost("dic/add")]
|
||||
|
||||
public async Task<BaseResponse<string>> AddDic(AddDicParam param)
|
||||
{
|
||||
var result = await _systemService.AddDic(param);
|
||||
if (result.isok)
|
||||
return this.ToResult(result.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
[HttpPost("dic/update")]
|
||||
|
||||
public async Task<BaseResponse<string>> UpdateDic(UpdateDicParam param)
|
||||
{
|
||||
var result = await _systemService.UpdateDic(param);
|
||||
if (result.isok)
|
||||
return this.ToResult(result.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
|
||||
[HttpGet("dic/delete")]
|
||||
|
||||
public async Task<BaseResponse<string>> DeleteDic(string id)
|
||||
{
|
||||
var result = await _systemService.DeleteDic(id);
|
||||
if (result.isok)
|
||||
return this.ToResult(result.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(result.msg);
|
||||
}
|
||||
|
||||
|
||||
[HttpGet("dic/list")]
|
||||
|
||||
public async Task<ListResponse<SystemDicResponse>> GetDicList(string id="")
|
||||
{
|
||||
var result = await _systemService.GetDicList(id);
|
||||
return this.ToListResult(result);
|
||||
}
|
||||
[HttpGet("dic/list/code")]
|
||||
|
||||
public async Task<ListResponse<SystemDicResponse>> GetDicListByCode(string code)
|
||||
{
|
||||
var result = await _systemService.GetDicListByCode(code);
|
||||
return this.ToListResult(result);
|
||||
}
|
||||
[HttpGet("dic/get/code")]
|
||||
|
||||
public async Task<BaseResponse<SystemDic>> GetDictByCode(string code)
|
||||
{
|
||||
var result = await _systemService.GetDictByCode(code);
|
||||
return this.ToResult(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
using AdminService.Tasks;
|
||||
using AdminService.Tasks.Param;
|
||||
using Common;
|
||||
using CommonDomain;
|
||||
using CommonInfrastructure;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace AdminApi.Task
|
||||
{
|
||||
[ApiController, Route("adminapi/[controller]")]
|
||||
[Authorize]
|
||||
public class TaskController : BaseController
|
||||
{
|
||||
private readonly ITaskService _taskService;
|
||||
|
||||
public TaskController(ITaskService taskService)
|
||||
{
|
||||
_taskService = taskService;
|
||||
}
|
||||
|
||||
[HttpGet("Init")]
|
||||
public async Task<string> Init()
|
||||
{
|
||||
return await _taskService.InitDefaultTasks();
|
||||
}
|
||||
|
||||
[HttpGet("DayTaskReward")]
|
||||
public async Task<string> DayTaskReward()
|
||||
{
|
||||
return await _taskService.DayTaskReward();
|
||||
}
|
||||
[HttpPost("AddTask")]
|
||||
public async Task<BaseResponse<string>> AddTask([FromBody] GameTask param)
|
||||
{
|
||||
var res = await _taskService.AddTask(param);
|
||||
if (res.isok)
|
||||
return this.ToResult(res.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(res.msg);
|
||||
}
|
||||
[HttpGet("DeleteTask")]
|
||||
public async Task<BaseResponse<string>> DeleteTask(string id)
|
||||
{
|
||||
var res = await _taskService.DeleteTask(id);
|
||||
if (res.isok)
|
||||
return this.ToResult(res.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(res.msg);
|
||||
}
|
||||
[HttpGet("UpdateTaskState")]
|
||||
public async Task<BaseResponse<string>> UpdateTaskState(string id, int state)
|
||||
{
|
||||
var res = await _taskService.UpdateTaskState(id, state);
|
||||
if (res.isok)
|
||||
return this.ToResult(res.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(res.msg);
|
||||
}
|
||||
|
||||
[HttpPost("UpdateTask")]
|
||||
public async Task<BaseResponse<string>> UpdateTask([FromBody] GameTask param)
|
||||
{
|
||||
var res = await _taskService.UpdateTask(param);
|
||||
if (res.isok)
|
||||
return this.ToResult(res.msg);
|
||||
else
|
||||
return this.ToFailResult<string>(res.msg);
|
||||
}
|
||||
|
||||
[HttpPost("GetTaskList")]
|
||||
public async Task<PageResponse<GameTask>> GetTaskList(GetTaskListParam param)
|
||||
{
|
||||
var res = await _taskService.GetTaskList(param);
|
||||
return this.ToPageResult(res.list, res.count);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using AdminService.User.Param;
|
||||
using Common;
|
||||
using Common.Utils;
|
||||
using CommonDomain.BaseModel;
|
||||
using CommonInfrastructure;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using MongoDB.Driver;
|
||||
using MongoRepository;
|
||||
using UserDomain.Entity;
|
||||
using UserDomain.Enum;
|
||||
|
||||
namespace AdminApi.UserAssetChanges
|
||||
{
|
||||
[ApiController, Route("adminapi/[controller]")]
|
||||
[Authorize]
|
||||
public class UserAssetChangeController : BaseController
|
||||
{
|
||||
private readonly IMongoBaseRepository<UserReward> _rewardMongo;
|
||||
private readonly IMongoBaseRepository<UserEntity> _userMongo;
|
||||
|
||||
public UserAssetChangeController(IMongoBaseRepository<UserReward> rewardMongo, IMongoBaseRepository<UserEntity> userMongo)
|
||||
{
|
||||
_rewardMongo = rewardMongo;
|
||||
_userMongo = userMongo;
|
||||
}
|
||||
|
||||
[HttpPost("list")]
|
||||
public async Task<PageResponse<UserAssetChangeDto>> GetList([FromBody] UserAssetChangeQueryParam param)
|
||||
{
|
||||
var filter = Builders<UserReward>.Filter.Empty;
|
||||
|
||||
filter &= Builders<UserReward>.Filter.Nin(x => x.Source, new List<RewardSource>() { RewardSource.开炮, RewardSource.击杀鱼});
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(param.UserId))
|
||||
{
|
||||
filter &= Builders<UserReward>.Filter.Eq(x => x.UserId, param.UserId);
|
||||
}
|
||||
|
||||
if (param.CurrencyTypes != null && param.CurrencyTypes.Count > 0)
|
||||
{
|
||||
filter &= Builders<UserReward>.Filter.In(x => x.CurrencyType, param.CurrencyTypes);
|
||||
}
|
||||
|
||||
if (param.ChangeTypes != null && param.ChangeTypes.Count > 0)
|
||||
{
|
||||
filter &= Builders<UserReward>.Filter.In(x => x.Type, param.ChangeTypes);
|
||||
}
|
||||
|
||||
if (param.Sources != null && param.Sources.Count > 0)
|
||||
{
|
||||
var sourceEnums = param.Sources.Select(s => Enum.TryParse<RewardSource>(s, out var src) ? src : (RewardSource?)null)
|
||||
.Where(x => x.HasValue)
|
||||
.Select(x => x.Value)
|
||||
.ToList();
|
||||
if (sourceEnums.Count > 0)
|
||||
{
|
||||
filter &= Builders<UserReward>.Filter.In(x => x.Source, sourceEnums);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (param.CurrencyType.HasValue)
|
||||
{
|
||||
filter &= Builders<UserReward>.Filter.Eq(x => x.CurrencyType, param.CurrencyType.Value);
|
||||
}
|
||||
|
||||
if (param.TimeStart.HasValue)
|
||||
{
|
||||
filter &= Builders<UserReward>.Filter.Gte(x => x.CreateTime, param.TimeStart.Value);
|
||||
}
|
||||
if (param.TimeEnd.HasValue)
|
||||
{
|
||||
filter &= Builders<UserReward>.Filter.Lte(x => x.CreateTime, param.TimeEnd.Value);
|
||||
}
|
||||
|
||||
var sort = Builders<UserReward>.Sort.Descending(x => x.CreateTime);
|
||||
var result = await _rewardMongo.QueryPageAsync(
|
||||
filter,
|
||||
sort,
|
||||
true,
|
||||
param.PageSize.Value,
|
||||
param.PageIndex.Value
|
||||
);
|
||||
|
||||
var list = result.list ?? new List<UserReward>();
|
||||
var userIds = list.Select(x => x.UserId).Distinct().ToList();
|
||||
var users = await _userMongo.FilterByAsync(x => userIds.Contains(x.Id));
|
||||
var userDict = users.ToDictionary(u => u.Id, u => u.BaseInfo?.UserName ?? "");
|
||||
|
||||
if (list == null)
|
||||
return this.ToPageResult(new List<UserAssetChangeDto>(), 0);
|
||||
var dtos = list.Select(x => new UserAssetChangeDto
|
||||
{
|
||||
Id = x.Id,
|
||||
UserId = x.UserId,
|
||||
UserName = userDict.TryGetValue(x.UserId, out var name) ? name : "",
|
||||
Type = x.Type,
|
||||
ChangeTypeText = x.Type == 1 ? "增加" : "减少",
|
||||
Source = x.Source.ToString(),
|
||||
CurrencyType = x.CurrencyType,
|
||||
CurrencyTypeText = x.CurrencyType == 1 ? "金币" : x.CurrencyType == 2 ? "保险箱金币" : "钻石",
|
||||
BeforeCount = x.BeforeCount,
|
||||
ChangeCount = x.ChangeCount,
|
||||
AfterCount = x.AfterCount,
|
||||
Remark = x.Remark,
|
||||
CreateTime = x.CreateTime.ToDateTime()
|
||||
});
|
||||
|
||||
return this.ToPageResult(dtos, (int)result.count);
|
||||
}
|
||||
|
||||
[HttpGet("sources")]
|
||||
public BaseResponse<List<SourceOptionDto>> GetSources()
|
||||
{
|
||||
var sources = Enum.GetValues<RewardSource>()
|
||||
.Select(x => new SourceOptionDto { Value = x.ToString(), Label = x.ToString() })
|
||||
.ToList();
|
||||
return this.ToResult(sources);
|
||||
}
|
||||
}
|
||||
|
||||
public class UserAssetChangeDto
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string UserId { get; set; }
|
||||
public string UserName { get; set; }
|
||||
public int Type { get; set; }
|
||||
public string ChangeTypeText { get; set; }
|
||||
public string Source { get; set; }
|
||||
public int CurrencyType { get; set; }
|
||||
public string CurrencyTypeText { get; set; }
|
||||
public long BeforeCount { get; set; }
|
||||
public long ChangeCount { get; set; }
|
||||
public long AfterCount { get; set; }
|
||||
public string Remark { get; set; }
|
||||
public DateTime CreateTime { get; set; }
|
||||
}
|
||||
|
||||
public class SourceOptionDto
|
||||
{
|
||||
public string Value { get; set; }
|
||||
public string Label { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,67 @@
|
||||
using System;
|
||||
|
||||
namespace AdminApplication.DTOs;
|
||||
|
||||
public class CustomerDto
|
||||
{
|
||||
public string Id { get; set; }
|
||||
|
||||
public string? UserId { get; set; }
|
||||
|
||||
public string? UserName { get; set; }
|
||||
|
||||
public int Status { get; set; }
|
||||
|
||||
public string? Avatar { get; set; }
|
||||
|
||||
public DateTime CreateTime { get; set; }
|
||||
}
|
||||
|
||||
public class ConversationDto
|
||||
{
|
||||
public string Id { get; set; }
|
||||
|
||||
public string? UserId { get; set; }
|
||||
|
||||
public string? UserName { get; set; }
|
||||
|
||||
public string? UserAvatar { get; set; }
|
||||
|
||||
public string? CustomerId { get; set; }
|
||||
|
||||
public string? CustomerName { get; set; }
|
||||
|
||||
public string? CustomerAvatar { get; set; }
|
||||
|
||||
public int Status { get; set; }
|
||||
|
||||
public DateTime CreateTime { get; set; }
|
||||
|
||||
public DateTime? EndTime { get; set; }
|
||||
}
|
||||
|
||||
public class MessageDto
|
||||
{
|
||||
public string Id { get; set; }
|
||||
|
||||
public string? ConversationId { get; set; }
|
||||
|
||||
public int SenderType { get; set; }
|
||||
|
||||
public string? SenderId { get; set; }
|
||||
|
||||
public string? SenderName { get; set; }
|
||||
|
||||
public string? SenderAvatar { get; set; }
|
||||
|
||||
public int ContentType { get; set; }
|
||||
|
||||
public string? Content { get; set; }
|
||||
|
||||
public DateTime CreateTime { get; set; }
|
||||
}
|
||||
|
||||
public class DeleteMessagesBatchRequest
|
||||
{
|
||||
public List<string> Ids { get; set; } = new List<string>();
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
using MongoRepository;
|
||||
using MongoRepository.Attributes;
|
||||
using MongoRepository.Config;
|
||||
|
||||
namespace Domain.Admin.Admin
|
||||
{
|
||||
[BsonCollection("admin_operation_log", ServiceDatabases.Admin)]
|
||||
[BsonIgnoreExtraElements]
|
||||
public class AdminOperationLog : MongoBaseEntity
|
||||
{
|
||||
[BsonElement("adminid")]
|
||||
public string? AdminId { get; set; }
|
||||
|
||||
[BsonElement("adminname")]
|
||||
public string? AdminName { get; set; }
|
||||
|
||||
[BsonElement("module")]
|
||||
public int Module { get; set; }
|
||||
|
||||
[BsonElement("actiontype")]
|
||||
public string? ActionType { get; set; }
|
||||
|
||||
[BsonElement("description")]
|
||||
public string? Description { get; set; }
|
||||
|
||||
[BsonElement("httpmethod")]
|
||||
public string? HttpMethod { get; set; }
|
||||
|
||||
[BsonElement("requestpath")]
|
||||
public string? RequestPath { get; set; }
|
||||
|
||||
[BsonElement("requestparams")]
|
||||
public string? RequestParams { get; set; }
|
||||
|
||||
[BsonElement("response")]
|
||||
public string? Response { get; set; }
|
||||
|
||||
[BsonElement("ip")]
|
||||
public string? Ip { get; set; }
|
||||
|
||||
[BsonElement("status")]
|
||||
public int Status { get; set; } = 1;
|
||||
|
||||
[BsonElement("operatetime")]
|
||||
[BsonDateTimeOptions(Kind = DateTimeKind.Local, DateOnly = false)]
|
||||
public DateTime OperateTime { get; set; } = DateTime.Now;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Domain.Admin.Admin.ENum
|
||||
{
|
||||
public enum EnumNavigationCategory
|
||||
{
|
||||
[Description("后台菜单")]
|
||||
System,
|
||||
|
||||
[Description("前台菜单")]
|
||||
Customer
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Domain.Admin.Admin.ENum
|
||||
{
|
||||
public enum EnumOperationModule
|
||||
{
|
||||
[Description("用户管理")]
|
||||
User = 1,
|
||||
|
||||
[Description("系统用户")]
|
||||
SystemUser = 2,
|
||||
|
||||
[Description("角色管理")]
|
||||
Role = 3,
|
||||
|
||||
[Description("菜单管理")]
|
||||
Navigation = 4,
|
||||
|
||||
[Description("字典管理")]
|
||||
Dictionary = 5,
|
||||
|
||||
[Description("游戏配置")]
|
||||
GameConfig = 6,
|
||||
|
||||
[Description("房间管理")]
|
||||
Room = 7,
|
||||
|
||||
[Description("客服管理")]
|
||||
Customer = 8,
|
||||
|
||||
[Description("靓号管理")]
|
||||
BeautifulNumber = 9,
|
||||
|
||||
[Description("公告管理")]
|
||||
Notice = 10,
|
||||
|
||||
[Description("任务管理")]
|
||||
Task = 11,
|
||||
|
||||
[Description("其他")]
|
||||
Other = 99
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Domain.Admin.Admin.ENum
|
||||
{
|
||||
|
||||
public enum EnumResource
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
[Description("菜单")]
|
||||
Navigation,
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
[Description("资源")]
|
||||
Resource,
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
[Description("代码块")]
|
||||
Block
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using Domain.Admin.Admin;
|
||||
|
||||
namespace Domain.Admin.Admin
|
||||
{
|
||||
public interface IAdminOperationLogService
|
||||
{
|
||||
Task<bool> AddLog(AdminOperationLog log);
|
||||
|
||||
Task<(List<AdminOperationLog> list, long count)> QueryPage(Param.OperationLogQueryParam param);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
namespace Domain.Admin.Admin.Param
|
||||
{
|
||||
public class OperationLogQueryParam
|
||||
{
|
||||
public int PageIndex { get; set; } = 1;
|
||||
|
||||
public int PageSize { get; set; } = 20;
|
||||
|
||||
public string? AdminId { get; set; }
|
||||
|
||||
public string? AdminName { get; set; }
|
||||
|
||||
public int? Module { get; set; }
|
||||
|
||||
public DateTime? StartTime { get; set; }
|
||||
|
||||
public DateTime? EndTime { get; set; }
|
||||
|
||||
public int? Status { get; set; }
|
||||
}
|
||||
|
||||
public class AddOperationLogParam
|
||||
{
|
||||
public string? AdminId { get; set; }
|
||||
|
||||
public string? AdminName { get; set; }
|
||||
|
||||
public int Module { get; set; }
|
||||
|
||||
public string? ActionType { get; set; }
|
||||
|
||||
public string? Description { get; set; }
|
||||
|
||||
public string? HttpMethod { get; set; }
|
||||
|
||||
public string? RequestPath { get; set; }
|
||||
|
||||
public string? RequestParams { get; set; }
|
||||
|
||||
public string? Response { get; set; }
|
||||
|
||||
public string? Ip { get; set; }
|
||||
|
||||
public int Status { get; set; } = 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
using MongoRepository;
|
||||
using MongoRepository.Attributes;
|
||||
using MongoRepository.Config;
|
||||
|
||||
namespace Domain.Admin.Admin
|
||||
{
|
||||
[BsonCollection("system_user_dic", ServiceDatabases.Admin)]
|
||||
[BsonIgnoreExtraElements]
|
||||
public class SystemDic :MongoBaseEntity
|
||||
{
|
||||
|
||||
public string ParentId { set; get; } = "";
|
||||
|
||||
[BsonElement("rolename")]
|
||||
public string? Name { get; set; }
|
||||
|
||||
[BsonElement("order")]
|
||||
public int Order { get; set; }
|
||||
|
||||
[BsonElement("icon")]
|
||||
public string? Icon { get; set; }
|
||||
|
||||
[BsonElement("value")]
|
||||
public string? Value { get; set; }
|
||||
[BsonElement("code")]
|
||||
public string? Code { get; set; }
|
||||
|
||||
[BsonElement("remark")]
|
||||
public string? Remark { get; set; }
|
||||
|
||||
|
||||
|
||||
[BsonElement("remark1")]
|
||||
public string? Remark1 { get; set; }
|
||||
|
||||
public void Init()
|
||||
{
|
||||
Id = ObjectId.GenerateNewId().ToString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
using Domain.Admin.Admin.ENum;
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
using MongoRepository;
|
||||
using MongoRepository.Attributes;
|
||||
using MongoRepository.Config;
|
||||
|
||||
namespace Domain.Admin.Admin
|
||||
{
|
||||
[BsonCollection("system_user_nav", ServiceDatabases.Admin)]
|
||||
[BsonIgnoreExtraElements]
|
||||
public class SystemNavigation :MongoBaseEntity
|
||||
{
|
||||
|
||||
[BsonElement("parentId")]
|
||||
public string ParentId { set; get; } = "";
|
||||
|
||||
[BsonElement("name")]
|
||||
public string? Name { get; set; }
|
||||
|
||||
[BsonElement("order")]
|
||||
public int Order { get; set; }
|
||||
|
||||
[BsonElement("icon")]
|
||||
public string? Icon { get; set; }
|
||||
|
||||
[BsonElement("url")]
|
||||
public string? Url { get; set; }
|
||||
|
||||
[BsonElement("category")]
|
||||
public EnumNavigationCategory Category { get; set; }
|
||||
|
||||
[BsonElement("target")]
|
||||
public string? Target { get; set; }
|
||||
|
||||
[BsonElement("isresource")]
|
||||
public EnumResource IsResource { get; set; }
|
||||
public void Init()
|
||||
{
|
||||
Id = ObjectId.GenerateNewId().ToString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
using Common.CommonModel;
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
using MongoRepository;
|
||||
using MongoRepository.Attributes;
|
||||
using MongoRepository.Config;
|
||||
|
||||
namespace Domain.Admin.Admin
|
||||
{
|
||||
[BsonCollection("system_user", ServiceDatabases.Admin)]
|
||||
[BsonIgnoreExtraElements]
|
||||
public class SystemUser:MongoBaseEntity
|
||||
{
|
||||
[BsonElement("username")]
|
||||
public string? UserName { get; set; }
|
||||
|
||||
|
||||
[BsonElement("displayname")]
|
||||
public string? DisplayName { get; set; }
|
||||
|
||||
[BsonElement("icon")]
|
||||
public string? Icon { get; set; }
|
||||
|
||||
[BsonElement("password")]
|
||||
public string? Password { get; set; }
|
||||
|
||||
|
||||
[BsonElement("registertime")]
|
||||
[BsonDateTimeOptions(Kind = DateTimeKind.Local, DateOnly = false)]
|
||||
public DateTime RegisterTime { get; set; } = DateTime.Now;
|
||||
|
||||
[BsonElement("state")]
|
||||
public int State { get; set; } = 1;
|
||||
|
||||
[BsonElement("systemrole")]
|
||||
public BaseIdName SystemRole { get; set; }
|
||||
|
||||
|
||||
public void Init()
|
||||
{
|
||||
Id = ObjectId.GenerateNewId().ToString();
|
||||
State = 1;
|
||||
RegisterTime=DateTime.Now;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
using MongoRepository;
|
||||
using MongoRepository.Attributes;
|
||||
using MongoRepository.Config;
|
||||
|
||||
namespace Domain.Admin.Admin
|
||||
{
|
||||
[BsonCollection("system_user_role", ServiceDatabases.Admin)]
|
||||
[BsonIgnoreExtraElements]
|
||||
public class SystemUserRole:MongoBaseEntity
|
||||
{
|
||||
[BsonElement("rolename")]
|
||||
public string? RoleName { get; set; }
|
||||
|
||||
[BsonElement("description")]
|
||||
public string? Description { get; set; }
|
||||
|
||||
|
||||
[BsonElement("navIds")]
|
||||
public List<string> NavIds { get; set; } = new List<string>();
|
||||
|
||||
public void Init()
|
||||
{
|
||||
Id = ObjectId.GenerateNewId().ToString();
|
||||
NavIds = new List<string>();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.3" />
|
||||
<PackageReference Include="MongoDB.Bson" Version="3.6.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Infrastructure\MongoRepository\MongoRepository.csproj" />
|
||||
<ProjectReference Include="..\..\CustomerSrc\CustomerService.Domain\CustomerService.Domain.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,50 @@
|
||||
using AdminDomain.BeautifulNumber;
|
||||
using Common.Utils;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
using MongoRepository;
|
||||
using MongoRepository.Attributes;
|
||||
using MongoRepository.Config;
|
||||
|
||||
namespace AdminDomain.Entity
|
||||
{
|
||||
[BsonCollection("beautiful_number_config", ServiceDatabases.Admin)]
|
||||
[BsonIgnoreExtraElements]
|
||||
public class BeautifulNumberConfig : MongoBaseEntity
|
||||
{
|
||||
public string Number { get; set; }
|
||||
public string RuleName { get; set; }
|
||||
|
||||
public string Level { get; set; }
|
||||
|
||||
public long Price { get; set; }
|
||||
|
||||
public BeautifulNumberStatus Status { get; set; }= BeautifulNumberStatus.Available;
|
||||
|
||||
public long SaleTime { get; set; } = 0;
|
||||
|
||||
public string BuyerUserId { get; set; }
|
||||
|
||||
public string UserId { get; set; }
|
||||
|
||||
public string Remark { get; set; }
|
||||
|
||||
public long CreateTime { get; set; } = DateTime.Now.ToTimeStamp();
|
||||
}
|
||||
|
||||
[BsonCollection("beautiful_number_rule", ServiceDatabases.Admin)]
|
||||
[BsonIgnoreExtraElements]
|
||||
public class BeautifulNumberRule : MongoBaseEntity
|
||||
{
|
||||
|
||||
public string RuleName { get; set; }
|
||||
|
||||
|
||||
public string Level { get; set; }
|
||||
|
||||
public long Price { get; set; }
|
||||
|
||||
public int Sort { get; set; }
|
||||
|
||||
public bool IsEnable { get; set; } = true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace AdminDomain.BeautifulNumber
|
||||
{
|
||||
public enum BeautifulNumberRuleEnum
|
||||
{
|
||||
[Description("普通号")]
|
||||
Normal = 0,
|
||||
|
||||
[Description("3连号")]
|
||||
TripleRepeat = 1,
|
||||
|
||||
[Description("4连号")]
|
||||
QuadrupleRepeat = 2,
|
||||
|
||||
[Description("5连号")]
|
||||
QuintupleRepeat = 3,
|
||||
|
||||
[Description("6连号")]
|
||||
SextupleRepeat = 4,
|
||||
|
||||
[Description("7连号")]
|
||||
SeptupleRepeat = 5,
|
||||
|
||||
[Description("4顺子")]
|
||||
Straight4 = 10,
|
||||
|
||||
[Description("5顺子")]
|
||||
Straight5 = 11,
|
||||
|
||||
[Description("6顺子")]
|
||||
Straight6 = 12,
|
||||
|
||||
[Description("双连2组")]
|
||||
DoubleRepeat2 = 20,
|
||||
|
||||
[Description("双连3组")]
|
||||
DoubleRepeat3 = 21,
|
||||
|
||||
[Description("双连4组")]
|
||||
DoubleRepeat4 = 22,
|
||||
|
||||
[Description("混搭")]
|
||||
Mixed = 30,
|
||||
|
||||
[Description("5连+3任意")]
|
||||
QuintupleWithAny = 31,
|
||||
|
||||
[Description("3任意+5连")]
|
||||
AnyWithQuintuple = 32,
|
||||
|
||||
[Description("夹心3连")]
|
||||
SandwichTriple = 40,
|
||||
|
||||
[Description("间隔2连")]
|
||||
Alternating2 = 41,
|
||||
|
||||
[Description("对称3连")]
|
||||
SymmetricTriple = 42
|
||||
}
|
||||
|
||||
public enum BeautifulNumberLevel
|
||||
{
|
||||
[Description("D")]
|
||||
D = 0,
|
||||
|
||||
[Description("C")]
|
||||
C = 1,
|
||||
|
||||
[Description("B")]
|
||||
B = 2,
|
||||
|
||||
[Description("A")]
|
||||
A = 3,
|
||||
|
||||
[Description("S")]
|
||||
S = 4
|
||||
}
|
||||
|
||||
public enum BeautifulNumberStatus
|
||||
{
|
||||
[Description("未售")]
|
||||
Available = 0,
|
||||
|
||||
[Description("已售")]
|
||||
Sold = 1,
|
||||
|
||||
[Description("冻结")]
|
||||
Frozen = 2,
|
||||
|
||||
[Description("普通号")]
|
||||
Normal = 3
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using AdminDomain.Customer.Param;
|
||||
using CustomerService.Domain.Entity;
|
||||
|
||||
namespace AdminDomain.Customer
|
||||
{
|
||||
public interface ICustomerService
|
||||
{
|
||||
Task<(bool isOk, string msg, CustomerEntity? customer)> AddCustomer(AddCustomerParam param);
|
||||
Task<(bool isOk, string msg)> UpdateCustomer(UpdateCustomerParam param);
|
||||
Task<(bool isOk, string msg)> DeleteCustomer(string id);
|
||||
Task<(bool isOk, string msg, List<CustomerEntity>? customers, int total)> GetCustomerList(GetCustomerListParam param);
|
||||
Task<(bool isOk, string msg, CustomerEntity? customer)> GetCustomerById(string id);
|
||||
Task<(bool isOk, string msg, ConversationEntity? conversation)> GetConversationById(string id);
|
||||
Task<(bool isOk, string msg, List<ConversationEntity>? conversations, int total)> GetConversationList(GetConversationListParam param);
|
||||
Task<(bool isOk, string msg)> DeleteConversation(string conversationId);
|
||||
Task<(bool isOk, string msg, List<MessageEntity>? messages)> GetChatLog(string conversationId);
|
||||
Task<(bool isOk, string msg)> DeleteMessage(string id);
|
||||
Task<(bool isOk, string msg)> DeleteAllMessages(string conversationId);
|
||||
Task<(bool isOk, string msg)> DeleteMessagesBatch(List<string> ids);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
|
||||
namespace AdminDomain.Customer.Param
|
||||
{
|
||||
public class AddCustomerParam
|
||||
{
|
||||
public string UserId { get; set; } = "";
|
||||
public string UserName { get; set; } = "";
|
||||
public string? Avatar { get; set; }
|
||||
}
|
||||
|
||||
public class UpdateCustomerParam
|
||||
{
|
||||
public string Id { get; set; } = "";
|
||||
public string UserName { get; set; } = "";
|
||||
public string? Avatar { get; set; }
|
||||
public int Status { get; set; }
|
||||
}
|
||||
|
||||
public class GetCustomerListParam
|
||||
{
|
||||
public int PageIndex { get; set; } = 1;
|
||||
public int PageSize { get; set; } = 10;
|
||||
public string? UserName { get; set; }
|
||||
public int? Status { get; set; }
|
||||
}
|
||||
|
||||
public class GetConversationListParam
|
||||
{
|
||||
public int PageIndex { get; set; } = 1;
|
||||
public int PageSize { get; set; } = 10;
|
||||
public string? UserId { get; set; }
|
||||
public string? CustomerId { get; set; }
|
||||
public int? Status { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
|
||||
namespace Domain.Admin.Notices;
|
||||
|
||||
public class NoticeDto
|
||||
{
|
||||
public string Id { get; set; }
|
||||
|
||||
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; }
|
||||
|
||||
public long CreateTime { get; set; }
|
||||
}
|
||||
|
||||
public class NoticeListItemDto
|
||||
{
|
||||
public string Id { get; set; }
|
||||
|
||||
public string Title { get; set; }
|
||||
|
||||
public string? Recommend { get; set; }
|
||||
|
||||
public string? TopImage { get; set; }
|
||||
|
||||
public long CreateTime { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
**/.classpath
|
||||
**/.dockerignore
|
||||
**/.env
|
||||
**/.git
|
||||
**/.gitignore
|
||||
**/.project
|
||||
**/.settings
|
||||
**/.toolstarget
|
||||
**/.vs
|
||||
**/.vscode
|
||||
**/*.*proj.user
|
||||
**/*.dbmdl
|
||||
**/*.jfm
|
||||
**/azds.yaml
|
||||
**/bin
|
||||
**/charts
|
||||
**/docker-compose*
|
||||
**/Dockerfile*
|
||||
**/node_modules
|
||||
**/npm-debug.log
|
||||
**/obj
|
||||
**/secrets.dev.yaml
|
||||
**/values.dev.yaml
|
||||
LICENSE
|
||||
README.md
|
||||
!**/.gitignore
|
||||
!.git/HEAD
|
||||
!.git/config
|
||||
!.git/packed-refs
|
||||
!.git/refs/heads/**
|
||||
@@ -0,0 +1,54 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<EnableDefaultContentItems>false</EnableDefaultContentItems>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.3.9" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.24" />
|
||||
<PackageReference Include="NLog.Web.AspNetCore" Version="6.1.2" />
|
||||
<PackageReference Include="Autofac" Version="8.3.0" />
|
||||
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="10.0.0" />
|
||||
<PackageReference Include="AutoMapper" Version="14.0.0" />
|
||||
<PackageReference Include="DotNetCore.CAP" Version="8.3.4" />
|
||||
<PackageReference Include="DotNetCore.CAP.Dashboard" Version="8.3.4" />
|
||||
<PackageReference Include="DotNetCore.CAP.PostgreSql" Version="8.3.4" />
|
||||
<PackageReference Include="DotNetCore.CAP.RabbitMQ" Version="8.3.4" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<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="..\..\Infrastructure\ScheduledService\ScheduledService.csproj" />
|
||||
<ProjectReference Include="..\..\Infrastructure\SharpAbp.Infrastructure.Api\SharpAbp.Infrastructure.Api.csproj" />
|
||||
<ProjectReference Include="..\..\UserSrc\UserService\UserService.csproj" />
|
||||
<ProjectReference Include="..\..\FishGameSrc\FishGameService\FishGameService.csproj" />
|
||||
<ProjectReference Include="..\AdminApi\AdminApi.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="..\..\nlog.config" Link="nlog.config" CopyToOutputDirectory="PreserveNewest" CopyToPublishDirectory="PreserveNewest" />
|
||||
<None Update="docker-compose.yaml">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Dockerfile">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="wwwroot\**\*" CopyToOutputDirectory="PreserveNewest" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Properties\PublishProfiles\" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,986 @@
|
||||
{
|
||||
"ActivityName": "炮台/战神/翅膀奖池",
|
||||
"IsOpen": false,
|
||||
"StartTime": 0,
|
||||
"EndTime": 0,
|
||||
"Pools": [
|
||||
{
|
||||
"PoolType": 1,
|
||||
"Level": "A",
|
||||
"Count": 2,
|
||||
"Weight": 1,
|
||||
"PrizeList": [
|
||||
{
|
||||
"PrizeId": "587001",
|
||||
"Name": "剑气如虹",
|
||||
"Icon": "",
|
||||
"Type": 3,
|
||||
"Count": 1,
|
||||
"OtherId": "11028",
|
||||
"Weight": 1,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "587002",
|
||||
"Name": "万圣通灵",
|
||||
"Icon": "",
|
||||
"Type": 3,
|
||||
"Count": 1,
|
||||
"OtherId": "11029",
|
||||
"Weight": 2,
|
||||
"TotalCount": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"PoolType": 1,
|
||||
"Level": "B",
|
||||
"Count": 8,
|
||||
"Weight": 10,
|
||||
"PrizeList": [
|
||||
{
|
||||
"PrizeId": "587101",
|
||||
"Name": "狂暴卡",
|
||||
"Icon": "http://47.109.132.12/uploads/40004.png?v=1784285974",
|
||||
"Type": 3,
|
||||
"Count": 60,
|
||||
"OtherId": "40004",
|
||||
"Weight": 10,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "587102",
|
||||
"Name": "狂暴卡",
|
||||
"Icon": "http://47.109.132.12/uploads/40004.png?v=1784285974",
|
||||
"Type": 3,
|
||||
"Count": 60,
|
||||
"OtherId": "40004",
|
||||
"Weight": 10,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "587103",
|
||||
"Name": "金币",
|
||||
"Icon": "",
|
||||
"Type": 1,
|
||||
"Count": 500000,
|
||||
"OtherId": "",
|
||||
"Weight": 10,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "587104",
|
||||
"Name": "金币",
|
||||
"Icon": "",
|
||||
"Type": 1,
|
||||
"Count": 500000,
|
||||
"OtherId": "",
|
||||
"Weight": 10,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "587105",
|
||||
"Name": "钻石",
|
||||
"Icon": "",
|
||||
"Type": 2,
|
||||
"Count": 50,
|
||||
"OtherId": "",
|
||||
"Weight": 10,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "587106",
|
||||
"Name": "钻石",
|
||||
"Icon": "",
|
||||
"Type": 2,
|
||||
"Count": 50,
|
||||
"OtherId": "",
|
||||
"Weight": 10,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "587107",
|
||||
"Name": "冰冻卡",
|
||||
"Icon": "http://47.109.132.12/uploads/40003.png?v=1784285984",
|
||||
"Type": 3,
|
||||
"Count": 50,
|
||||
"OtherId": "40003",
|
||||
"Weight": 10,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "587108",
|
||||
"Name": "冰冻卡",
|
||||
"Icon": "http://47.109.132.12/uploads/40003.png?v=1784285984",
|
||||
"Type": 3,
|
||||
"Count": 50,
|
||||
"OtherId": "40003",
|
||||
"Weight": 10,
|
||||
"TotalCount": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"PoolType": 1,
|
||||
"Level": "C",
|
||||
"Count": 20,
|
||||
"Weight": 20,
|
||||
"PrizeList": [
|
||||
{
|
||||
"PrizeId": "587201",
|
||||
"Name": "狂暴卡",
|
||||
"Icon": "http://47.109.132.12/uploads/40004.png?v=1784285974",
|
||||
"Type": 3,
|
||||
"Count": 10,
|
||||
"OtherId": "40004",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "587202",
|
||||
"Name": "狂暴卡",
|
||||
"Icon": "http://47.109.132.12/uploads/40004.png?v=1784285974",
|
||||
"Type": 3,
|
||||
"Count": 10,
|
||||
"OtherId": "40004",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "587203",
|
||||
"Name": "狂暴卡",
|
||||
"Icon": "http://47.109.132.12/uploads/40004.png?v=1784285974",
|
||||
"Type": 3,
|
||||
"Count": 10,
|
||||
"OtherId": "40004",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "587204",
|
||||
"Name": "狂暴卡",
|
||||
"Icon": "http://47.109.132.12/uploads/40004.png?v=1784285974",
|
||||
"Type": 3,
|
||||
"Count": 10,
|
||||
"OtherId": "40004",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "587205",
|
||||
"Name": "钻石",
|
||||
"Icon": "",
|
||||
"Type": 2,
|
||||
"Count": 5,
|
||||
"OtherId": "",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "587206",
|
||||
"Name": "钻石",
|
||||
"Icon": "",
|
||||
"Type": 2,
|
||||
"Count": 5,
|
||||
"OtherId": "",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "587207",
|
||||
"Name": "钻石",
|
||||
"Icon": "",
|
||||
"Type": 2,
|
||||
"Count": 15,
|
||||
"OtherId": "",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "587208",
|
||||
"Name": "钻石",
|
||||
"Icon": "",
|
||||
"Type": 2,
|
||||
"Count": 15,
|
||||
"OtherId": "",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "587209",
|
||||
"Name": "召唤卡",
|
||||
"Icon": "http://47.109.132.12/uploads/40006.png",
|
||||
"Type": 3,
|
||||
"Count": 3,
|
||||
"OtherId": "40006",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "587210",
|
||||
"Name": "召唤卡",
|
||||
"Icon": "http://47.109.132.12/uploads/40006.png",
|
||||
"Type": 3,
|
||||
"Count": 3,
|
||||
"OtherId": "40006",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "587211",
|
||||
"Name": "冰冻卡",
|
||||
"Icon": "http://47.109.132.12/uploads/40003.png?v=1784285984",
|
||||
"Type": 3,
|
||||
"Count": 3,
|
||||
"OtherId": "40003",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "587212",
|
||||
"Name": "冰冻卡",
|
||||
"Icon": "http://47.109.132.12/uploads/40003.png?v=1784285984",
|
||||
"Type": 3,
|
||||
"Count": 3,
|
||||
"OtherId": "40003",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "587213",
|
||||
"Name": "金币",
|
||||
"Icon": "",
|
||||
"Type": 1,
|
||||
"Count": 100000,
|
||||
"OtherId": "",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "587214",
|
||||
"Name": "金币",
|
||||
"Icon": "",
|
||||
"Type": 1,
|
||||
"Count": 100000,
|
||||
"OtherId": "",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "587215",
|
||||
"Name": "金币",
|
||||
"Icon": "",
|
||||
"Type": 1,
|
||||
"Count": 50000,
|
||||
"OtherId": "",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "587216",
|
||||
"Name": "金币",
|
||||
"Icon": "",
|
||||
"Type": 1,
|
||||
"Count": 50000,
|
||||
"OtherId": "",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "587217",
|
||||
"Name": "冰冻卡",
|
||||
"Icon": "http://47.109.132.12/uploads/40003.png?v=1784285984",
|
||||
"Type": 3,
|
||||
"Count": 3,
|
||||
"OtherId": "40003",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "587218",
|
||||
"Name": "冰冻卡",
|
||||
"Icon": "http://47.109.132.12/uploads/40003.png?v=1784285984",
|
||||
"Type": 3,
|
||||
"Count": 3,
|
||||
"OtherId": "40003",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "587219",
|
||||
"Name": "刮刮卡",
|
||||
"Icon": "http://47.109.132.12/uploads/40005.png",
|
||||
"Type": 3,
|
||||
"Count": 1,
|
||||
"OtherId": "40005",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "587220",
|
||||
"Name": "刮刮卡",
|
||||
"Icon": "http://47.109.132.12/uploads/40005.png",
|
||||
"Type": 3,
|
||||
"Count": 1,
|
||||
"OtherId": "40005",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"PoolType": 2,
|
||||
"Level": "A",
|
||||
"Count": 3,
|
||||
"Weight": 1,
|
||||
"PrizeList": [
|
||||
{
|
||||
"PrizeId": "588001",
|
||||
"Name": "孙悟空",
|
||||
"Icon": "",
|
||||
"Type": 3,
|
||||
"Count": 1,
|
||||
"OtherId": "13002",
|
||||
"Weight": 1,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "588002",
|
||||
"Name": "杨戬",
|
||||
"Icon": "",
|
||||
"Type": 3,
|
||||
"Count": 1,
|
||||
"OtherId": "13003",
|
||||
"Weight": 2,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "588003",
|
||||
"Name": "熊猫侠客",
|
||||
"Icon": "",
|
||||
"Type": 3,
|
||||
"Count": 1,
|
||||
"OtherId": "13004",
|
||||
"Weight": 3,
|
||||
"TotalCount": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"PoolType": 2,
|
||||
"Level": "B",
|
||||
"Count": 3,
|
||||
"Weight": 10,
|
||||
"PrizeList": [
|
||||
{
|
||||
"PrizeId": "588101",
|
||||
"Name": "穹渊核弹",
|
||||
"Icon": "http://47.109.132.12/uploads/48004.png?timespan=1788935361",
|
||||
"Type": 3,
|
||||
"Count": 1,
|
||||
"OtherId": "48004",
|
||||
"Weight": 10,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "588102",
|
||||
"Name": "噬浪核弹",
|
||||
"Icon": "http://47.109.132.12/uploads/48003.png?timespan=1788935284",
|
||||
"Type": 3,
|
||||
"Count": 1,
|
||||
"OtherId": "48003",
|
||||
"Weight": 10,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "588103",
|
||||
"Name": "霜雷核弹",
|
||||
"Icon": "http://47.109.132.12/uploads/48002.png?timespan=1788935210",
|
||||
"Type": 3,
|
||||
"Count": 1,
|
||||
"OtherId": "48002",
|
||||
"Weight": 10,
|
||||
"TotalCount": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"PoolType": 2,
|
||||
"Level": "C",
|
||||
"Count": 20,
|
||||
"Weight": 20,
|
||||
"PrizeList": [
|
||||
{
|
||||
"PrizeId": "588201",
|
||||
"Name": "涌爆核弹",
|
||||
"Icon": "http://47.109.132.12/uploads/48001.png?timespan=1788935117",
|
||||
"Type": 3,
|
||||
"Count": 1,
|
||||
"OtherId": "48001",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "588202",
|
||||
"Name": "涌爆核弹",
|
||||
"Icon": "http://47.109.132.12/uploads/48001.png?timespan=1788935117",
|
||||
"Type": 3,
|
||||
"Count": 1,
|
||||
"OtherId": "48001",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "588203",
|
||||
"Name": "狂暴卡",
|
||||
"Icon": "http://47.109.132.12/uploads/40004.png?v=1784285974",
|
||||
"Type": 3,
|
||||
"Count": 10,
|
||||
"OtherId": "40004",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "588204",
|
||||
"Name": "狂暴卡",
|
||||
"Icon": "http://47.109.132.12/uploads/40004.png?v=1784285974",
|
||||
"Type": 3,
|
||||
"Count": 10,
|
||||
"OtherId": "40004",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "588205",
|
||||
"Name": "狂暴卡",
|
||||
"Icon": "http://47.109.132.12/uploads/40004.png?v=1784285974",
|
||||
"Type": 3,
|
||||
"Count": 10,
|
||||
"OtherId": "40004",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "588206",
|
||||
"Name": "狂暴卡",
|
||||
"Icon": "http://47.109.132.12/uploads/40004.png?v=1784285974",
|
||||
"Type": 3,
|
||||
"Count": 10,
|
||||
"OtherId": "40004",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "588207",
|
||||
"Name": "金币",
|
||||
"Icon": "",
|
||||
"Type": 1,
|
||||
"Count": 100000,
|
||||
"OtherId": "",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "588208",
|
||||
"Name": "金币",
|
||||
"Icon": "",
|
||||
"Type": 1,
|
||||
"Count": 100000,
|
||||
"OtherId": "",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "588209",
|
||||
"Name": "钻石",
|
||||
"Icon": "",
|
||||
"Type": 2,
|
||||
"Count": 5,
|
||||
"OtherId": "",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "588210",
|
||||
"Name": "钻石",
|
||||
"Icon": "",
|
||||
"Type": 2,
|
||||
"Count": 5,
|
||||
"OtherId": "",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "588211",
|
||||
"Name": "召唤卡",
|
||||
"Icon": "http://47.109.132.12/uploads/40006.png",
|
||||
"Type": 3,
|
||||
"Count": 3,
|
||||
"OtherId": "40006",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "588212",
|
||||
"Name": "召唤卡",
|
||||
"Icon": "http://47.109.132.12/uploads/40006.png",
|
||||
"Type": 3,
|
||||
"Count": 3,
|
||||
"OtherId": "40006",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "588213",
|
||||
"Name": "召唤卡",
|
||||
"Icon": "http://47.109.132.12/uploads/40006.png",
|
||||
"Type": 3,
|
||||
"Count": 3,
|
||||
"OtherId": "40006",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "588214",
|
||||
"Name": "召唤卡",
|
||||
"Icon": "http://47.109.132.12/uploads/40006.png",
|
||||
"Type": 3,
|
||||
"Count": 3,
|
||||
"OtherId": "40006",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "588215",
|
||||
"Name": "冰冻卡",
|
||||
"Icon": "http://47.109.132.12/uploads/40003.png?v=1784285984",
|
||||
"Type": 3,
|
||||
"Count": 3,
|
||||
"OtherId": "40003",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "588216",
|
||||
"Name": "冰冻卡",
|
||||
"Icon": "http://47.109.132.12/uploads/40003.png?v=1784285984",
|
||||
"Type": 3,
|
||||
"Count": 3,
|
||||
"OtherId": "40003",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "588217",
|
||||
"Name": "冰冻卡",
|
||||
"Icon": "http://47.109.132.12/uploads/40003.png?v=1784285984",
|
||||
"Type": 3,
|
||||
"Count": 3,
|
||||
"OtherId": "40003",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "588218",
|
||||
"Name": "冰冻卡",
|
||||
"Icon": "http://47.109.132.12/uploads/40003.png?v=1784285984",
|
||||
"Type": 3,
|
||||
"Count": 3,
|
||||
"OtherId": "40003",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "588219",
|
||||
"Name": "刮刮卡",
|
||||
"Icon": "http://47.109.132.12/uploads/40005.png",
|
||||
"Type": 3,
|
||||
"Count": 1,
|
||||
"OtherId": "40005",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "588220",
|
||||
"Name": "刮刮卡",
|
||||
"Icon": "http://47.109.132.12/uploads/40005.png",
|
||||
"Type": 3,
|
||||
"Count": 1,
|
||||
"OtherId": "40005",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "588221",
|
||||
"Name": "金币",
|
||||
"Icon": "",
|
||||
"Type": 1,
|
||||
"Count": 50000,
|
||||
"OtherId": "",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "588222",
|
||||
"Name": "金币",
|
||||
"Icon": "",
|
||||
"Type": 1,
|
||||
"Count": 50000,
|
||||
"OtherId": "",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "588223",
|
||||
"Name": "金币",
|
||||
"Icon": "",
|
||||
"Type": 1,
|
||||
"Count": 50000,
|
||||
"OtherId": "",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "588224",
|
||||
"Name": "金币",
|
||||
"Icon": "",
|
||||
"Type": 1,
|
||||
"Count": 50000,
|
||||
"OtherId": "",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"PoolType": 3,
|
||||
"Level": "A",
|
||||
"Count": 2,
|
||||
"Weight": 1,
|
||||
"PrizeList": [
|
||||
{
|
||||
"PrizeId": "589001",
|
||||
"Name": "堕天使之翼",
|
||||
"Icon": "",
|
||||
"Type": 3,
|
||||
"Count": 1,
|
||||
"OtherId": "12007",
|
||||
"Weight": 1,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "589002",
|
||||
"Name": "流火之焰",
|
||||
"Icon": "",
|
||||
"Type": 3,
|
||||
"Count": 1,
|
||||
"OtherId": "12029",
|
||||
"Weight": 2,
|
||||
"TotalCount": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"PoolType": 3,
|
||||
"Level": "B",
|
||||
"Count": 10,
|
||||
"Weight": 10,
|
||||
"PrizeList": [
|
||||
{
|
||||
"PrizeId": "589101",
|
||||
"Name": "穹渊核弹",
|
||||
"Icon": "http://47.109.132.12/uploads/48004.png?timespan=1788935361",
|
||||
"Type": 3,
|
||||
"Count": 1,
|
||||
"OtherId": "48004",
|
||||
"Weight": 10,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "589102",
|
||||
"Name": "穹渊核弹",
|
||||
"Icon": "http://47.109.132.12/uploads/48004.png?timespan=1788935361",
|
||||
"Type": 3,
|
||||
"Count": 1,
|
||||
"OtherId": "48004",
|
||||
"Weight": 10,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "589103",
|
||||
"Name": "噬浪核弹",
|
||||
"Icon": "http://47.109.132.12/uploads/48003.png?timespan=1788935284",
|
||||
"Type": 3,
|
||||
"Count": 1,
|
||||
"OtherId": "48003",
|
||||
"Weight": 10,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "589104",
|
||||
"Name": "噬浪核弹",
|
||||
"Icon": "http://47.109.132.12/uploads/48003.png?timespan=1788935284",
|
||||
"Type": 3,
|
||||
"Count": 1,
|
||||
"OtherId": "48003",
|
||||
"Weight": 10,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "589105",
|
||||
"Name": "霜雷核弹",
|
||||
"Icon": "http://47.109.132.12/uploads/48002.png?timespan=1788935210",
|
||||
"Type": 3,
|
||||
"Count": 1,
|
||||
"OtherId": "48002",
|
||||
"Weight": 10,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "589106",
|
||||
"Name": "霜雷核弹",
|
||||
"Icon": "http://47.109.132.12/uploads/48002.png?timespan=1788935210",
|
||||
"Type": 3,
|
||||
"Count": 1,
|
||||
"OtherId": "48002",
|
||||
"Weight": 10,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "589107",
|
||||
"Name": "涌爆核弹",
|
||||
"Icon": "http://47.109.132.12/uploads/48001.png?timespan=1788935117",
|
||||
"Type": 3,
|
||||
"Count": 1,
|
||||
"OtherId": "48001",
|
||||
"Weight": 10,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "589108",
|
||||
"Name": "涌爆核弹",
|
||||
"Icon": "http://47.109.132.12/uploads/48001.png?timespan=1788935117",
|
||||
"Type": 3,
|
||||
"Count": 1,
|
||||
"OtherId": "48001",
|
||||
"Weight": 10,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "589109",
|
||||
"Name": "金币",
|
||||
"Icon": "",
|
||||
"Type": 1,
|
||||
"Count": 100000,
|
||||
"OtherId": "",
|
||||
"Weight": 10,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "589110",
|
||||
"Name": "金币",
|
||||
"Icon": "",
|
||||
"Type": 1,
|
||||
"Count": 100000,
|
||||
"OtherId": "",
|
||||
"Weight": 10,
|
||||
"TotalCount": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"PoolType": 3,
|
||||
"Level": "C",
|
||||
"Count": 18,
|
||||
"Weight": 20,
|
||||
"PrizeList": [
|
||||
{
|
||||
"PrizeId": "589201",
|
||||
"Name": "狂暴卡",
|
||||
"Icon": "http://47.109.132.12/uploads/40004.png?v=1784285974",
|
||||
"Type": 3,
|
||||
"Count": 10,
|
||||
"OtherId": "40004",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "589202",
|
||||
"Name": "狂暴卡",
|
||||
"Icon": "http://47.109.132.12/uploads/40004.png?v=1784285974",
|
||||
"Type": 3,
|
||||
"Count": 10,
|
||||
"OtherId": "40004",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "589203",
|
||||
"Name": "狂暴卡",
|
||||
"Icon": "http://47.109.132.12/uploads/40004.png?v=1784285974",
|
||||
"Type": 3,
|
||||
"Count": 10,
|
||||
"OtherId": "40004",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "589204",
|
||||
"Name": "狂暴卡",
|
||||
"Icon": "http://47.109.132.12/uploads/40004.png?v=1784285974",
|
||||
"Type": 3,
|
||||
"Count": 10,
|
||||
"OtherId": "40004",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "589205",
|
||||
"Name": "金币",
|
||||
"Icon": "",
|
||||
"Type": 1,
|
||||
"Count": 100000,
|
||||
"OtherId": "",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "589206",
|
||||
"Name": "金币",
|
||||
"Icon": "",
|
||||
"Type": 1,
|
||||
"Count": 100000,
|
||||
"OtherId": "",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "589207",
|
||||
"Name": "钻石",
|
||||
"Icon": "",
|
||||
"Type": 2,
|
||||
"Count": 5,
|
||||
"OtherId": "",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "589208",
|
||||
"Name": "钻石",
|
||||
"Icon": "",
|
||||
"Type": 2,
|
||||
"Count": 5,
|
||||
"OtherId": "",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "589209",
|
||||
"Name": "召唤卡",
|
||||
"Icon": "http://47.109.132.12/uploads/40006.png",
|
||||
"Type": 3,
|
||||
"Count": 3,
|
||||
"OtherId": "40006",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "589210",
|
||||
"Name": "召唤卡",
|
||||
"Icon": "http://47.109.132.12/uploads/40006.png",
|
||||
"Type": 3,
|
||||
"Count": 3,
|
||||
"OtherId": "40006",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "589211",
|
||||
"Name": "召唤卡",
|
||||
"Icon": "http://47.109.132.12/uploads/40006.png",
|
||||
"Type": 3,
|
||||
"Count": 3,
|
||||
"OtherId": "40006",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "589212",
|
||||
"Name": "召唤卡",
|
||||
"Icon": "http://47.109.132.12/uploads/40006.png",
|
||||
"Type": 3,
|
||||
"Count": 3,
|
||||
"OtherId": "40006",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "589213",
|
||||
"Name": "冰冻卡",
|
||||
"Icon": "http://47.109.132.12/uploads/40003.png?v=1784285984",
|
||||
"Type": 3,
|
||||
"Count": 3,
|
||||
"OtherId": "40003",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "589214",
|
||||
"Name": "冰冻卡",
|
||||
"Icon": "http://47.109.132.12/uploads/40003.png?v=1784285984",
|
||||
"Type": 3,
|
||||
"Count": 3,
|
||||
"OtherId": "40003",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "589215",
|
||||
"Name": "冰冻卡",
|
||||
"Icon": "http://47.109.132.12/uploads/40003.png?v=1784285984",
|
||||
"Type": 3,
|
||||
"Count": 3,
|
||||
"OtherId": "40003",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "589216",
|
||||
"Name": "冰冻卡",
|
||||
"Icon": "http://47.109.132.12/uploads/40003.png?v=1784285984",
|
||||
"Type": 3,
|
||||
"Count": 3,
|
||||
"OtherId": "40003",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "589217",
|
||||
"Name": "刮刮卡",
|
||||
"Icon": "http://47.109.132.12/uploads/40005.png",
|
||||
"Type": 3,
|
||||
"Count": 1,
|
||||
"OtherId": "40005",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
},
|
||||
{
|
||||
"PrizeId": "589218",
|
||||
"Name": "刮刮卡",
|
||||
"Icon": "http://47.109.132.12/uploads/40005.png",
|
||||
"Type": 3,
|
||||
"Count": 1,
|
||||
"OtherId": "40005",
|
||||
"Weight": 20,
|
||||
"TotalCount": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"CostsByPool": {
|
||||
"1": [100, 200, 300, 500, 800, 1000],
|
||||
"2": [100, 200, 300, 500, 800, 1000],
|
||||
"3": [100, 200, 300, 500, 800, 1000]
|
||||
},
|
||||
"UserMaxDraw": 100
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
|
||||
"AllowedHosts": "*",
|
||||
"JwtConfig": {
|
||||
"TokenKey": "token",
|
||||
"Issuer": "admin",
|
||||
"Audience": "",
|
||||
"SecretKey": "henanhuashengchongwuyouxiangongsikejizhongxin",
|
||||
"ValidateIssuer": false,
|
||||
"ValidateAudience": false,
|
||||
"ValidateIssuerSigningKey": false,
|
||||
"ValidateLifetime": false
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"PgSqlConnection": "host=pgsql;database=data_0524;user id=data_0524;password=123456;"
|
||||
},
|
||||
"CapConfig": {
|
||||
"Host": "rabbitmq",
|
||||
"UserName": "admin",
|
||||
"Password": "123456",
|
||||
"VirtualHost": "/",
|
||||
"PGLogHost": "host=pgsql;database=data_0524;user id=data_0524;password=123456;"
|
||||
},
|
||||
"RedisConfig": {
|
||||
"Host": "redis",
|
||||
"Port": 6379,
|
||||
"UserName": "",
|
||||
"Password": "123456",
|
||||
"Database": 1,
|
||||
"ConnectionString": "redis:6379,password=123456,defaultDatabase=2,abortConnect=false"
|
||||
},
|
||||
"MongoDbSettings": {
|
||||
"username": "root",
|
||||
"password": "root23456",
|
||||
"ServerUrl": "mongodb://mongo:27017"
|
||||
},
|
||||
"WXConfig": {
|
||||
"AppId": "wxceab9ce5b4474219",
|
||||
"Secret": "5a291d7b295c76da579611da3f83e059",
|
||||
"Url": "https://api.weixin.qq.com"
|
||||
},
|
||||
"BulletConfig": {
|
||||
"SignKey": "63215f0afa3f5b25",
|
||||
"MaxBulletPerUser": 100,
|
||||
"TimeWindowSeconds": 500000
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
using DotNetCore.CAP.Messages;
|
||||
using AdminService.Tasks;
|
||||
|
||||
namespace AdminHost.Extension
|
||||
{
|
||||
public static class CapExtension
|
||||
{
|
||||
public static IServiceCollection AddCap(this IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
services.AddCap(x =>
|
||||
{
|
||||
x.UseDashboard();
|
||||
x.UseRabbitMQ(rb =>
|
||||
{
|
||||
rb.HostName = configuration["CapConfig:Host"];
|
||||
rb.UserName = configuration["CapConfig:UserName"];
|
||||
rb.Password = configuration["CapConfig:Password"];
|
||||
rb.VirtualHost = configuration["CapConfig:VirtualHost"];
|
||||
rb.ExchangeName = "cap.exchange";
|
||||
});
|
||||
x.UsePostgreSql(op =>
|
||||
{
|
||||
op.ConnectionString = configuration["CapConfig:PGLogHost"];
|
||||
op.Schema = "public";
|
||||
});
|
||||
|
||||
x.SucceedMessageExpiredAfter = 7 * 24 * 3600;
|
||||
x.FailedRetryCount = 5;
|
||||
x.FailedRetryInterval = 10;
|
||||
x.FailedThresholdCallback = ObjFailedThresholdCallback;
|
||||
|
||||
});
|
||||
return services;
|
||||
}
|
||||
|
||||
private static async void ObjFailedThresholdCallback(FailedInfo failed)
|
||||
{
|
||||
var exception = failed.Message.Headers["cap-exception"] ?? string.Empty;
|
||||
var name = failed.Message.GetName();
|
||||
if (failed.MessageType == MessageType.Publish)
|
||||
{
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using ScheduledService;
|
||||
using UserService.Tasks;
|
||||
|
||||
namespace AdminHost.Jobs;
|
||||
|
||||
public class DailyTaskJob : CronJobService
|
||||
{
|
||||
private readonly ITaskService _taskService;
|
||||
|
||||
public DailyTaskJob(
|
||||
IScheduleConfig<DailyTaskJob> config,
|
||||
ITaskService taskService,
|
||||
ILogger<CronJobService> baseLogger)
|
||||
: base(config.CronExpression, config.TimeZoneInfo, baseLogger)
|
||||
{
|
||||
_taskService = taskService;
|
||||
}
|
||||
|
||||
public override async Task DoWork(CancellationToken cancellationToken)
|
||||
{
|
||||
await _taskService.DayTaskReward();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
|
||||
using System.Text;
|
||||
using Common;
|
||||
using CommonDomain;
|
||||
using CommonService;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace AdminHost.Middleware
|
||||
{
|
||||
public class ApiLoggingMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly ILogger<ApiLoggingMiddleware> _logger;
|
||||
private readonly IApiLogService _apiLogService;
|
||||
|
||||
public ApiLoggingMiddleware(RequestDelegate next, ILogger<ApiLoggingMiddleware> logger, IApiLogService apiLogService)
|
||||
{
|
||||
_next = next;
|
||||
_logger = logger;
|
||||
_apiLogService = apiLogService;
|
||||
}
|
||||
public async Task Invoke(HttpContext httpContext)
|
||||
{
|
||||
var request = httpContext.Request;
|
||||
if (request.Path.StartsWithSegments(new PathString("/api")) && request.Method.ToLower() != "options")
|
||||
{
|
||||
var requestTime = DateTime.Now;
|
||||
var requestBodyContent = string.Empty;
|
||||
var originalRequest = httpContext.Response.Body;
|
||||
if (request.Method == "POST")
|
||||
{
|
||||
request.EnableBuffering();
|
||||
requestBodyContent = await new StreamReader(request.Body).ReadToEndAsync();
|
||||
request.Body.Position = 0;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
using (var responseBody = new MemoryStream())
|
||||
{
|
||||
var response = httpContext.Response;
|
||||
response.Body = responseBody;
|
||||
await _next(httpContext);
|
||||
string responseBodyContent = null;
|
||||
responseBodyContent = await FormatResponse(response);
|
||||
await responseBody.CopyToAsync(originalRequest);
|
||||
|
||||
string token = string.Empty, version = string.Empty, userAgent = string.Empty, source = string.Empty, deviceId = string.Empty, device = string.Empty, ip = string.Empty,sign=string.Empty;
|
||||
if (httpContext.Request != null && httpContext.Request.Headers != null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(httpContext.Request.Headers["Token"]))
|
||||
{
|
||||
token = httpContext.Request.Headers["Token"] + ""; }
|
||||
|
||||
if (!string.IsNullOrEmpty(httpContext.Request.Headers["Version"]))
|
||||
{
|
||||
version = httpContext.Request.Headers["Version"] + "";
|
||||
}
|
||||
if (!string.IsNullOrEmpty(httpContext.Request.Headers["User-Agent"]))
|
||||
{
|
||||
userAgent = httpContext.Request.Headers["User-Agent"] + "";
|
||||
}
|
||||
if (!string.IsNullOrEmpty(httpContext.Request.Headers["Source"]))
|
||||
{
|
||||
source = httpContext.Request.Headers["Source"] + "";
|
||||
}
|
||||
if (!string.IsNullOrEmpty(httpContext.Request.Headers["DeviceId"]))
|
||||
{
|
||||
deviceId = httpContext.Request.Headers["DeviceId"] + "";
|
||||
}
|
||||
if (!string.IsNullOrEmpty(httpContext.Request.Headers["Device"]))
|
||||
{
|
||||
device = httpContext.Request.Headers["Device"] + "";
|
||||
}
|
||||
if (!string.IsNullOrEmpty(httpContext.Request.Headers["Ip"]))
|
||||
{
|
||||
ip = httpContext.Request.Headers["Ip"] + "";
|
||||
}
|
||||
if (!string.IsNullOrEmpty(httpContext.Request.Headers["Sign"]))
|
||||
{
|
||||
sign = httpContext.Request.Headers["Sign"] + "";
|
||||
}
|
||||
}
|
||||
try
|
||||
{
|
||||
if (!string.IsNullOrEmpty(responseBodyContent))
|
||||
{
|
||||
BaseResponse<object> baseResponse = JsonConvert.DeserializeObject<BaseResponse<object>>(responseBodyContent);
|
||||
if (baseResponse != null && baseResponse.Data != null)
|
||||
{
|
||||
baseResponse.Data = null;
|
||||
}
|
||||
responseBodyContent = JsonConvert.SerializeObject(baseResponse);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "API响应日志脱敏序列化失败 path={Path}", request.Path);
|
||||
}
|
||||
var now = DateTime.Now;
|
||||
var apilog = new ApiLog()
|
||||
{
|
||||
requestTime = now,
|
||||
accessToken = token,
|
||||
clientIP = httpContext.Connection.RemoteIpAddress.ToString(),
|
||||
consumeTime = (DateTime.Now - requestTime).Milliseconds,
|
||||
requestHead= JsonConvert.SerializeObject(httpContext.Request.Headers),
|
||||
requestUrl = request.Path,
|
||||
requestMethod = request.Method.ToLower(),
|
||||
requestQuery = request.QueryString.ToString(),
|
||||
requestBody = requestBodyContent,
|
||||
httpStatus = httpContext.Response.StatusCode,
|
||||
responseBody = responseBodyContent,
|
||||
userAgent = userAgent,
|
||||
device= device,
|
||||
deviceId= deviceId,
|
||||
ip=ip,
|
||||
source=source,
|
||||
version=version,
|
||||
sign=sign
|
||||
};
|
||||
await _apiLogService.InsertOneAsync(apilog);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "API日志记录失败 path={Path}", request.Path);
|
||||
}
|
||||
finally
|
||||
{
|
||||
httpContext.Response.Body = originalRequest;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await _next(httpContext);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<string> FormatResponse(HttpResponse response)
|
||||
{
|
||||
response.Body.Seek(0, SeekOrigin.Begin);
|
||||
string text = await new StreamReader(response.Body).ReadToEndAsync();
|
||||
response.Body.Seek(0, SeekOrigin.Begin);
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ApiLoggingMiddlewareExtensions
|
||||
{
|
||||
public static IApplicationBuilder UseApiLogging(this IApplicationBuilder app)
|
||||
{
|
||||
return app.UseMiddleware<ApiLoggingMiddleware>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
using AdminService.System;
|
||||
using AdminService.Customer;
|
||||
using AdminService.Admin;
|
||||
using Autofac;
|
||||
using CommonService;
|
||||
using MongoRepository;
|
||||
using UserService;
|
||||
using UserService.BeautifulNumber;
|
||||
|
||||
namespace AdminHost.Middleware
|
||||
{
|
||||
public class AutofacModule : Module
|
||||
{
|
||||
protected override void Load(ContainerBuilder builder)
|
||||
{
|
||||
builder.RegisterGeneric(typeof(MongoBaseRepository<>)).As(typeof(IMongoBaseRepository<>)).InstancePerLifetimeScope();
|
||||
|
||||
builder.RegisterType<RedisService.RedisService>()
|
||||
.As<RedisService.IRedisService>()
|
||||
.InstancePerLifetimeScope();
|
||||
|
||||
builder.RegisterAssemblyTypes(typeof(ApiLogService).Assembly)
|
||||
.Where(t => t.Name.EndsWith("Service"))
|
||||
.AsImplementedInterfaces()
|
||||
.InstancePerLifetimeScope();
|
||||
|
||||
builder.RegisterAssemblyTypes(typeof(AdminOperationLogService).Assembly)
|
||||
.Where(t => t.Name.EndsWith("Service"))
|
||||
.AsImplementedInterfaces()
|
||||
.InstancePerLifetimeScope();
|
||||
|
||||
builder.RegisterAssemblyTypes(typeof(SystemService).Assembly)
|
||||
.Where(t => t.Name.EndsWith("Service"))
|
||||
.AsImplementedInterfaces()
|
||||
.InstancePerLifetimeScope();
|
||||
|
||||
builder.RegisterAssemblyTypes(typeof(UserFeedbackService).Assembly)
|
||||
.Where(t => t.Name.EndsWith("Service"))
|
||||
.AsImplementedInterfaces()
|
||||
.InstancePerLifetimeScope();
|
||||
|
||||
builder.RegisterType<CommonService.BackgroundOperationService>()
|
||||
.As<CommonService.IBackgroundOperationService>()
|
||||
.InstancePerLifetimeScope();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
using System.Text;
|
||||
using Domain.Admin.Admin;
|
||||
using Domain.Admin.Admin.ENum;
|
||||
|
||||
namespace AdminHost.Middleware
|
||||
{
|
||||
public class OperationLogMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly ILogger<OperationLogMiddleware> _logger;
|
||||
|
||||
public OperationLogMiddleware(RequestDelegate next, ILogger<OperationLogMiddleware> logger)
|
||||
{
|
||||
_next = next;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task InvokeAsync(HttpContext context, Domain.Admin.Admin.IAdminOperationLogService logService)
|
||||
{
|
||||
var path = context.Request.Path.Value ?? "";
|
||||
var method = context.Request.Method;
|
||||
|
||||
bool shouldLog = path.StartsWith("/adminapi", StringComparison.OrdinalIgnoreCase) ||
|
||||
path.StartsWith("/api/Admin", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
if (!shouldLog)
|
||||
{
|
||||
await _next(context);
|
||||
return;
|
||||
}
|
||||
|
||||
if (path.Contains("/login", StringComparison.OrdinalIgnoreCase) ||
|
||||
path.Contains("/GetAccessToken", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
await _next(context);
|
||||
return;
|
||||
}
|
||||
|
||||
string? requestBody = null;
|
||||
|
||||
if (method == "POST" || method == "PUT")
|
||||
{
|
||||
context.Request.EnableBuffering();
|
||||
using var reader = new StreamReader(context.Request.Body, Encoding.UTF8, leaveOpen: true);
|
||||
requestBody = await reader.ReadToEndAsync();
|
||||
context.Request.Body.Position = 0;
|
||||
}
|
||||
|
||||
var startTime = DateTime.Now;
|
||||
string? ip = GetClientIp(context);
|
||||
string? adminId = null;
|
||||
string? adminName = null;
|
||||
|
||||
if (context.User.Identity?.IsAuthenticated == true)
|
||||
{
|
||||
adminId = context.User.FindFirst("uid")?.Value;
|
||||
adminName = context.User.FindFirst("dname")?.Value
|
||||
?? context.User.FindFirst(System.Security.Claims.ClaimTypes.Name)?.Value;
|
||||
}
|
||||
|
||||
if (adminId == "administrator" || adminName == "administrator")
|
||||
{
|
||||
await _next(context);
|
||||
return;
|
||||
}
|
||||
|
||||
int statusCode = 200;
|
||||
|
||||
try
|
||||
{
|
||||
await _next(context);
|
||||
statusCode = context.Response.StatusCode;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
statusCode = 500;
|
||||
_logger.LogError(ex, "请求处理异常");
|
||||
throw;
|
||||
}
|
||||
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var log = new AdminOperationLog
|
||||
{
|
||||
AdminId = adminId,
|
||||
AdminName = adminName,
|
||||
Module = (int)GetModuleFromPath(path),
|
||||
ActionType = GetActionType(method, path),
|
||||
Description = $"{GetActionType(method, path)} - {path}",
|
||||
HttpMethod = method,
|
||||
RequestPath = path,
|
||||
RequestParams = requestBody?.Length > 4000 ? requestBody[..4000] : requestBody,
|
||||
Response = null,
|
||||
Ip = ip,
|
||||
Status = statusCode >= 200 && statusCode < 400 ? 1 : 0,
|
||||
OperateTime = startTime
|
||||
};
|
||||
|
||||
await logService.AddLog(log);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "记录操作日志失败");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static string? GetClientIp(HttpContext context)
|
||||
{
|
||||
var ip = context.Request.Headers["X-Forwarded-For"].FirstOrDefault();
|
||||
if (string.IsNullOrEmpty(ip))
|
||||
{
|
||||
ip = context.Connection.RemoteIpAddress?.ToString();
|
||||
}
|
||||
return ip;
|
||||
}
|
||||
|
||||
private static EnumOperationModule GetModuleFromPath(string path)
|
||||
{
|
||||
if (path.Contains("/AdminUser", StringComparison.OrdinalIgnoreCase))
|
||||
return EnumOperationModule.User;
|
||||
if (path.Contains("/Customer", StringComparison.OrdinalIgnoreCase))
|
||||
return EnumOperationModule.Customer;
|
||||
if (path.Contains("/BeautifulNumber", StringComparison.OrdinalIgnoreCase))
|
||||
return EnumOperationModule.BeautifulNumber;
|
||||
if (path.Contains("/Notice", StringComparison.OrdinalIgnoreCase))
|
||||
return EnumOperationModule.Notice;
|
||||
if (path.Contains("/Task", StringComparison.OrdinalIgnoreCase))
|
||||
return EnumOperationModule.Task;
|
||||
if (path.Contains("/GameConfig", StringComparison.OrdinalIgnoreCase))
|
||||
return EnumOperationModule.GameConfig;
|
||||
if (path.Contains("/Room", StringComparison.OrdinalIgnoreCase))
|
||||
return EnumOperationModule.Room;
|
||||
if (path.Contains("/System/user", StringComparison.OrdinalIgnoreCase) ||
|
||||
path.Contains("/system/user", StringComparison.OrdinalIgnoreCase))
|
||||
return EnumOperationModule.SystemUser;
|
||||
if (path.Contains("/System/role", StringComparison.OrdinalIgnoreCase) ||
|
||||
path.Contains("/system/role", StringComparison.OrdinalIgnoreCase))
|
||||
return EnumOperationModule.Role;
|
||||
if (path.Contains("/System/nav", StringComparison.OrdinalIgnoreCase) ||
|
||||
path.Contains("/system/nav", StringComparison.OrdinalIgnoreCase))
|
||||
return EnumOperationModule.Navigation;
|
||||
if (path.Contains("/System/dic", StringComparison.OrdinalIgnoreCase) ||
|
||||
path.Contains("/system/dic", StringComparison.OrdinalIgnoreCase))
|
||||
return EnumOperationModule.Dictionary;
|
||||
|
||||
return EnumOperationModule.Other;
|
||||
}
|
||||
|
||||
private static string GetActionType(string method, string path)
|
||||
{
|
||||
if (path.Contains("/Ban", StringComparison.OrdinalIgnoreCase) ||
|
||||
path.Contains("/ban", StringComparison.OrdinalIgnoreCase))
|
||||
return "封号";
|
||||
if (path.Contains("/Unban", StringComparison.OrdinalIgnoreCase) ||
|
||||
path.Contains("/unban", StringComparison.OrdinalIgnoreCase))
|
||||
return "解封";
|
||||
if (path.Contains("/Batch", StringComparison.OrdinalIgnoreCase) ||
|
||||
path.Contains("/batch", StringComparison.OrdinalIgnoreCase))
|
||||
return "批量操作";
|
||||
if (path.Contains("/Email", StringComparison.OrdinalIgnoreCase) ||
|
||||
path.Contains("/email", StringComparison.OrdinalIgnoreCase))
|
||||
return "发送邮件";
|
||||
if (path.Contains("/Init", StringComparison.OrdinalIgnoreCase) ||
|
||||
path.Contains("/init", StringComparison.OrdinalIgnoreCase))
|
||||
return "初始化";
|
||||
if (path.Contains("/Freeze", StringComparison.OrdinalIgnoreCase) ||
|
||||
path.Contains("/freeze", StringComparison.OrdinalIgnoreCase))
|
||||
return "冻结";
|
||||
if (path.Contains("/Unfreeze", StringComparison.OrdinalIgnoreCase) ||
|
||||
path.Contains("/unfreeze", StringComparison.OrdinalIgnoreCase))
|
||||
return "解冻";
|
||||
if (path.Contains("/Give", StringComparison.OrdinalIgnoreCase) ||
|
||||
path.Contains("/give", StringComparison.OrdinalIgnoreCase))
|
||||
return "赠送";
|
||||
if (path.Contains("/Clear", StringComparison.OrdinalIgnoreCase) ||
|
||||
path.Contains("/clear", StringComparison.OrdinalIgnoreCase))
|
||||
return "清空";
|
||||
if (path.Contains("/Robot", StringComparison.OrdinalIgnoreCase) ||
|
||||
path.Contains("/robot", StringComparison.OrdinalIgnoreCase))
|
||||
return "机器人管理";
|
||||
if (path.Contains("/state", StringComparison.OrdinalIgnoreCase) ||
|
||||
path.Contains("/State", StringComparison.OrdinalIgnoreCase))
|
||||
return "设置状态";
|
||||
if (path.Contains("/icon", StringComparison.OrdinalIgnoreCase) ||
|
||||
path.Contains("/Icon", StringComparison.OrdinalIgnoreCase))
|
||||
return "修改头像";
|
||||
if (path.Contains("/list", StringComparison.OrdinalIgnoreCase) ||
|
||||
path.Contains("/List", StringComparison.OrdinalIgnoreCase) ||
|
||||
path.Contains("/GetAll", StringComparison.OrdinalIgnoreCase) ||
|
||||
path.Contains("/getall", StringComparison.OrdinalIgnoreCase) ||
|
||||
path.Contains("/Get", StringComparison.OrdinalIgnoreCase) ||
|
||||
path.Contains("/get", StringComparison.OrdinalIgnoreCase) ||
|
||||
path.Contains("/check", StringComparison.OrdinalIgnoreCase) ||
|
||||
path.Contains("/Check", StringComparison.OrdinalIgnoreCase))
|
||||
return "查询";
|
||||
if (path.Contains("/add", StringComparison.OrdinalIgnoreCase) ||
|
||||
path.Contains("/create", StringComparison.OrdinalIgnoreCase) ||
|
||||
path.Contains("/Add", StringComparison.OrdinalIgnoreCase) ||
|
||||
path.Contains("/Create", StringComparison.OrdinalIgnoreCase) ||
|
||||
path.Contains("/send", StringComparison.OrdinalIgnoreCase) ||
|
||||
path.Contains("/Send", StringComparison.OrdinalIgnoreCase))
|
||||
return "新增";
|
||||
if (path.Contains("/update", StringComparison.OrdinalIgnoreCase) ||
|
||||
path.Contains("/edit", StringComparison.OrdinalIgnoreCase) ||
|
||||
path.Contains("/Update", StringComparison.OrdinalIgnoreCase) ||
|
||||
path.Contains("/Edit", StringComparison.OrdinalIgnoreCase) ||
|
||||
path.Contains("/set-", StringComparison.OrdinalIgnoreCase))
|
||||
return "修改";
|
||||
if (path.Contains("/delete", StringComparison.OrdinalIgnoreCase) ||
|
||||
path.Contains("/Delete", StringComparison.OrdinalIgnoreCase))
|
||||
return "删除";
|
||||
|
||||
return method switch
|
||||
{
|
||||
"GET" => "查询",
|
||||
"POST" => "新增",
|
||||
"PUT" => "修改",
|
||||
"DELETE" => "删除",
|
||||
_ => "操作"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static class OperationLogMiddlewareExtensions
|
||||
{
|
||||
public static IApplicationBuilder UseOperationLog(this IApplicationBuilder builder)
|
||||
{
|
||||
return builder.UseMiddleware<OperationLogMiddleware>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
using System.Text.Json;
|
||||
using AdminApi;
|
||||
using AdminHost.Extension;
|
||||
using AdminHost.Jobs;
|
||||
using AdminHost.Middleware;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using AdminService;
|
||||
using Autofac;
|
||||
using Autofac.Extensions.DependencyInjection;
|
||||
using Common.Request;
|
||||
|
||||
using JWTHelper;
|
||||
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Diagnostics.HealthChecks;
|
||||
using Microsoft.Extensions.Options;
|
||||
using MongoRepository.Config;
|
||||
using Newtonsoft.Json;
|
||||
using NLog.Extensions.Logging;
|
||||
using NLog.Web;
|
||||
using RedisHelper;
|
||||
using ScheduledService;
|
||||
using SharpAbp.Infrastructure.Api.Middleware;
|
||||
|
||||
Environment.SetEnvironmentVariable("SERVICE_NAME",
|
||||
Environment.GetEnvironmentVariable("SERVICE_NAME")
|
||||
?? System.Reflection.Assembly.GetEntryAssembly()?.GetName().Name
|
||||
?? "AdminHost");
|
||||
Environment.SetEnvironmentVariable("INSTANCE_ID",
|
||||
Environment.GetEnvironmentVariable("INSTANCE_ID") ?? "default");
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Add services to the container.
|
||||
builder.Services.AddControllers()
|
||||
.AddApplicationPart(typeof(AssemblyMarker).Assembly)
|
||||
.AddControllersAsServices()
|
||||
.ConfigureApiBehaviorOptions(options =>
|
||||
{
|
||||
options.SuppressModelStateInvalidFilter = true;
|
||||
});
|
||||
|
||||
builder.Services.AddControllers();
|
||||
builder.Logging.AddNLog();
|
||||
builder.Host.UseNLog();
|
||||
|
||||
builder.Services.AddControllers().AddNewtonsoftJson(
|
||||
option => {
|
||||
option.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
|
||||
option.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Local;
|
||||
option.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
|
||||
option.SerializerSettings.ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver();
|
||||
}
|
||||
);
|
||||
|
||||
builder.Configuration.AddJsonFile(Path.Combine("Config", "system.json"), optional: true);
|
||||
builder.Services.Configure<MongoDbSettings>(builder.Configuration.GetSection(nameof(MongoDbSettings)));
|
||||
builder.Services.AddSingleton<IMongoDbSettings>(sp => sp.GetRequiredService<IOptions<MongoDbSettings>>().Value);
|
||||
|
||||
builder.Services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
||||
builder.Services.AddSingleton<IRequestApi, RequestApi>();
|
||||
builder.Services.AddAutoMapper(typeof(AutoMapperConfig));
|
||||
|
||||
builder.Services.AddHealthChecks()
|
||||
.AddCheck("self", () => HealthCheckResult.Healthy(), tags: new[] { "self" });
|
||||
|
||||
|
||||
|
||||
builder.Services.AddCors(options =>
|
||||
{
|
||||
options.AddPolicy("Cors", policy =>
|
||||
{
|
||||
policy
|
||||
.SetIsOriginAllowed(_ => true)
|
||||
.AllowAnyHeader()
|
||||
.AllowAnyMethod()
|
||||
.AllowCredentials();
|
||||
});
|
||||
});
|
||||
|
||||
builder.Services.AddSignalR();
|
||||
|
||||
#region jwt
|
||||
builder.Services.AddCustomJwt(builder.Configuration);
|
||||
#endregion
|
||||
|
||||
#region redis
|
||||
builder.Services.AddRedisCache(builder.Configuration);
|
||||
#endregion
|
||||
|
||||
#region Cap
|
||||
builder.Services.AddCap(builder.Configuration);
|
||||
#endregion
|
||||
|
||||
builder.Services.AddCronJob<DailyTaskJob>(c =>
|
||||
{
|
||||
c.TimeZoneInfo = TimeZoneInfo.Local;
|
||||
c.CronExpression = @"0 0 0 * * *";
|
||||
});
|
||||
|
||||
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
|
||||
builder.Host.ConfigureContainer<ContainerBuilder>(builder =>
|
||||
{
|
||||
builder.RegisterModule<AutofacModule>();
|
||||
});
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
|
||||
|
||||
if (app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
}
|
||||
|
||||
app.UseCors("Cors");
|
||||
app.UseRouting();
|
||||
app.UseStaticFiles();
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
|
||||
app.UseApiLogging();
|
||||
|
||||
app.UseOperationLog();
|
||||
|
||||
app.MapControllers();
|
||||
app.UseGlobalExceptionHandler();
|
||||
app.MapHealthChecks("/health", new HealthCheckOptions
|
||||
{
|
||||
Predicate = _ => true,
|
||||
ResponseWriter = async (context, report) =>
|
||||
{
|
||||
context.Response.ContentType = "application/json";
|
||||
var result = new
|
||||
{
|
||||
status = report.Status.ToString(),
|
||||
checks = report.Entries.Select(e => new
|
||||
{
|
||||
name = e.Key,
|
||||
status = e.Value.Status.ToString(),
|
||||
description = e.Value.Description,
|
||||
duration = e.Value.Duration.TotalMilliseconds
|
||||
}),
|
||||
totalDuration = report.TotalDuration.TotalMilliseconds
|
||||
};
|
||||
await context.Response.WriteAsync(System.Text.Json.JsonSerializer.Serialize(result));
|
||||
}
|
||||
});
|
||||
|
||||
app.Run();
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:57330",
|
||||
"sslPort": 0
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"http": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"applicationUrl": "http://localhost:8002/",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*",
|
||||
"CapConfig": {
|
||||
"Host": "rabbitmq",
|
||||
"UserName": "admin",
|
||||
"Password": "123456",
|
||||
"VirtualHost": "/",
|
||||
"PGLogHost": "host=pgsql;database=data_0524;user id=data_0524;password=123456;"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
name: webapi
|
||||
services:
|
||||
0524admin:
|
||||
image: 0524admin
|
||||
container_name: '0524admin'
|
||||
restart: always
|
||||
ports:
|
||||
- '5241:80'
|
||||
@@ -0,0 +1,135 @@
|
||||
|
||||
|
||||
layui.define(['laytpl', 'layer', 'element', 'util'], function(exports){
|
||||
exports('setter', {
|
||||
baseurl:'http://localhost:8002/',
|
||||
customerApiUrl: 'http://localhost:8007/',
|
||||
container: 'LAY_app' //容器ID
|
||||
,base: layui.cache.base //记录layuiAdmin文件夹所在路径
|
||||
,views: layui.cache.base + 'views/' //视图所在目录
|
||||
, entry: 'user/administrators/list' //默认视图文件名
|
||||
,engine: '.html' //视图文件后缀名
|
||||
,pageTabs: true //是否开启页面选项卡功能
|
||||
,type: 'tab' //tab切换模式,不设置则每次点击都刷新
|
||||
|
||||
, name: '朋客捕鱼后台管理'
|
||||
,tableName: 'layuiAdmin' //本地存储表名
|
||||
,MOD_NAME: 'admin' //模块事件名
|
||||
|
||||
,debug: false //是否开启调试模式。如开启,接口异常时会抛出异常 URL 等信息
|
||||
|
||||
,interceptor: true //是否开启未登入拦截
|
||||
|
||||
//自定义请求字段
|
||||
,request: {
|
||||
tokenName: 'token' //自动携带 token 的字段名。可设置 false 不携带。
|
||||
}
|
||||
|
||||
//自定义响应字段
|
||||
,response: {
|
||||
statusName: 'code' //数据状态的字段名称
|
||||
,statusCode: {
|
||||
ok: 0 //数据状态一切正常的状态码
|
||||
, logout: 401 //登录状态失效的状态码
|
||||
}
|
||||
,msgName: 'info' //状态信息的字段名称
|
||||
,dataName: 'data' //数据详情的字段名称
|
||||
}
|
||||
|
||||
//独立页面路由,可随意添加(无需写参数)
|
||||
,indPage: [
|
||||
'/user/login' //登入页
|
||||
,'/user/reg' //注册页
|
||||
,'/user/forget' //找回密码
|
||||
,'/template/tips/test' //独立页的一个测试 demo
|
||||
]
|
||||
|
||||
//扩展的第三方模块
|
||||
,extend: [
|
||||
'echarts', //echarts 核心包
|
||||
'echartsTheme' //echarts 主题
|
||||
]
|
||||
|
||||
//主题配置
|
||||
,theme: {
|
||||
//内置主题配色方案
|
||||
color: [{
|
||||
main: '#20222A' //主题色
|
||||
,selected: '#009688' //选中色
|
||||
,alias: 'default' //默认别名
|
||||
},{
|
||||
main: '#03152A'
|
||||
,selected: '#3B91FF'
|
||||
,alias: 'dark-blue' //藏蓝
|
||||
},{
|
||||
main: '#2E241B'
|
||||
,selected: '#A48566'
|
||||
,alias: 'coffee' //咖啡
|
||||
},{
|
||||
main: '#50314F'
|
||||
,selected: '#7A4D7B'
|
||||
,alias: 'purple-red' //紫红
|
||||
},{
|
||||
main: '#344058'
|
||||
,logo: '#1E9FFF'
|
||||
,selected: '#1E9FFF'
|
||||
,alias: 'ocean' //海洋
|
||||
},{
|
||||
main: '#3A3D49'
|
||||
,logo: '#2F9688'
|
||||
,selected: '#5FB878'
|
||||
,alias: 'green' //墨绿
|
||||
},{
|
||||
main: '#20222A'
|
||||
,logo: '#F78400'
|
||||
,selected: '#F78400'
|
||||
,alias: 'red' //橙色
|
||||
},{
|
||||
main: '#28333E'
|
||||
,logo: '#AA3130'
|
||||
,selected: '#AA3130'
|
||||
,alias: 'fashion-red' //时尚红
|
||||
},{
|
||||
main: '#24262F'
|
||||
,logo: '#3A3D49'
|
||||
,selected: '#009688'
|
||||
,alias: 'classic-black' //经典黑
|
||||
},{
|
||||
logo: '#226A62'
|
||||
,header: '#2F9688'
|
||||
,alias: 'green-header' //墨绿头
|
||||
},{
|
||||
main: '#344058'
|
||||
,logo: '#0085E8'
|
||||
,selected: '#1E9FFF'
|
||||
,header: '#1E9FFF'
|
||||
,alias: 'ocean-header' //海洋头
|
||||
},{
|
||||
header: '#393D49'
|
||||
,alias: 'classic-black-header' //经典黑
|
||||
},{
|
||||
main: '#50314F'
|
||||
,logo: '#50314F'
|
||||
,selected: '#7A4D7B'
|
||||
,header: '#50314F'
|
||||
,alias: 'purple-red-header' //紫红头
|
||||
},{
|
||||
main: '#28333E'
|
||||
,logo: '#28333E'
|
||||
,selected: '#AA3130'
|
||||
,header: '#AA3130'
|
||||
,alias: 'fashion-red-header' //时尚红头
|
||||
},{
|
||||
main: '#28333E'
|
||||
,logo: '#009688'
|
||||
,selected: '#009688'
|
||||
,header: '#009688'
|
||||
,alias: 'green-header' //墨绿头
|
||||
}]
|
||||
|
||||
//初始的颜色索引,对应上面的配色方案数组索引
|
||||
//如果本地已经有主题色记录,则以本地记录为优先,除非请求本地数据(localStorage)
|
||||
,initColorIndex: 0
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,131 @@
|
||||
|
||||
layui.define(["table", "admin", "form", 'user', 'tree'], function (e) {
|
||||
var i = (layui.$, layui.admin),
|
||||
$ = layui.$,
|
||||
t = layui.view,
|
||||
l = layui.table,
|
||||
r = layui.form,
|
||||
tree = layui.tree,
|
||||
admin = layui.admin,
|
||||
setter = layui.setter;
|
||||
var treedata = [];
|
||||
//提交数据
|
||||
|
||||
l.render({
|
||||
elem: "#LAY-user-back-manage",
|
||||
url: setter.baseurl + "adminapi/banner/list",
|
||||
method: "post",
|
||||
contentType: "application/json",
|
||||
page: true,
|
||||
where: {
|
||||
keyWord: "",
|
||||
state: -1
|
||||
},
|
||||
request: {
|
||||
pageName: 'pageIndex' //页码的参数名称,默认:page
|
||||
, limitName: 'pageSize' //每页数据量的参数名,默认:limit
|
||||
},
|
||||
limit: 10,
|
||||
headers: {
|
||||
'token': layui.data(setter.tableName)[setter.request.tokenName]
|
||||
},
|
||||
cols: [
|
||||
[
|
||||
{
|
||||
field: "title",
|
||||
title: "标题",
|
||||
}, {
|
||||
field: "url",
|
||||
title: "链接"
|
||||
}
|
||||
, {
|
||||
field: "times",
|
||||
title: "时长(秒)"
|
||||
}, {
|
||||
field: "seeCount",
|
||||
title: "浏览次数"
|
||||
}, {
|
||||
field: "state",
|
||||
title: "状态",
|
||||
templet: function (d) {
|
||||
if (d.state == 1)
|
||||
return "正常";
|
||||
else
|
||||
return "停止"
|
||||
}
|
||||
}, {
|
||||
field: "position",
|
||||
title: "位置",
|
||||
templet: function (d) {
|
||||
var str = "";
|
||||
console.log(d)
|
||||
d.position.forEach(function (currentValue, index, array) {
|
||||
|
||||
str += currentValue.title + ","
|
||||
});
|
||||
|
||||
return str;
|
||||
}
|
||||
}, {
|
||||
field: "createTime",
|
||||
title: "加入时间",
|
||||
sort: !0
|
||||
}, {
|
||||
title: "操作",
|
||||
align: "center",
|
||||
fixed: "right",
|
||||
toolbar: "#table-banneradmin-admin"
|
||||
}]
|
||||
],
|
||||
text: "对不起,加载出现异常!",
|
||||
response: {
|
||||
statusName: 'code', //数据状态的字段名称,默认:code
|
||||
statusCode: 0, //成功的状态码,默认:0
|
||||
msgName: 'info', //状态信息的字段名称,默认:msg
|
||||
countName: 'count', //数据总数的字段名称,默认:count
|
||||
dataName: 'data' //数据列表的字段名称,默认:data
|
||||
}
|
||||
}),
|
||||
|
||||
l.on("tool(LAY-user-back-manage)", function (e) {
|
||||
var l = e.data;
|
||||
console.log(e.event)
|
||||
if ("del" === e.event) {
|
||||
layer.confirm("确定删除此广告?", function (i) {
|
||||
//提交数据
|
||||
admin.req({
|
||||
method: "get",
|
||||
url: setter.baseurl + "adminapi/banner/delete?id=" + e.data.id,//实际使用请改成服务端真实接口
|
||||
async: true,
|
||||
done: function (res) {
|
||||
|
||||
e.del(), layer.close(i)
|
||||
}
|
||||
})
|
||||
|
||||
})
|
||||
}
|
||||
if ("edit" === e.event) {
|
||||
i.popup({
|
||||
title: "编辑广告",
|
||||
area: ["500px", "600px"],
|
||||
id: "LAY-popup-user-add",
|
||||
success: function (e, i) {
|
||||
t(this.id).render("user/administrators/bannerform", l).done(function () {
|
||||
|
||||
r.render(null, "layuiadmin-form-admin"),
|
||||
|
||||
r.on("submit(LAY-user-back-submit)", function (e) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}), e("banneradmin", {})
|
||||
});
|
||||
@@ -0,0 +1,142 @@
|
||||
/** layuiAdmin.pro-v1.4.0 LPPL License By https://www.layui.com/admin/ */;
|
||||
layui.define(function (exports) {
|
||||
var $ = layui.$;
|
||||
var i = (layui.$, layui.layer, layui.laytpl, layui.setter, layui.view, layui.admin);
|
||||
|
||||
// Helper: convert PascalCase or any case keys to camelCase recursively
|
||||
function toCamelKey(s) {
|
||||
if (!s || typeof s !== 'string') return s;
|
||||
// handle underscores and PascalCase
|
||||
var str = s.replace(/_([a-z])/g, function (m, p1) { return p1.toUpperCase(); });
|
||||
// if contains uppercase letters after first char, convert first char to lower
|
||||
return str.charAt(0).toLowerCase() + str.slice(1);
|
||||
}
|
||||
|
||||
function convertKeysToCamel(obj) {
|
||||
if (obj === null || obj === undefined) return obj;
|
||||
if (Array.isArray(obj)) return obj.map(convertKeysToCamel);
|
||||
if (typeof obj === 'object') {
|
||||
var res = {};
|
||||
for (var k in obj) {
|
||||
if (!Object.prototype.hasOwnProperty.call(obj, k)) continue;
|
||||
var nk = toCamelKey(k);
|
||||
res[nk] = convertKeysToCamel(obj[k]);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
// Wrap admin.req so that all responses have their .data keys converted to camelCase
|
||||
if (i && i.req) {
|
||||
var _origReq = i.req.bind(i);
|
||||
i.req = function (options) {
|
||||
if (!options) return _origReq(options);
|
||||
var userDone = options.done;
|
||||
options.done = function (res) {
|
||||
try {
|
||||
if (res && res.data) {
|
||||
res.data = convertKeysToCamel(res.data);
|
||||
}
|
||||
} catch (e) {
|
||||
console && console.error && console.error('convertKeysToCamel error', e);
|
||||
}
|
||||
if (typeof userDone === 'function') userDone(res);
|
||||
};
|
||||
return _origReq(options);
|
||||
};
|
||||
}
|
||||
|
||||
// Enum definitions: populate as needed. Keys are field names in camelCase, values are arrays of {value,text}
|
||||
window.enumDefinitions = window.enumDefinitions || {
|
||||
fishType: [{ value: 1, text: '普通鱼' }, { value: 2, text: 'Boss鱼' }, { value: 3, text: '黄金鱼' }, { value: 4, text: '特殊鱼' }, { value: 5, text: '特殊Boss' }],
|
||||
saleType: [{ value: 1, text: '钻石' }, { value: 2, text: '房卡' }, { value: 4, text: '金币' }, { value: 5, text: '道具' }]
|
||||
};
|
||||
|
||||
function replaceInputWithSelect($form, name, options, currentValue) {
|
||||
var $input = $form.find('[name="' + name + '"]');
|
||||
if ($input.length === 0) return;
|
||||
// create select
|
||||
var $select = $('<select></select>');
|
||||
$select.attr('name', name);
|
||||
$options = options || [];
|
||||
$select.append($('<option value=""></option>'));
|
||||
$options.forEach(function (opt) {
|
||||
var $opt = $('<option></option>').attr('value', opt.value).text(opt.text);
|
||||
if (String(opt.value) === String(currentValue)) $opt.attr('selected', 'selected');
|
||||
$select.append($opt);
|
||||
});
|
||||
$input.replaceWith($select);
|
||||
}
|
||||
|
||||
// Normalize input/select/textarea name attributes to camelCase
|
||||
function normalizeFormInputNames($container) {
|
||||
try {
|
||||
var $root = $($container || document);
|
||||
$root.find('input[name],select[name],textarea[name]').each(function () {
|
||||
var $el = $(this);
|
||||
var name = $el.attr('name');
|
||||
if (!name) return;
|
||||
var newName = toCamelKey(name);
|
||||
if (newName !== name) {
|
||||
$el.attr('name', newName);
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
console && console.error && console.error('normalizeFormInputNames error', e);
|
||||
}
|
||||
}
|
||||
|
||||
window.renderEnumControls = function ($container) {
|
||||
try {
|
||||
var $ = layui.$;
|
||||
var $form = $($container || document);
|
||||
// normalize names first
|
||||
normalizeFormInputNames($form);
|
||||
Object.keys(window.enumDefinitions).forEach(function (field) {
|
||||
var options = window.enumDefinitions[field];
|
||||
// find any input/select with this name and replace if it's not already a select
|
||||
var $els = $form.find('[name="' + field + '"]');
|
||||
$els.each(function () {
|
||||
var $el = $(this);
|
||||
if ($el.is('select')) return; // already select
|
||||
var currentVal = $el.val();
|
||||
replaceInputWithSelect($form, field, options, currentVal);
|
||||
});
|
||||
});
|
||||
} catch (e) {
|
||||
console && console.error && console.error('renderEnumControls error', e);
|
||||
}
|
||||
};
|
||||
|
||||
// Wrap form.render to invoke enum rendering after original render
|
||||
if (layui && layui.form && layui.form.render) {
|
||||
var _origFormRender = layui.form.render.bind(layui.form);
|
||||
layui.form.render = function () {
|
||||
var res = _origFormRender.apply(null, arguments);
|
||||
try {
|
||||
// arguments may include filter name; find forms with that lay-filter
|
||||
var filter = arguments && arguments.length ? arguments[0] : null;
|
||||
if (typeof filter === 'string') {
|
||||
// find forms with lay-filter
|
||||
var $forms = layui.$("[lay-filter='" + filter + "']");
|
||||
$forms.each(function () { window.renderEnumControls(this); });
|
||||
} else {
|
||||
window.renderEnumControls(document);
|
||||
}
|
||||
} catch (e) { }
|
||||
return res;
|
||||
};
|
||||
}
|
||||
|
||||
i.events.logout = function () {
|
||||
i.req({
|
||||
url: "./json/user/logout.js",
|
||||
type: "get",
|
||||
data: {},
|
||||
done: function (e) {
|
||||
i.exit()
|
||||
}
|
||||
})
|
||||
}, exports("common", {})
|
||||
});
|
||||
@@ -0,0 +1,93 @@
|
||||
|
||||
layui.define(["table", "admin", "form", 'user', 'tree'], function (e) {
|
||||
var i = (layui.$, layui.admin),
|
||||
$ = layui.$,
|
||||
t = layui.view,
|
||||
l = layui.treeTable,
|
||||
r = layui.form,
|
||||
tree = layui.tree,
|
||||
admin = layui.admin,
|
||||
setter = layui.setter;
|
||||
var treedata = [];
|
||||
//提交数据
|
||||
|
||||
l.render({
|
||||
elem: "#LAY-user-back-dic",
|
||||
url: setter.baseurl + "adminapi/System/dic/list",
|
||||
method: "get",
|
||||
page: false,
|
||||
limit: 0,
|
||||
headers: {
|
||||
'token': layui.data(setter.tableName)[setter.request.tokenName]
|
||||
},
|
||||
treeNode:true,
|
||||
cols: [
|
||||
[
|
||||
{
|
||||
field: "name",
|
||||
title: "名称"
|
||||
}, {
|
||||
field: "order",
|
||||
title: "序号"
|
||||
}, {
|
||||
title: "操作",
|
||||
width: 300,
|
||||
align: "center",
|
||||
fixed: "right",
|
||||
toolbar: "#table-role-admin"
|
||||
}]
|
||||
],
|
||||
text: "对不起,加载出现异常!"
|
||||
}), l.on("tool(LAY-user-back-dic)", function (e) {
|
||||
var l = e.data;
|
||||
console.log(e.event)
|
||||
if ("del" === e.event) {
|
||||
layer.confirm("确定删除此菜单?", function (i) {
|
||||
//提交数据
|
||||
admin.req({
|
||||
method: "get",
|
||||
url: setter.baseurl + "adminapi/System/dic/delete?id=" + e.data.id,//实际使用请改成服务端真实接口
|
||||
async: true,
|
||||
done: function (res) {
|
||||
|
||||
e.del(), layer.close(i)
|
||||
}
|
||||
})
|
||||
|
||||
})
|
||||
}
|
||||
if ("edit" === e.event) {
|
||||
i.popup({
|
||||
title: "编辑菜单",
|
||||
area: ["500px", "480px"],
|
||||
id: "LAY-popup-user-add",
|
||||
success: function (e, i) {
|
||||
t(this.id).render("user/administrators/dicform", l).done(function () {
|
||||
r.render(null, "layuiadmin-form-dic"), r.on("submit(LAY-user-dic-submit)", function (e) {
|
||||
|
||||
var param = e.field;
|
||||
param.dicid
|
||||
//提交数据
|
||||
admin.req({
|
||||
method: "post",
|
||||
contentType: "application/json;charset=UTF-8",
|
||||
dataType: "json",
|
||||
headers: {
|
||||
'token': layui.data(setter.tableName)[setter.request.tokenName]
|
||||
},
|
||||
url: setter.baseurl + "adminapi/System/dic/update",//实际使用请改成服务端真实接口
|
||||
async: false,
|
||||
data: JSON.stringify(param),
|
||||
done: function (res) {
|
||||
|
||||
layui.table.reload("LAY-user-back-dic"), layer.close(i)
|
||||
}
|
||||
})
|
||||
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}), e("dic", {})
|
||||
});
|
||||
@@ -0,0 +1,93 @@
|
||||
|
||||
layui.define(["table", "admin", "form", 'user', 'tree'], function (e) {
|
||||
var i = (layui.$, layui.admin),
|
||||
$ = layui.$,
|
||||
t = layui.view,
|
||||
l = layui.treeTable,
|
||||
r = layui.form,
|
||||
tree = layui.tree,
|
||||
admin = layui.admin,
|
||||
setter = layui.setter;
|
||||
var treedata = [];
|
||||
//提交数据
|
||||
|
||||
l.render({
|
||||
elem: "#LAY-user-back-nav",
|
||||
url: setter.baseurl + "adminapi/System/nav/list",
|
||||
method: "get",
|
||||
page: false,
|
||||
limit: 0,
|
||||
headers: {
|
||||
'token': layui.data(setter.tableName)[setter.request.tokenName]
|
||||
},
|
||||
treeNode:true,
|
||||
cols: [
|
||||
[
|
||||
{
|
||||
field: "name",
|
||||
title: "名称"
|
||||
}, {
|
||||
field: "order",
|
||||
title: "序号"
|
||||
}, {
|
||||
title: "操作",
|
||||
width: 300,
|
||||
align: "center",
|
||||
fixed: "right",
|
||||
toolbar: "#table-role-admin"
|
||||
}]
|
||||
],
|
||||
text: "对不起,加载出现异常!"
|
||||
}), l.on("tool(LAY-user-back-nav)", function (e) {
|
||||
var l = e.data;
|
||||
console.log(e.event)
|
||||
if ("del" === e.event) {
|
||||
layer.confirm("确定删除此菜单?", function (i) {
|
||||
//提交数据
|
||||
admin.req({
|
||||
method: "get",
|
||||
url: setter.baseurl + "adminapi/System/nav/delete?id=" + e.data.id,//实际使用请改成服务端真实接口
|
||||
async: true,
|
||||
done: function (res) {
|
||||
|
||||
e.del(), layer.close(i)
|
||||
}
|
||||
})
|
||||
|
||||
})
|
||||
}
|
||||
if ("edit" === e.event) {
|
||||
i.popup({
|
||||
title: "编辑菜单",
|
||||
area: ["500px", "480px"],
|
||||
id: "LAY-popup-user-add",
|
||||
success: function (e, i) {
|
||||
t(this.id).render("user/administrators/navform", l).done(function () {
|
||||
r.render(null, "layuiadmin-form-nav"), r.on("submit(LAY-user-nav-submit)", function (e) {
|
||||
|
||||
var param = e.field;
|
||||
param.navid
|
||||
//提交数据
|
||||
admin.req({
|
||||
method: "post",
|
||||
contentType: "application/json;charset=UTF-8",
|
||||
dataType: "json",
|
||||
headers: {
|
||||
'token': layui.data(setter.tableName)[setter.request.tokenName]
|
||||
},
|
||||
url: setter.baseurl + "adminapi/System/nav/update",//实际使用请改成服务端真实接口
|
||||
async: false,
|
||||
data: JSON.stringify(param),
|
||||
done: function (res) {
|
||||
|
||||
layui.table.reload("LAY-user-back-nav"), layer.close(i)
|
||||
}
|
||||
})
|
||||
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}), e("nav", {})
|
||||
});
|
||||
@@ -0,0 +1,175 @@
|
||||
|
||||
layui.define(["table", "admin", "form"], function (e) {
|
||||
var i = (layui.$, layui.admin),
|
||||
$ = layui.$,
|
||||
t = layui.view,
|
||||
l = layui.table,
|
||||
r = layui.form,
|
||||
|
||||
admin = layui.admin,
|
||||
setter = layui.setter;
|
||||
|
||||
//提交数据
|
||||
console.log(setter);
|
||||
l.render({
|
||||
elem: "#LAY-user-back-manage",
|
||||
url: setter.baseurl + "adminapi/Mall/GetAllProductList",
|
||||
method: "get",
|
||||
headers: {
|
||||
'token': layui.data(setter.tableName)[setter.request.tokenName]
|
||||
},
|
||||
|
||||
cols: [
|
||||
[
|
||||
{
|
||||
field: "number",
|
||||
title: "编号",
|
||||
templet: function (d) {
|
||||
return d.productInfo?.number??"";
|
||||
}
|
||||
},
|
||||
{
|
||||
field: "name",
|
||||
title: "名称",
|
||||
}, {
|
||||
field: "productName",
|
||||
title: "产品名称",
|
||||
templet: function (d) {
|
||||
|
||||
return d.productInfo?.name ?? "";
|
||||
}
|
||||
},
|
||||
{
|
||||
field: "saleInfo",
|
||||
title: "售价信息",
|
||||
templet: function (d) {
|
||||
// 1 钻石 2 房卡 4 金币 5 道具
|
||||
var tempName = "";
|
||||
if (d.moneySaleInfo != null && d.moneySaleInfo.price > 0) {
|
||||
tempName += d.moneySaleInfo.price + "元 " + (d.moneySaleInfo.limitCount == 0 ? "不限购" : " 限购"+d.moneySaleInfo.limitCount + "个 </br>");
|
||||
}
|
||||
|
||||
|
||||
if (d.diamondSaleInfo != null && d.diamondSaleInfo.price > 0) {
|
||||
tempName += (d.diamondSaleInfo.price + "钻石 " + (d.diamondSaleInfo.limitCount == 0 ? "不限购" : "限购"+d.diamondSaleInfo.limitCount + "个</br>"));
|
||||
}
|
||||
|
||||
if (d.scoreSaleInfo != null && d.scoreSaleInfo.price > 0) {
|
||||
tempName += d.scoreSaleInfo.price + "积分" + (d.scoreSaleInfo.limitCount == 0 ? "不限购" : "限购"+d.scoreSaleInfo.limitCount + "个");
|
||||
}
|
||||
|
||||
return tempName
|
||||
}
|
||||
|
||||
}, {
|
||||
field: "state",
|
||||
title: "状态",
|
||||
templet: function (d) {
|
||||
if (d.state == 0)
|
||||
return "下架";
|
||||
else
|
||||
return "上架"
|
||||
}
|
||||
}, {
|
||||
title: "操作",
|
||||
align: "center",
|
||||
fixed: "right",
|
||||
toolbar: "#table-product-admin"
|
||||
}]
|
||||
],
|
||||
text: "对不起,加载出现异常!",
|
||||
response: {
|
||||
statusName: 'code', //数据状态的字段名称,默认:code
|
||||
statusCode: 0, //成功的状态码,默认:0
|
||||
msgName: 'info', //状态信息的字段名称,默认:msg
|
||||
countName: 'count', //数据总数的字段名称,默认:count
|
||||
dataName: 'data' //数据列表的字段名称,默认:data
|
||||
}
|
||||
}),
|
||||
|
||||
l.on("tool(LAY-user-back-manage)", function (e) {
|
||||
var l = e.data;
|
||||
|
||||
if ("del" === e.event) {
|
||||
layer.confirm("确定删除此产品?", function (i) {
|
||||
//提交数据
|
||||
admin.req({
|
||||
method: "get",
|
||||
url: setter.baseurl + "adminapi/Mall/DeleteProduct?id=" + e.data.id,//实际使用请改成服务端真实接口
|
||||
async: true,
|
||||
done: function (res) {
|
||||
|
||||
e.del(), layer.close(i)
|
||||
}
|
||||
})
|
||||
|
||||
})
|
||||
}
|
||||
if ("state" === e.event) {
|
||||
|
||||
admin.req({
|
||||
method: "get",
|
||||
url: setter.baseurl + "adminapi/Mall/UpdateProductState?id=" + e.data.id + "&state=" + (e.data.state == 0 ? 1 : 0),//实际使用请改成服务端真实接口
|
||||
async: true,
|
||||
done: function (res) {
|
||||
layer.msg("操作成功");
|
||||
layui.table.reload("LAY-user-back-manage");
|
||||
}
|
||||
})
|
||||
}
|
||||
if ("edit" === e.event) {
|
||||
i.popup({
|
||||
title: "编辑产品",
|
||||
area: ["500px", "600px"],
|
||||
id: "LAY-popup-product-add",
|
||||
success: function (e, i) {
|
||||
t(this.id).render("mall/productform", l).done(function () {
|
||||
|
||||
r.render(null, "layuiadmin-form-admin"), r.on("submit(LAY-product-back-submit)", function (data) {
|
||||
|
||||
console.log(data)
|
||||
var param = {
|
||||
id: data.field.id,
|
||||
name: data.field.name,
|
||||
imageUrl: data.field.imageUrl,
|
||||
detailType: parseInt(data.field.detailType),
|
||||
|
||||
number: data.field.baseProduct
|
||||
|
||||
};
|
||||
|
||||
param.moneySaleInfo = { limitCount: data.field.moneyLimitCount, price: parseFloat(data.field.moneyPrice) }
|
||||
param.diamondSaleInfo = { limitCount: data.field.diamondLimitCount, price: parseFloat(data.field.diamondPrice) }
|
||||
param.scoreSaleInfo = { limitCount: data.field.scoreLimitCount, price: parseFloat(data.field.scorePrice) }
|
||||
|
||||
//提交数据
|
||||
admin.req({
|
||||
method: "post",
|
||||
contentType: "application/json;charset=UTF-8",
|
||||
dataType: "json",
|
||||
headers: {
|
||||
'token': layui.data(setter.tableName)[setter.request.tokenName]
|
||||
},
|
||||
url: setter.baseurl + "adminapi/mall/UpdateProduct",//实际使用请改成服务端真实接口
|
||||
async: false,
|
||||
data: JSON.stringify(param),
|
||||
done: function (res) {
|
||||
|
||||
layui.table.reload("LAY-user-back-manage"), layer.close(i)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}), e("productlistjs", {})
|
||||
});
|
||||
@@ -0,0 +1,33 @@
|
||||
/** layuiAdmin.pro-v1.4.0 LPPL License By https://www.layui.com/admin/ */;
|
||||
layui.define(["form", "upload"], function (t) {
|
||||
var i = layui.$,
|
||||
e = layui.layer,
|
||||
n = (layui.laytpl, layui.setter, layui.view, layui.admin),
|
||||
a = layui.form,
|
||||
s = layui.upload;
|
||||
i("body");
|
||||
a.render(), a.verify({
|
||||
nickname: function (t, i) {
|
||||
return new RegExp("^[a-zA-Z0-9_一-龥\\s·]+$").test(t) ? /(^\_)|(\__)|(\_+$)/.test(t) ? "用户名首尾不能出现下划线'_'" : /^\d+\d+\d$/.test(t) ? "用户名不能全为数字" : void 0 : "用户名不能有特殊字符"
|
||||
},
|
||||
pass: [/^[\S]{6,12}$/, "密码必须6到12位,且不能出现空格"],
|
||||
repass: function (t) {
|
||||
if (t !== i("#LAY_password").val()) return "两次密码输入不一致"
|
||||
}
|
||||
});
|
||||
|
||||
a.on("submit(setmypass)", function (t) {
|
||||
|
||||
admin.req({
|
||||
method: "get",
|
||||
url: setter.baseurl + "adminapi/System/user/delete?id=" + e.data.id,//实际使用请改成服务端真实接口
|
||||
async: true,
|
||||
done: function (res) {
|
||||
|
||||
return e.msg("修改成功"), !1
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
}), t("set", {})
|
||||
});
|
||||
@@ -0,0 +1,175 @@
|
||||
|
||||
layui.define(["table", "admin", "form", 'user', 'tree'], function (e) {
|
||||
var i = (layui.$, layui.admin),
|
||||
$ = layui.$,
|
||||
t = layui.view,
|
||||
l = layui.table,
|
||||
r = layui.form,
|
||||
|
||||
admin = layui.admin,
|
||||
setter = layui.setter;
|
||||
//提交数据
|
||||
|
||||
l.render({
|
||||
elem: "#LAY-task-back-manage",
|
||||
url: setter.baseurl + "adminapi/task/GetTaskList",
|
||||
method: "post",
|
||||
contentType: "application/json",
|
||||
page: true,
|
||||
where: {
|
||||
keyWord: "",
|
||||
state: -1
|
||||
},
|
||||
request: {
|
||||
pageName: 'pageIndex' //页码的参数名称,默认:page
|
||||
, limitName: 'pageSize' //每页数据量的参数名,默认:limit
|
||||
},
|
||||
limit: 10,
|
||||
headers: {
|
||||
'token': layui.data(setter.tableName)[setter.request.tokenName]
|
||||
},
|
||||
cols: [
|
||||
[
|
||||
{
|
||||
field: "number",
|
||||
title: "任务编号",
|
||||
}, {
|
||||
field: "remark",
|
||||
title: "任务描述"
|
||||
}, {
|
||||
field: "taskType",
|
||||
title: "任务类型",
|
||||
templet: function (d) {
|
||||
if (d.taskType == 1)
|
||||
return "每日任务";
|
||||
|
||||
if (d.taskType == 2)
|
||||
return "在线时长任务";
|
||||
}
|
||||
}, {
|
||||
field: "taskEvent",
|
||||
title: "事件",
|
||||
templet: function (d) {
|
||||
var tem = "";
|
||||
switch (d.taskEvent) {
|
||||
case 1: tem = "整点在线奖励"; break;
|
||||
case 2: tem = "在线时长";break;
|
||||
case 3: tem = "捕获任意鱼";break;
|
||||
case 4: tem = "捕获黄金鱼";break;
|
||||
case 5: tem = "捕获Boss鱼"; break;
|
||||
case 6: tem = "赢取金币"; break;
|
||||
case 7: tem = "使用狂暴"; break;
|
||||
case 8: tem = "开炮任务"; break;
|
||||
case 9: tem = "登录任务"; break;
|
||||
case 10: tem = "充值任务"; break;
|
||||
default:tem = "未知";break;
|
||||
}
|
||||
|
||||
return tem;
|
||||
}
|
||||
}, {
|
||||
field: "taskCount",
|
||||
title: "任务条件"
|
||||
}
|
||||
, {
|
||||
field: "exp",
|
||||
title: "战令经验"
|
||||
}
|
||||
, {
|
||||
field: "state",
|
||||
title: "状态",
|
||||
templet: function (d) {
|
||||
if (d.state == 1)
|
||||
return "启用";
|
||||
else
|
||||
return "禁用"
|
||||
}
|
||||
}
|
||||
, {
|
||||
width: 240,
|
||||
title: "操作",
|
||||
align: "center",
|
||||
fixed: "right",
|
||||
toolbar: "#table-taskadmin-admin"
|
||||
}]
|
||||
],
|
||||
text: "对不起,加载出现异常!",
|
||||
response: {
|
||||
statusName: 'code', //数据状态的字段名称,默认:code
|
||||
statusCode: 0, //成功的状态码,默认:0
|
||||
msgName: 'info', //状态信息的字段名称,默认:msg
|
||||
countName: 'count', //数据总数的字段名称,默认:count
|
||||
dataName: 'data' //数据列表的字段名称,默认:data
|
||||
}
|
||||
}),
|
||||
|
||||
l.on("tool(LAY-task-back-manage)", function (e) {
|
||||
var l = e.data;
|
||||
console.log(e.event)
|
||||
if ("del" === e.event) {
|
||||
layer.confirm("确定删除此任务?", function (i) {
|
||||
//提交数据
|
||||
admin.req({
|
||||
method: "get",
|
||||
url: setter.baseurl + "adminapi/task/DeleteTask?id=" + e.data.id,//实际使用请改成服务端真实接口
|
||||
async: true,
|
||||
done: function (res) {
|
||||
|
||||
e.del(), layer.close(i)
|
||||
}
|
||||
})
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
if ("state" === e.event) {
|
||||
|
||||
admin.req({
|
||||
method: "get",
|
||||
url: setter.baseurl + "adminapi/Task/UpdateTaskState?id=" + e.data.id + "&state=" + (e.data.state == 1 ? 2 :1),//实际使用请改成服务端真实接口
|
||||
async: true,
|
||||
done: function (res) {
|
||||
layer.msg("操作成功");
|
||||
layui.table.reload("LAY-task-back-manage");
|
||||
}
|
||||
})
|
||||
}
|
||||
if ("edit" === e.event) {
|
||||
i.popup({
|
||||
title: "编辑任务",
|
||||
area: ["500px", "600px"],
|
||||
id: "LAY-popup-user-add",
|
||||
success: function (e, i) {
|
||||
t(this.id).render("config/taskform", l).done(function () {
|
||||
|
||||
r.render(null, "layuiadmin-form-admin"), r.on("submit(LAY-user-back-submit)", function (e) {
|
||||
|
||||
console.log(e.field)
|
||||
|
||||
//提交数据
|
||||
admin.req({
|
||||
method: "post",
|
||||
contentType: "application/json;charset=UTF-8",
|
||||
dataType: "json",
|
||||
headers: {
|
||||
'token': layui.data(setter.tableName)[setter.request.tokenName]
|
||||
},
|
||||
url: setter.baseurl + "adminapi/task/AddTask",//实际使用请改成服务端真实接口
|
||||
async: false,
|
||||
data: JSON.stringify(e.field),
|
||||
done: function (res) {
|
||||
|
||||
layui.table.reload("LAY-task-back-manage"), layer.close(i)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}), e("task", {})
|
||||
});
|
||||
@@ -0,0 +1,15 @@
|
||||
/** layuiAdmin.pro-v1.4.0 LPPL License By https://www.layui.com/admin/ */;
|
||||
layui.define("form", function (e) {
|
||||
var t = layui.$,
|
||||
i = (layui.layer, layui.laytpl, layui.setter, layui.view, layui.admin, layui.form),
|
||||
a = t("body");
|
||||
i.verify({
|
||||
nickname: function (e, t) {
|
||||
return new RegExp("^[a-zA-Z0-9_一-龥\\s·]+$").test(e) ? /(^\_)|(\__)|(\_+$)/.test(e) ? "用户名首尾不能出现下划线'_'" : /^\d+\d+\d$/.test(e) ? "用户名不能全为数字" : void 0 : "用户名不能有特殊字符"
|
||||
},
|
||||
pass: [/^[\S]{6,12}$/, "密码必须6到12位,且不能出现空格"]
|
||||
}), a.on("click", "#LAY-user-get-vercode", function () {
|
||||
t(this);
|
||||
this.src = "https://www.oschina.net/action/user/captcha?t=" + (new Date).getTime()
|
||||
}), e("user", {})
|
||||
});
|
||||
@@ -0,0 +1,243 @@
|
||||
|
||||
layui.define(["table", "admin", "form", 'user', 'tree'], function (e) {
|
||||
var i = (layui.$, layui.admin),
|
||||
$ = layui.$,
|
||||
t = layui.view,
|
||||
l = layui.table,
|
||||
r = layui.form,
|
||||
tree = layui.tree,
|
||||
admin = layui.admin,
|
||||
setter = layui.setter;
|
||||
var treedata = [];
|
||||
//提交数据
|
||||
|
||||
l.render({
|
||||
elem: "#LAY-user-back-manage",
|
||||
url: setter.baseurl + "adminapi/System/user/list",
|
||||
method: "post",
|
||||
contentType: "application/json",
|
||||
page: true,
|
||||
where: {
|
||||
keyWord: "",
|
||||
state: -1
|
||||
},
|
||||
request: {
|
||||
pageName: 'pageIndex' //页码的参数名称,默认:page
|
||||
, limitName: 'pageSize' //每页数据量的参数名,默认:limit
|
||||
},
|
||||
limit: 10,
|
||||
headers: {
|
||||
'token': layui.data(setter.tableName)[setter.request.tokenName]
|
||||
},
|
||||
cols: [
|
||||
[
|
||||
{
|
||||
field: "userName",
|
||||
title: "用户名",
|
||||
}, {
|
||||
field: "icon",
|
||||
title: "头像",
|
||||
templet: function (d) {
|
||||
if (d.icon == null || d.icon == "")
|
||||
return "";
|
||||
return "<image src='"+d.icon+"'>"
|
||||
}
|
||||
}, {
|
||||
field: "displayName",
|
||||
title: "名字"
|
||||
}, {
|
||||
field: "state",
|
||||
title: "状态",
|
||||
templet: function (d) {
|
||||
if (d.state == 0)
|
||||
return "禁用";
|
||||
else
|
||||
return "正常"
|
||||
}
|
||||
}, {
|
||||
field: "systemRole",
|
||||
title: "角色",
|
||||
templet: function (d) {
|
||||
if (d.systemRole == null)
|
||||
return "未选择"
|
||||
return d.systemRole.name
|
||||
}
|
||||
}, {
|
||||
field: "registerTime",
|
||||
title: "加入时间",
|
||||
sort: !0
|
||||
}, {
|
||||
title: "操作",
|
||||
align: "center",
|
||||
fixed: "right",
|
||||
toolbar: "#table-useradmin-admin"
|
||||
}]
|
||||
],
|
||||
text: "对不起,加载出现异常!",
|
||||
response: {
|
||||
statusName: 'code', //数据状态的字段名称,默认:code
|
||||
statusCode: 0, //成功的状态码,默认:0
|
||||
msgName: 'info', //状态信息的字段名称,默认:msg
|
||||
countName: 'count', //数据总数的字段名称,默认:count
|
||||
dataName: 'data' //数据列表的字段名称,默认:data
|
||||
}
|
||||
}),
|
||||
|
||||
l.on("tool(LAY-user-back-manage)", function (e) {
|
||||
var l = e.data;
|
||||
console.log(e.event)
|
||||
if ("del" === e.event) {
|
||||
layer.confirm("确定删除此管理员?", function (i) {
|
||||
//提交数据
|
||||
admin.req({
|
||||
method: "get",
|
||||
url: setter.baseurl + "adminapi/System/user/delete?id=" + e.data.id,//实际使用请改成服务端真实接口
|
||||
async: true,
|
||||
done: function (res) {
|
||||
|
||||
e.del(), layer.close(i)
|
||||
}
|
||||
})
|
||||
|
||||
})
|
||||
}
|
||||
if ("edit" === e.event) {
|
||||
i.popup({
|
||||
title: "编辑管理员",
|
||||
area: ["500px", "600px"],
|
||||
id: "LAY-popup-user-add",
|
||||
success: function (e, i) {
|
||||
t(this.id).render("user/administrators/adminform", l).done(function () {
|
||||
|
||||
r.render(null, "layuiadmin-form-admin"), r.on("submit(LAY-user-back-submit)", function (e) {
|
||||
|
||||
var param = {
|
||||
id: e.field.id,
|
||||
userName: e.field.userName,
|
||||
displayName: e.field.displayName,
|
||||
icon: e.field.icon,
|
||||
password: e.field.password,
|
||||
systemRole: {
|
||||
id: e.field.systemRole,
|
||||
name: $('#systemRole option:selected').text()
|
||||
}
|
||||
};
|
||||
|
||||
//提交数据
|
||||
admin.req({
|
||||
method: "post",
|
||||
contentType: "application/json;charset=UTF-8",
|
||||
dataType: "json",
|
||||
headers: {
|
||||
'token': layui.data(setter.tableName)[setter.request.tokenName]
|
||||
},
|
||||
url: setter.baseurl + "adminapi/System/user/update",//实际使用请改成服务端真实接口
|
||||
async: false,
|
||||
data: JSON.stringify(param),
|
||||
done: function (res) {
|
||||
|
||||
layui.table.reload("LAY-user-back-manage"), layer.close(i)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}), l.render({
|
||||
elem: "#LAY-user-back-role",
|
||||
url: setter.baseurl + "adminapi/System/role/list",
|
||||
method: "get",
|
||||
page: false,
|
||||
limit: 0,
|
||||
headers: {
|
||||
'token': layui.data(setter.tableName)[setter.request.tokenName]
|
||||
},
|
||||
cols: [
|
||||
[
|
||||
{
|
||||
field: "roleName",
|
||||
title: "角色名"
|
||||
}, {
|
||||
field: "description",
|
||||
title: "具体描述"
|
||||
}, {
|
||||
title: "操作",
|
||||
width: 300,
|
||||
align: "center",
|
||||
fixed: "right",
|
||||
toolbar: "#table-useradmin-admin"
|
||||
}]
|
||||
],
|
||||
text: "对不起,加载出现异常!"
|
||||
}), l.on("tool(LAY-user-back-role)", function (e) {
|
||||
var l = e.data;
|
||||
console.log(e.event)
|
||||
if ("del" === e.event) {
|
||||
layer.confirm("确定删除此角色?", function (i) {
|
||||
//提交数据
|
||||
admin.req({
|
||||
method: "get",
|
||||
url: setter.baseurl + "adminapi/System/role/delete?id=" + e.data.id,//实际使用请改成服务端真实接口
|
||||
async: true,
|
||||
done: function (res) {
|
||||
|
||||
e.del(), layer.close(i)
|
||||
}
|
||||
})
|
||||
|
||||
})
|
||||
}
|
||||
if ("edit" === e.event) {
|
||||
i.popup({
|
||||
title: "编辑角色",
|
||||
area: ["500px", "480px"],
|
||||
id: "LAY-popup-user-add",
|
||||
success: function (e, i) {
|
||||
t(this.id).render("user/administrators/roleform", l).done(function () {
|
||||
r.render(null, "layuiadmin-form-role"), r.on("submit(LAY-user-role-submit)", function (e) {
|
||||
|
||||
var param = e.field;
|
||||
param.navid
|
||||
//提交数据
|
||||
admin.req({
|
||||
method: "post",
|
||||
contentType: "application/json;charset=UTF-8",
|
||||
dataType: "json",
|
||||
headers: {
|
||||
'token': layui.data(setter.tableName)[setter.request.tokenName]
|
||||
},
|
||||
url: setter.baseurl + "adminapi/System/role/update",//实际使用请改成服务端真实接口
|
||||
async: false,
|
||||
data: JSON.stringify(param),
|
||||
done: function (res) {
|
||||
|
||||
layui.table.reload("LAY-user-back-role"), layer.close(i)
|
||||
}
|
||||
})
|
||||
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
if ("role" === e.event) {
|
||||
i.popup({
|
||||
title: "编辑权限",
|
||||
area: ["500px", "600px"],
|
||||
id: "LAY-popup-user-tree-add",
|
||||
success: function (e, i) {
|
||||
t(this.id).render("user/administrators/roletree", l).done(function () {
|
||||
|
||||
|
||||
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}), e("useradmin", {})
|
||||
});
|
||||
@@ -0,0 +1,89 @@
|
||||
/** layuiAdmin.pro-v1.4.0 LPPL License By https://www.layui.com/admin/ */;
|
||||
layui.extend({
|
||||
setter: "config",
|
||||
admin: "lib/admin",
|
||||
view: "lib/view"
|
||||
}).define(["setter", "admin"], function (e) {
|
||||
var a = layui.setter,
|
||||
n = layui.element,
|
||||
i = layui.admin,
|
||||
t = i.tabsPage,
|
||||
l = layui.view,
|
||||
o = function () {
|
||||
var e = layui.router(),
|
||||
r = e.path,
|
||||
y = i.correctRouter(e.path.join("/"));
|
||||
r.length || (r = [""]), "" === r[r.length - 1] && (r[r.length - 1] = a.entry);
|
||||
var h = function (e) {
|
||||
o.haveInit && d(".layui-layer").each(function () {
|
||||
var e = d(this),
|
||||
a = e.attr("times");
|
||||
e.hasClass("layui-layim") || layer.close(a)
|
||||
}), o.haveInit = !0, d(s).scrollTop(0), delete t.type
|
||||
};
|
||||
return "tab" === t.type && ("/" !== y || "/" === y && i.tabsBody().html()) ? (i.tabsBodyChange(t.index), h(t.type)) : (l().render(r.join("/")).then(function (l) {
|
||||
var o, r = d("#LAY_app_tabsheader>li");
|
||||
r.each(function (e) {
|
||||
var a = d(this),
|
||||
n = a.attr("lay-id");
|
||||
n === y && (o = !0, t.index = e)
|
||||
}), a.pageTabs && "/" !== y && (o || (d(s).append('<div class="layadmin-tabsbody-item layui-show"></div>'), t.index = r.length, n.tabAdd(u, {
|
||||
title: "<span>" + (l.title || "新标签页") + "</span>",
|
||||
id: y,
|
||||
attr: e.href
|
||||
}))), this.container = i.tabsBody(t.index), a.pageTabs || this.container.scrollTop(0), n.tabChange(u, y), i.tabsBodyChange(t.index)
|
||||
}).done(function () {
|
||||
layui.use("common", layui.cache.callback.common), c.on("resize", layui.data.resize), n.render("breadcrumb", "breadcrumb"), i.tabsBody(t.index).on("scroll", function () {
|
||||
var e = d(this),
|
||||
a = d(".layui-laydate"),
|
||||
n = d(".layui-layer")[0];
|
||||
a[0] && (a.each(function () {
|
||||
var e = d(this);
|
||||
e.hasClass("layui-laydate-static") || e.remove()
|
||||
}), e.find("input").blur()), n && layer.closeAll("tips")
|
||||
})
|
||||
}), void h())
|
||||
},
|
||||
r = function (e) {
|
||||
var n, t = layui.router(),
|
||||
r = l(a.container),
|
||||
s = i.correctRouter(t.path.join("/"));
|
||||
if (layui.each(a.indPage, function (e, a) {
|
||||
if (s === a) return n = !0
|
||||
}), layui.config({
|
||||
base: a.base + "controller/"
|
||||
}), n || "/user/login" === s) r.render(t.path.join("/")).done(function () {
|
||||
i.pageType = "alone"
|
||||
});
|
||||
else {
|
||||
if (a.interceptor) {
|
||||
var u = layui.data(a.tableName);
|
||||
if (!u[a.request.tokenName]) return location.hash = "/user/login/redirect=" + encodeURIComponent(s)
|
||||
}
|
||||
"console" === i.pageType ? o() : r.render("layout").done(function () {
|
||||
o(), layui.element.render(), i.screen() < 2 && i.sideFlexible(), i.pageType = "console"
|
||||
})
|
||||
}
|
||||
},
|
||||
s = "#LAY_app_body",
|
||||
u = "layadmin-layout-tabs",
|
||||
d = layui.$,
|
||||
c = d(window);
|
||||
layui.link(a.base + "style/admin.css?v=" + (i.v + "-1"), function () {
|
||||
r()
|
||||
}, "layuiAdmin"), window.onhashchange = function () {
|
||||
// 只在首次加载时渲染layout,之后只切换tab
|
||||
if (!o.haveInit) {
|
||||
r();
|
||||
} else {
|
||||
// 已经初始化,只切换到对应tab
|
||||
o();
|
||||
}
|
||||
layui.event.call(this, a.MOD_NAME, "hash({*})", layui.router())
|
||||
}, layui.each(a.extend, function (e, n) {
|
||||
var i = {};
|
||||
i[n] = "{/}" + a.base + "lib/extend/" + n, layui.extend(i)
|
||||
}), e("index", {
|
||||
render: o
|
||||
})
|
||||
});
|
||||
@@ -0,0 +1,2 @@
|
||||
/** layuiAdmin.pro-v1.4.0 LPPL License By https://www.layui.com/admin/ */
|
||||
;layui.define(function(e){e("echartsTheme",{color:["#009688","#1E9FFF","#5FB878","#FFB980","#D87A80","#8d98b3","#e5cf0d","#97b552","#95706d","#dc69aa","#07a2a4","#9a7fd1","#588dd5","#f5994e","#c05050","#59678c","#c9ab00","#7eb00a","#6f5553","#c14089"],title:{textStyle:{fontWeight:"normal",color:"#666"}},dataRange:{itemWidth:15,color:["#009688","#e0ffff"]},toolbox:{color:["#1e90ff","#1e90ff","#1e90ff","#1e90ff"],effectiveColor:"#ff4500"},tooltip:{backgroundColor:"rgba(50,50,50,0.5)",axisPointer:{type:"line",lineStyle:{color:"#009688"},crossStyle:{color:"#008acd"},shadowStyle:{color:"rgba(200,200,200,0.2)"}}},dataZoom:{dataBackgroundColor:"#efefff",fillerColor:"rgba(182,162,222,0.2)",handleColor:"#008acd"},grid:{borderColor:"#eee"},categoryAxis:{axisLine:{lineStyle:{color:"#009688"}},axisTick:{show:!1},splitLine:{lineStyle:{color:["#eee"]}}},valueAxis:{axisLine:{lineStyle:{color:"#009688"}},splitArea:{show:!0,areaStyle:{color:["rgba(250,250,250,0.1)","rgba(200,200,200,0.1)"]}},splitLine:{lineStyle:{color:["#eee"]}}},polar:{axisLine:{lineStyle:{color:"#ddd"}},splitArea:{show:!0,areaStyle:{color:["rgba(250,250,250,0.2)","rgba(200,200,200,0.2)"]}},splitLine:{lineStyle:{color:"#ddd"}}},timeline:{lineStyle:{color:"#009688"},controlStyle:{normal:{color:"#009688"},emphasis:{color:"#009688"}},symbol:"emptyCircle",symbolSize:3},bar:{itemStyle:{normal:{barBorderRadius:2},emphasis:{barBorderRadius:2}}},line:{smooth:!0,symbol:"emptyCircle",symbolSize:3},k:{itemStyle:{normal:{color:"#d87a80",color0:"#2ec7c9",lineStyle:{color:"#d87a80",color0:"#2ec7c9"}}}},scatter:{symbol:"circle",symbolSize:4},radar:{symbol:"emptyCircle",symbolSize:3},map:{itemStyle:{normal:{areaStyle:{color:"#ddd"},label:{textStyle:{color:"#d87a80"}}},emphasis:{areaStyle:{color:"#fe994e"}}}},force:{itemStyle:{normal:{linkStyle:{color:"#1e90ff"}}}},chord:{itemStyle:{normal:{borderWidth:1,borderColor:"rgba(128, 128, 128, 0.5)",chordStyle:{lineStyle:{color:"rgba(128, 128, 128, 0.5)"}}},emphasis:{borderWidth:1,borderColor:"rgba(128, 128, 128, 0.5)",chordStyle:{lineStyle:{color:"rgba(128, 128, 128, 0.5)"}}}}},gauge:{axisLine:{lineStyle:{color:[[.2,"#2ec7c9"],[.8,"#5ab1ef"],[1,"#d87a80"]],width:10}},axisTick:{splitNumber:10,length:15,lineStyle:{color:"auto"}},splitLine:{length:22,lineStyle:{color:"auto"}},pointer:{width:5}},textStyle:{fontFamily:"微软雅黑, Arial, Verdana, sans-serif"}})});
|
||||
@@ -0,0 +1,2 @@
|
||||
/** layuiAdmin.pro-v1.4.0 LPPL License By https://www.layui.com/admin/ */
|
||||
;layui.define(["laytpl","layer"],function(e){var t=layui.jquery,a=layui.laytpl,n=layui.layer,r=layui.setter,o=(layui.device(),layui.hint()),i=function(e){return new d(e)},s="LAY_app_body",d=function(e){this.id=e,this.container=t("#"+(e||s))};i.loading=function(e){e.append(this.elemLoad=t('<i class="layui-anim layui-anim-rotate layui-anim-loop layui-icon layui-icon-loading layadmin-loading"></i>'))},i.removeLoad=function(){this.elemLoad&&this.elemLoad.remove()},i.exit=function(){layui.data(r.tableName,{key:r.request.tokenName,remove:!0}),location.hash="/user/login"},i.req=function(e){var a=e.success,n=e.error,o=r.request,s=r.response,d=function(){return r.debug?"<br><cite>URL:</cite>"+e.url:""};if(e.data=e.data||{},e.headers=e.headers||{},o.tokenName){var l="string"==typeof e.data?JSON.parse(e.data):e.data;e.data[o.tokenName]=o.tokenName in l?e.data[o.tokenName]:layui.data(r.tableName)[o.tokenName]||"",e.headers[o.tokenName]=o.tokenName in e.headers?e.headers[o.tokenName]:layui.data(r.tableName)[o.tokenName]||""}return delete e.success,delete e.error,t.ajax(t.extend({type:"get",dataType:"json",success:function(t){var n=s.statusCode;if(t[s.statusName]==n.ok)"function"==typeof e.done&&e.done(t);else if(t[s.statusName]==n.logout)i.exit();else{var r=["<cite>Error:</cite> "+(t[s.msgName]||"返回状态码异常"),d()].join("");i.error(r)}"function"==typeof a&&a(t)},error:function(e,t){var a=["请求异常,请重试<br><cite>错误信息:</cite>"+t,d()].join("");i.error(a),"function"==typeof n&&n(res)}},e))},i.popup=function(e){var a=e.success,r=e.skin;return delete e.success,delete e.skin,n.open(t.extend({type:1,title:"提示",content:"",id:"LAY-system-view-popup",skin:"layui-layer-admin"+(r?" "+r:""),shadeClose:!0,closeBtn:!1,success:function(e,r){var o=t('<i class="layui-icon" close>ဆ</i>');e.append(o),o.on("click",function(){n.close(r)}),"function"==typeof a&&a.apply(this,arguments)}},e))},i.error=function(e,a){return i.popup(t.extend({content:e,maxWidth:300,offset:"t",anim:6,id:"LAY_adminError"},a))},d.prototype.render=function(e,a){var n=this;layui.router();return e=r.views+e+r.engine,t("#"+s).children(".layadmin-loading").remove(),i.loading(n.container),t.ajax({url:e,type:"get",dataType:"html",data:{v:layui.cache.version},success:function(e){e="<div>"+e+"</div>";var r=t(e).find("title"),o=r.text()||(e.match(/\<title\>([\s\S]*)\<\/title>/)||[])[1],s={title:o,body:e};r.remove(),n.params=a||{},n.then&&(n.then(s),delete n.then),n.parse(e),i.removeLoad(),n.done&&(n.done(s),delete n.done)},error:function(e){return i.removeLoad(),n.render.isError?i.error("请求视图文件异常,状态:"+e.status):(404===e.status?n.render("template/tips/404"):n.render("template/tips/error"),void(n.render.isError=!0))}}),n},d.prototype.parse=function(e,n,r){var s=this,d="object"==typeof e,l=d?e:t(e),u=d?e:l.find("*[template]"),c=function(e){var n=a(e.dataElem.html()),o=t.extend({params:y.params},e.res);e.dataElem.after(n.render(o)),"function"==typeof r&&r();try{e.done&&new Function("d",e.done)(o)}catch(i){console.error(e.dataElem[0],"\n存在错误回调脚本\n\n",i)}},y=layui.router();l.find("title").remove(),s.container[n?"after":"html"](l.children()),y.params=s.params||{};for(var p=u.length;p>0;p--)!function(){var e=u.eq(p-1),t=e.attr("lay-done")||e.attr("lay-then"),n=a(e.attr("lay-url")||"").render(y),r=a(e.attr("lay-data")||"").render(y),s=a(e.attr("lay-headers")||"").render(y);try{r=new Function("return "+r+";")()}catch(d){o.error("lay-data: "+d.message),r={}}try{s=new Function("return "+s+";")()}catch(d){o.error("lay-headers: "+d.message),s=s||{}}n?i.req({type:e.attr("lay-type")||"get",url:n,data:r,dataType:"json",headers:s,success:function(a){c({dataElem:e,res:a,done:t})}}):c({dataElem:e,done:t})}();return s},d.prototype.send=function(e,t){var n=a(e||this.container.html()).render(t||{});return this.container.html(n),this},d.prototype.refresh=function(e){var t=this,a=t.container.next(),n=a.attr("lay-templateid");return t.id!=n?t:(t.parse(t.container,"refresh",function(){t.container.siblings('[lay-templateid="'+t.id+'"]:last').remove(),"function"==typeof e&&e()}),t)},d.prototype.then=function(e){return this.then=e,this},d.prototype.done=function(e){return this.done=e,this},e("view",i)});
|
||||
@@ -0,0 +1,2 @@
|
||||
/** layuiAdmin.pro-v1.4.0 LPPL License By https://www.layui.com/admin/ */
|
||||
#LAY_app,body,html{height:100%}.layui-layout-body{overflow:auto}#LAY-user-login,.layadmin-user-display-show{display:block!important}.layadmin-user-login{position:relative;left:0;top:0;padding:110px 0;min-height:100%;box-sizing:border-box}.layadmin-user-login-main{width:375px;margin:0 auto;box-sizing:border-box}.layadmin-user-login-box{padding:20px}.layadmin-user-login-header{text-align:center}.layadmin-user-login-header h2{margin-bottom:10px;font-weight:300;font-size:30px;color:#000}.layadmin-user-login-header p{font-weight:300;color:#999}.layadmin-user-login-body .layui-form-item{position:relative}.layadmin-user-login-icon{position:absolute;left:1px;top:1px;width:38px;line-height:36px;text-align:center;color:#d2d2d2}.layadmin-user-login-body .layui-form-item .layui-input{padding-left:38px}.layadmin-user-login-codeimg{max-height:38px;width:100%;cursor:pointer;box-sizing:border-box}.layadmin-user-login-other{position:relative;font-size:0;line-height:38px;padding-top:20px}.layadmin-user-login-other>*{display:inline-block;vertical-align:middle;margin-right:10px;font-size:14px}.layadmin-user-login-other .layui-icon{position:relative;top:2px;font-size:26px}.layadmin-user-login-other a:hover{opacity:.8}.layadmin-user-jump-change{float:right}.layadmin-user-login-footer{position:absolute;left:0;bottom:0;width:100%;line-height:30px;padding:20px;text-align:center;box-sizing:border-box;color:rgba(0,0,0,.5)}.layadmin-user-login-footer span{padding:0 5px}.layadmin-user-login-footer a{padding:0 5px;color:rgba(0,0,0,.5)}.layadmin-user-login-footer a:hover{color:rgba(0,0,0,1)}.layadmin-user-login-main[bgimg]{background-color:#fff;box-shadow:0 0 5px rgba(0,0,0,.05)}.ladmin-user-login-theme{position:fixed;bottom:0;left:0;width:100%;text-align:center}.ladmin-user-login-theme ul{display:inline-block;padding:5px;background-color:#fff}.ladmin-user-login-theme ul li{display:inline-block;vertical-align:top;width:64px;height:43px;cursor:pointer;transition:all .3s;-webkit-transition:all .3s;background-color:#f2f2f2}.ladmin-user-login-theme ul li:hover{opacity:.9}@media screen and (max-width:768px){.layadmin-user-login{padding-top:60px}.layadmin-user-login-main{width:300px}.layadmin-user-login-box{padding:10px}}
|
||||
|
After Width: | Height: | Size: 832 B |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 455 B |
|
After Width: | Height: | Size: 457 B |
|
After Width: | Height: | Size: 6.0 KiB |
|
After Width: | Height: | Size: 410 KiB |
|
After Width: | Height: | Size: 19 KiB |
@@ -0,0 +1 @@
|
||||
<div style="text-align:center;padding:40px;color:#999;">此页面已废弃,奖池配置已整合到列表页</div>
|
||||
@@ -0,0 +1,708 @@
|
||||
<title>奖池抽奖活动</title>
|
||||
|
||||
<div class="layui-card layadmin-header">
|
||||
<div class="layui-breadcrumb" lay-filter="breadcrumb">
|
||||
<a lay-href="">主页</a>
|
||||
<a><cite>活动</cite></a>
|
||||
<a><cite>奖池抽奖活动</cite></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-fluid">
|
||||
<!-- 活动配置 -->
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header">活动配置</div>
|
||||
<div class="layui-card-body">
|
||||
<form class="layui-form" lay-filter="lottery-form">
|
||||
<input type="hidden" name="activityId" id="activityId">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">活动名称</label>
|
||||
<div class="layui-input-inline" style="width:400px;">
|
||||
<input type="text" name="activityName" lay-verify="required" placeholder="请输入活动名称" autocomplete="off" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">状态</label>
|
||||
<div class="layui-input-inline">
|
||||
<select name="isOpen">
|
||||
<option value="false">关闭</option>
|
||||
<option value="true">开启</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">开始时间</label>
|
||||
<div class="layui-input-inline">
|
||||
<input type="text" name="startTime" id="startTime" placeholder="yyyy-MM-dd HH:mm:ss" autocomplete="off" class="layui-input">
|
||||
</div>
|
||||
<div class="layui-form-mid layui-word-aux">留空=立即开始</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">结束时间</label>
|
||||
<div class="layui-input-inline">
|
||||
<input type="text" name="endTime" id="endTime" placeholder="yyyy-MM-dd HH:mm:ss" autocomplete="off" class="layui-input">
|
||||
</div>
|
||||
<div class="layui-form-mid layui-word-aux">留空=不限制</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">炮台价格</label>
|
||||
<div class="layui-input-inline" style="width:400px;"><input type="text" name="costsCannon" class="layui-input" placeholder="炮台奖池每次抽取消耗(必填)"></div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">战神价格</label>
|
||||
<div class="layui-input-inline" style="width:400px;"><input type="text" name="costsGodWar" class="layui-input" placeholder="战神奖池每次抽取消耗(必填)"></div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">翅膀价格</label>
|
||||
<div class="layui-input-inline" style="width:400px;"><input type="text" name="costsWing" class="layui-input" placeholder="翅膀奖池每次抽取消耗(必填)"></div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">每人上限</label>
|
||||
<div class="layui-input-inline" style="width:120px;">
|
||||
<input type="number" name="userMaxDraw" value="100" placeholder="次数" autocomplete="off" class="layui-input">
|
||||
</div>
|
||||
<div class="layui-form-mid layui-word-aux">单用户最大抽奖次数(0=不限制)</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 奖池等级 -->
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header">奖池等级</div>
|
||||
<div class="layui-card-body">
|
||||
<div class="layui-tab layui-tab-brief" lay-filter="lottery-pool-type">
|
||||
<ul class="layui-tab-title">
|
||||
<li class="layui-this" lay-id="1">炮台奖池</li>
|
||||
<li lay-id="2">战神奖池</li>
|
||||
<li lay-id="3">翅膀奖池</li>
|
||||
</ul>
|
||||
<div class="layui-tab-content">
|
||||
<div class="layui-tab-item layui-show">
|
||||
<div class="layui-btn-container" style="margin-bottom:10px;">
|
||||
<button class="layui-btn layui-btn-sm" id="btn-add-pool"><i class="layui-icon layui-icon-add-1"></i> 添加等级</button>
|
||||
<button class="layui-btn layui-btn-sm layui-btn-primary" id="btn-init-lottery"><i class="layui-icon layui-icon-refresh"></i> 初始化整个配置数据</button>
|
||||
<button class="layui-btn layui-btn-sm layui-btn-normal" id="btn-save"><i class="layui-icon layui-icon-ok"></i> 保存配置</button>
|
||||
<button class="layui-btn layui-btn-sm layui-btn-warm" id="btn-reload"><i class="layui-icon layui-icon-refresh"></i> 刷新</button>
|
||||
</div>
|
||||
<table id="table-pool" lay-filter="table-pool"></table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 奖池等级操作栏 -->
|
||||
<script type="text/html" id="bar-pool">
|
||||
<a class="layui-btn layui-btn-xs" lay-event="edit">编辑</a>
|
||||
<a class="layui-btn layui-btn-xs layui-btn-normal" lay-event="prize">奖项</a>
|
||||
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a>
|
||||
</script>
|
||||
|
||||
<!-- 奖项管理对话框模板 -->
|
||||
<script type="text/html" id="prize-dialog-tpl">
|
||||
<div class="prize-container">
|
||||
<div class="prize-left">
|
||||
<div class="prize-toolbar">
|
||||
<button class="layui-btn layui-btn-sm" id="btn-add-prize"><i class="layui-icon layui-icon-add-1"></i> 新增</button>
|
||||
<button class="layui-btn layui-btn-sm layui-btn-danger" id="btn-delete-prize"><i class="layui-icon layui-icon-delete"></i> 删除</button>
|
||||
</div>
|
||||
<div class="prize-table-wrap">
|
||||
<table id="table-prize-list"></table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="prize-right">
|
||||
<h4>奖项详情</h4>
|
||||
<div class="layui-form" lay-filter="prize-form">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">奖项ID</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="prizeId" id="prizeId" class="layui-input" placeholder="唯一标识">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">奖项名称</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="prizeName" id="prizeName" class="layui-input" placeholder="如:1000金币">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">图标</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="prizeIcon" id="prizeIcon" class="layui-input" placeholder="图标URL">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">类型</label>
|
||||
<div class="layui-input-block">
|
||||
<select name="prizeType" id="prizeType" lay-filter="prizeType">
|
||||
<option value="1">金币</option>
|
||||
<option value="2">钻石</option>
|
||||
<option value="3">道具</option>
|
||||
<option value="4">道具碎片</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">数量</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="number" name="prizeCount" id="prizeCount" class="layui-input" placeholder="奖励数量">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item prize-prop-select" style="display:none;">
|
||||
<label class="layui-form-label">道具编号</label>
|
||||
<div class="layui-input-block">
|
||||
<select name="prizeOtherId" id="prizeOtherId" lay-filter="prizeOtherId">
|
||||
<option value="">请选择道具/碎片</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">权重</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="number" name="prizeWeight" id="prizeWeight" class="layui-input" placeholder="抽中权重,越大越易中">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">总库存</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="number" name="prizeTotalCount" id="prizeTotalCount" class="layui-input" placeholder="0=无限">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item" style="margin-top:20px;">
|
||||
<button class="layui-btn layui-btn-normal" id="btn-save-prize" style="width:100%;">保存</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script>
|
||||
layui.use(['admin', 'table', 'form', 'layer', 'laydate', 'util', 'element'], function () {
|
||||
var $ = layui.$,
|
||||
admin = layui.admin,
|
||||
table = layui.table,
|
||||
form = layui.form,
|
||||
layer = layui.layer,
|
||||
laydate = layui.laydate,
|
||||
util = layui.util,
|
||||
element = layui.element,
|
||||
setter = layui.setter;
|
||||
|
||||
// ============ 数据 ============
|
||||
var activityData = null; // 完整活动数据(camelCase keys,来自服务端)
|
||||
var poolsData = []; // 奖池等级列表(本地编辑,camelCase keys)
|
||||
var currentPoolIndex = -1; // 当前编辑的奖池等级索引
|
||||
var currentPrizeIndex = -1; // 当前编辑的奖项索引
|
||||
var currentPoolType = 1; // 1=炮台,2=战神,3=翅膀
|
||||
var propList = []; // 道具列表
|
||||
var propFragmentList = []; // 碎片单独存于 PropFragments,不在 PropConfig 中
|
||||
function propIcon(number) {
|
||||
var key = String(number || '').trim();
|
||||
var prop = propList.find(function(p) { return String(p.number) === key; });
|
||||
if (prop) return prop.imageUrl || prop.icon || '';
|
||||
var fragment = propFragmentList.find(function(p) { return String(p.number) === key; });
|
||||
if (!fragment) return '';
|
||||
var parent = propList.find(function(p) { return String(p.number) === String(fragment.productNumber); });
|
||||
return fragment.imageUrl || (parent && parent.imageUrl) || '';
|
||||
}
|
||||
|
||||
function prizeIcon(prize) {
|
||||
// 道具和碎片使用最新配置图片,兼容历史奖项保存的失效 icon。
|
||||
var type = parseInt(prize.type, 10);
|
||||
return ((type === 3 || type === 4) ? propIcon(prize.otherId) : '') || prize.icon || (prize.product && prize.product.imageUrl) || '';
|
||||
}
|
||||
|
||||
function newClientId() {
|
||||
return 'pool_' + Date.now() + '_' + Math.random().toString(16).slice(2);
|
||||
}
|
||||
|
||||
function findPoolIndex(row) {
|
||||
if (!row) return -1;
|
||||
return poolsData.findIndex(function(p) { return p._clientId === row._clientId; });
|
||||
}
|
||||
|
||||
// 初始化:防止loading残影
|
||||
setTimeout(function() {
|
||||
if (window.parent && window.parent.$ && window.parent.layer) {
|
||||
window.parent.$('.layui-layer-shade').remove();
|
||||
window.parent.layer.closeAll();
|
||||
}
|
||||
$('.layui-layer-shade').remove();
|
||||
layer.closeAll();
|
||||
}, 100);
|
||||
|
||||
// ============ 数据加载 ============
|
||||
function loadActivity() {
|
||||
admin.req({
|
||||
method: 'get',
|
||||
url: setter.baseurl + 'adminapi/Lottery/list',
|
||||
done: function (res) {
|
||||
if (res.code === 0) {
|
||||
if (res.data && res.data.length > 0) {
|
||||
activityData = res.data[0];
|
||||
} else {
|
||||
activityData = {
|
||||
activityId: '',
|
||||
activityName: '',
|
||||
isOpen: false,
|
||||
startTime: 0,
|
||||
endTime: 0,
|
||||
costsByPool: {},
|
||||
userMaxDraw: 100,
|
||||
pools: []
|
||||
};
|
||||
}
|
||||
poolsData = (activityData.pools || []).map(function(p) {
|
||||
var item = $.extend({}, p);
|
||||
item.poolType = parseInt(item.poolType) || 1;
|
||||
item._clientId = newClientId();
|
||||
return item;
|
||||
});
|
||||
fillForm();
|
||||
renderPoolTable();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function loadProps() {
|
||||
admin.req({
|
||||
method: 'get',
|
||||
url: setter.baseurl + 'adminapi/GameConfig',
|
||||
done: function (res) {
|
||||
if (res.code === 0 && res.data) {
|
||||
propList = res.data.propConfig || [];
|
||||
propFragmentList = res.data.propFragments || [];
|
||||
renderPoolTable();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ============ 表单回填 ============
|
||||
function fillForm() {
|
||||
if (!activityData) return;
|
||||
// 先用 jQuery 清空再赋值,避免 layui form.val 累积
|
||||
var costsByPool = activityData.costsByPool || {};
|
||||
$('input[name="costsCannon"]').val((costsByPool['1'] || []).join(','));
|
||||
$('input[name="costsGodWar"]').val((costsByPool['2'] || []).join(','));
|
||||
$('input[name="costsWing"]').val((costsByPool['3'] || []).join(','));
|
||||
$('input[name="activityName"]').val(activityData.activityName || '');
|
||||
$('input[name="userMaxDraw"]').val(activityData.userMaxDraw || 100);
|
||||
$('select[name="isOpen"]').val(activityData.isOpen ? 'true' : 'false');
|
||||
$('#activityId').val(activityData.activityId || '');
|
||||
|
||||
if (activityData.startTime) {
|
||||
var sd = new Date(activityData.startTime * 1000);
|
||||
$('#startTime').val(util.toDateString(sd, 'yyyy-MM-dd HH:mm:ss'));
|
||||
}
|
||||
if (activityData.endTime) {
|
||||
var ed = new Date(activityData.endTime * 1000);
|
||||
$('#endTime').val(util.toDateString(ed, 'yyyy-MM-dd HH:mm:ss'));
|
||||
}
|
||||
form.render();
|
||||
}
|
||||
|
||||
// ============ 日期选择器 ============
|
||||
laydate.render({ elem: '#startTime', type: 'datetime' });
|
||||
laydate.render({ elem: '#endTime', type: 'datetime' });
|
||||
|
||||
// ============ 奖池等级表格 ============
|
||||
function renderPoolTable() {
|
||||
table.render({
|
||||
elem: '#table-pool',
|
||||
data: poolsData.filter(function(p) { return (parseInt(p.poolType) || 1) === currentPoolType; }),
|
||||
page: false,
|
||||
cols: [[
|
||||
{ type: 'numbers', title: '序号', width: 60 },
|
||||
{ field: 'level', title: '等级', width: 100 },
|
||||
{ field: 'count', title: '数量', width: 80 },
|
||||
{ field: 'weight', title: '权重', width: 80 },
|
||||
{ title: '奖项数', width: 80, templet: function(d) {
|
||||
return (d.prizeList && d.prizeList.length) ? d.prizeList.length + '项' : '无';
|
||||
}},
|
||||
{ title: '操作', width: 200, fixed: 'right', toolbar: '#bar-pool' }
|
||||
]]
|
||||
});
|
||||
}
|
||||
|
||||
// ============ 奖池等级事件 ============
|
||||
table.on('tool(table-pool)', function(obj) {
|
||||
var idx = findPoolIndex(obj.data);
|
||||
if (idx < 0) {
|
||||
layer.msg('未找到当前奖池,请刷新页面后重试');
|
||||
return;
|
||||
}
|
||||
var data = poolsData[idx];
|
||||
|
||||
if (obj.event === 'del') {
|
||||
layer.confirm('确定删除等级「' + (data.level || '') + '」吗?', function(ix) {
|
||||
poolsData.splice(idx, 1);
|
||||
renderPoolTable();
|
||||
layer.close(ix);
|
||||
});
|
||||
} else if (obj.event === 'edit') {
|
||||
showPoolEdit(data, false);
|
||||
} else if (obj.event === 'prize') {
|
||||
currentPoolIndex = idx;
|
||||
showPrizeDialog(data);
|
||||
}
|
||||
});
|
||||
|
||||
// 添加等级
|
||||
$('#btn-add-pool').on('click', function() {
|
||||
var newItem = { _clientId: newClientId(), poolType: currentPoolType, level: '', count: 0, weight: 10, prizeList: [] };
|
||||
showPoolEdit(newItem, true);
|
||||
});
|
||||
|
||||
// 保存配置
|
||||
$('#btn-save').on('click', function() {
|
||||
saveActivity();
|
||||
});
|
||||
|
||||
$('#btn-init-lottery').on('click', function() {
|
||||
layer.confirm('将用当前线上奖池制作的模板覆盖整个奖池活动配置(3组奖池、等级、奖项、权重和价格)。活动会保持关闭,玩家抽奖历史不清除。确定继续吗?', function(index) {
|
||||
layer.close(index);
|
||||
admin.req({
|
||||
method: 'post',
|
||||
url: setter.baseurl + 'adminapi/Lottery/initialize',
|
||||
done: function(res) {
|
||||
if (res.code === 0) {
|
||||
layer.msg('初始化成功,活动保持关闭');
|
||||
loadActivity();
|
||||
} else {
|
||||
layer.msg(res.info || '初始化失败');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// 刷新
|
||||
$('#btn-reload').on('click', function() {
|
||||
loadActivity();
|
||||
layer.msg('刷新成功');
|
||||
});
|
||||
|
||||
element.on('tab(lottery-pool-type)', function() {
|
||||
currentPoolType = parseInt($(this).attr('lay-id')) || 1;
|
||||
currentPoolIndex = -1;
|
||||
currentPrizeIndex = -1;
|
||||
renderPoolTable();
|
||||
});
|
||||
|
||||
// ============ 奖池等级编辑弹窗 ============
|
||||
function showPoolEdit(data, isAdd) {
|
||||
var title = isAdd ? '添加等级' : '编辑等级';
|
||||
var html = '<div style="padding:20px;">' +
|
||||
'<form class="layui-form">' +
|
||||
'<div class="layui-form-item">' +
|
||||
'<label class="layui-form-label">等级</label>' +
|
||||
'<div class="layui-input-block">' +
|
||||
'<input type="text" name="level" value="' + (data.level || '') + '" placeholder="如: A, B, C" class="layui-input">' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="layui-form-item">' +
|
||||
'<label class="layui-form-label">数量</label>' +
|
||||
'<div class="layui-input-block">' +
|
||||
'<input type="number" name="count" value="' + (data.count || 0) + '" placeholder="奖项总数量" class="layui-input">' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="layui-form-item">' +
|
||||
'<label class="layui-form-label">权重</label>' +
|
||||
'<div class="layui-input-block">' +
|
||||
'<input type="number" name="weight" value="' + (data.weight || 10) + '" placeholder="等级权重,越大越易中" class="layui-input">' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</form>' +
|
||||
'</div>';
|
||||
|
||||
layer.open({
|
||||
type: 1,
|
||||
title: title,
|
||||
area: ['450px', '300px'],
|
||||
content: html,
|
||||
btn: ['保存', '取消'],
|
||||
success: function(layero) { form.render(); },
|
||||
yes: function(idx, layero) {
|
||||
var level = layero.find('input[name="level"]').val();
|
||||
var count = parseInt(layero.find('input[name="count"]').val()) || 0;
|
||||
var weight = parseInt(layero.find('input[name="weight"]').val()) || 10;
|
||||
|
||||
if (!level) { layer.msg('等级不能为空'); return; }
|
||||
|
||||
if (isAdd) {
|
||||
poolsData.push({ _clientId: data._clientId || newClientId(), poolType: currentPoolType, level: level, count: count, weight: weight, prizeList: [] });
|
||||
} else {
|
||||
data.level = level;
|
||||
data.count = count;
|
||||
data.weight = weight;
|
||||
}
|
||||
layer.close(idx);
|
||||
renderPoolTable();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ============ 奖项管理弹窗 ============
|
||||
function showPrizeDialog(poolData) {
|
||||
if (!poolData.prizeList) poolData.prizeList = [];
|
||||
currentPrizeIndex = -1;
|
||||
|
||||
var dialogContent = $('#prize-dialog-tpl').html();
|
||||
|
||||
layer.open({
|
||||
type: 1,
|
||||
title: '等级「' + (poolData.level || '') + '」- 奖项管理',
|
||||
area: ['1000px', '620px'],
|
||||
content: dialogContent,
|
||||
shadeClose: true,
|
||||
success: function(layero) {
|
||||
// 必须先渲染 form,否则 select 不显示
|
||||
form.render(null, 'prize-form');
|
||||
|
||||
renderPrizeTable(layero, poolData);
|
||||
|
||||
// 绑定按钮事件
|
||||
layero.find('#btn-add-prize').on('click', function() {
|
||||
poolData.prizeList.push({
|
||||
prizeId: '', name: '', icon: '', type: 1, count: 0,
|
||||
otherId: '', weight: 10, totalCount: 0, drawnCount: 0
|
||||
});
|
||||
currentPrizeIndex = poolData.prizeList.length - 1;
|
||||
renderPrizeTable(layero, poolData, currentPrizeIndex);
|
||||
loadPrizeForm(poolData.prizeList[currentPrizeIndex], layero);
|
||||
});
|
||||
|
||||
layero.find('#btn-delete-prize').on('click', function() {
|
||||
if (currentPrizeIndex < 0) return layer.msg('请先选择要删除的奖项');
|
||||
poolData.prizeList.splice(currentPrizeIndex, 1);
|
||||
currentPrizeIndex = -1;
|
||||
clearPrizeForm(layero);
|
||||
renderPrizeTable(layero, poolData, -1);
|
||||
});
|
||||
|
||||
layero.find('#btn-save-prize').on('click', function() {
|
||||
savePrizeItem(layero, poolData);
|
||||
});
|
||||
|
||||
// 类型切换(filter 名必须与 HTML lay-filter 一致)
|
||||
form.on('select(prizeType)', function(obj) {
|
||||
if (obj.value === '3' || obj.value === '4') {
|
||||
layero.find('.prize-prop-select').show();
|
||||
renderPrizeProductOptions(layero, parseInt(obj.value, 10), '');
|
||||
} else {
|
||||
layero.find('.prize-prop-select').hide();
|
||||
}
|
||||
});
|
||||
form.on('select(prizeOtherId)', function(obj) {
|
||||
var type = parseInt(layero.find('#prizeType').val(), 10);
|
||||
var source = type === 4 ? propFragmentList : propList;
|
||||
var product = source.find(function(item) { return String(item.number) === String(obj.value); });
|
||||
if (!product) return;
|
||||
layero.find('#prizeName').val(product.name || '');
|
||||
layero.find('#prizeIcon').val(propIcon(product.number));
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function renderPrizeTable(layero, poolData, selectIndex) {
|
||||
var list = poolData.prizeList || [];
|
||||
|
||||
table.render({
|
||||
elem: layero.find('#table-prize-list'),
|
||||
data: list,
|
||||
page: false,
|
||||
height: 380,
|
||||
cols: [[
|
||||
{ type: 'numbers', title: '序号', width: 60 },
|
||||
{ field: 'prizeId', title: 'ID', width: 80 },
|
||||
{ field: 'name', title: '名称', minWidth: 100 },
|
||||
{ title: '图标', width: 70, templet: function(d) {
|
||||
var src = prizeIcon(d);
|
||||
return src ? '<img src="' + src + '" style="width:32px;height:32px;object-fit:contain" alt="图标">' : '';
|
||||
}},
|
||||
{ field: 'type', title: '类型', width: 80, templet: function(d) {
|
||||
var map = {1: '金币', 2: '钻石', 3: '道具', 4: '碎片'};
|
||||
return map[d.type] || '金币';
|
||||
}},
|
||||
{ field: 'count', title: '数量', width: 80 },
|
||||
{ field: 'weight', title: '权重', width: 60 },
|
||||
{ field: 'totalCount', title: '库存', width: 60, templet: function(d) {
|
||||
return d.totalCount || '∞';
|
||||
}}
|
||||
]],
|
||||
done: function() {
|
||||
if (list.length > 0 && (selectIndex === undefined || selectIndex < 0)) {
|
||||
selectIndex = 0;
|
||||
}
|
||||
if (selectIndex >= 0 && selectIndex < list.length) {
|
||||
currentPrizeIndex = selectIndex;
|
||||
var $tr = layero.find('#table-prize-list + .layui-table-view tr[data-index="' + selectIndex + '"]');
|
||||
if ($tr.length) {
|
||||
$tr.addClass('layui-table-click');
|
||||
loadPrizeForm(list[selectIndex], layero);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 行点击切换
|
||||
layero.find('#table-prize-list + .layui-table-view').off('click').on('click', 'tr[data-index]', function() {
|
||||
var idx = parseInt($(this).attr('data-index'));
|
||||
if (idx >= 0 && idx < list.length) {
|
||||
currentPrizeIndex = idx;
|
||||
layero.find('tr[data-index]').removeClass('layui-table-click');
|
||||
$(this).addClass('layui-table-click');
|
||||
loadPrizeForm(list[idx], layero);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function loadPrizeForm(data, layero) {
|
||||
layero.find('#prizeId').val(data.prizeId || '');
|
||||
layero.find('#prizeName').val(data.name || '');
|
||||
layero.find('#prizeIcon').val(prizeIcon(data));
|
||||
layero.find('#prizeType').val(data.type || 1);
|
||||
layero.find('#prizeCount').val(data.count || 0);
|
||||
renderPrizeProductOptions(layero, parseInt(data.type, 10) || 1, data.otherId || '');
|
||||
layero.find('#prizeWeight').val(data.weight || 10);
|
||||
layero.find('#prizeTotalCount').val(data.totalCount || 0);
|
||||
|
||||
if (data.type === 3 || data.type === 4) {
|
||||
layero.find('.prize-prop-select').show();
|
||||
} else {
|
||||
layero.find('.prize-prop-select').hide();
|
||||
}
|
||||
form.render('select');
|
||||
}
|
||||
|
||||
function renderPrizeProductOptions(layero, type, selected) {
|
||||
var select = layero.find('#prizeOtherId');
|
||||
var source = type === 4 ? propFragmentList : propList;
|
||||
select.empty().append('<option value="">请选择道具/碎片</option>');
|
||||
source.forEach(function(item) {
|
||||
var number = String(item.number || '');
|
||||
select.append($('<option></option>').val(number).text(number + ' - ' + (item.name || '')));
|
||||
});
|
||||
select.val(String(selected || ''));
|
||||
form.render('select', 'prize-form');
|
||||
}
|
||||
|
||||
function clearPrizeForm(layero) {
|
||||
layero.find('#prizeId').val('');
|
||||
layero.find('#prizeName').val('');
|
||||
layero.find('#prizeIcon').val('');
|
||||
layero.find('#prizeType').val('1');
|
||||
layero.find('#prizeCount').val('0');
|
||||
layero.find('#prizeOtherId').val('');
|
||||
layero.find('#prizeWeight').val('10');
|
||||
layero.find('#prizeTotalCount').val('0');
|
||||
layero.find('.prize-prop-select').hide();
|
||||
form.render('select');
|
||||
}
|
||||
|
||||
function savePrizeItem(layero, poolData) {
|
||||
if (currentPrizeIndex < 0) return layer.msg('请先选择要编辑的奖项');
|
||||
var list = poolData.prizeList;
|
||||
list[currentPrizeIndex] = {
|
||||
prizeId: layero.find('#prizeId').val() || '',
|
||||
name: layero.find('#prizeName').val() || '',
|
||||
icon: layero.find('#prizeIcon').val() || propIcon(layero.find('#prizeOtherId').val()) || '',
|
||||
type: parseInt(layero.find('#prizeType').val()) || 1,
|
||||
count: parseInt(layero.find('#prizeCount').val()) || 0,
|
||||
otherId: layero.find('#prizeOtherId').val() || '',
|
||||
weight: parseInt(layero.find('#prizeWeight').val()) || 10,
|
||||
totalCount: parseInt(layero.find('#prizeTotalCount').val()) || 0,
|
||||
drawnCount: 0
|
||||
};
|
||||
renderPrizeTable(layero, poolData, currentPrizeIndex);
|
||||
renderPoolTable();
|
||||
layer.msg('保存成功');
|
||||
}
|
||||
|
||||
// ============ 保存活动 ============
|
||||
function saveActivity() {
|
||||
var activityName = $('input[name="activityName"]').val();
|
||||
if (!activityName) { layer.msg('活动名称不能为空'); return; }
|
||||
|
||||
var startTimeStr = $('input[name="startTime"]').val();
|
||||
var endTimeStr = $('input[name="endTime"]').val();
|
||||
function parseCosts(name) {
|
||||
var value = $('input[name="' + name + '"]').val() || '';
|
||||
return value.split(',').map(function(s) { return parseInt(s.trim()) || 0; }).filter(function(n) { return n > 0; });
|
||||
}
|
||||
|
||||
var costsCannon = parseCosts('costsCannon');
|
||||
var costsGodWar = parseCosts('costsGodWar');
|
||||
var costsWing = parseCosts('costsWing');
|
||||
if (costsCannon.length === 0) { layer.msg('请配置炮台奖池消耗金币'); return; }
|
||||
if (costsGodWar.length === 0) { layer.msg('请配置战神奖池消耗金币'); return; }
|
||||
if (costsWing.length === 0) { layer.msg('请配置翅膀奖池消耗金币'); return; }
|
||||
|
||||
var startTs = startTimeStr ? Math.floor(new Date(startTimeStr).getTime() / 1000) : 0;
|
||||
var endTs = endTimeStr ? Math.floor(new Date(endTimeStr).getTime() / 1000) : 0;
|
||||
|
||||
var param = {
|
||||
activityId: $('#activityId').val() || '',
|
||||
activityName: activityName,
|
||||
isOpen: $('select[name="isOpen"]').val() === 'true',
|
||||
startTime: startTs,
|
||||
endTime: endTs,
|
||||
userMaxDraw: parseInt($('input[name="userMaxDraw"]').val()) || 100,
|
||||
costsByPool: {
|
||||
1: costsCannon,
|
||||
2: costsGodWar,
|
||||
3: costsWing
|
||||
},
|
||||
// _clientId 仅用于解决 Layui 行副本定位问题,不提交到服务端。
|
||||
pools: poolsData.map(function(p) {
|
||||
var copy = $.extend(true, {}, p);
|
||||
delete copy._clientId;
|
||||
(copy.prizeList || []).forEach(function(prize) {
|
||||
var type = parseInt(prize.type, 10);
|
||||
if (type === 3 || type === 4) {
|
||||
prize.icon = prize.icon || propIcon(prize.otherId) || '';
|
||||
}
|
||||
});
|
||||
return copy;
|
||||
})
|
||||
};
|
||||
|
||||
admin.req({
|
||||
method: 'post',
|
||||
contentType: 'application/json;charset=UTF-8',
|
||||
url: setter.baseurl + 'adminapi/Lottery/save',
|
||||
data: JSON.stringify(param),
|
||||
done: function(res) {
|
||||
if (res.code === 0) {
|
||||
layer.msg('保存成功');
|
||||
loadActivity();
|
||||
} else {
|
||||
layer.msg(res.info || '保存失败');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ============ 启动 ============
|
||||
loadProps();
|
||||
loadActivity();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.prize-container { display: flex; height: 480px; padding: 10px; }
|
||||
.prize-left { flex: 1; margin-right: 15px; }
|
||||
.prize-table-wrap { height: 400px; overflow: auto; }
|
||||
.prize-right { width: 300px; border-left: 1px solid #e6e6e6; padding-left: 15px; }
|
||||
.prize-right h4 { margin-bottom: 15px; color: #333; font-size: 16px; padding-bottom: 10px; border-bottom: 1px solid #eee; }
|
||||
.prize-toolbar { margin-bottom: 10px; }
|
||||
.prize-toolbar .layui-btn { margin-right: 5px; }
|
||||
.prize-right .layui-form-item { margin-bottom: 10px; }
|
||||
.prize-right .layui-form-label { padding: 9px 0; width: 60px; }
|
||||
.prize-right .layui-input-block { margin-left: 70px; }
|
||||
</style>
|
||||
@@ -0,0 +1,298 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>后台充值/扣除</title>
|
||||
<meta name="renderer" content="webkit">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||||
<link rel="stylesheet" href="../../../layui/css/layui.css">
|
||||
<script src="../../../layui/layui.js"></script>
|
||||
<script src="../../../layui/xm-select.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="layui-fluid">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header">后台充值/扣除</div>
|
||||
<div class="layui-card-body">
|
||||
<!-- 操作表单 -->
|
||||
<form class="layui-form" lay-filter="operation-form" style="margin-bottom:20px;">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">目标用户</label>
|
||||
<div class="layui-input-inline" style="width:300px;">
|
||||
<input type="text" name="targetUserId" id="targetUser" required lay-verify="required"
|
||||
placeholder="请输入用户ID或用户名" class="layui-input">
|
||||
<input type="hidden" id="searchedUserId" value="">
|
||||
<div class="layui-form-mid layui-word-aux">支持ID或用户名搜索</div>
|
||||
</div>
|
||||
<button type="button" class="layui-btn layui-btn-normal" id="btn-search-user">
|
||||
<i class="layui-icon layui-icon-search"></i> 搜索
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">操作类型</label>
|
||||
<div class="layui-input-inline">
|
||||
<input type="radio" name="operationType" value="1" title="充值" checked>
|
||||
<input type="radio" name="operationType" value="2" title="扣除">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">货币类型</label>
|
||||
<div class="layui-input-inline">
|
||||
<select name="currencyType" lay-verify="required">
|
||||
<option value="1">金币</option>
|
||||
<option value="2">钻石</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">操作数量</label>
|
||||
<div class="layui-input-inline">
|
||||
<input type="number" name="amount" required lay-verify="required|number"
|
||||
placeholder="请输入数量" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">备注</label>
|
||||
<div class="layui-input-inline" style="width:400px;">
|
||||
<input type="text" name="remark" placeholder="备注信息" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-input-block">
|
||||
<button class="layui-btn layui-btn-danger" lay-submit lay-filter="btn-deduct">扣除</button>
|
||||
<button class="layui-btn layui-btn-normal" lay-submit lay-filter="btn-charge">充值</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<!-- 操作日志查询 -->
|
||||
<div class="layui-form">
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-inline">
|
||||
<input type="text" name="adminUser" id="adminUser-search"
|
||||
placeholder="后台操作者ID/用户名" class="layui-input">
|
||||
</div>
|
||||
<div class="layui-inline">
|
||||
<input type="text" name="targetUserSearch" id="targetUser-search"
|
||||
placeholder="目标用户ID/用户名" class="layui-input">
|
||||
</div>
|
||||
<div class="layui-inline">
|
||||
<select name="operationTypeSearch" id="operationType-search">
|
||||
<option value="">全部</option>
|
||||
<option value="1">充值</option>
|
||||
<option value="2">扣除</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="layui-inline">
|
||||
<button class="layui-btn" id="btn-search-logs">
|
||||
<i class="layui-icon layui-icon-search"></i> 查询
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 操作日志表格 -->
|
||||
<table id="table-logs" lay-filter="table-logs"></table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
layui.use(['table', 'form', 'layer'], function(){
|
||||
var table = layui.table;
|
||||
var form = layui.form;
|
||||
var layer = layui.layer;
|
||||
var $ = layui.$;
|
||||
var setter = layui.setter;
|
||||
|
||||
// 渲染表单元素
|
||||
form.render();
|
||||
|
||||
// 货币类型名称映射
|
||||
var currencyNames = { 1: '金币', 2: '钻石' };
|
||||
|
||||
// 初始化操作日志表格
|
||||
var tableIns = table.render({
|
||||
elem: '#table-logs',
|
||||
url: setter.baseurl + 'adminapi/BackgroundOperation/logs',
|
||||
headers: { 'token': layui.data(setter.tableName)[setter.request.tokenName] },
|
||||
method: 'get',
|
||||
page: true,
|
||||
limit: 20,
|
||||
limits: [20, 50, 100],
|
||||
where: {
|
||||
adminUserId: '',
|
||||
targetUserId: '',
|
||||
operationType: ''
|
||||
},
|
||||
request: {
|
||||
pageName: 'pageIndex',
|
||||
limitName: 'pageSize'
|
||||
},
|
||||
cols: [[
|
||||
{ type: 'numbers', title: '序号', width: 60 },
|
||||
{ field: 'OperationTime', title: '操作时间', width: 160, templet: function(d) {
|
||||
return formatDateTime(d.operationTime);
|
||||
}},
|
||||
{ field: 'adminUserName', title: '操作者', width: 120 },
|
||||
{ field: 'targetUserName', title: '目标用户', width: 120 },
|
||||
{ field: 'operationType', title: '操作类型', width: 100, templet: function(d) {
|
||||
return d.operationType === 1 ?
|
||||
'<span class="layui-badge layui-bg-green">充值</span>' :
|
||||
'<span class="layui-badge layui-bg-red">扣除</span>';
|
||||
}},
|
||||
{ field: 'currencyType', title: '货币类型', width: 100, templet: function(d) {
|
||||
return currencyNames[d.currencyType] || '未知(' + d.currencyType + ')';
|
||||
}},
|
||||
{ field: 'amount', title: '数量', width: 100 },
|
||||
{ field: 'remark', title: '备注', minWidth: 200 }
|
||||
]],
|
||||
response: {
|
||||
statusCode: 0, // 规定成功的状态码,默认:0
|
||||
msgName: 'info', // 规定状态信息的字段名称,默认:msg
|
||||
countName: 'count', // 规定数据总数的字段名称,默认:count
|
||||
dataName: 'data' // 规定数据列表的字段名称,默认:data
|
||||
}
|
||||
});
|
||||
|
||||
// 搜索用户(用于绑定目标用户)
|
||||
$('#btn-search-user').click(function(){
|
||||
var keyword = $('#targetUser').val().trim();
|
||||
if (!keyword) {
|
||||
layer.msg('请输入用户ID或用户名');
|
||||
return;
|
||||
}
|
||||
|
||||
// 显示加载中
|
||||
var loading = layer.load(1);
|
||||
|
||||
$.ajax({
|
||||
url: setter.baseurl + 'adminapi/BackgroundOperation/search-user',
|
||||
type: 'get',
|
||||
data: { keyword: keyword },
|
||||
headers: { 'token': layui.data(setter.tableName)[setter.request.tokenName] },
|
||||
dataType: 'json',
|
||||
success: function(res) {
|
||||
layer.close(loading);
|
||||
if (res.code === 0) {
|
||||
// 搜索成功,显示用户信息并保存 userId
|
||||
var user = res.data;
|
||||
$('#searchedUserId').val(user.userId);
|
||||
layer.alert(
|
||||
'用户信息:<br>' +
|
||||
'用户ID:' + user.userId + '<br>' +
|
||||
'用户名:' + user.userName + '<br>' +
|
||||
'手机号:' + (user.userPhone || '未绑定') + '<br>' +
|
||||
'金币:' + user.gold + '<br>' +
|
||||
'钻石:' + user.diamond + '<br>' +
|
||||
'注册时间:' + formatDateTime(user.registerTime),
|
||||
{
|
||||
icon: 1,
|
||||
title: '用户信息'
|
||||
}
|
||||
);
|
||||
} else {
|
||||
layer.msg(res.info || '用户不存在');
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
layer.close(loading);
|
||||
console.error('搜索用户失败:', error);
|
||||
layer.msg('搜索用户失败,请检查网络连接');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 充值按钮
|
||||
form.on('submit(btn-charge)', function(data){
|
||||
var field = data.field;
|
||||
field.operationType = 1; // 充值
|
||||
|
||||
$.ajax({
|
||||
url: setter.baseurl + 'adminapi/BackgroundOperation/charge',
|
||||
type: 'post',
|
||||
contentType: 'application/json',
|
||||
headers: { 'token': layui.data(setter.tableName)[setter.request.tokenName] },
|
||||
data: JSON.stringify({
|
||||
targetUserId: $('#searchedUserId').val(),
|
||||
currencyType: parseInt(field.currencyType),
|
||||
amount: parseFloat(field.amount),
|
||||
remark: field.remark || ''
|
||||
}),
|
||||
success: function(res) {
|
||||
if (res.code === 0) {
|
||||
layer.msg('充值成功', {icon: 1});
|
||||
tableIns.reload();
|
||||
} else {
|
||||
layer.msg(res.info || '充值失败');
|
||||
}
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
// 扣除按钮
|
||||
form.on('submit(btn-deduct)', function(data){
|
||||
var field = data.field;
|
||||
field.operationType = 2; // 扣除
|
||||
|
||||
layer.confirm('确认要扣除 ' + field.amount + ' ' +
|
||||
(field.currencyType == 1 ? '金币' : '钻石') + '?', function(idx) {
|
||||
|
||||
$.ajax({
|
||||
url: setter.baseurl + 'adminapi/BackgroundOperation/deduct',
|
||||
type: 'post',
|
||||
contentType: 'application/json',
|
||||
headers: { 'token': layui.data(setter.tableName)[setter.request.tokenName] },
|
||||
data: JSON.stringify({
|
||||
targetUserId: $('#searchedUserId').val(),
|
||||
currencyType: parseInt(field.currencyType),
|
||||
amount: parseFloat(field.amount),
|
||||
remark: field.remark || ''
|
||||
}),
|
||||
success: function(res) {
|
||||
if (res.code === 0) {
|
||||
layer.msg('扣除成功', {icon: 1});
|
||||
tableIns.reload();
|
||||
} else {
|
||||
layer.msg(res.info || '扣除失败');
|
||||
}
|
||||
}
|
||||
});
|
||||
layer.close(idx);
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
// 查询日志
|
||||
$('#btn-search-logs').click(function(){
|
||||
var where = {};
|
||||
var adminUser = $('#adminUser-search').val().trim();
|
||||
var targetUser = $('#targetUser-search').val().trim();
|
||||
var operationType = $('#operationType-search').val();
|
||||
|
||||
if (adminUser) where.adminUserId = adminUser;
|
||||
if (targetUser) where.targetUserId = targetUser;
|
||||
if (operationType) where.operationType = parseInt(operationType);
|
||||
|
||||
tableIns.reload({
|
||||
where: where,
|
||||
page: { curr: 1 }
|
||||
});
|
||||
});
|
||||
|
||||
function formatDateTime(timestamp) {
|
||||
if (!timestamp) return '';
|
||||
var date = new Date(timestamp * 1000);
|
||||
return date.toLocaleString('zh-CN');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,715 @@
|
||||
|
||||
<title>靓号管理</title>
|
||||
|
||||
<div class="layui-card layadmin-header">
|
||||
<div class="layui-breadcrumb" lay-filter="breadcrumb">
|
||||
<a lay-href="">主页</a>
|
||||
<a><cite>靓号管理</cite></a>
|
||||
<a><cite>靓号列表</cite></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-fluid">
|
||||
<div class="layui-form layui-card-header layuiadmin-card-header-auto" lay-filter="layadmin-beautifulnumber-formlist">
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-inline">
|
||||
<label class="layui-form-label">靓号</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="numberSearch" placeholder="靓号搜索" autocomplete="off" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-inline">
|
||||
<label class="layui-form-label">等级</label>
|
||||
<div class="layui-input-block">
|
||||
<select name="level">
|
||||
<option value="">全部</option>
|
||||
<option value="S">S级</option>
|
||||
<option value="A">A级</option>
|
||||
<option value="B">B级</option>
|
||||
<option value="C">C级</option>
|
||||
<option value="D">D级</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-inline">
|
||||
<label class="layui-form-label">状态</label>
|
||||
<div class="layui-input-block">
|
||||
<select name="status">
|
||||
<option value="">全部</option>
|
||||
<option value="0">未售</option>
|
||||
<option value="1">已售</option>
|
||||
<option value="2">冻结</option>
|
||||
<option value="3">普通号</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-inline">
|
||||
<button class="layui-btn layuiadmin-btn-beautifulnumber" lay-submit lay-filter="LAY-beautifulnumber-front-search">
|
||||
<i class="layui-icon layui-icon-search layuiadmin-button-btn"></i> 搜索
|
||||
</button>
|
||||
<button class="layui-btn layui-btn-primary" lay-filter="LAY-beautifulnumber-front-reset">
|
||||
<i class="layui-icon layui-icon-refresh layuiadmin-button-btn"></i> 重置
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-body">
|
||||
<div class="layui-btn-group">
|
||||
<button class="layui-btn layui-btn-sm" lay-event="addNumber">
|
||||
<i class="layui-icon layui-icon-add-circle"></i> 添加靓号
|
||||
</button>
|
||||
<button class="layui-btn layui-btn-sm" lay-event="batchAddNumbers">
|
||||
<i class="layui-icon layui-icon-add-circle"></i> 批量添加
|
||||
</button>
|
||||
<button class="layui-btn layui-btn-sm layui-btn-normal" lay-event="initRules">
|
||||
<i class="layui-icon layui-icon-refresh"></i> 初始化规则
|
||||
</button>
|
||||
<button class="layui-btn layui-btn-sm" lay-event="initNumbers">
|
||||
<i class="layui-icon layui-icon-refresh"></i> 根据规则生成靓号
|
||||
</button>
|
||||
<button class="layui-btn layui-btn-sm layui-btn-warm" lay-event="refresh">
|
||||
<i class="layui-icon layui-icon-refresh"></i> 刷新
|
||||
</button>
|
||||
</div>
|
||||
<br><br>
|
||||
|
||||
<table id="LAY-beautifulnumber-back-manage" lay-filter="LAY-beautifulnumber-back-manage"></table>
|
||||
|
||||
<script type="text/html" id="table-beautifulnumber-admin">
|
||||
<a class="layui-btn layui-btn-normal layui-btn-xs" lay-event="give"><i class="layui-icon layui-icon-gift"></i>赠送</a>
|
||||
<!--<a class="layui-btn layui-btn-xs" lay-event="edit"><i class="layui-icon layui-icon-edit"></i>编辑</a>-->
|
||||
<a class="layui-btn layui-btn-xs" lay-event="editPrice"><i class="layui-icon layui-icon-edit"></i>改价</a>
|
||||
{{# if(d.status === 0){ }}
|
||||
<a class="layui-btn layui-btn-warm layui-btn-xs" lay-event="freeze"><i class="layui-icon layui-icon-close"></i>冻结</a>
|
||||
{{# } else if(d.status === 2){ }}
|
||||
<a class="layui-btn layui-btn-normal layui-btn-xs" lay-event="unfreeze"><i class="layui-icon layui-icon-ok"></i>解冻</a>
|
||||
{{# } }}
|
||||
{{# if(d.status !== 3){ }}
|
||||
<a class="layui-btn layui-btn-primary layui-btn-xs" lay-event="setNormal"><i class="layui-icon layui-icon-set"></i>普通号</a>
|
||||
{{# } }}
|
||||
{{# if(d.status !== 1){ }}
|
||||
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del"><i class="layui-icon layui-icon-delete"></i>删除</a>
|
||||
{{# } }}
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
layui.use(['admin', 'table', 'form'], function () {
|
||||
var i = (layui.$, layui.admin),
|
||||
$ = layui.$,
|
||||
admin = layui.admin,
|
||||
view = layui.view,
|
||||
table = layui.table,
|
||||
setter = layui.setter,
|
||||
form = layui.form;
|
||||
|
||||
form.render(null, 'layadmin-beautifulnumber-formlist');
|
||||
|
||||
//监听搜索
|
||||
form.on('submit(LAY-beautifulnumber-front-search)', function (data) {
|
||||
var field = data.field;
|
||||
// 转换状态为数字
|
||||
if (field.status) {
|
||||
field.status = parseInt(field.status);
|
||||
}
|
||||
//执行重载
|
||||
table.reload('LAY-beautifulnumber-back-manage', {
|
||||
where: field,
|
||||
page: { curr: 1 }
|
||||
});
|
||||
});
|
||||
|
||||
// 监听重置
|
||||
$('.layui-btn-group').on('click', '[lay-event="refresh"]', function () {
|
||||
table.reload('LAY-beautifulnumber-back-manage');
|
||||
});
|
||||
|
||||
// 添加单个靓号
|
||||
$('.layui-btn-group').on('click', '[lay-event="addNumber"]', function () {
|
||||
var content =
|
||||
'<form class="layui-form" style="padding: 20px;" lay-filter="layuiadmin-form-add-number">' +
|
||||
'<div class="layui-form-item">' +
|
||||
'<label class="layui-form-label">靓号</label>' +
|
||||
'<div class="layui-input-block">' +
|
||||
'<input type="text" name="number" class="layui-input" placeholder="输入靓号" required>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="layui-form-item">' +
|
||||
'<label class="layui-form-label">等级</label>' +
|
||||
'<div class="layui-input-block">' +
|
||||
'<select name="level">' +
|
||||
'<option value="S">S级</option>' +
|
||||
'<option value="A">A级</option>' +
|
||||
'<option value="B">B级</option>' +
|
||||
'<option value="C">C级</option>' +
|
||||
'<option value="D" selected>D级</option>' +
|
||||
'</select>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="layui-form-item">' +
|
||||
'<label class="layui-form-label">价格</label>' +
|
||||
'<div class="layui-input-block">' +
|
||||
'<input type="number" name="price" class="layui-input" placeholder="金币数量" value="0">' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
|
||||
'<div class="layui-form-item">' +
|
||||
'<div class="layui-input-block">' +
|
||||
'<button class="layui-btn" lay-submit lay-filter="LAY-add-number-submit">添加</button>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</form>';
|
||||
|
||||
layer.open({
|
||||
type: 1,
|
||||
title: '添加靓号',
|
||||
area: ['600px', '450px'],
|
||||
content: content
|
||||
});
|
||||
|
||||
layui.form.render();
|
||||
layui.form.on('submit(LAY-add-number-submit)', function(dataField) {
|
||||
var param = {
|
||||
number: dataField.field.number,
|
||||
level: dataField.field.level,
|
||||
price: parseInt(dataField.field.price) || 0
|
||||
};
|
||||
admin.req({
|
||||
method: "post",
|
||||
contentType: "application/json;charset=UTF-8",
|
||||
url: setter.baseurl + "adminapi/BeautifulNumber/add-number",
|
||||
data: JSON.stringify(param),
|
||||
done: function (res) {
|
||||
if (res.code === 0 || res.code === 200) {
|
||||
layer.closeAll();
|
||||
layer.msg('添加成功');
|
||||
table.reload("LAY-beautifulnumber-back-manage");
|
||||
} else {
|
||||
layer.msg(res.info || '添加失败');
|
||||
}
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
});
|
||||
|
||||
// 批量添加靓号
|
||||
$('.layui-btn-group').on('click', '[lay-event="batchAddNumbers"]', function () {
|
||||
var content =
|
||||
'<form class="layui-form" style="padding: 20px;" lay-filter="layuiadmin-form-batch-add">' +
|
||||
'<div class="layui-form-item">' +
|
||||
'<label class="layui-form-label">靓号列表</label>' +
|
||||
'<div class="layui-input-block">' +
|
||||
'<textarea name="numbers" class="layui-textarea" placeholder="每行一个号码" style="height: 200px;" required></textarea>' +
|
||||
'<div style="color:#999;font-size:12px;margin-top:5px;">每行输入一个靓号,系统会自动过滤已存在的号码</div>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="layui-form-item">' +
|
||||
'<label class="layui-form-label">等级</label>' +
|
||||
'<div class="layui-input-block">' +
|
||||
'<select name="level">' +
|
||||
'<option value="S">S级</option>' +
|
||||
'<option value="A">A级</option>' +
|
||||
'<option value="B">B级</option>' +
|
||||
'<option value="C">C级</option>' +
|
||||
'<option value="D" selected>D级</option>' +
|
||||
'</select>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="layui-form-item">' +
|
||||
'<label class="layui-form-label">价格</label>' +
|
||||
'<div class="layui-input-block">' +
|
||||
'<input type="number" name="price" class="layui-input" placeholder="金币数量" value="0">' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
|
||||
'<div class="layui-form-item">' +
|
||||
'<div class="layui-input-block">' +
|
||||
'<button class="layui-btn" lay-submit lay-filter="LAY-batch-add-submit">批量添加</button>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</form>';
|
||||
|
||||
layer.open({
|
||||
type: 1,
|
||||
title: '批量添加靓号',
|
||||
area: ['500px', '500px'],
|
||||
content: content
|
||||
});
|
||||
|
||||
layui.form.render();
|
||||
layui.form.on('submit(LAY-batch-add-submit)', function(dataField) {
|
||||
var numbersText = dataField.field.numbers.trim();
|
||||
if (!numbersText) {
|
||||
layer.msg('请输入靓号列表');
|
||||
return false;
|
||||
}
|
||||
var numbers = numbersText.split('\n').map(function(n) { return n.trim(); }).filter(function(n) { return n; });
|
||||
var param = {
|
||||
numbers: numbers,
|
||||
level: dataField.field.level,
|
||||
price: parseInt(dataField.field.price) || 0
|
||||
|
||||
};
|
||||
admin.req({
|
||||
method: "post",
|
||||
contentType: "application/json;charset=UTF-8",
|
||||
url: setter.baseurl + "adminapi/BeautifulNumber/batch-add-numbers",
|
||||
data: JSON.stringify(param),
|
||||
done: function (res) {
|
||||
if (res.code === 0 || res.code === 200) {
|
||||
layer.closeAll();
|
||||
layer.msg(res.data ? res.data.message : '添加成功');
|
||||
table.reload("LAY-beautifulnumber-back-manage");
|
||||
} else {
|
||||
layer.msg(res.info || '添加失败');
|
||||
}
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
});
|
||||
|
||||
// 监听初始化规则
|
||||
$('.layui-btn-group').on('click', '[lay-event="initRules"]', function () {
|
||||
layer.confirm("确定初始化靓号规则吗?", function (idx) {
|
||||
admin.req({
|
||||
method: "post",
|
||||
url: setter.baseurl + "adminapi/BeautifulNumber/init-rules",
|
||||
done: function (res) {
|
||||
if (res.code === 0 || res.code === 200) {
|
||||
layer.msg('初始化成功');
|
||||
} else {
|
||||
layer.msg(res.info || '初始化失败');
|
||||
}
|
||||
layer.close(idx);
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
// 监听根据规则生成靓号
|
||||
$('.layui-btn-group').on('click', '[lay-event="initNumbers"]', function () {
|
||||
layer.confirm("确定根据规则生成靓号吗?已售出的靓号不会被删除,未售出的靓号将按规则重新生成。", function (idx) {
|
||||
admin.req({
|
||||
method: "post",
|
||||
url: setter.baseurl + "adminapi/BeautifulNumber/init-numbers",
|
||||
done: function (res) {
|
||||
layer.close(idx);
|
||||
if (res.code === 0 || res.code === 200) {
|
||||
layer.msg(res.info || '初始化成功');
|
||||
table.reload("LAY-beautifulnumber-back-manage");
|
||||
} else {
|
||||
layer.msg(res.info || '初始化失败');
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
table.render({
|
||||
elem: "#LAY-beautifulnumber-back-manage",
|
||||
url: setter.baseurl + "adminapi/BeautifulNumber/list",
|
||||
method: "get",
|
||||
headers: {
|
||||
'token': layui.data(setter.tableName)[setter.request.tokenName]
|
||||
},
|
||||
page: true,
|
||||
where: {},
|
||||
request: {
|
||||
pageName: 'page',
|
||||
limitName: 'size'
|
||||
},
|
||||
limit: 10,
|
||||
cols: [[
|
||||
{ type: 'checkbox', fixed: 'left' },
|
||||
{ field: "number", title: "靓号", width: 150 },
|
||||
{ field: "ruleName", title: "规则", width: 120 },
|
||||
{
|
||||
field: "level", title: "等级", width: 80,
|
||||
templet: function (d) {
|
||||
var colors = { 'S': '#ff4d4f', 'A': '#faad14', 'B': '#722ed1', 'C': '#1890ff', 'D': '#8c8c8c' };
|
||||
return '<span style="color:' + (colors[d.level] || '#8c8c8c') + ';font-weight:bold;">' + d.level + '</span>';
|
||||
}
|
||||
},
|
||||
{ field: "price", title: "价格", width: 120, templet: function (d) { return formatNumber(d.price); } },
|
||||
{
|
||||
field: "status", title: "状态", width: 100,
|
||||
templet: function (d) {
|
||||
var statusMap = { 0: '<span class="layui-badge">未售</span>', 1: '<span class="layui-badge layui-bg-green">已售</span>', 2: '<span class="layui-badge layui-bg-orange">冻结</span>', 3: '<span class="layui-badge layui-bg-gray">普通号</span>' };
|
||||
return statusMap[d.status] || '-';
|
||||
}
|
||||
},
|
||||
{
|
||||
field: "saleTime", title: "售出时间", width: 160,
|
||||
templet: function (d) {
|
||||
return d.saleTime ? formatTimestamp(d.saleTime) : '-';
|
||||
}
|
||||
},
|
||||
{ field: "userId", title: "购买者ID", width: 150 },
|
||||
|
||||
{ width: 450, title: "操作", align: "center", fixed: "right", toolbar: "#table-beautifulnumber-admin" }
|
||||
]],
|
||||
text: "对不起,加载出现异常!",
|
||||
response: {
|
||||
statusName: 'code',
|
||||
statusCode: 0,
|
||||
msgName: 'info',
|
||||
countName: 'count',
|
||||
dataName: 'data'
|
||||
}
|
||||
});
|
||||
|
||||
table.on("tool(LAY-beautifulnumber-back-manage)", function (e) {
|
||||
var l = e.data;
|
||||
|
||||
if ("del" === e.event) {
|
||||
layer.confirm("确定删除此靓号么?", function (i) {
|
||||
admin.req({
|
||||
method: "post",
|
||||
contentType: "application/json;charset=UTF-8",
|
||||
headers: {
|
||||
'token': layui.data(setter.tableName)[setter.request.tokenName]
|
||||
},
|
||||
url: setter.baseurl + "adminapi/BeautifulNumber/delete",
|
||||
data: JSON.stringify({ id: l.id }),
|
||||
done: function (res) {
|
||||
if (res.code === 0 || res.code === 200) {
|
||||
e.del(), layer.close(i);
|
||||
layer.msg('删除成功');
|
||||
} else {
|
||||
layer.msg(res.info || '删除失败');
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
if ("editPrice" === e.event) {
|
||||
i.popup({
|
||||
title: "修改价格",
|
||||
area: ["400px", "200px"],
|
||||
id: "LAY-popup-edit-price",
|
||||
success: function (div, idx) {
|
||||
$(div).html(
|
||||
'<form class="layui-form" style="padding: 20px;" lay-filter="layadmin-form-edit-price">' +
|
||||
'<div class="layui-form-item">' +
|
||||
'<label class="layui-form-label">靓号</label>' +
|
||||
'<div class="layui-input-block">' +
|
||||
'<input type="text" class="layui-input" value="' + (l.number || '') + '" disabled>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="layui-form-item">' +
|
||||
'<label class="layui-form-label">新价格</label>' +
|
||||
'<div class="layui-input-block">' +
|
||||
'<input type="number" name="price" class="layui-input" value="' + (l.price || 0) + '" required>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="layui-form-item">' +
|
||||
'<div class="layui-input-block">' +
|
||||
'<button class="layui-btn" lay-submit lay-filter="LAY-edit-price-submit">修改</button>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</form>'
|
||||
);
|
||||
form.render(null, "layadmin-form-edit-price");
|
||||
form.on("submit(LAY-edit-price-submit)", function (data) {
|
||||
admin.req({
|
||||
method: "post",
|
||||
contentType: "application/json;charset=UTF-8",
|
||||
headers: {
|
||||
'token': layui.data(setter.tableName)[setter.request.tokenName]
|
||||
},
|
||||
url: setter.baseurl + "adminapi/BeautifulNumber/update-price",
|
||||
data: JSON.stringify({ id: l.id, price: parseInt(data.field.price) }),
|
||||
done: function (res) {
|
||||
if (res.code === 0 || res.code === 200) {
|
||||
layer.close(idx);
|
||||
layer.msg('修改成功');
|
||||
table.reload("LAY-beautifulnumber-back-manage");
|
||||
} else {
|
||||
layer.msg(res.info || '修改失败');
|
||||
}
|
||||
}
|
||||
})
|
||||
return false;
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if ("freeze" === e.event) {
|
||||
layer.confirm("确定冻结此靓号么?", function (i) {
|
||||
admin.req({
|
||||
method: "post",
|
||||
contentType: "application/json;charset=UTF-8",
|
||||
headers: {
|
||||
'token': layui.data(setter.tableName)[setter.request.tokenName]
|
||||
},
|
||||
url: setter.baseurl + "adminapi/BeautifulNumber/freeze",
|
||||
data: JSON.stringify({ id: l.id }),
|
||||
done: function (res) {
|
||||
if (res.code === 0 || res.code === 200) {
|
||||
layer.msg('冻结成功');
|
||||
table.reload("LAY-beautifulnumber-back-manage");
|
||||
} else {
|
||||
layer.msg(res.info || '冻结失败');
|
||||
}
|
||||
layer.close(i);
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
if ("unfreeze" === e.event) {
|
||||
layer.confirm("确定解冻此靓号么?", function (i) {
|
||||
admin.req({
|
||||
method: "post",
|
||||
contentType: "application/json;charset=UTF-8",
|
||||
headers: {
|
||||
'token': layui.data(setter.tableName)[setter.request.tokenName]
|
||||
},
|
||||
url: setter.baseurl + "adminapi/BeautifulNumber/unfreeze",
|
||||
data: JSON.stringify({ id: l.id }),
|
||||
done: function (res) {
|
||||
if (res.code === 0 || res.code === 200) {
|
||||
layer.msg('解冻成功');
|
||||
table.reload("LAY-beautifulnumber-back-manage");
|
||||
} else {
|
||||
layer.msg(res.info || '解冻失败');
|
||||
}
|
||||
layer.close(i);
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
if ("setNormal" === e.event) {
|
||||
layer.confirm("确定将此靓号设为普通号么?", function (i) {
|
||||
admin.req({
|
||||
method: "post",
|
||||
contentType: "application/json;charset=UTF-8",
|
||||
headers: {
|
||||
'token': layui.data(setter.tableName)[setter.request.tokenName]
|
||||
},
|
||||
url: setter.baseurl + "adminapi/BeautifulNumber/set-normal",
|
||||
data: JSON.stringify({ id: l.id }),
|
||||
done: function (res) {
|
||||
if (res.code === 0 || res.code === 200) {
|
||||
layer.msg('设置成功');
|
||||
table.reload("LAY-beautifulnumber-back-manage");
|
||||
} else {
|
||||
layer.msg(res.info || '设置失败');
|
||||
}
|
||||
layer.close(i);
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
if ("give" === e.event) {
|
||||
i.popup({
|
||||
title: "赠送靓号给用户",
|
||||
area: ["600px", "380px"],
|
||||
id: "LAY-popup-give-number",
|
||||
success: function (div, idx) {
|
||||
$(div).html(
|
||||
'<div style="padding:20px;">' +
|
||||
'<div class="layui-form-item">' +
|
||||
'<label class="layui-form-label" style="width:70px;">靓号</label>' +
|
||||
'<div class="layui-input-block" style="left:90px;">' +
|
||||
'<span class="layui-badge layui-bg-blue" style="padding:6px 15px;font-size:14px;">' + (l.number || '') + '</span>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="layui-form-item" style="margin-bottom:5px;">' +
|
||||
'<label class="layui-form-label" style="width:70px;">选择用户</label>' +
|
||||
'<div class="layui-input-block" style="left:90px;position:relative;">' +
|
||||
'<input type="text" id="user-search-input" class="layui-input" style="padding-right:35px;" placeholder="输入用户名、昵称、用户编号或手机号搜索">' +
|
||||
'<i class="layui-icon layui-icon-loading layui-anim layui-anim-loop" id="search-loading" style="position:absolute;right:10px;top:10px;display:none;"></i>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div id="user-dropdown" class="user-dropdown-list" style="display:none;position:absolute;left:90px;width:calc(100% - 90px);max-height:250px;overflow-y:auto;background:#fff;border:1px solid #e6e6e6;border-radius:2px;box-shadow:0 2px 4px rgba(0,0,0,.1);z-index:99999;"></div>' +
|
||||
'<div style="margin-top:10px;color:#999;font-size:12px;margin-left:70px;">提示:可按用户名、昵称、用户编号、手机号进行模糊搜索</div>' +
|
||||
'<div id="selected-user-info" style="margin-top:15px;padding:15px;background:#e8f4ff;border-radius:6px;border:1px solid #b8d6f0;display:none;margin-left:70px;">' +
|
||||
'<div style="display:flex;align-items:center;justify-content:space-between;">' +
|
||||
'<div>' +
|
||||
'<span style="color:#666;">已选择用户:</span>' +
|
||||
'<span id="selected-user-text" style="color:#1e88e5;font-weight:bold;font-size:15px;"></span>' +
|
||||
'</div>' +
|
||||
'<button type="button" class="layui-btn layui-btn-sm layui-btn-danger" id="btn-clear-user"><i class="layui-icon layui-icon-close"></i> 清除</button>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div style="margin-top:20px;text-align:center;">' +
|
||||
'<button type="button" class="layui-btn layui-btn-primary layui-btn-sm" id="btn-cancel-give" style="margin-right:15px;">取消</button>' +
|
||||
'<button type="button" class="layui-btn layui-btn-sm" id="btn-confirm-give"><i class="layui-icon layui-icon-ok"></i> 确认赠送</button>' +
|
||||
'</div>' +
|
||||
'</div>'
|
||||
);
|
||||
|
||||
var selectedUser = null;
|
||||
var searchTimer = null;
|
||||
var $dropdown = $('#user-dropdown');
|
||||
var $input = $('#user-search-input');
|
||||
|
||||
function renderDropdown(users) {
|
||||
if (!users || users.length === 0) {
|
||||
$dropdown.html('<div style="padding:15px;color:#999;text-align:center;">未找到匹配的用户</div>').show();
|
||||
return;
|
||||
}
|
||||
var html = '';
|
||||
users.forEach(function(user) {
|
||||
var number = user.baseInfo ? user.baseInfo.number : '';
|
||||
var name = user.baseInfo ? user.baseInfo.userName : '';
|
||||
var mobile = user.baseInfo ? user.baseInfo.mobile : '';
|
||||
html += '<div class="user-dropdown-item" data-id="' + user.id + '" data-number="' + number + '" data-name="' + name + '" data-mobile="' + mobile + '" style="padding:12px 15px;cursor:pointer;border-bottom:1px solid #f0f0f0;">' +
|
||||
'<div style="display:flex;justify-content:space-between;align-items:center;">' +
|
||||
'<div><span style="font-weight:bold;color:#333;font-size:14px;">' + name + '</span> <span style="color:#666;font-size:12px;">[' + number + ']</span></div>' +
|
||||
(mobile ? '<span style="color:#999;font-size:12px;">' + mobile + '</span>' : '<span style="color:#999;font-size:12px;">无手机号</span>') +
|
||||
'</div></div>';
|
||||
});
|
||||
$dropdown.html(html).show();
|
||||
}
|
||||
|
||||
function searchUsers(keyword) {
|
||||
if (!keyword || keyword.length < 1) {
|
||||
$dropdown.hide();
|
||||
return;
|
||||
}
|
||||
$('#search-loading').show();
|
||||
admin.req({
|
||||
method: "post",
|
||||
contentType: "application/json;charset=UTF-8",
|
||||
url: setter.baseurl + "adminapi/AdminUser/GetUserList",
|
||||
headers: {
|
||||
'token': layui.data(setter.tableName)[setter.request.tokenName]
|
||||
},
|
||||
data: JSON.stringify({ keyWord: keyword, pageIndex: 1, pageSize: 20 }),
|
||||
done: function (res) {
|
||||
$('#search-loading').hide();
|
||||
if (res.data && res.data.length > 0) {
|
||||
renderDropdown(res.data);
|
||||
} else {
|
||||
renderDropdown([]);
|
||||
}
|
||||
},
|
||||
fail: function() {
|
||||
$('#search-loading').hide();
|
||||
renderDropdown([]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$input.on('input', function() {
|
||||
var keyword = $(this).val().trim();
|
||||
clearTimeout(searchTimer);
|
||||
if (keyword.length < 1) {
|
||||
$dropdown.hide();
|
||||
return;
|
||||
}
|
||||
searchTimer = setTimeout(function() {
|
||||
searchUsers(keyword);
|
||||
}, 300);
|
||||
});
|
||||
|
||||
$input.on('focus', function() {
|
||||
var keyword = $(this).val().trim();
|
||||
if (keyword.length >= 1 && $dropdown.find('.user-dropdown-item').length > 0) {
|
||||
$dropdown.show();
|
||||
}
|
||||
});
|
||||
|
||||
$(document).off('click', '.user-dropdown-item').on('click', '.user-dropdown-item', function() {
|
||||
var userId = $(this).data('id');
|
||||
var userNumber = $(this).data('number');
|
||||
var userName = $(this).data('name');
|
||||
var userMobile = $(this).data('mobile');
|
||||
selectedUser = { id: userId, number: userNumber, name: userName, mobile: userMobile };
|
||||
$input.val(name + ' [' + userNumber + ']' + (userMobile ? ' ' + userMobile : ''));
|
||||
$dropdown.hide();
|
||||
$('#selected-user-text').text(userName + ' [' + userNumber + ']' + (userMobile ? ' ' + userMobile : ''));
|
||||
$('#selected-user-info').show();
|
||||
});
|
||||
|
||||
$(document).on('click', function(e) {
|
||||
if (!$(e.target).closest('#user-search-input, #user-dropdown').length) {
|
||||
$dropdown.hide();
|
||||
}
|
||||
});
|
||||
|
||||
$('#btn-clear-user').on('click', function() {
|
||||
selectedUser = null;
|
||||
$input.val('').focus();
|
||||
$('#selected-user-info').hide();
|
||||
});
|
||||
|
||||
$('#btn-cancel-give').on('click', function() {
|
||||
layer.close(idx);
|
||||
});
|
||||
|
||||
$('#btn-confirm-give').on('click', function() {
|
||||
if (!selectedUser) {
|
||||
layer.msg('请先选择用户');
|
||||
return;
|
||||
}
|
||||
admin.req({
|
||||
method: "post",
|
||||
contentType: "application/json;charset=UTF-8",
|
||||
headers: {
|
||||
'token': layui.data(setter.tableName)[setter.request.tokenName]
|
||||
},
|
||||
url: setter.baseurl + "adminapi/BeautifulNumber/give-to-user",
|
||||
data: JSON.stringify({ number: l.number, userId: selectedUser.id }),
|
||||
done: function (res) {
|
||||
if (res.code === 0 || res.code === 200) {
|
||||
layer.close(idx);
|
||||
layer.msg('赠送成功');
|
||||
table.reload("LAY-beautifulnumber-back-manage");
|
||||
} else {
|
||||
layer.msg(res.info || '赠送失败');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
// 数字格式化
|
||||
function formatNumber(num) {
|
||||
if (!num) return '0';
|
||||
return num.toString().replace(/\B(?=(\d{4})+(?!\d))/g, ",");
|
||||
}
|
||||
|
||||
// 时间戳格式化
|
||||
function formatTimestamp(timestamp, format = 'yyyy-MM-dd hh:mm:ss') {
|
||||
if (!timestamp) return '-';
|
||||
const date = new Date(timestamp * 1000);
|
||||
const o = {
|
||||
'M+': date.getMonth() + 1,
|
||||
'd+': date.getDate(),
|
||||
'h+': date.getHours(),
|
||||
'm+': date.getMinutes(),
|
||||
's+': date.getSeconds()
|
||||
};
|
||||
if (/(y+)/.test(format)) {
|
||||
format = format.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length));
|
||||
}
|
||||
for (const k in o) {
|
||||
if (new RegExp('(' + k + ')').test(format)) {
|
||||
format = format.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (('00' + o[k]).substr(('' + o[k]).length)));
|
||||
}
|
||||
}
|
||||
return format;
|
||||
}
|
||||
|
||||
// e("beautifulnumber", {})
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,520 @@
|
||||
|
||||
<title>靓号规则配置</title>
|
||||
|
||||
<div class="layui-card layadmin-header">
|
||||
<div class="layui-breadcrumb" lay-filter="breadcrumb">
|
||||
<a lay-href="">主页</a>
|
||||
<a><cite>靓号管理</cite></a>
|
||||
<a><cite>规则配置</cite></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-fluid">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header">
|
||||
<div class="layui-btn-group">
|
||||
<button class="layui-btn layui-btn-sm" id="btn-add">
|
||||
<i class="layui-icon layui-icon-add-circle"></i> 添加规则
|
||||
</button>
|
||||
<button class="layui-btn layui-btn-sm" id="btn-save">
|
||||
<i class="layui-icon layui-icon-ok"></i> 保存规则
|
||||
</button>
|
||||
<button class="layui-btn layui-btn-sm layui-btn-normal" id="btn-init">
|
||||
<i class="layui-icon layui-icon-refresh"></i> 初始化规则
|
||||
</button>
|
||||
<button class="layui-btn layui-btn-sm layui-btn-warm" id="btn-reload">
|
||||
<i class="layui-icon layui-icon-refresh"></i> 刷新
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-card-body">
|
||||
<table id="LAY-rules-manage" lay-filter="LAY-rules-manage"></table>
|
||||
|
||||
<script type="text/html" id="table-rule-admin">
|
||||
<a class="layui-btn layui-btn-normal layui-btn-xs" lay-event="edit"><i class="layui-icon layui-icon-edit"></i>编辑</a>
|
||||
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del"><i class="layui-icon layui-icon-delete"></i>删除</a>
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="toolbar-rule">
|
||||
<div class="layui-btn-container">
|
||||
<button class="layui-btn layui-btn-sm" lay-event="add">添加规则</button>
|
||||
</div>
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
layui.use(['admin', 'table', 'form', 'layer'], function () {
|
||||
var $ = layui.$,
|
||||
admin = layui.admin,
|
||||
table = layui.table,
|
||||
form = layui.form,
|
||||
layer = layui.layer,
|
||||
setter = layui.setter;
|
||||
|
||||
var rules = [];
|
||||
|
||||
function loadRules() {
|
||||
admin.req({
|
||||
method: "get",
|
||||
url: setter.baseurl + "adminapi/BeautifulNumber/rules",
|
||||
done: function (res) {
|
||||
if (res.code === 0 && res.data) {
|
||||
rules = res.data;
|
||||
renderTable();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function renderTable() {
|
||||
|
||||
|
||||
table.render({
|
||||
elem: '#LAY-rules-manage',
|
||||
data: rules,
|
||||
cols: [[
|
||||
{ field: "ruleName", title: "规则名称", width: 150, edit: 'text' },
|
||||
{ field: "rulePattern", title: "生成模式", width: 150 },
|
||||
{ field: "level", title: "等级", width: 70, edit: 'text' },
|
||||
{ field: "price", title: "价格(金币)", width: 100, edit: 'text' },
|
||||
{ field: "sort", title: "排序", width: 70, edit: 'text' },
|
||||
{
|
||||
field: "isEnable",
|
||||
title: "启用",
|
||||
width: 80,
|
||||
templet: function(d) {
|
||||
return d.isEnable ? '<span class="layui-badge">是</span>' : '<span class="layui-badge layui-bg-gray">否</span>';
|
||||
}
|
||||
},
|
||||
{ width: 250, title: "操作", align: "center", fixed: "right", toolbar: "#table-rule-admin" }
|
||||
]],
|
||||
toolbar: '#toolbar-rule'
|
||||
});
|
||||
}
|
||||
|
||||
$('#btn-reload').on('click', function() {
|
||||
loadRules();
|
||||
layer.msg('刷新成功');
|
||||
});
|
||||
|
||||
$('#btn-add').on('click', function() {
|
||||
showAddDialog();
|
||||
});
|
||||
|
||||
$('#btn-init').on('click', function() {
|
||||
layer.confirm('确定初始化靓号规则吗?这将覆盖现有规则!', function(idx) {
|
||||
admin.req({
|
||||
method: "post",
|
||||
url: setter.baseurl + "adminapi/BeautifulNumber/init-rules",
|
||||
done: function (res) {
|
||||
layer.close(idx);
|
||||
if (res.code === 0 || res.code === 200) {
|
||||
layer.msg('初始化成功');
|
||||
loadRules();
|
||||
} else {
|
||||
layer.msg(res.info || '初始化失败');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
$('#btn-save').on('click', function() {
|
||||
layer.confirm('确定保存所有规则吗?', function(idx) {
|
||||
admin.req({
|
||||
method: "post",
|
||||
contentType: "application/json;charset=UTF-8",
|
||||
url: setter.baseurl + "adminapi/BeautifulNumber/update-rules",
|
||||
data: JSON.stringify(rules),
|
||||
done: function (res) {
|
||||
layer.close(idx);
|
||||
if (res.code === 0 || res.code === 200) {
|
||||
layer.msg('保存成功');
|
||||
loadRules();
|
||||
} else {
|
||||
layer.msg(res.info || '保存失败');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
table.on("tool(LAY-rules-manage)", function (obj) {
|
||||
var data = obj.data;
|
||||
|
||||
if ("del" === obj.event) {
|
||||
layer.confirm("确定删除此规则么?", function (i) {
|
||||
admin.req({
|
||||
method: "post",
|
||||
contentType: "application/json;charset=UTF-8",
|
||||
url: setter.baseurl + "adminapi/BeautifulNumber/delete-rule",
|
||||
data: JSON.stringify({ id: data.id }),
|
||||
done: function (res) {
|
||||
layer.close(i);
|
||||
if (res.code === 0 || res.code === 200) {
|
||||
layer.msg('删除成功');
|
||||
loadRules();
|
||||
} else {
|
||||
layer.msg(res.info || '删除失败');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if ("edit" === obj.event) {
|
||||
showEditDialog(data, obj);
|
||||
}
|
||||
});
|
||||
|
||||
table.on('toolbar(LAY-rules-manage)', function(obj){
|
||||
if(obj.event === 'add'){
|
||||
showAddDialog();
|
||||
}
|
||||
});
|
||||
|
||||
function showAddDialog() {
|
||||
var content =
|
||||
'<form class="layui-form" style="padding: 20px;" lay-filter="layuiadmin-form-add-rule">' +
|
||||
'<div class="layui-form-item">' +
|
||||
'<label class="layui-form-label">规则名称</label>' +
|
||||
'<div class="layui-input-block">' +
|
||||
'<input type="text" name="ruleName" class="layui-input" placeholder="如:AAAA、ABBB、ABCDEFGH、AAAABBBX">' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="layui-form-item">' +
|
||||
'<label class="layui-form-label">等级</label>' +
|
||||
'<div class="layui-input-block">' +
|
||||
'<select name="level">' +
|
||||
'<option value="S">S级</option>' +
|
||||
'<option value="A">A级</option>' +
|
||||
'<option value="B">B级</option>' +
|
||||
'<option value="C">C级</option>' +
|
||||
'<option value="D">D级</option>' +
|
||||
'</select>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="layui-form-item">' +
|
||||
'<label class="layui-form-label">价格</label>' +
|
||||
'<div class="layui-input-block">' +
|
||||
'<input type="number" name="price" class="layui-input" placeholder="金币数量">' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="layui-form-item">' +
|
||||
'<label class="layui-form-label">排序</label>' +
|
||||
'<div class="layui-input-block">' +
|
||||
'<input type="number" name="sort" class="layui-input" placeholder="数字越小越靠前" value="0">' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="layui-form-item">' +
|
||||
'<div class="layui-input-block">' +
|
||||
'<button class="layui-btn" lay-submit lay-filter="LAY-add-rule-submit">添加</button>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</form>';
|
||||
|
||||
layer.open({
|
||||
type: 1,
|
||||
title: '添加规则',
|
||||
area: ['650px', '500px'],
|
||||
content: content
|
||||
});
|
||||
|
||||
layui.form.render();
|
||||
layui.form.on('submit(LAY-add-rule-submit)', function(dataField) {
|
||||
var ruleName = dataField.field.ruleName.toUpperCase().trim();
|
||||
if (!ruleName) {
|
||||
layer.msg('请输入规则名称');
|
||||
return false;
|
||||
}
|
||||
|
||||
var newRule = {
|
||||
ruleName: ruleName,
|
||||
rulePattern: ruleName,
|
||||
level: dataField.field.level,
|
||||
price: parseInt(dataField.field.price) || 0,
|
||||
sort: parseInt(dataField.field.sort) || 0,
|
||||
isEnable: true
|
||||
};
|
||||
|
||||
admin.req({
|
||||
method: "post",
|
||||
contentType: "application/json;charset=UTF-8",
|
||||
url: setter.baseurl + "adminapi/BeautifulNumber/add-rule",
|
||||
data: JSON.stringify(newRule),
|
||||
done: function (res) {
|
||||
layer.closeAll();
|
||||
if (res.code === 0 || res.code === 200) {
|
||||
layer.msg('添加成功');
|
||||
loadRules();
|
||||
} else {
|
||||
layer.msg(res.info || '添加失败');
|
||||
}
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
function showEditDialog(data, obj) {
|
||||
var level = data.level || 'D';
|
||||
var isEnabled = data.isEnable ? 'checked' : '';
|
||||
var content =
|
||||
'<form class="layui-form" style="padding: 20px;" lay-filter="layuiadmin-form-edit-rule">' +
|
||||
'<div class="layui-form-item">' +
|
||||
'<label class="layui-form-label">规则名称</label>' +
|
||||
'<div class="layui-input-block">' +
|
||||
'<input type="text" name="ruleName" class="layui-input" value="' + (data.ruleName || '') + '" placeholder="如:KKKKKKKK KKKKTTTT ABCDEFGH KKKKXXXX">' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="layui-form-item">' +
|
||||
'<label class="layui-form-label">等级</label>' +
|
||||
'<div class="layui-input-block">' +
|
||||
'<select name="level">' +
|
||||
'<option value="S" ' + (level === 'S' ? 'selected' : '') + '>S级</option>' +
|
||||
'<option value="A" ' + (level === 'A' ? 'selected' : '') + '>A级</option>' +
|
||||
'<option value="B" ' + (level === 'B' ? 'selected' : '') + '>B级</option>' +
|
||||
'<option value="C" ' + (level === 'C' ? 'selected' : '') + '>C级</option>' +
|
||||
'<option value="D" ' + (level === 'D' ? 'selected' : '') + '>D级</option>' +
|
||||
'</select>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="layui-form-item">' +
|
||||
'<label class="layui-form-label">价格</label>' +
|
||||
'<div class="layui-input-block">' +
|
||||
'<input type="number" name="price" class="layui-input" value="' + (data.price || 0) + '" placeholder="金币数量">' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="layui-form-item">' +
|
||||
'<label class="layui-form-label">排序</label>' +
|
||||
'<div class="layui-input-block">' +
|
||||
'<input type="number" name="sort" class="layui-input" value="' + (data.sort || 0) + '" placeholder="数字越小越靠前">' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="layui-form-item">' +
|
||||
'<label class="layui-form-label">启用</label>' +
|
||||
'<div class="layui-input-block">' +
|
||||
'<input type="checkbox" name="isEnable" lay-skin="switch" lay-text="开启|关闭" ' + isEnabled + '>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="layui-form-item">' +
|
||||
'<div class="layui-input-block">' +
|
||||
'<button class="layui-btn" lay-submit lay-filter="LAY-edit-rule-submit">保存</button>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</form>';
|
||||
|
||||
layer.open({
|
||||
type: 1,
|
||||
title: '编辑规则',
|
||||
area: ['500px', '450px'],
|
||||
content: content
|
||||
});
|
||||
|
||||
layui.form.render();
|
||||
layui.form.on('submit(LAY-edit-rule-submit)', function(formData) {
|
||||
var isEnable = $(formData.form).find('input[name="isEnable"]').prop('checked');
|
||||
var ruleName = formData.field.ruleName.toUpperCase().trim();
|
||||
|
||||
if (!ruleName) {
|
||||
layer.msg('请输入规则名称');
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
var updatedRule = {
|
||||
id: data.id,
|
||||
ruleName: ruleName,
|
||||
|
||||
level: formData.field.level,
|
||||
price: parseInt(formData.field.price) || 0,
|
||||
sort: parseInt(formData.field.sort) || 0,
|
||||
isEnable: isEnable
|
||||
};
|
||||
|
||||
admin.req({
|
||||
method: "post",
|
||||
contentType: "application/json;charset=UTF-8",
|
||||
url: setter.baseurl + "adminapi/BeautifulNumber/update-rule",
|
||||
data: JSON.stringify(updatedRule),
|
||||
done: function (res) {
|
||||
layer.closeAll();
|
||||
if (res.code === 0 || res.code === 200) {
|
||||
layer.msg('保存成功');
|
||||
loadRules();
|
||||
} else {
|
||||
layer.msg(res.info || '保存失败');
|
||||
}
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
function generateRegexPattern(ruleName) {
|
||||
if (!ruleName) {
|
||||
return { valid: false, message: '请输入规则名称' };
|
||||
}
|
||||
|
||||
ruleName = ruleName.toUpperCase().trim();
|
||||
|
||||
if (ruleName.length!=8) {
|
||||
return { valid: false, message: '规则名称长度必须是8位' };
|
||||
}
|
||||
|
||||
// 检查是否全部是连续字母A-H (如 ABCDEFGH 表示8连号)
|
||||
var isAllConsecutive = true;
|
||||
var expectedChar = 'A';
|
||||
for (var i = 0; i < ruleName.length; i++) {
|
||||
if (ruleName[i] !== expectedChar) {
|
||||
isAllConsecutive = false;
|
||||
break;
|
||||
}
|
||||
expectedChar = String.fromCharCode(expectedChar.charCodeAt(0) + 1);
|
||||
}
|
||||
|
||||
// 如果是 ABCDEFGH 这种连续字母,表示全部是连续数字
|
||||
if (isAllConsecutive) {
|
||||
var len = ruleName.length;
|
||||
// 生成匹配连续数字的正则: 01234567, 12345678, 23456789
|
||||
var patterns = [];
|
||||
for (var start = 0; start <= 9 - len; start++) {
|
||||
var seq = '';
|
||||
for (var k = 0; k < len; k++) {
|
||||
seq += (start + k);
|
||||
}
|
||||
if (patterns.length > 0 && patterns.length < 10) {
|
||||
patterns.push(seq);
|
||||
}
|
||||
}
|
||||
if (patterns.length > 0) {
|
||||
return {
|
||||
valid: true,
|
||||
rulePattern: "^(" + patterns.join('|') + ")$"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
var regex = "^";
|
||||
var prevGroup = '';
|
||||
|
||||
for (var i = 0; i < ruleName.length; i++) {
|
||||
var char = ruleName[i];
|
||||
|
||||
if (char === 'A') {
|
||||
if (prevGroup !== 'A') {
|
||||
var aLen = 0;
|
||||
var j = i;
|
||||
while (j < ruleName.length && ruleName[j] === 'A') {
|
||||
aLen++;
|
||||
j++;
|
||||
}
|
||||
if (aLen === 1) {
|
||||
regex += "(\\d)";
|
||||
} else {
|
||||
regex += "(\\d)\\1{" + (aLen - 1) + "}";
|
||||
}
|
||||
prevGroup = 'A';
|
||||
}
|
||||
} else if (char === 'B') {
|
||||
if (prevGroup !== 'B') {
|
||||
var bLen = 0;
|
||||
var j = i;
|
||||
while (j < ruleName.length && ruleName[j] === 'B') {
|
||||
bLen++;
|
||||
j++;
|
||||
}
|
||||
regex += "(\\d{" + bLen + "})";
|
||||
prevGroup = 'B';
|
||||
}
|
||||
} else if (char === 'X') {
|
||||
regex += "\\d";
|
||||
prevGroup = 'X';
|
||||
} else if (char >= '0' && char <= '9') {
|
||||
regex += char;
|
||||
prevGroup = 'N';
|
||||
} else if (char >= 'A' && char <= 'H') {
|
||||
// C-H 也当作连续数字处理
|
||||
if (prevGroup !== 'CONSECUTIVE') {
|
||||
var cLen = 0;
|
||||
var j = i;
|
||||
while (j < ruleName.length) {
|
||||
var c = ruleName[j];
|
||||
if (c >= 'A' && c <= 'H') {
|
||||
cLen++;
|
||||
j++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
regex += "(\\d{" + cLen + "})";
|
||||
prevGroup = 'CONSECUTIVE';
|
||||
i = j - 1;
|
||||
}
|
||||
} else {
|
||||
return { valid: false, message: '规则名称只能包含A-H、X和数字' };
|
||||
}
|
||||
}
|
||||
|
||||
regex += "$";
|
||||
|
||||
try {
|
||||
new RegExp(regex);
|
||||
} catch(e) {
|
||||
return { valid: false, message: '生成的正则表达式无效: ' + regex };
|
||||
}
|
||||
|
||||
return {
|
||||
valid: true,
|
||||
rulePattern: regex
|
||||
};
|
||||
}
|
||||
|
||||
window.generateRegex = function() {
|
||||
var ruleName = $('#ruleNameInput').val().toUpperCase().trim();
|
||||
if (ruleName) {
|
||||
var result = generateRegexPattern(ruleName);
|
||||
if (result.valid) {
|
||||
$('#rulePatternInput').val(result.rulePattern);
|
||||
}
|
||||
} else {
|
||||
$('#rulePatternInput').val('');
|
||||
}
|
||||
};
|
||||
|
||||
window.generateEditRegex = function() {
|
||||
var ruleName = $('#editRuleNameInput').val().toUpperCase().trim();
|
||||
if (ruleName) {
|
||||
var result = generateRegexPattern(ruleName);
|
||||
if (result.valid) {
|
||||
$('#editRulePatternInput').val(result.rulePattern);
|
||||
}
|
||||
} else {
|
||||
$('#editRulePatternInput').val('');
|
||||
}
|
||||
};
|
||||
|
||||
table.on('edit(LAY-rules-manage)', function(obj){
|
||||
var field = obj.field;
|
||||
var value = obj.value;
|
||||
var data = obj.data;
|
||||
|
||||
if (!isNaN(value) && value !== '') {
|
||||
if (field === 'price' || field === 'sort') {
|
||||
data[field] = parseInt(value);
|
||||
}
|
||||
}
|
||||
|
||||
if (field === 'ruleName') {
|
||||
data.rulePattern = value;
|
||||
renderTable();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
loadRules();
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,59 @@
|
||||
<div class="layui-form" lay-filter="layuiadmin-form-admin" style="padding: 20px 30px 0 0;">
|
||||
<input type="hidden" name="id">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">任务编号</label>
|
||||
<div class="layui-input-inline">
|
||||
<input type="text" name="number" placeholder="" autocomplete="off" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">任务描述</label>
|
||||
<div class="layui-input-inline">
|
||||
<input type="text" name="remark" placeholder="" autocomplete="off" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">任务类型</label>
|
||||
<div class="layui-input-inline">
|
||||
<select name="taskType">
|
||||
<option value="1">每日任务</option>
|
||||
<option value="2">在线时长</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">任务事件</label>
|
||||
<div class="layui-input-inline">
|
||||
<select name="taskEvent">
|
||||
<option value="1">整点在线奖励</option>
|
||||
<option value="2">在线时长</option>
|
||||
<option value="3">捕获任意鱼</option>
|
||||
<option value="4">捕获黄金鱼</option>
|
||||
<option value="5">捕获Boss鱼</option>
|
||||
<option value="6">赢取金币</option>
|
||||
<option value="7">使用狂暴</option>
|
||||
<option value="8">开炮任务</option>
|
||||
<option value="9">登录任务</option>
|
||||
<option value="10">充值任务</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">任务条件</label>
|
||||
<div class="layui-input-inline">
|
||||
<input type="number" name="taskCount" placeholder="" autocomplete="off" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">战令经验</label>
|
||||
<div class="layui-input-inline">
|
||||
<input type="number" name="exp" placeholder="" autocomplete="off" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label"></label>
|
||||
<div class="layui-input-inline">
|
||||
<input type="button" lay-submit lay-filter="LAY-user-back-submit" value="确认" class="layui-btn">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,231 @@
|
||||
|
||||
<title>任务管理</title>
|
||||
|
||||
<div class="layui-card layadmin-header">
|
||||
<div class="layui-breadcrumb" lay-filter="breadcrumb">
|
||||
<a lay-href="">主页</a>
|
||||
<a><cite>游戏配置</cite></a>
|
||||
<a><cite>任务管理</cite></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-fluid">
|
||||
<div class="layui-card">
|
||||
<div class="layui-form layui-card-header layuiadmin-card-header-auto" lay-filter="layadmin-taskadmin-formlist">
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-inline">
|
||||
<label class="layui-form-label">状态</label>
|
||||
<div class="layui-input-block">
|
||||
<select name="state">
|
||||
<option value="-1">全部</option>
|
||||
<option value="1">启用</option>
|
||||
<option value="2">禁用</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-inline">
|
||||
<label class="layui-form-label">任务类型</label>
|
||||
<div class="layui-input-block">
|
||||
<select name="taskType">
|
||||
<option value="-1">全部</option>
|
||||
<option value="1">每日任务</option>
|
||||
<option value="2">在线时长</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-inline">
|
||||
<label class="layui-form-label">任务事件</label>
|
||||
<div class="layui-input-block">
|
||||
<select name="taskEvent">
|
||||
<option value="-1">全部</option>
|
||||
<option value="1">整点在线奖励</option>
|
||||
<option value="2">在线时长</option>
|
||||
<option value="3">捕获任意鱼</option>
|
||||
<option value="4">捕获黄金鱼</option>
|
||||
<option value="5">捕获Boss鱼</option>
|
||||
<option value="6">赢取金币</option>
|
||||
<option value="7">使用狂暴</option>
|
||||
<option value="8">开炮任务</option>
|
||||
<option value="9">登录任务</option>
|
||||
<option value="10">充值任务</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-card-body">
|
||||
<div style="padding-bottom: 10px;">
|
||||
<button class="layui-btn layuiadmin-btn-admin" data-type="add">添加</button>
|
||||
</div>
|
||||
|
||||
<table id="LAY-task-back-manage" lay-filter="LAY-task-back-manage"></table>
|
||||
|
||||
<script type="text/html" id="table-taskadmin-admin">
|
||||
<a class="layui-btn layui-btn-normal layui-btn-xs" lay-event="edit"><i class="layui-icon layui-icon-edit"></i>编辑</a>
|
||||
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del"><i class="layui-icon layui-icon-delete"></i>删除</a>
|
||||
<a class="layui-btn layui-btn-normal layui-btn-xs" lay-event="state"><i class="layui-icon layui-icon-edit"></i>状态</a>
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
|
||||
layui.use('task', layui.factory('task')).use(['admin','task', 'table'], function () {
|
||||
var $ = layui.$
|
||||
, admin = layui.admin
|
||||
, view = layui.view
|
||||
, table = layui.table
|
||||
, setter = layui.setter
|
||||
, form = layui.form;
|
||||
|
||||
|
||||
form.render('select'); //刷新select选择框渲染
|
||||
form.on('select', function (data) {
|
||||
var data1 = form.val("layadmin-taskadmin-formlist");
|
||||
data1.state = parseInt(data1.state);
|
||||
data1.taskType = parseInt(data1.taskType);
|
||||
data1.taskEvent = parseInt(data1.taskEvent);
|
||||
table.reload('LAY-task-back-manage', {
|
||||
where: data1
|
||||
});
|
||||
});
|
||||
//监听搜索
|
||||
form.on('submit(LAY-user-front-search)', function (data) {
|
||||
var field = data.field;
|
||||
|
||||
//执行重载
|
||||
table.reload('LAY-task-back-manage', {
|
||||
where: field
|
||||
});
|
||||
});
|
||||
|
||||
table.on('tool(LAY-task-back-manage)', function (obj) {
|
||||
var data = obj.data;
|
||||
if (obj.event === 'edit') {
|
||||
admin.popup({
|
||||
title: '编辑任务'
|
||||
, area: ["500px", "600px"]
|
||||
, id: 'LAY-popup-user-edit'
|
||||
, success: function (layero, index) {
|
||||
view(this.id).render('config/TaskForm').done(function () {
|
||||
form.val('layuiadmin-form-admin', {
|
||||
id: data.id || '',
|
||||
number: data.number || '',
|
||||
remark: data.remark || '',
|
||||
taskType: data.taskType || 1,
|
||||
taskEvent: data.taskEvent || 1,
|
||||
taskCount: data.taskCount || 0,
|
||||
exp: data.exp || 0,
|
||||
state: data.state || 1
|
||||
});
|
||||
form.render(null, 'layuiadmin-form-admin');
|
||||
form.on('submit(LAY-user-back-submit)', function (formData) {
|
||||
admin.req({
|
||||
method: "post",
|
||||
contentType: "application/json;charset=UTF-8",
|
||||
dataType: "json",
|
||||
headers: {
|
||||
'token': layui.data(setter.tableName)[setter.request.tokenName]
|
||||
},
|
||||
url: setter.baseurl + "adminapi/task/UpdateTask",
|
||||
async: false,
|
||||
data: JSON.stringify(formData.field),
|
||||
done: function (res) {
|
||||
if (res.code === 0) {
|
||||
layui.table.reload('LAY-task-back-manage');
|
||||
layer.close(index);
|
||||
} else {
|
||||
layer.msg(res.info || '操作失败');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
} else if (obj.event === 'del') {
|
||||
layer.confirm('确定删除吗?', function (index) {
|
||||
admin.req({
|
||||
url: setter.baseurl + "adminapi/task/DeleteTask",
|
||||
data: { id: data.id },
|
||||
done: function (res) {
|
||||
if (res.code === 0) {
|
||||
obj.del();
|
||||
layer.close(index);
|
||||
} else {
|
||||
layer.msg(res.info || '删除失败');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
} else if (obj.event === 'state') {
|
||||
var newState = data.state === 1 ? 2 : 1;
|
||||
admin.req({
|
||||
url: setter.baseurl + "adminapi/task/UpdateTaskState",
|
||||
data: { id: data.id, state: newState },
|
||||
done: function (res) {
|
||||
if (res.code === 0) {
|
||||
obj.update({ state: newState });
|
||||
} else {
|
||||
layer.msg(res.info || '状态更新失败');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
//事件
|
||||
var active = {
|
||||
add: function () {
|
||||
|
||||
admin.popup({
|
||||
title: '添加任务'
|
||||
, area: ["500px", "600px"]
|
||||
, id: 'LAY-popup-user-add'
|
||||
, success: function (layero, index) {
|
||||
view(this.id).render('config/TaskForm').done(function () {
|
||||
form.render(null, 'layuiadmin-form-admin');
|
||||
|
||||
//监听提交
|
||||
form.on('submit(LAY-user-back-submit)', function (data) {
|
||||
|
||||
console.log(data.field)
|
||||
|
||||
|
||||
//提交数据
|
||||
admin.req({
|
||||
method: "post",
|
||||
contentType: "application/json;charset=UTF-8",
|
||||
dataType: "json",
|
||||
headers: {
|
||||
'token': layui.data(setter.tableName)[setter.request.tokenName]
|
||||
},
|
||||
url: setter.baseurl + "adminapi/task/AddTask",//实际使用请改成服务端真实接口
|
||||
async: false,
|
||||
data: JSON.stringify(data.field),
|
||||
done: function (res) {
|
||||
|
||||
//$.ajax({});
|
||||
layui.table.reload('LAY-task-back-manage'); //重载表格
|
||||
layer.close(index); //执行关闭
|
||||
}
|
||||
})
|
||||
//提交 Ajax 成功后,关闭当前弹层并重载表格
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
$('.layui-btn.layuiadmin-btn-admin').on('click', function () {
|
||||
|
||||
var type = $(this).data('type');
|
||||
active[type] ? active[type].call(this) : '';
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
@@ -0,0 +1,254 @@
|
||||
|
||||
<title>聊天记录</title>
|
||||
|
||||
<div class="layui-card layadmin-header">
|
||||
<div class="layui-breadcrumb" lay-filter="breadcrumb">
|
||||
<a lay-href="">主页</a>
|
||||
<a lay-href="customer/conversation"><cite>会话列表</cite></a>
|
||||
<a><cite>聊天记录</cite></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-fluid">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header" style="display: flex; justify-content: space-between; align-items: center;">
|
||||
<span id="conversation-title">聊天记录</span>
|
||||
<div>
|
||||
<button class="layui-btn layui-btn-sm layui-btn-danger" onclick="deleteAllMessages()">清空记录</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-card-body">
|
||||
<table id="LAY-chatlog-manage" lay-filter="LAY-chatlog-manage"></table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/html" id="toolbar-chatlog">
|
||||
<div class="layui-btn-container">
|
||||
<button class="layui-btn layui-btn-sm layui-btn-danger" lay-event="deleteSelected">批量删除</button>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="table-chatlog-toolbar">
|
||||
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="delete"><i class="layui-icon layui-icon-delete"></i>删除</a>
|
||||
</script>
|
||||
|
||||
<script>
|
||||
function getParameterByName(name) {
|
||||
// 优先从 hash 中查找
|
||||
var hash = window.location.hash;
|
||||
if (hash && hash.includes('?')) {
|
||||
var searchPart = hash.substring(hash.indexOf('?'));
|
||||
var params = new URLSearchParams(searchPart);
|
||||
return params.get(name);
|
||||
}
|
||||
|
||||
// 如果 hash 中没有,再从主 URL 查找
|
||||
var params = new URLSearchParams(window.location.search);
|
||||
return params.get(name);
|
||||
}
|
||||
|
||||
layui.use(['admin', 'table'], function () {
|
||||
var $ = layui.$,
|
||||
admin = layui.admin,
|
||||
table = layui.table,
|
||||
setter = layui.setter;
|
||||
|
||||
// 获取URL参数
|
||||
var conversationId = getParameterByName('conversationId') || '';
|
||||
var userName = getParameterByName('userName') || '';
|
||||
var customerName = getParameterByName('customerName') || '';
|
||||
|
||||
console.log(conversationId)
|
||||
// 设置标题
|
||||
document.getElementById('conversation-title').textContent = '聊天记录 - ' + userName + ' 与 ' + customerName;
|
||||
|
||||
console.log('conversationId:', conversationId);
|
||||
|
||||
table.render({
|
||||
elem: "#LAY-chatlog-manage",
|
||||
url: setter.baseurl + "adminapi/customer/chatlog/" + conversationId,
|
||||
method: "get",
|
||||
headers: {
|
||||
'token': layui.data(setter.tableName)[setter.request.tokenName]
|
||||
},
|
||||
page: false,
|
||||
toolbar: '#toolbar-chatlog',
|
||||
defaultToolbar: [],
|
||||
cols: [[
|
||||
{ type: 'checkbox', fixed: 'left' },
|
||||
{
|
||||
field: "senderType",
|
||||
title: "发送者",
|
||||
width: 100,
|
||||
templet: function(d) {
|
||||
return d.senderType == 1 ? '<span class="layui-badge layui-bg-blue">用户</span>' : '<span class="layui-badge">客服</span>';
|
||||
}
|
||||
},
|
||||
{ field: "senderName", title: "发送者名称", width: 150 },
|
||||
{
|
||||
field: "contentType",
|
||||
title: "类型",
|
||||
width: 100,
|
||||
templet: function(d) {
|
||||
return d.contentType == 1 ? '<span class="layui-badge layui-bg-green">文字</span>' : '<span class="layui-badge">图片</span>';
|
||||
}
|
||||
},
|
||||
{
|
||||
field: "content",
|
||||
title: "内容",
|
||||
templet: function(d) {
|
||||
if (d.contentType == 2) {
|
||||
return '<img src="' + d.content + '" style="max-width:200px;max-height:100px;cursor:pointer;" lay-event="viewImage">';
|
||||
}
|
||||
return d.content || '-';
|
||||
}
|
||||
},
|
||||
{
|
||||
field: "createTime",
|
||||
title: "发送时间",
|
||||
width: 180,
|
||||
templet: function(d) {
|
||||
return d.createTime ? formatTimestamp(d.createTime) : '-';
|
||||
}
|
||||
},
|
||||
{ title: "操作", width: 100, align: "center", fixed: "right", toolbar: "#table-chatlog-toolbar" }
|
||||
]],
|
||||
text: "对不起,加载出现异常!",
|
||||
response: {
|
||||
statusName: 'code',
|
||||
statusCode: 0,
|
||||
msgName: 'info',
|
||||
countName: 'count',
|
||||
dataName: 'data'
|
||||
}
|
||||
});
|
||||
|
||||
// 点击图片查看大图
|
||||
table.on('tool(LAY-chatlog-manage)', function(e) {
|
||||
if (e.event === 'viewImage') {
|
||||
layer.open({
|
||||
type: 1,
|
||||
title: '图片预览',
|
||||
area: ['800px', '600px'],
|
||||
content: '<div style="text-align:center;padding:20px;"><img src="' + e.data.content + '" style="max-width:100%;max-height:100%;"></div>'
|
||||
});
|
||||
}
|
||||
if (e.event === 'delete') {
|
||||
layer.confirm("确定删除此消息吗?", function(index) {
|
||||
admin.req({
|
||||
method: "delete",
|
||||
url: setter.baseurl + "adminapi/customer/message/" + e.data.id,
|
||||
headers: {
|
||||
'token': layui.data(setter.tableName)[setter.request.tokenName]
|
||||
},
|
||||
done: function (res) {
|
||||
if (res.code === 0) {
|
||||
layer.msg('删除成功');
|
||||
table.reload("LAY-chatlog-manage");
|
||||
} else {
|
||||
layer.msg(res.info || '删除失败');
|
||||
}
|
||||
layer.close(index);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 批量删除
|
||||
table.on('toolbar(LAY-chatlog-manage)', function(obj) {
|
||||
var checkStatus = table.checkStatus(obj.config.id);
|
||||
if (obj.event === 'deleteSelected') {
|
||||
var data = checkStatus.data;
|
||||
if (data.length === 0) {
|
||||
layer.msg('请选择要删除的消息');
|
||||
return;
|
||||
}
|
||||
layer.confirm("确定删除选中的 " + data.length + " 条消息吗?", function(index) {
|
||||
var ids = data.map(function(d) { return d.id; });
|
||||
admin.req({
|
||||
method: "post",
|
||||
contentType: "application/json;charset=UTF-8",
|
||||
url: setter.baseurl + "adminapi/customer/messages/delete-batch",
|
||||
headers: {
|
||||
'token': layui.data(setter.tableName)[setter.request.tokenName]
|
||||
},
|
||||
data: JSON.stringify({ ids: ids }),
|
||||
done: function (res) {
|
||||
if (res.code === 0) {
|
||||
layer.msg('删除成功');
|
||||
table.reload("LAY-chatlog-manage");
|
||||
} else {
|
||||
layer.msg(res.info || '删除失败');
|
||||
}
|
||||
layer.close(index);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 清空所有记录
|
||||
window.deleteAllMessages = function() {
|
||||
if (!conversationId) {
|
||||
layer.msg('无效的会话ID');
|
||||
return;
|
||||
}
|
||||
layer.confirm("确定清空此会话的所有聊天记录吗?此操作不可恢复!", function(index) {
|
||||
admin.req({
|
||||
method: "delete",
|
||||
url: setter.baseurl + "adminapi/customer/messages/" + conversationId,
|
||||
headers: {
|
||||
'token': layui.data(setter.tableName)[setter.request.tokenName]
|
||||
},
|
||||
done: function (res) {
|
||||
if (res.code === 0) {
|
||||
layer.msg('清空成功');
|
||||
table.reload("LAY-chatlog-manage");
|
||||
} else {
|
||||
layer.msg(res.info || '清空失败');
|
||||
}
|
||||
layer.close(index);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// 时间戳格式化
|
||||
function formatTimestamp(timestamp) {
|
||||
if (!timestamp) return '-';
|
||||
// 处理时间戳,可能是字符串或数字
|
||||
var date;
|
||||
if (typeof timestamp === 'string') {
|
||||
// 如果是ISO格式字符串,直接解析
|
||||
date = new Date(timestamp);
|
||||
} else {
|
||||
// 如果是时间戳,假设是毫秒级别
|
||||
date = new Date(timestamp);
|
||||
}
|
||||
|
||||
// 转换为北京时间 (UTC+8)
|
||||
var utc = date.getTime() + (date.getTimezoneOffset() * 60000);
|
||||
var beijingDate = new Date(utc + (3600000 * 8));
|
||||
|
||||
var o = {
|
||||
'M+': beijingDate.getMonth() + 1,
|
||||
'd+': beijingDate.getDate(),
|
||||
'h+': beijingDate.getHours(),
|
||||
'm+': beijingDate.getMinutes(),
|
||||
's+': beijingDate.getSeconds()
|
||||
};
|
||||
var format = 'yyyy-MM-dd hh:mm:ss';
|
||||
if (/(y+)/.test(format)) {
|
||||
format = format.replace(RegExp.$1, (beijingDate.getFullYear() + '').substr(4 - RegExp.$1.length));
|
||||
}
|
||||
for (var k in o) {
|
||||
if (new RegExp('(' + k + ')').test(format)) {
|
||||
format = format.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (('00' + o[k]).substr(('' + o[k]).length)));
|
||||
}
|
||||
}
|
||||
return format;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,215 @@
|
||||
|
||||
<title>会话管理</title>
|
||||
|
||||
<div class="layui-card layadmin-header">
|
||||
<div class="layui-breadcrumb" lay-filter="breadcrumb">
|
||||
<a lay-href="">主页</a>
|
||||
<a><cite>客服管理</cite></a>
|
||||
<a><cite>会话列表</cite></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-fluid">
|
||||
<div class="layui-form layui-card-header layuiadmin-card-header-auto" lay-filter="layadmin-conversation-formlist">
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-inline">
|
||||
<label class="layui-form-label">用户ID</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="userId" placeholder="请输入用户ID" autocomplete="off" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-inline">
|
||||
<label class="layui-form-label">客服ID</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="customerId" placeholder="请输入客服ID" autocomplete="off" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-inline">
|
||||
<label class="layui-form-label">状态</label>
|
||||
<div class="layui-input-block">
|
||||
<select name="status">
|
||||
<option value="">全部</option>
|
||||
<option value="0">等待中</option>
|
||||
<option value="1">进行中</option>
|
||||
<option value="2">已结束</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-inline">
|
||||
<button class="layui-btn layuiadmin-btn-conversation" lay-submit lay-filter="LAY-conversation-front-search">
|
||||
<i class="layui-icon layui-icon-search layuiadmin-button-btn"></i> 搜索
|
||||
</button>
|
||||
<button class="layui-btn layui-btn-primary" lay-filter="LAY-conversation-front-reset">
|
||||
<i class="layui-icon layui-icon-refresh layuiadmin-button-btn"></i> 重置
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-body">
|
||||
<div class="layui-btn-group">
|
||||
<button class="layui-btn layui-btn-sm layui-btn-normal" lay-event="refresh">
|
||||
<i class="layui-icon layui-icon-refresh"></i> 刷新
|
||||
</button>
|
||||
</div>
|
||||
<br><br>
|
||||
|
||||
<table id="LAY-conversation-manage" lay-filter="LAY-conversation-manage"></table>
|
||||
|
||||
<script type="text/html" id="table-conversation-admin">
|
||||
<a class="layui-btn layui-btn-normal layui-btn-xs" lay-event="chatlog"><i class="layui-icon layui-icon-log"></i>聊天记录</a>
|
||||
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="delete"><i class="layui-icon layui-icon-delete"></i>删除</a>
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
layui.use(['admin', 'table', 'form', 'layer'], function () {
|
||||
var $ = layui.$,
|
||||
admin = layui.admin,
|
||||
view = layui.view,
|
||||
table = layui.table,
|
||||
layer = layui.layer,
|
||||
setter = layui.setter,
|
||||
form = layui.form,
|
||||
customerApiUrl = setter.customerApiUrl || setter.baseurl;
|
||||
|
||||
form.render(null, 'layadmin-conversation-formlist');
|
||||
|
||||
//监听搜索
|
||||
form.on('submit(LAY-conversation-front-search)', function (data) {
|
||||
var field = data.field;
|
||||
//执行重载
|
||||
table.reload('LAY-conversation-manage', {
|
||||
where: field,
|
||||
page: { curr: 1 }
|
||||
});
|
||||
});
|
||||
|
||||
// 监听重置
|
||||
$('.layui-btn-group').on('click', '[lay-event="refresh"]', function () {
|
||||
table.reload('LAY-conversation-manage');
|
||||
});
|
||||
|
||||
table.render({
|
||||
elem: "#LAY-conversation-manage",
|
||||
url: customerApiUrl + "customerapi/customer/conversations",
|
||||
method: "get",
|
||||
headers: {
|
||||
'token': layui.data(setter.tableName)[setter.request.tokenName]
|
||||
},
|
||||
page: true,
|
||||
where: {
|
||||
userId: "",
|
||||
customerId: "",
|
||||
status: ""
|
||||
},
|
||||
request: {
|
||||
pageName: 'pageIndex',
|
||||
limitName: 'pageSize'
|
||||
},
|
||||
limit: 10,
|
||||
cols: [[
|
||||
{ field: "userName", title: "用户名称", width: 150 },
|
||||
{ field: "userId", title: "用户ID", width: 200 },
|
||||
{ field: "customerName", title: "客服名称", width: 150 },
|
||||
{ field: "customerId", title: "客服ID", width: 200 },
|
||||
{
|
||||
field: "status",
|
||||
title: "状态",
|
||||
width: 100,
|
||||
templet: function(d) {
|
||||
var statusMap = {
|
||||
0: '<span class="layui-badge">等待中</span>',
|
||||
1: '<span class="layui-badge layui-bg-green">进行中</span>',
|
||||
2: '<span class="layui-badge layui-bg-gray">已结束</span>'
|
||||
};
|
||||
return statusMap[d.status] || '-';
|
||||
}
|
||||
},
|
||||
{
|
||||
field: "createTime",
|
||||
title: "创建时间",
|
||||
width: 180,
|
||||
templet: function(d) {
|
||||
return d.createTime ? formatTimestamp(d.createTime) : '-';
|
||||
}
|
||||
},
|
||||
{
|
||||
field: "endTime",
|
||||
title: "结束时间",
|
||||
width: 180,
|
||||
templet: function(d) {
|
||||
return d.endTime ? formatTimestamp(d.endTime) : '-';
|
||||
}
|
||||
},
|
||||
{ width: 190, title: "操作", align: "center", fixed: "right", toolbar: "#table-conversation-admin" }
|
||||
]],
|
||||
text: "对不起,加载出现异常!",
|
||||
response: {
|
||||
statusName: 'code',
|
||||
statusCode: 0,
|
||||
msgName: 'info',
|
||||
countName: 'count',
|
||||
dataName: 'data'
|
||||
}
|
||||
});
|
||||
|
||||
// 监听工具条
|
||||
table.on("tool(LAY-conversation-manage)", function (e) {
|
||||
var l = e.data;
|
||||
|
||||
if ("chatlog" === e.event) {
|
||||
// 打开新页面查看聊天记录
|
||||
var url = 'customer/chatlog.html?conversationId=' + l.id + '&userName=' + encodeURIComponent(l.userName || '') + '&customerName=' + encodeURIComponent(l.customerName || '');
|
||||
window.location.hash = '/' + url;
|
||||
} else if ("delete" === e.event) {
|
||||
layer.confirm('确定删除该会话及全部聊天记录吗?此操作不可恢复。', function(index) {
|
||||
admin.req({
|
||||
method: 'delete',
|
||||
// 删除属于后台管理操作,必须走带鉴权的 AdminApi。
|
||||
url: setter.baseurl + 'adminapi/Customer/conversation/' + encodeURIComponent(l.id),
|
||||
done: function(res) {
|
||||
if (res.code === 0 || res.code === 200) {
|
||||
e.del();
|
||||
layer.close(index);
|
||||
layer.msg(res.info || '删除成功');
|
||||
table.reload('LAY-conversation-manage');
|
||||
} else {
|
||||
layer.msg(res.info || '删除失败');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 时间戳格式化
|
||||
function formatTimestamp(timestamp) {
|
||||
if (!timestamp) return '-';
|
||||
var date = new Date(timestamp);
|
||||
var o = {
|
||||
'M+': date.getMonth() + 1,
|
||||
'd+': date.getDate(),
|
||||
'h+': date.getHours(),
|
||||
'm+': date.getMinutes(),
|
||||
's+': date.getSeconds()
|
||||
};
|
||||
var format = 'yyyy-MM-dd hh:mm:ss';
|
||||
if (/(y+)/.test(format)) {
|
||||
format = format.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length));
|
||||
}
|
||||
for (var k in o) {
|
||||
if (new RegExp('(' + k + ')').test(format)) {
|
||||
format = format.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (('00' + o[k]).substr(('' + o[k]).length)));
|
||||
}
|
||||
}
|
||||
return format;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,452 @@
|
||||
|
||||
<title>客服管理</title>
|
||||
|
||||
<div class="layui-card layadmin-header">
|
||||
<div class="layui-breadcrumb" lay-filter="breadcrumb">
|
||||
<a lay-href="">主页</a>
|
||||
<a><cite>客服管理</cite></a>
|
||||
<a><cite>客服列表</cite></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-fluid">
|
||||
<div class="layui-form layui-card-header layuiadmin-card-header-auto" lay-filter="layadmin-customer-formlist">
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-inline">
|
||||
<label class="layui-form-label">客服名称</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="text" name="userName" placeholder="请输入客服名称" autocomplete="off" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-inline">
|
||||
<label class="layui-form-label">状态</label>
|
||||
<div class="layui-input-block">
|
||||
<select name="status">
|
||||
<option value="">全部</option>
|
||||
<option value="0">离线</option>
|
||||
<option value="1">在线</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-inline">
|
||||
<button class="layui-btn layuiadmin-btn-customer" lay-submit lay-filter="LAY-customer-front-search">
|
||||
<i class="layui-icon layui-icon-search layuiadmin-button-btn"></i> 搜索
|
||||
</button>
|
||||
<button class="layui-btn layui-btn-primary" lay-filter="LAY-customer-front-reset">
|
||||
<i class="layui-icon layui-icon-refresh layuiadmin-button-btn"></i> 重置
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-body">
|
||||
<div class="layui-btn-group">
|
||||
<button class="layui-btn layui-btn-sm" lay-event="add">
|
||||
<i class="layui-icon layui-icon-add-1"></i> 添加客服
|
||||
</button>
|
||||
<button class="layui-btn layui-btn-sm layui-btn-normal" lay-event="refresh">
|
||||
<i class="layui-icon layui-icon-refresh"></i> 刷新
|
||||
</button>
|
||||
</div>
|
||||
<br><br>
|
||||
|
||||
<table id="LAY-customer-manage" lay-filter="LAY-customer-manage"></table>
|
||||
|
||||
<script type="text/html" id="table-customer-admin">
|
||||
<a class="layui-btn layui-btn-normal layui-btn-xs" lay-event="edit"><i class="layui-icon layui-icon-edit"></i>编辑</a>
|
||||
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del"><i class="layui-icon layui-icon-delete"></i>删除</a>
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.avatar-upload-box { position: relative; display: inline-block; }
|
||||
.avatar-upload-box .upload-btn { position: absolute; top: 0; left: 0; width: 80px; height: 80px; opacity: 0; cursor: pointer; }
|
||||
.avatar-upload-preview { width: 80px; height: 80px; border: 1px dashed #ddd; border-radius: 50%; display: flex; align-items: center; justify-content: center; background: #fafafa; cursor: pointer; overflow: hidden; }
|
||||
.avatar-upload-preview:hover { border-color: #009688; }
|
||||
.avatar-upload-preview img { width: 100%; height: 100%; object-fit: cover; }
|
||||
.avatar-upload-preview .placeholder { color: #999; font-size: 12px; text-align: center; }
|
||||
</style>
|
||||
|
||||
<script>
|
||||
layui.use(['admin', 'table', 'form', 'layer'], function () {
|
||||
var $ = layui.$,
|
||||
admin = layui.admin,
|
||||
view = layui.view,
|
||||
table = layui.table,
|
||||
setter = layui.setter,
|
||||
form = layui.form,
|
||||
layer = layui.layer;
|
||||
|
||||
form.render(null, 'layadmin-customer-formlist');
|
||||
|
||||
//监听搜索
|
||||
form.on('submit(LAY-customer-front-search)', function (data) {
|
||||
var field = data.field;
|
||||
//执行重载
|
||||
table.reload('LAY-customer-manage', {
|
||||
where: field,
|
||||
page: { curr: 1 }
|
||||
});
|
||||
});
|
||||
|
||||
// 监听重置
|
||||
$('.layui-btn-group').on('click', '[lay-event="refresh"]', function () {
|
||||
table.reload('LAY-customer-manage');
|
||||
});
|
||||
|
||||
table.render({
|
||||
elem: "#LAY-customer-manage",
|
||||
url: setter.baseurl + "adminapi/customer/list",
|
||||
method: "get",
|
||||
headers: {
|
||||
'token': layui.data(setter.tableName)[setter.request.tokenName]
|
||||
},
|
||||
page: true,
|
||||
where: {
|
||||
userName: "",
|
||||
status: ""
|
||||
},
|
||||
request: {
|
||||
pageName: 'pageIndex',
|
||||
limitName: 'pageSize'
|
||||
},
|
||||
limit: 10,
|
||||
cols: [[
|
||||
{ field: "userName", title: "客服名称", width: 150 },
|
||||
{ field: "userId", title: "客服ID", width: 200 },
|
||||
{
|
||||
field: "avatar",
|
||||
title: "头像",
|
||||
width: 100,
|
||||
templet: function(d) {
|
||||
return d.avatar ? '<img src="' + d.avatar + '" style="width:40px;height:40px;">' : '-';
|
||||
}
|
||||
},
|
||||
{
|
||||
field: "status",
|
||||
title: "状态",
|
||||
width: 100,
|
||||
templet: function(d) {
|
||||
return d.status == 1 ? '<span class="layui-badge layui-bg-green">在线</span>' : '<span class="layui-badge">离线</span>';
|
||||
}
|
||||
},
|
||||
{
|
||||
field: "createTime",
|
||||
title: "创建时间",
|
||||
width: 180,
|
||||
templet: function(d) {
|
||||
return d.createTime ? formatTimestamp(d.createTime) : '-';
|
||||
}
|
||||
},
|
||||
{ width: 180, title: "操作", align: "center", fixed: "right", toolbar: "#table-customer-admin" }
|
||||
]],
|
||||
text: "对不起,加载出现异常!",
|
||||
response: {
|
||||
statusName: 'code',
|
||||
statusCode: 0,
|
||||
msgName: 'info',
|
||||
countName: 'count',
|
||||
dataName: 'data'
|
||||
}
|
||||
});
|
||||
|
||||
// 监听工具条
|
||||
table.on("tool(LAY-customer-manage)", function (e) {
|
||||
var l = e.data;
|
||||
|
||||
if ("del" === e.event) {
|
||||
layer.confirm("确定删除此客服吗?", function (i) {
|
||||
admin.req({
|
||||
method: "delete",
|
||||
url: setter.baseurl + "adminapi/customer/" + e.data.id,
|
||||
headers: {
|
||||
'token': layui.data(setter.tableName)[setter.request.tokenName]
|
||||
},
|
||||
done: function (res) {
|
||||
if (res.code === 0) {
|
||||
e.del();
|
||||
layer.close(i);
|
||||
layer.msg('删除成功');
|
||||
} else {
|
||||
layer.msg(res.info || '删除失败');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if ("edit" === e.event) {
|
||||
var avatarHtml = l.avatar
|
||||
? '<img src="' + l.avatar + '" style="width:100%;height:100%;">'
|
||||
: '<div class="placeholder">点击上传</div>';
|
||||
admin.popup({
|
||||
title: "编辑客服",
|
||||
area: ["500px", "450px"],
|
||||
id: "LAY-popup-customer-edit",
|
||||
success: function (div, idx) {
|
||||
$(div).html(
|
||||
'<form class="layui-form" style="padding: 20px;" lay-filter="layuiadmin-form-customer">' +
|
||||
'<div class="layui-form-item">' +
|
||||
'<label class="layui-form-label">客服名称</label>' +
|
||||
'<div class="layui-input-block">' +
|
||||
'<input type="text" name="userName" class="layui-input" value="' + (l.userName || '') + '" required>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="layui-form-item">' +
|
||||
'<label class="layui-form-label">头像</label>' +
|
||||
'<div class="layui-input-block">' +
|
||||
'<div class="avatar-upload-box">' +
|
||||
'<div class="avatar-upload-preview" id="avatarPreview">' + avatarHtml + '</div>' +
|
||||
'<input type="file" class="upload-btn" accept="image/*" id="avatarUpload">' +
|
||||
'</div>' +
|
||||
'<input type="hidden" name="avatar" id="avatarInput" value="' + (l.avatar || '') + '">' +
|
||||
'<div style="margin-top:8px;color:#999;font-size:12px;">点击图片可预览,支持jpg、png格式</div>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="layui-form-item">' +
|
||||
'<label class="layui-form-label">状态</label>' +
|
||||
'<div class="layui-input-block">' +
|
||||
'<select name="status">' +
|
||||
'<option value="0" ' + (l.status == 0 ? 'selected' : '') + '>离线</option>' +
|
||||
'<option value="1" ' + (l.status == 1 ? 'selected' : '') + '>在线</option>' +
|
||||
'</select>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="layui-form-item">' +
|
||||
'<div class="layui-input-block">' +
|
||||
'<button class="layui-btn" lay-submit lay-filter="LAY-customer-edit-submit">提交</button>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</form>'
|
||||
);
|
||||
form.render(null, "layuiadmin-form-customer");
|
||||
|
||||
var $preview = $('#avatarPreview');
|
||||
var $input = $('#avatarInput');
|
||||
var $fileInput = $('#avatarUpload');
|
||||
|
||||
$preview.on('click', function() {
|
||||
var url = $input.val();
|
||||
if (url) {
|
||||
layer.open({
|
||||
type: 1,
|
||||
title: '头像预览',
|
||||
area: ['auto', 'auto'],
|
||||
shadeClose: true,
|
||||
content: '<div style="padding:20px;text-align:center;"><img src="' + url + '" style="max-width:300px;max-height:300px;border-radius:50%;"></div>'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$fileInput.on('change', function() {
|
||||
var file = this.files[0];
|
||||
if (file) {
|
||||
var formData = new FormData();
|
||||
formData.append('file', file);
|
||||
|
||||
$.ajax({
|
||||
url: setter.baseurl + 'adminapi/ImageUpload/upload/temp',
|
||||
type: 'POST',
|
||||
data: formData,
|
||||
processData: false,
|
||||
contentType: false,
|
||||
headers: { 'token': layui.data(setter.tableName)[setter.request.tokenName] },
|
||||
success: function(res) {
|
||||
if (res.code === 0 || res.code === 200) {
|
||||
var path = res.data.path;
|
||||
$input.val(path);
|
||||
$preview.html('<img src="' + path + '" style="width:100%;height:100%;">');
|
||||
layer.msg('上传成功');
|
||||
} else {
|
||||
layer.msg(res.info || '上传失败');
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
layer.msg('上传失败');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
form.on("submit(LAY-customer-edit-submit)", function (data) {
|
||||
var param = {
|
||||
id: l.id,
|
||||
userName: data.field.userName,
|
||||
avatar: data.field.avatar,
|
||||
status: parseInt(data.field.status)
|
||||
};
|
||||
admin.req({
|
||||
method: "put",
|
||||
contentType: "application/json;charset=UTF-8",
|
||||
headers: {
|
||||
'token': layui.data(setter.tableName)[setter.request.tokenName]
|
||||
},
|
||||
url: setter.baseurl + "adminapi/customer",
|
||||
data: JSON.stringify(param),
|
||||
done: function (res) {
|
||||
if (res.code === 0) {
|
||||
layer.close(idx);
|
||||
layer.msg('修改成功');
|
||||
table.reload("LAY-customer-manage");
|
||||
} else {
|
||||
layer.msg(res.info || '修改失败');
|
||||
}
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 添加客服
|
||||
$('.layui-btn-group').on('click', '[lay-event="add"]', function () {
|
||||
admin.popup({
|
||||
title: "添加客服",
|
||||
area: ["500px", "450px"],
|
||||
id: "LAY-popup-customer-add",
|
||||
success: function (div, idx) {
|
||||
$(div).html(
|
||||
'<form class="layui-form" style="padding: 20px;" lay-filter="layuiadmin-form-customer-add">' +
|
||||
'<div class="layui-form-item">' +
|
||||
'<label class="layui-form-label">客服ID</label>' +
|
||||
'<div class="layui-input-block">' +
|
||||
'<input type="text" name="userId" id="userIdInput" class="layui-input" placeholder="请输入客服用户ID" required>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="layui-form-item">' +
|
||||
'<label class="layui-form-label">客服名称</label>' +
|
||||
'<div class="layui-input-block">' +
|
||||
'<input type="text" name="userName" class="layui-input" placeholder="请输入客服名称" required>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="layui-form-item">' +
|
||||
'<label class="layui-form-label">头像</label>' +
|
||||
'<div class="layui-input-block">' +
|
||||
'<div class="avatar-upload-box">' +
|
||||
'<div class="avatar-upload-preview" id="avatarPreviewAdd">' +
|
||||
'<div class="placeholder">点击上传</div>' +
|
||||
'</div>' +
|
||||
'<input type="file" class="upload-btn" accept="image/*" id="avatarUploadAdd">' +
|
||||
'</div>' +
|
||||
'<input type="hidden" name="avatar" id="avatarInputAdd" value="">' +
|
||||
'<div style="margin-top:8px;color:#999;font-size:12px;">点击图片可预览,支持jpg、png格式</div>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="layui-form-item">' +
|
||||
'<div class="layui-input-block">' +
|
||||
'<button class="layui-btn" lay-submit lay-filter="LAY-customer-add-submit">添加</button>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</form>'
|
||||
);
|
||||
form.render(null, "layuiadmin-form-customer-add");
|
||||
|
||||
var $preview = $('#avatarPreviewAdd');
|
||||
var $input = $('#avatarInputAdd');
|
||||
var $fileInput = $('#avatarUploadAdd');
|
||||
|
||||
$preview.on('click', function() {
|
||||
var url = $input.val();
|
||||
if (url) {
|
||||
layer.open({
|
||||
type: 1,
|
||||
title: '头像预览',
|
||||
area: ['auto', 'auto'],
|
||||
shadeClose: true,
|
||||
content: '<div style="padding:20px;text-align:center;"><img src="' + url + '" style="max-width:300px;max-height:300px;border-radius:50%;"></div>'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$fileInput.on('change', function() {
|
||||
var file = this.files[0];
|
||||
if (file) {
|
||||
var userId = $('#userIdInput').val() || ('new_' + Date.now());
|
||||
var formData = new FormData();
|
||||
formData.append('file', file);
|
||||
|
||||
$.ajax({
|
||||
url: setter.baseurl + 'adminapi/ImageUpload/upload/temp',
|
||||
type: 'POST',
|
||||
data: formData,
|
||||
processData: false,
|
||||
contentType: false,
|
||||
headers: { 'token': layui.data(setter.tableName)[setter.request.tokenName] },
|
||||
success: function(res) {
|
||||
if (res.code === 0 || res.code === 200) {
|
||||
var path = res.data.path;
|
||||
$input.val(path);
|
||||
$preview.html('<img src="' + path + '" style="width:100%;height:100%;">');
|
||||
layer.msg('上传成功');
|
||||
} else {
|
||||
layer.msg(res.info || '上传失败');
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
layer.msg('上传失败');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
form.on("submit(LAY-customer-add-submit)", function (data) {
|
||||
var param = {
|
||||
userId: data.field.userId,
|
||||
userName: data.field.userName,
|
||||
avatar: data.field.avatar
|
||||
};
|
||||
admin.req({
|
||||
method: "post",
|
||||
contentType: "application/json;charset=UTF-8",
|
||||
headers: {
|
||||
'token': layui.data(setter.tableName)[setter.request.tokenName]
|
||||
},
|
||||
url: setter.baseurl + "adminapi/customer",
|
||||
data: JSON.stringify(param),
|
||||
done: function (res) {
|
||||
if (res.code === 0) {
|
||||
layer.close(idx);
|
||||
layer.msg('添加成功');
|
||||
table.reload("LAY-customer-manage");
|
||||
} else {
|
||||
layer.msg(res.info || '添加失败');
|
||||
}
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 时间戳格式化
|
||||
function formatTimestamp(timestamp) {
|
||||
if (!timestamp) return '-';
|
||||
var date = new Date(timestamp);
|
||||
var o = {
|
||||
'M+': date.getMonth() + 1,
|
||||
'd+': date.getDate(),
|
||||
'h+': date.getHours(),
|
||||
'm+': date.getMinutes(),
|
||||
's+': date.getSeconds()
|
||||
};
|
||||
var format = 'yyyy-MM-dd hh:mm:ss';
|
||||
if (/(y+)/.test(format)) {
|
||||
format = format.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length));
|
||||
}
|
||||
for (var k in o) {
|
||||
if (new RegExp('(' + k + ')').test(format)) {
|
||||
format = format.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (('00' + o[k]).substr(('' + o[k]).length)));
|
||||
}
|
||||
}
|
||||
return format;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,238 @@
|
||||
|
||||
<title>邮件管理</title>
|
||||
|
||||
<div class="layui-card layadmin-header">
|
||||
<div class="layui-breadcrumb" lay-filter="breadcrumb">
|
||||
<a lay-href="">主页</a>
|
||||
<a><cite>用户</cite></a>
|
||||
<a><cite>邮件管理</cite></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-fluid">
|
||||
<div class="layui-form layui-card-header layuiadmin-card-header-auto" lay-filter="layadmin-emaillist-filter">
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-inline">
|
||||
<label class="layui-form-label">关键词</label>
|
||||
<div class="layui-input-inline">
|
||||
<input type="text" name="userNumber" placeholder="编号/昵称/标题/内容" autocomplete="off" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-inline">
|
||||
<label class="layui-form-label">已读状态</label>
|
||||
<div class="layui-input-inline">
|
||||
<select name="readStatus">
|
||||
<option value="">全部</option>
|
||||
<option value="1">已读</option>
|
||||
<option value="2">未读</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-inline">
|
||||
<label class="layui-form-label">领取状态</label>
|
||||
<div class="layui-input-inline">
|
||||
<select name="rewardStatus">
|
||||
<option value="">全部</option>
|
||||
<option value="1">已领取</option>
|
||||
<option value="2">未领取</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-inline">
|
||||
<label class="layui-form-label">发送时间</label>
|
||||
<div class="layui-input-inline">
|
||||
<input type="text" name="timeRange" id="timeRange" placeholder="选择时间范围" autocomplete="off" class="layui-input">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-inline">
|
||||
<button class="layui-btn layuiadmin-btn-list" lay-submit lay-filter="LAY-emaillist-search">
|
||||
<i class="layui-icon layui-icon-search layuiadmin-button-btn"></i> 搜索
|
||||
</button>
|
||||
<button class="layui-btn layui-btn-primary" lay-filter="LAY-emaillist-reset">
|
||||
<i class="layui-icon layui-icon-refresh layuiadmin-button-btn"></i> 重置
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-body">
|
||||
<table id="LAY-emaillist-manage" lay-filter="LAY-emaillist-manage"></table>
|
||||
|
||||
<script type="text/html" id="table-emaillist-toolbar">
|
||||
<div class="layui-btn-container">
|
||||
<button class="layui-btn layui-btn-sm layui-btn-primary" lay-event="refresh">
|
||||
<i class="layui-icon layui-icon-refresh"></i> 刷新
|
||||
</button>
|
||||
<button class="layui-btn layui-btn-sm layui-btn-danger" lay-event="batchdel">
|
||||
<i class="layui-icon layui-icon-delete"></i> 批量删除
|
||||
</button>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="table-emaillist-bar">
|
||||
<a class="layui-btn layui-btn-xs layui-btn-danger" lay-event="del"><i class="layui-icon layui-icon-delete"></i>删除</a>
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="isReadTpl">
|
||||
{{# if(d.isRead){ }}
|
||||
<span class="layui-badge layui-bg-blue">已读</span>
|
||||
{{# } else { }}
|
||||
<span class="layui-badge layui-bg-gray">未读</span>
|
||||
{{# } }}
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="isRewardTpl">
|
||||
{{# if(d.isReward){ }}
|
||||
<span class="layui-badge layui-bg-green">已领取</span>
|
||||
{{# } else { }}
|
||||
<span class="layui-badge layui-bg-orange">未领取</span>
|
||||
{{# } }}
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
layui.use(['admin', 'table', 'form', 'laydate'], function () {
|
||||
var $ = layui.$,
|
||||
admin = layui.admin,
|
||||
table = layui.table,
|
||||
form = layui.form,
|
||||
laydate = layui.laydate,
|
||||
setter = layui.setter;
|
||||
|
||||
laydate.render({
|
||||
elem: '#timeRange',
|
||||
type: 'datetime',
|
||||
range: '~'
|
||||
});
|
||||
|
||||
form.render(null, 'layadmin-emaillist-filter');
|
||||
|
||||
form.on('submit(LAY-emaillist-search)', function (data) {
|
||||
var field = data.field;
|
||||
if (field.timeRange) {
|
||||
var times = field.timeRange.split('~');
|
||||
field.startTime = new Date(times[0].trim()).getTime();
|
||||
field.endTime = new Date(times[1].trim()).getTime();
|
||||
}
|
||||
delete field.timeRange;
|
||||
if (field.readStatus) field.readStatus = parseInt(field.readStatus);
|
||||
if (field.rewardStatus) field.rewardStatus = parseInt(field.rewardStatus);
|
||||
|
||||
table.reload('LAY-emaillist-manage', {
|
||||
where: field,
|
||||
page: { curr: 1 }
|
||||
});
|
||||
});
|
||||
|
||||
form.on('submit(LAY-emaillist-reset)', function () {
|
||||
$('input[name="userNumber"]').val('');
|
||||
$('select[name="readStatus"]').val('');
|
||||
$('select[name="rewardStatus"]').val('');
|
||||
$('input[name="timeRange"]').val('');
|
||||
form.render('select');
|
||||
table.reload('LAY-emaillist-manage', {
|
||||
where: {},
|
||||
page: { curr: 1 }
|
||||
});
|
||||
});
|
||||
|
||||
table.render({
|
||||
elem: "#LAY-emaillist-manage",
|
||||
url: setter.baseurl + "adminapi/AdminUser/GetEmailList",
|
||||
method: "post",
|
||||
contentType: "application/json",
|
||||
headers: {
|
||||
'token': layui.data(setter.tableName)[setter.request.tokenName]
|
||||
},
|
||||
page: true,
|
||||
toolbar: '#table-emaillist-toolbar',
|
||||
defaultToolbar: ['filter', 'exports', 'print'],
|
||||
request: {
|
||||
pageName: 'pageIndex',
|
||||
limitName: 'pageSize'
|
||||
},
|
||||
limit: 10,
|
||||
cols: [[
|
||||
{ type: 'checkbox', fixed: 'left' },
|
||||
{ type: 'numbers', title: '序号', width: 60 },
|
||||
{ field: "userId", title: "用户ID", width: 280 },
|
||||
{ field: "userName", title: "用户昵称", width: 120, templet: function(d) { return d.userName || '-'; } },
|
||||
{ field: "userNumber", title: "用户编号", width: 120, templet: function(d) { return d.userNumber || '-'; } },
|
||||
{ field: "title", title: "邮件标题", minWidth: 200 },
|
||||
{ field: "rewardText", title: "奖励内容", width: 180 },
|
||||
{ field: "isRead", title: "已读", width: 70, templet: '#isReadTpl' },
|
||||
{ field: "isReward", title: "领取", width: 80, templet: '#isRewardTpl' },
|
||||
{ field: "sendTime", title: "发送时间", width: 160, templet: function(d) {
|
||||
if (!d.sendTime) return '-';
|
||||
var dt = new Date(d.sendTime * 1000);
|
||||
return dt.getFullYear() + '-' + pad(dt.getMonth()+1) + '-' + pad(dt.getDate()) + ' ' + pad(dt.getHours()) + ':' + pad(dt.getMinutes()) + ':' + pad(dt.getSeconds());
|
||||
}},
|
||||
{ width: 80, title: "操作", align: "center", fixed: "right", toolbar: "#table-emaillist-bar" }
|
||||
]],
|
||||
text: "对不起,加载出现异常!",
|
||||
response: {
|
||||
statusName: 'code',
|
||||
statusCode: 0,
|
||||
msgName: 'info',
|
||||
countName: 'count',
|
||||
dataName: 'data'
|
||||
}
|
||||
});
|
||||
|
||||
function pad(n) { return (n < 10 ? '0' : '') + n; }
|
||||
|
||||
table.on("tool(LAY-emaillist-manage)", function (e) {
|
||||
var l = e.data;
|
||||
if ("del" === e.event) {
|
||||
layer.confirm('确定删除该邮件吗?', function (idx) {
|
||||
admin.req({
|
||||
url: setter.baseurl + 'adminapi/AdminUser/DeleteEmail?id=' + l.id,
|
||||
method: 'get',
|
||||
done: function (res) {
|
||||
layer.close(idx);
|
||||
if (res.code === 0) {
|
||||
layer.msg('删除成功');
|
||||
} else {
|
||||
layer.msg(res.info || '删除失败');
|
||||
}
|
||||
table.reload('LAY-emaillist-manage');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
table.on('toolbar(LAY-emaillist-manage)', function (obj) {
|
||||
if (obj.event === 'batchdel') {
|
||||
var checkStatus = table.checkStatus('LAY-emaillist-manage');
|
||||
var data = checkStatus.data;
|
||||
if (data.length === 0) return layer.msg('请选择要删除的数据');
|
||||
layer.confirm('确定删除选中的 ' + data.length + ' 条邮件吗?', function (idx) {
|
||||
var ids = data.map(function (item) { return item.id; });
|
||||
admin.req({
|
||||
url: setter.baseurl + 'adminapi/AdminUser/BatchDeleteEmail',
|
||||
contentType: 'application/json',
|
||||
method: 'post',
|
||||
data: JSON.stringify(ids),
|
||||
done: function (res) {
|
||||
layer.close(idx);
|
||||
if (res.code === 0) {
|
||||
layer.msg(res.info || '删除成功');
|
||||
} else {
|
||||
layer.msg(res.info || '删除失败');
|
||||
}
|
||||
table.reload('LAY-emaillist-manage');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,646 @@
|
||||
<title>战令配置</title>
|
||||
|
||||
<div class="layui-card layadmin-header">
|
||||
<div class="layui-breadcrumb" lay-filter="breadcrumb">
|
||||
<a lay-href="">主页</a>
|
||||
<a><cite>游戏管理</cite></a>
|
||||
<a><cite>战令配置</cite></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-fluid">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header">基础配置</div>
|
||||
<div class="layui-card-body">
|
||||
<form class="layui-form" lay-filter="bo-form">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">是否开启</label>
|
||||
<div class="layui-input-inline">
|
||||
<input type="checkbox" name="isOpen" lay-skin="switch" lay-text="开启|关闭">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">结束时间</label>
|
||||
<div class="layui-input-inline">
|
||||
<input type="text" name="endTime" id="endTime" class="layui-input" placeholder="选择日期时间">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">规则描述</label>
|
||||
<div class="layui-input-inline" style="width:400px;">
|
||||
<input type="text" name="ruleDesc" class="layui-input" placeholder="规则描述">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">购买描述</label>
|
||||
<div class="layui-input-inline" style="width:400px;">
|
||||
<input type="text" name="buyDesc" class="layui-input" placeholder="购买描述">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">进阶描述</label>
|
||||
<div class="layui-input-inline" style="width:400px;">
|
||||
<input type="text" name="advanceGiftDesc" class="layui-input" placeholder="进阶奖励描述">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-input-block">
|
||||
<button class="layui-btn" lay-submit lay-filter="save-base">保存基础配置</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header">等级配置</div>
|
||||
<div class="layui-card-body">
|
||||
<div class="layui-btn-container" style="margin-bottom:10px;">
|
||||
<button class="layui-btn layui-btn-sm layui-btn-normal" lay-filter="init-level"><i class="layui-icon layui-icon-refresh"></i> 初始化</button>
|
||||
<button class="layui-btn layui-btn-sm" lay-filter="add-level"><i class="layui-icon layui-icon-add-1"></i> 添加等级</button>
|
||||
<button class="layui-btn layui-btn-sm" lay-filter="save-levels"><i class="layui-icon layui-icon-ok"></i> 保存</button>
|
||||
<button class="layui-btn layui-btn-sm layui-btn-normal" onclick="saveAct('BattleOrderConfig')"><i class="layui-icon layui-icon-export"></i> 保存到初始化文件</button>
|
||||
</div>
|
||||
<table id="table-level" lay-filter="table-level"></table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header">进阶配置</div>
|
||||
<div class="layui-card-body">
|
||||
<div class="layui-btn-container" style="margin-bottom:10px;">
|
||||
<button class="layui-btn layui-btn-sm layui-btn-normal" lay-filter="init-advance"><i class="layui-icon layui-icon-refresh"></i> 初始化</button>
|
||||
<button class="layui-btn layui-btn-sm" lay-filter="add-advance"><i class="layui-icon layui-icon-add-1"></i> 添加进阶</button>
|
||||
<button class="layui-btn layui-btn-sm" lay-filter="save-advances"><i class="layui-icon layui-icon-ok"></i> 保存</button>
|
||||
</div>
|
||||
<table id="table-advance" lay-filter="table-advance"></table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/html" id="bar-level">
|
||||
<a class="layui-btn layui-btn-xs" lay-event="edit">编辑</a>
|
||||
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a>
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="bar-advance">
|
||||
<a class="layui-btn layui-btn-xs" lay-event="edit">编辑</a>
|
||||
<a class="layui-btn layui-btn-xs layui-btn-normal" lay-event="reward">奖励</a>
|
||||
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a>
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="tpl-payType">
|
||||
{{# var m = {1:'金币',2:'钻石',3:'现金'}; }}
|
||||
{{= m[d.payType] || '金币' }}
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="tpl-advance-type">
|
||||
{{# var m = {1:'豪华',2:'至尊',3:'豪华+至尊'}; }}
|
||||
{{= m[d.type] || '豪华' }}
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="reward-dialog-tpl">
|
||||
<div class="reward-tabs" style="padding:10px;">
|
||||
<div class="layui-tab" lay-filter="reward-tab">
|
||||
<ul class="layui-tab-title">
|
||||
<li class="layui-this">普通奖励</li>
|
||||
<li>豪华奖励</li>
|
||||
<li>至尊奖励</li>
|
||||
</ul>
|
||||
<div class="layui-tab-content" style="height:480px;">
|
||||
<div class="layui-tab-item layui-show" data-key="normalGiftList"></div>
|
||||
<div class="layui-tab-item" data-key="luxuryGiftList"></div>
|
||||
<div class="layui-tab-item" data-key="supremeGiftList"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="gift-editor-tpl">
|
||||
<div class="gift-container">
|
||||
<div class="gift-left">
|
||||
<div class="gift-toolbar">
|
||||
<button class="layui-btn layui-btn-sm" id="btn-add-gift"><i class="layui-icon layui-icon-add-1"></i> 新增</button>
|
||||
<button class="layui-btn layui-btn-sm layui-btn-danger" id="btn-delete-gift"><i class="layui-icon layui-icon-delete"></i> 删除</button>
|
||||
</div>
|
||||
<div class="gift-table-wrap"><table id="table-gift-list"></table></div>
|
||||
</div>
|
||||
<div class="gift-right">
|
||||
<h4>礼包项详情</h4>
|
||||
<div class="layui-form">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">类型</label>
|
||||
<div class="layui-input-block">
|
||||
<select id="giftType" lay-filter="giftType">
|
||||
<option value="1">金币</option>
|
||||
<option value="2">钻石</option>
|
||||
<option value="3">道具</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">数量</label>
|
||||
<div class="layui-input-block"><input type="number" id="giftCount" class="layui-input" placeholder="数量"></div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">随机</label>
|
||||
<div class="layui-input-block"><input type="checkbox" id="giftIsRandom" lay-skin="switch" lay-text="是|否"></div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">最小值</label>
|
||||
<div class="layui-input-block"><input type="number" id="giftMinCount" class="layui-input" placeholder="最小值"></div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">最大值</label>
|
||||
<div class="layui-input-block"><input type="number" id="giftMaxCount" class="layui-input" placeholder="最大值"></div>
|
||||
</div>
|
||||
<div class="layui-form-item gift-prop-select" style="display:none;">
|
||||
<label class="layui-form-label">道具编号</label>
|
||||
<div class="layui-input-block"><input type="text" id="giftProductNumber" class="layui-input" placeholder="输入道具编号"></div>
|
||||
</div>
|
||||
<div class="layui-form-item" style="margin-top:10px;">
|
||||
<button class="layui-btn layui-btn-normal" id="btn-save-gift" style="width:100%;">保存</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script>
|
||||
layui.use(['admin', 'table', 'form', 'layer', 'laydate', 'element', 'upload'], function () {
|
||||
var $ = layui.$, admin = layui.admin, table = layui.table, form = layui.form,
|
||||
layer = layui.layer, laydate = layui.laydate, setter = layui.setter, element = layui.element, upload = layui.upload;
|
||||
|
||||
var activityConfig = null;
|
||||
var rewardCtx = null; // { giftList, title }
|
||||
|
||||
setTimeout(function() {
|
||||
$('.layui-layer-shade').remove(); layer.closeAll();
|
||||
}, 100);
|
||||
|
||||
laydate.render({ elem: '#endTime', type: 'datetime' });
|
||||
|
||||
function loadConfig() {
|
||||
admin.req({ method: 'get', url: setter.baseurl + 'adminapi/ActivityConfig/Get',
|
||||
done: function(r2) {
|
||||
activityConfig = (r2.code === 0 && r2.data) ? r2.data : { battleOrderConfig: {} };
|
||||
initForm();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function bo() {
|
||||
if (!activityConfig.battleOrderConfig) activityConfig.battleOrderConfig = {};
|
||||
return activityConfig.battleOrderConfig;
|
||||
}
|
||||
|
||||
function initForm() {
|
||||
var b = bo();
|
||||
$('input[name="isOpen"]').prop('checked', b.isOpen);
|
||||
$('input[name="endTime"]').val(b.endTime ? new Date(b.endTime * 1000).toLocaleString() : '');
|
||||
$('input[name="ruleDesc"]').val(b.ruleDesc || '');
|
||||
$('input[name="buyDesc"]').val(b.buyDesc || '');
|
||||
$('input[name="advanceGiftDesc"]').val(b.advanceGiftDesc || '');
|
||||
form.render();
|
||||
renderLevelTable();
|
||||
renderAdvanceTable();
|
||||
}
|
||||
|
||||
function save(msg) {
|
||||
admin.req({
|
||||
method: 'post', contentType: 'application/json;charset=UTF-8',
|
||||
url: setter.baseurl + 'adminapi/ActivityConfig/UpdateBattleOrder',
|
||||
data: JSON.stringify(activityConfig.battleOrderConfig),
|
||||
done: function(r) {
|
||||
r.code === 0 ? (layer.msg(msg || '保存成功'), loadConfig()) : layer.msg(r.info || '保存失败');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
form.on('submit(save-base)', function(d) {
|
||||
var b = bo();
|
||||
b.isOpen = d.field.isOpen === 'on';
|
||||
b.ruleDesc = d.field.ruleDesc || '';
|
||||
b.buyDesc = d.field.buyDesc || '';
|
||||
b.advanceGiftDesc = d.field.advanceGiftDesc || '';
|
||||
if (d.field.endTime) b.endTime = Math.floor(new Date(d.field.endTime).getTime() / 1000);
|
||||
save('保存成功'); return false;
|
||||
});
|
||||
|
||||
// ==================== 等级表格 ====================
|
||||
function renderLevelTable() {
|
||||
var levels = bo().levelConfig || [];
|
||||
table.render({ elem: '#table-level', data: levels, page: true,
|
||||
cols: [[
|
||||
{ type: 'numbers', title: '序号', width: 60 },
|
||||
{ field: 'level', title: '等级', width: 70, sort: true },
|
||||
{ field: 'exp', title: '升级经验', width: 90 },
|
||||
{ title: '普通奖励', width: 90, templet: function(d) { return (d.normalGiftList||[]).length + '项'; } },
|
||||
{ title: '豪华奖励', width: 90, templet: function(d) { return (d.luxuryGiftList||[]).length + '项'; } },
|
||||
{ title: '至尊奖励', width: 90, templet: function(d) { return (d.supremeGiftList||[]).length + '项'; } },
|
||||
{ field: 'payType', title: '付费', width: 70, templet: '#tpl-payType' },
|
||||
{ field: 'payCount', title: '费用', width: 80 },
|
||||
{ title: '操作', width: 200, fixed: 'right', toolbar: '#bar-level' }
|
||||
]]
|
||||
});
|
||||
}
|
||||
|
||||
// ==================== 进阶表格 ====================
|
||||
function renderAdvanceTable() {
|
||||
var list = bo().advanceGift || [];
|
||||
table.render({ elem: '#table-advance', data: list, page: false,
|
||||
cols: [[
|
||||
{ type: 'numbers', title: '序号', width: 60 },
|
||||
{ field: 'type', title: '类型', width: 80, templet: '#tpl-advance-type' },
|
||||
{ field: 'name', title: '名称', width: 120 },
|
||||
{ field: 'count', title: '数量', width: 80 },
|
||||
{ field: 'payType', title: '付费', width: 70, templet: '#tpl-payType' },
|
||||
{ field: 'payCount', title: '费用', width: 80 },
|
||||
{ title: '奖励', width: 80, templet: function(d) { return (d.giftList||[]).length + '项'; } },
|
||||
{ title: '操作', width: 180, fixed: 'right', toolbar: '#bar-advance' }
|
||||
]]
|
||||
});
|
||||
}
|
||||
|
||||
// ==================== 等级 CRUD ====================
|
||||
function showLevelEdit(data, isAdd) {
|
||||
var readonly = isAdd ? '' : 'readonly';
|
||||
var html = '<div style="padding:15px;"><form class="layui-form">' +
|
||||
'<div class="layui-row"><div class="layui-col-md6"><div class="layui-form-item"><label class="layui-form-label">等级</label><div class="layui-input-block"><input type="number" id="dlg-level" value="'+(data.level||'')+'" class="layui-input" '+readonly+'></div></div></div>' +
|
||||
'<div class="layui-col-md6"><div class="layui-form-item"><label class="layui-form-label">升级经验</label><div class="layui-input-block"><input type="number" id="dlg-exp" value="'+(data.exp||0)+'" class="layui-input"></div></div></div></div>' +
|
||||
'<div class="layui-row"><div class="layui-col-md6"><div class="layui-form-item"><label class="layui-form-label">付费类型</label><div class="layui-input-block"><select id="dlg-payType"><option value="1"'+(data.payType===1?' selected':'')+'>金币</option><option value="2"'+(data.payType===2?' selected':'')+'>钻石</option><option value="3"'+(data.payType===3?' selected':'')+'>现金</option></select></div></div></div>' +
|
||||
'<div class="layui-col-md6"><div class="layui-form-item"><label class="layui-form-label">付费金额</label><div class="layui-input-block"><input type="number" id="dlg-payCount" value="'+(data.payCount||0)+'" class="layui-input"></div></div></div></div>' +
|
||||
'<div style="margin-top:10px;border-top:1px solid #eee;padding-top:10px;"><div class="layui-tab" lay-filter="dlg-reward-tab"><ul class="layui-tab-title"><li class="layui-this">普通奖励</li><li>豪华奖励</li><li>至尊奖励</li></ul>' +
|
||||
'<div class="layui-tab-content" style="height:300px;padding:5px 0;">' +
|
||||
'<div class="layui-tab-item layui-show"><div class="inplace-gift" data-key="normalGiftList"></div></div>' +
|
||||
'<div class="layui-tab-item"><div class="inplace-gift" data-key="luxuryGiftList"></div></div>' +
|
||||
'<div class="layui-tab-item"><div class="inplace-gift" data-key="supremeGiftList"></div></div>' +
|
||||
'</div></div></div></form></div>';
|
||||
layer.open({ type:1, title:isAdd?'添加等级':'编辑等级', area:['850px','580px'], content:html, btn:['保存','取消'],
|
||||
success:function(l){
|
||||
form.render('select');
|
||||
element.render('tab');
|
||||
var keys = ['normalGiftList','luxuryGiftList','supremeGiftList'];
|
||||
var tgt = {};
|
||||
keys.forEach(function(k){ tgt[k] = data[k] || []; });
|
||||
renderInplaceGift(l.find('.inplace-gift[data-key="normalGiftList"]'), tgt.normalGiftList);
|
||||
element.on('tab(dlg-reward-tab)', function(o){
|
||||
var k = keys[o.index];
|
||||
var ph = l.find('.inplace-gift[data-key="'+k+'"]');
|
||||
if (ph.is(':empty')) renderInplaceGift(ph, tgt[k]);
|
||||
});
|
||||
window._dlgGiftData = tgt;
|
||||
},
|
||||
yes:function(idx,l){
|
||||
var tgt = window._dlgGiftData || {};
|
||||
var item = {
|
||||
level: parseInt(l.find('#dlg-level').val()) || data.level,
|
||||
exp: parseInt(l.find('#dlg-exp').val()) || 0,
|
||||
normalGiftList: tgt.normalGiftList || [],
|
||||
luxuryGiftList: tgt.luxuryGiftList || [],
|
||||
supremeGiftList: tgt.supremeGiftList || [],
|
||||
payType: parseInt(l.find('#dlg-payType').val()) || 1,
|
||||
payCount: parseInt(l.find('#dlg-payCount').val()) || 0
|
||||
};
|
||||
var b = bo();
|
||||
if (!b.levelConfig) b.levelConfig = [];
|
||||
if (isAdd) {
|
||||
if (b.levelConfig.some(function(x) { return x.level === item.level; }))
|
||||
{ layer.msg('等级已存在'); return; }
|
||||
b.levelConfig.push(item);
|
||||
} else {
|
||||
var i = b.levelConfig.findIndex(function(x) { return x.level === data.level; });
|
||||
if (i > -1) b.levelConfig[i] = item;
|
||||
}
|
||||
delete window._dlgGiftData;
|
||||
layer.close(idx); save(isAdd?'添加成功':'保存成功');
|
||||
}
|
||||
});
|
||||
}
|
||||
function renderInplaceGift(container, giftList) {
|
||||
if (!giftList) giftList = [];
|
||||
var html = '<div style="margin-bottom:8px;"><button class="layui-btn layui-btn-xs layui-btn-normal btn-ip-add"><i class="layui-icon layui-icon-add-1"></i> 新增奖励项</button></div>' +
|
||||
'<table class="ip-gift-table"><thead><tr><th style="width:60px">类型</th><th style="width:60px">数量</th><th style="width:60px">最小值</th><th style="width:60px">最大值</th><th style="width:60px">随机</th><th style="width:100px">道具ID</th><th style="width:60px">操作</th></tr></thead><tbody></tbody></table>';
|
||||
container.html(html);
|
||||
var tbody = container.find('tbody');
|
||||
function renderRows(){
|
||||
tbody.empty();
|
||||
giftList.forEach(function(g,idx){
|
||||
var typeMap = {1:'金币',2:'钻石',3:'道具'};
|
||||
var tr = $('<tr><td>'+ (typeMap[g.type]||'金币') +'</td><td>'+ (g.count||0) +'</td><td>'+ (g.minCount||0) +'</td><td>'+ (g.maxCount||0) +'</td><td>'+ (g.isRandom?'是':'否') +'</td><td>'+ (g.otherId||'') +'</td><td><button class="layui-btn layui-btn-xs btn-ip-edit" data-idx="'+idx+'">编辑</button><button class="layui-btn layui-btn-xs layui-btn-danger btn-ip-del" data-idx="'+idx+'">删除</button></td></tr>');
|
||||
tbody.append(tr);
|
||||
});
|
||||
}
|
||||
renderRows();
|
||||
container.find('.btn-ip-add').off('click').on('click', function(){
|
||||
giftList.push({type:1,count:100,isRandom:false,minCount:0,maxCount:0,otherId:''});
|
||||
renderRows();
|
||||
});
|
||||
container.off('click','.btn-ip-del').on('click','.btn-ip-del', function(){
|
||||
var idx = parseInt($(this).data('idx'));
|
||||
giftList.splice(idx,1);
|
||||
renderRows();
|
||||
});
|
||||
container.off('click','.btn-ip-edit').on('click','.btn-ip-edit', function(){
|
||||
var idx = parseInt($(this).data('idx'));
|
||||
var g = giftList[idx];
|
||||
if (!g) return;
|
||||
var ehtml = '<div style="padding:15px;"><form class="layui-form"><div class="layui-form-item"><label class="layui-form-label">类型</label><div class="layui-input-block"><select id="ip-type"><option value="1"'+(g.type===1?' selected':'')+'>金币</option><option value="2"'+(g.type===2?' selected':'')+'>钻石</option><option value="3"'+(g.type===3?' selected':'')+'>道具</option></select></div></div>' +
|
||||
'<div class="layui-form-item"><label class="layui-form-label">数量</label><div class="layui-input-block"><input type="number" id="ip-count" value="'+(g.count||0)+'" class="layui-input"></div></div>' +
|
||||
'<div class="layui-form-item"><label class="layui-form-label">随机</label><div class="layui-input-block"><input type="checkbox" id="ip-random" lay-skin="switch" lay-text="是|否"'+(g.isRandom?' checked':'')+'></div></div>' +
|
||||
'<div class="layui-form-item"><label class="layui-form-label">最小值</label><div class="layui-input-block"><input type="number" id="ip-min" value="'+(g.minCount||0)+'" class="layui-input"></div></div>' +
|
||||
'<div class="layui-form-item"><label class="layui-form-label">最大值</label><div class="layui-input-block"><input type="number" id="ip-max" value="'+(g.maxCount||0)+'" class="layui-input"></div></div>' +
|
||||
'<div class="layui-form-item gift-prop-select" style="'+(g.type===3?'':'display:none;')+'"><label class="layui-form-label">道具编号</label><div class="layui-input-block"><input type="text" id="ip-oid" class="layui-input" value="'+(g.otherId||'')+'" placeholder="输入道具编号"></div></div></form></div>';
|
||||
layer.open({type:1,title:'编辑奖励项',area:['420px','420px'],content:ehtml,btn:['保存','取消'],
|
||||
success:function(el){ form.render(); form.on('select(ip-type)', function(o){ el.find('.gift-prop-select').toggle(o.value==='3'); }); },
|
||||
yes:function(ei,el){
|
||||
var t = parseInt(el.find('#ip-type').val())||1;
|
||||
var propNum = t===3 ? (el.find('#ip-oid').val()||'') : '';
|
||||
giftList[idx] = {
|
||||
type:t, count: parseInt(el.find('#ip-count').val())||0,
|
||||
isRandom: el.find('#ip-random')[0].checked,
|
||||
minCount: parseInt(el.find('#ip-min').val())||0,
|
||||
maxCount: parseInt(el.find('#ip-max').val())||0,
|
||||
otherId: propNum
|
||||
};
|
||||
layer.close(ei);
|
||||
renderRows();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// ==================== 进阶 CRUD ====================
|
||||
function showAdvanceEdit(data, isAdd) {
|
||||
var readonly = isAdd ? '' : 'readonly';
|
||||
var html = '<div style="padding:20px;"><form class="layui-form">' +
|
||||
'<div class="layui-row"><div class="layui-col-md6"><div class="layui-form-item"><label class="layui-form-label">类型</label><div class="layui-input-block"><select id="dlg-type" '+readonly+'><option value="1"'+(data.type===1?' selected':'')+'>豪华</option><option value="2"'+(data.type===2?' selected':'')+'>至尊</option><option value="3"'+(data.type===3?' selected':'')+'>豪华+至尊</option></select></div></div></div>' +
|
||||
'<div class="layui-col-md6"><div class="layui-form-item"><label class="layui-form-label">名称</label><div class="layui-input-block"><input type="text" id="dlg-name" value="'+(data.name||'')+'" class="layui-input"></div></div></div></div>' +
|
||||
'<div class="layui-row"><div class="layui-col-md6"><div class="layui-form-item"><label class="layui-form-label">奖励数量</label><div class="layui-input-block"><input type="number" id="dlg-count" value="'+(data.count||0)+'" class="layui-input"></div></div></div>' +
|
||||
'<div class="layui-col-md6"><div class="layui-form-item"><label class="layui-form-label">图片</label><div class="layui-input-block" style="display:flex;"><input type="text" id="dlg-image" value="'+(data.imageUrl||'')+'" class="layui-input" placeholder="图片URL" style="flex:1;margin-right:5px;"><button type="button" id="btn-upload-advance-img" class="layui-btn layui-btn-sm"><i class="layui-icon layui-icon-upload"></i></button></div></div></div></div>' +
|
||||
'<div class="layui-row"><div class="layui-col-md6"><div class="layui-form-item"><label class="layui-form-label">付费类型</label><div class="layui-input-block"><select id="dlg-payType"><option value="1"'+(data.payType===1?' selected':'')+'>金币</option><option value="2"'+(data.payType===2?' selected':'')+'>钻石</option><option value="3"'+(data.payType===3?' selected':'')+'>现金</option></select></div></div></div>' +
|
||||
'<div class="layui-col-md6"><div class="layui-form-item"><label class="layui-form-label">付费金额</label><div class="layui-input-block"><input type="number" id="dlg-payCount" value="'+(data.payCount||0)+'" class="layui-input"></div></div></div></div>' +
|
||||
'</form></div>';
|
||||
layer.open({ type:1, title:isAdd?'添加进阶':'编辑进阶', area:['550px','380px'], content:html, btn:['保存','取消'],
|
||||
success:function(l){
|
||||
form.render('select');
|
||||
layui.upload.render({
|
||||
elem: l.find('#btn-upload-advance-img'),
|
||||
url: (layui.setter.baseurl||'/')+'adminapi/ImageUpload/upload/temp',
|
||||
done: function(r) {
|
||||
if (r.code === 0) l.find('#dlg-image').val(r.data.path);
|
||||
}
|
||||
});
|
||||
},
|
||||
yes:function(idx,l){
|
||||
var item = {
|
||||
type: parseInt(l.find('#dlg-type').val()) || 1,
|
||||
name: l.find('#dlg-name').val() || '',
|
||||
count: parseInt(l.find('#dlg-count').val()) || 0,
|
||||
imageUrl: l.find('#dlg-image').val() || '',
|
||||
payType: parseInt(l.find('#dlg-payType').val()) || 1,
|
||||
payCount: parseInt(l.find('#dlg-payCount').val()) || 0,
|
||||
giftList: data.giftList || []
|
||||
};
|
||||
var b = bo();
|
||||
if (!b.advanceGift) b.advanceGift = [];
|
||||
if (isAdd) {
|
||||
if (b.advanceGift.some(function(x) { return x.type === item.type; }))
|
||||
{ layer.msg('类型已存在'); return; }
|
||||
b.advanceGift.push(item);
|
||||
} else {
|
||||
var i = b.advanceGift.findIndex(function(x) { return x.type === data.type; });
|
||||
if (i > -1) b.advanceGift[i] = item;
|
||||
}
|
||||
layer.close(idx); save(isAdd?'添加成功':'保存成功');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ==================== 奖励编辑弹窗(三层tab,与奖池页面一致) ====================
|
||||
function showRewardDialog(title, levelData) {
|
||||
rewardCtx = { normal: levelData.normalGiftList || [], luxury: levelData.luxuryGiftList || [], supreme: levelData.supremeGiftList || [] };
|
||||
var html = '<div class="reward-tabs" style="padding:10px;"><div class="layui-tab" lay-filter="reward-tab">' +
|
||||
'<ul class="layui-tab-title"><li class="layui-this">普通奖励</li><li>豪华奖励</li><li>至尊奖励</li></ul>' +
|
||||
'<div class="layui-tab-content" style="height:480px;">' +
|
||||
'<div class="layui-tab-item layui-show"><div class="gift-placeholder" data-key="normalGiftList"></div></div>' +
|
||||
'<div class="layui-tab-item"><div class="gift-placeholder" data-key="luxuryGiftList"></div></div>' +
|
||||
'<div class="layui-tab-item"><div class="gift-placeholder" data-key="supremeGiftList"></div></div>' +
|
||||
'</div></div></div>';
|
||||
|
||||
layer.open({
|
||||
type: 1, title: title, area: ['1000px', '620px'],
|
||||
content: html, shade: 0.1, shadeClose: true,
|
||||
success: function(layero) {
|
||||
// 初始化第一个tab的编辑器
|
||||
initGiftEditor(layero, rewardCtx.normal);
|
||||
// tab切换时重新初始化编辑器
|
||||
form.on('tab(reward-tab)', function(o) {
|
||||
var key = o.index === 0 ? 'normalGiftList' : o.index === 1 ? 'luxuryGiftList' : 'supremeGiftList';
|
||||
var list = rewardCtx[key];
|
||||
var ph = layero.find('.gift-placeholder[data-key="' + key + '"]');
|
||||
ph.html($('#gift-editor-tpl').html());
|
||||
var propOpts = '<option value="">请选择道具</option>';
|
||||
propList.forEach(function(p) { propOpts += '<option value="'+p.number+'">'+p.number+' - '+p.name+'</option>'; });
|
||||
ph.find('#giftProductNumber').html(propOpts);
|
||||
form.render();
|
||||
bindGiftEvents(ph, list);
|
||||
});
|
||||
},
|
||||
end: function() { renderLevelTable(); renderAdvanceTable(); }
|
||||
});
|
||||
}
|
||||
|
||||
function initGiftEditor(layero, giftList) {
|
||||
var ph = layero.find('.gift-placeholder[data-key="normalGiftList"]');
|
||||
ph.html($('#gift-editor-tpl').html());
|
||||
var propOpts = '<option value="">请选择道具</option>';
|
||||
propList.forEach(function(p) { propOpts += '<option value="'+p.number+'">'+p.number+' - '+p.name+'</option>'; });
|
||||
ph.find('#giftProductNumber').html(propOpts);
|
||||
form.render();
|
||||
bindGiftEvents(ph, giftList);
|
||||
}
|
||||
|
||||
function bindGiftEvents(container, giftList) {
|
||||
var $c = $(container);
|
||||
currentGiftIndex = -1;
|
||||
renderGiftTable($c, giftList);
|
||||
|
||||
$c.find('#btn-add-gift').on('click', function() {
|
||||
giftList.push({ type:1, count:100, isRandom:false, minCount:0, maxCount:0, otherId:'' });
|
||||
currentGiftIndex = giftList.length - 1;
|
||||
renderGiftTable($c, giftList, currentGiftIndex);
|
||||
loadGiftForm(giftList[giftList.length-1], $c);
|
||||
});
|
||||
$c.find('#btn-delete-gift').on('click', function() {
|
||||
if (currentGiftIndex < 0) return layer.msg('请先选择要删除的项');
|
||||
giftList.splice(currentGiftIndex, 1);
|
||||
currentGiftIndex = -1; clearGiftForm($c);
|
||||
renderGiftTable($c, giftList, -1);
|
||||
});
|
||||
$c.find('#btn-save-gift').on('click', function() {
|
||||
if (currentGiftIndex < 0) return layer.msg('请先选择要编辑的项');
|
||||
var t = parseInt($c.find('#giftType').val()) || 1;
|
||||
var oid = t === 3 ? ($c.find('#giftProductNumber').val()||'') : '';
|
||||
giftList[currentGiftIndex] = {
|
||||
type: t, count: parseInt($c.find('#giftCount').val()) || 0,
|
||||
isRandom: $c.find('#giftIsRandom')[0].checked,
|
||||
minCount: parseInt($c.find('#giftMinCount').val()) || 0,
|
||||
maxCount: parseInt($c.find('#giftMaxCount').val()) || 0,
|
||||
otherId: oid
|
||||
};
|
||||
renderGiftTable($c, giftList, currentGiftIndex);
|
||||
layer.msg('保存成功');
|
||||
});
|
||||
|
||||
form.on('select(giftType)', function(o) {
|
||||
$c.find('.gift-prop-select').toggle(o.value === '3');
|
||||
$c.find('#giftCount').val(0); $c.find('#giftIsRandom')[0].checked = false; form.render('checkbox');
|
||||
});
|
||||
$c.find('#giftCount').on('input', function() { $c.find('#giftIsRandom')[0].checked = false; form.render('checkbox'); });
|
||||
$c.find('#giftIsRandom').on('click', function() {
|
||||
if (this.checked) setTimeout(function() { $c.find('#giftCount').val(0); }, 10);
|
||||
});
|
||||
}
|
||||
|
||||
var currentGiftIndex = -1;
|
||||
|
||||
function renderGiftTable(container, giftList, selectIndex) {
|
||||
table.render({ elem: container.find('#table-gift-list'), data: giftList, page: false, height: 380,
|
||||
cols: [[
|
||||
{ type: 'numbers', title: '序号', width: 60 },
|
||||
{ field: 'type', title: '类型', width: 80, templet: function(d) { return ({1:'金币',2:'钻石',3:'道具'})[d.type]||'金币'; }},
|
||||
{ field: 'count', title: '数量', width: 80 },
|
||||
{ field: 'minCount', title: '最小值', width: 80 },
|
||||
{ field: 'maxCount', title: '最大值', width: 80 },
|
||||
{ field: 'isRandom', title: '随机', width: 70, templet: function(d) { return d.isRandom ? '是' : '否'; }},
|
||||
{ field: 'otherId', title: '道具Id', width: 120 }
|
||||
]],
|
||||
done: function() {
|
||||
if (selectIndex === undefined) selectIndex = giftList.length > 0 ? 0 : -1;
|
||||
if (selectIndex >= 0 && selectIndex < giftList.length) {
|
||||
currentGiftIndex = selectIndex;
|
||||
var tr = container.find('#table-gift-list + .layui-table-view tr[data-index="'+selectIndex+'"]');
|
||||
if (tr.length) { tr.addClass('layui-table-click'); loadGiftForm(giftList[selectIndex], container); }
|
||||
}
|
||||
}
|
||||
});
|
||||
container.find('#table-gift-list + .layui-table-view').off('click').on('click', 'tr[data-index]', function() {
|
||||
var idx = parseInt($(this).attr('data-index'));
|
||||
if (idx >= 0 && idx < giftList.length) {
|
||||
currentGiftIndex = idx;
|
||||
container.find('tr[data-index]').removeClass('layui-table-click');
|
||||
$(this).addClass('layui-table-click');
|
||||
loadGiftForm(giftList[idx], container);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function loadGiftForm(data, container) {
|
||||
container.find('#giftType').val(data.type || 1);
|
||||
container.find('#giftCount').val(data.count || 0);
|
||||
container.find('#giftIsRandom')[0].checked = data.isRandom || false;
|
||||
container.find('#giftMinCount').val(data.minCount || 0);
|
||||
container.find('#giftMaxCount').val(data.maxCount || 0);
|
||||
var oid = data.otherId || '';
|
||||
container.find('#giftProductNumber').val(oid);
|
||||
container.find('.gift-prop-select').toggle(data.type === 3);
|
||||
form.render('checkbox'); form.render('select');
|
||||
}
|
||||
|
||||
function clearGiftForm(container) {
|
||||
container.find('#giftType').val('1');
|
||||
container.find('#giftCount').val('0');
|
||||
container.find('#giftIsRandom')[0].checked = false;
|
||||
container.find('#giftMinCount').val('0');
|
||||
container.find('#giftMaxCount').val('0');
|
||||
container.find('#giftProductNumber').val('');
|
||||
container.find('.gift-prop-select').hide();
|
||||
form.render('checkbox'); form.render('select');
|
||||
}
|
||||
|
||||
// ==================== 初始化按钮(从 JSON 还原) ====================
|
||||
$(document).on('click', '[lay-filter="init-level"],[lay-filter="init-advance"]', function() {
|
||||
layer.confirm('确定要从配置文件还原战令数据吗?', function(i) {
|
||||
layer.close(i);
|
||||
admin.req({
|
||||
method: 'get',
|
||||
url: setter.baseurl + 'adminapi/GameConfig/InitActivitySection/BattleOrderConfig',
|
||||
done: function(r) {
|
||||
if (r.code === 0 || r.code === 200) {
|
||||
layer.msg('初始化成功');
|
||||
loadConfig();
|
||||
} else {
|
||||
layer.msg(r.info || '初始化失败');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
$(document).on('click', '[lay-filter="add-level"]', function() {
|
||||
var b = bo(), levels = b.levelConfig || [];
|
||||
var max = levels.length ? Math.max.apply(null, levels.map(function(l){return l.level;})) : 0;
|
||||
showLevelEdit({ level:max+1, exp:(max+1)*100, normalGiftList:[], luxuryGiftList:[], supremeGiftList:[], payType:1, payCount:(max+1)*100 }, true);
|
||||
});
|
||||
$(document).on('click', '[lay-filter="add-advance"]', function() {
|
||||
var b = bo(), list = b.advanceGift || [];
|
||||
var used = list.map(function(a){return a.type;});
|
||||
var nt = 1; while (used.indexOf(nt) > -1) nt++;
|
||||
showAdvanceEdit({ type:nt, name:({1:'豪华',2:'至尊',3:'豪华+至尊'})[nt]||'进阶', count:100, payType:1, payCount:10000, giftList:[] }, true);
|
||||
});
|
||||
$(document).on('click', '[lay-filter="save-levels"],[lay-filter="save-advances"]', function() { save('保存成功'); });
|
||||
|
||||
// ==================== 表格操作事件 ====================
|
||||
table.on('tool(table-level)', function(obj) {
|
||||
var d = obj.data, b = bo(), i = b.levelConfig.findIndex(function(x){return x.level===d.level;});
|
||||
if (i < 0) return;
|
||||
if (obj.event === 'del') {
|
||||
layer.confirm('确定删除等级'+d.level+'?', function(idx) { b.levelConfig.splice(i,1); layer.close(idx); save('删除成功'); });
|
||||
} else if (obj.event === 'edit') {
|
||||
showLevelEdit(d, false);
|
||||
}
|
||||
});
|
||||
|
||||
table.on('tool(table-advance)', function(obj) {
|
||||
var d = obj.data, b = bo(), i = b.advanceGift.findIndex(function(x){return String(x.type)===String(d.type);});
|
||||
if (i < 0) return;
|
||||
if (obj.event === 'del') {
|
||||
layer.confirm('确定删除进阶'+d.name+'?', function(idx) { b.advanceGift.splice(i,1); layer.close(idx); save('删除成功'); });
|
||||
} else if (obj.event === 'edit') {
|
||||
showAdvanceEdit(d);
|
||||
} else if (obj.event === 'reward') {
|
||||
if (!b.advanceGift[i].giftList) b.advanceGift[i].giftList = [];
|
||||
var html = '<div class="gift-placeholder">'+$('#gift-editor-tpl').html()+'</div>';
|
||||
layer.open({
|
||||
type:1, title:'进阶 - '+d.name+' 奖励列表', area:['1000px','620px'],
|
||||
content: html, shade: 0.1, shadeClose: true,
|
||||
success: function(layero) {
|
||||
// 奖励类型下拉与道具编号输入是两个不同字段,不能把道具选项塞进类型下拉。
|
||||
form.render();
|
||||
bindGiftEvents(layero.find('.gift-placeholder'), b.advanceGift[i].giftList);
|
||||
},
|
||||
end: function() { renderLevelTable(); renderAdvanceTable(); }
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
window.saveAct=function(n){layui.admin.req({method:'get',url:(layui.setter.baseurl||'/')+'adminapi/ActivityConfig/SaveSection/'+n,done:function(r){layui.layer.msg(r.info||(r.code===0?'保存成功':'保存失败'))}})}
|
||||
loadConfig();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.gift-container { display: flex; height: 460px; padding: 10px; }
|
||||
.gift-left { flex: 1; margin-right: 15px; }
|
||||
.gift-table-wrap { height: 380px; overflow: auto; }
|
||||
.gift-right { width: 280px; border-left: 1px solid #e6e6e6; padding-left: 15px; }
|
||||
.gift-right h4 { margin-bottom: 12px; color: #333; font-size: 15px; padding-bottom: 8px; border-bottom: 1px solid #eee; }
|
||||
.gift-toolbar { margin-bottom: 8px; }
|
||||
.gift-toolbar .layui-btn { margin-right: 5px; }
|
||||
.gift-right .layui-form-item { margin-bottom: 10px; }
|
||||
.gift-right .layui-form-label { padding: 9px 0; width: 60px; }
|
||||
.gift-right .layui-input-block { margin-left: 70px; }
|
||||
.reward-tabs .layui-tab-title { height: 42px; }
|
||||
.reward-tabs .layui-tab-title li { line-height: 42px; }
|
||||
.reward-tabs .layui-tab-content { padding: 10px 0; }
|
||||
</style>
|
||||
@@ -0,0 +1,179 @@
|
||||
<title>Boss道具掉落配置</title>
|
||||
|
||||
<div class="layui-card layadmin-header">
|
||||
<div class="layui-breadcrumb" lay-filter="breadcrumb">
|
||||
<a lay-href="">主页</a>
|
||||
<a><cite>游戏管理</cite></a>
|
||||
<a><cite>Boss道具掉落配置</cite></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-fluid">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header">
|
||||
<div class="layui-btn-container">
|
||||
<button class="layui-btn layui-btn-sm layui-btn-normal" lay-filter="init-bossdrop"><i class="layui-icon layui-icon-refresh"></i> 初始化配置</button>
|
||||
<button class="layui-btn layui-btn-sm" lay-filter="save-bossdrop"><i class="layui-icon layui-icon-ok"></i> 保存配置</button>
|
||||
<button class="layui-btn layui-btn-sm layui-btn-warm" lay-filter="reload-bossdrop"><i class="layui-icon layui-icon-refresh"></i> 刷新</button>
|
||||
<button class="layui-btn layui-btn-sm layui-btn-normal" onclick="saveSection('BossDropConfig')"><i class="layui-icon layui-icon-export"></i> 保存到初始化文件</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-card-body">
|
||||
<form class="layui-form" lay-filter="bd-form">
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">不掉落权重</label>
|
||||
<div class="layui-input-inline" style="width:200px;">
|
||||
<input type="number" name="noDropWeight" class="layui-input" placeholder="不掉落权重(值越大越难掉落)">
|
||||
</div>
|
||||
<div class="layui-form-mid layui-word-aux">总权重 = 不掉落权重 + 各掉落项权重之和</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<div class="layui-input-block">
|
||||
<button class="layui-btn" lay-submit lay-filter="save-base">保存基础配置</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header">掉落道具列表(所有Boss通用)</div>
|
||||
<div class="layui-card-body">
|
||||
<div class="layui-btn-container" style="margin-bottom:10px;">
|
||||
<button class="layui-btn layui-btn-sm" lay-filter="add-item"><i class="layui-icon layui-icon-add-1"></i> 添加掉落项</button>
|
||||
<button class="layui-btn layui-btn-sm" lay-filter="save-items"><i class="layui-icon layui-icon-ok"></i> 保存</button>
|
||||
</div>
|
||||
<table id="table-items" lay-filter="table-items"></table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/html" id="bar-item">
|
||||
<a class="layui-btn layui-btn-xs" lay-event="edit"><i class="layui-icon layui-icon-edit"></i>编辑</a>
|
||||
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del"><i class="layui-icon layui-icon-delete"></i>删除</a>
|
||||
</script>
|
||||
|
||||
<script>
|
||||
layui.use(['admin', 'table', 'form', 'layer'], function () {
|
||||
var $ = layui.$, admin = layui.admin, table = layui.table, form = layui.form, layer = layui.layer, setter = layui.setter;
|
||||
var gameConfig = null, propList = [];
|
||||
|
||||
setTimeout(function() { $('.layui-layer-shade').remove(); layer.closeAll(); }, 100);
|
||||
|
||||
function loadConfig() {
|
||||
admin.req({
|
||||
method: "get",
|
||||
url: setter.baseurl + "adminapi/GameConfig",
|
||||
done: function (res) {
|
||||
if (res.code === 0 && res.data) {
|
||||
gameConfig = res.data;
|
||||
if (!gameConfig.bossDropConfig) gameConfig.bossDropConfig = { noDropWeight: 5000, dropItems: [] };
|
||||
propList = (gameConfig.propConfig || []).filter(function(p){ return p.isSale; }).map(function(p){ return { number: p.number, name: p.name }; });
|
||||
initForm();
|
||||
renderTable();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function bd() { return gameConfig.bossDropConfig; }
|
||||
|
||||
function initForm() {
|
||||
var cfg = bd();
|
||||
$('input[name="noDropWeight"]').val(cfg.noDropWeight || 5000);
|
||||
form.render();
|
||||
}
|
||||
|
||||
function saveBase() {
|
||||
var cfg = bd();
|
||||
cfg.noDropWeight = parseInt($('input[name="noDropWeight"]').val()) || 5000;
|
||||
save();
|
||||
}
|
||||
|
||||
function save() {
|
||||
admin.req({
|
||||
method: "post", contentType: "application/json;charset=UTF-8",
|
||||
url: setter.baseurl + "adminapi/GameConfig/UpdateBossDrop",
|
||||
data: JSON.stringify(bd()),
|
||||
done: function (r) {
|
||||
r.code === 0 ? (layer.msg('保存成功'), loadConfig()) : layer.msg(r.info || '保存失败');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function renderTable() {
|
||||
var items = bd().dropItems || [];
|
||||
table.render({ elem: '#table-items', data: items, page: false,
|
||||
cols: [[
|
||||
{ type: 'numbers', title: '序号', width: 60 },
|
||||
{ field: 'propName', title: '道具名称', width: 150 },
|
||||
{ field: 'count', title: '数量', width: 80 },
|
||||
{ field: 'weight', title: '权重', width: 80 },
|
||||
{ title: '操作', width: 160, fixed: 'right', toolbar: '#bar-item' }
|
||||
]]
|
||||
});
|
||||
}
|
||||
|
||||
form.on('submit(save-base)', function() { saveBase(); return false; });
|
||||
|
||||
function showItemEdit(data, idx) {
|
||||
var items = bd().dropItems || [];
|
||||
var opts = '<option value="">请选择道具</option>';
|
||||
(gameConfig.propConfig || []).forEach(function(p) { opts += '<option value="'+p.number+'"'+(p.number===data.propNumber?' selected':'')+'>'+p.number+' - '+p.name+'</option>'; });
|
||||
var html = '<div style="padding:20px;"><form class="layui-form">' +
|
||||
'<div class="layui-form-item"><label class="layui-form-label">道具</label><div class="layui-input-block"><select id="dlg-prop" lay-search>'+opts+'</select></div></div>' +
|
||||
'<div class="layui-form-item"><label class="layui-form-label">数量</label><div class="layui-input-block"><input type="number" id="dlg-count" value="'+(data.count||1)+'" class="layui-input"></div></div>' +
|
||||
'<div class="layui-form-item"><label class="layui-form-label">权重</label><div class="layui-input-block"><input type="number" id="dlg-weight" value="'+(data.weight||100)+'" class="layui-input"></div></div>' +
|
||||
'</form></div>';
|
||||
layer.open({ type:1, title: idx < 0 ? '添加掉落项' : '编辑掉落项', area:['420px','320px'], content:html, btn:['保存','取消'],
|
||||
success:function(l){ form.render('select'); },
|
||||
yes:function(i,l){
|
||||
var item = {
|
||||
propNumber: l.find('#dlg-prop').val() || '',
|
||||
propName: l.find('#dlg-prop option:selected').text().replace(/^[^-]+ - /, ''),
|
||||
count: parseInt(l.find('#dlg-count').val()) || 1,
|
||||
weight: parseInt(l.find('#dlg-weight').val()) || 0
|
||||
};
|
||||
if (idx < 0) items.push(item); else items[idx] = item;
|
||||
layer.close(i); renderTable(); save();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$(document).on('click', '[lay-filter="add-item"]', function() { showItemEdit({ propNumber:'', propName:'', count:1, weight:100 }, -1); });
|
||||
$(document).on('click', '[lay-filter="save-items"]', function() { save(); });
|
||||
|
||||
table.on('tool(table-items)', function(obj) {
|
||||
var items = bd().dropItems || [];
|
||||
if (obj.event === 'del') {
|
||||
layer.confirm('确定删除?', function(i) { items.splice(items.indexOf(obj.data), 1); layer.close(i); renderTable(); save(); });
|
||||
} else if (obj.event === 'edit') {
|
||||
var idx = items.indexOf(obj.data);
|
||||
showItemEdit(obj.data, idx);
|
||||
}
|
||||
});
|
||||
|
||||
$(document).on('click', '[lay-filter="init-bossdrop"]', function() {
|
||||
layer.confirm('确定要从配置文件还原Boss掉落配置吗?', function(i) {
|
||||
layer.close(i);
|
||||
admin.req({
|
||||
method: 'get',
|
||||
url: setter.baseurl + 'adminapi/GameConfig/InitSection/BossDropConfig',
|
||||
done: function(r) {
|
||||
if (r.code === 0 || r.code === 200) {
|
||||
layer.msg('初始化成功');
|
||||
loadConfig();
|
||||
} else {
|
||||
layer.msg(r.info || '初始化失败');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
$(document).on('click', '[lay-filter="reload-bossdrop"]', function() { loadConfig(); layer.msg('刷新成功'); });
|
||||
|
||||
window.saveSection=function(n){layui.admin.req({method:'get',url:(layui.setter.baseurl||'/')+'adminapi/GameConfig/SaveSection/'+n,done:function(r){layui.layer.msg(r.info||(r.code===0?'保存成功':'保存失败'))}})}
|
||||
loadConfig();
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,393 @@
|
||||
|
||||
<title>机器人AI配置</title>
|
||||
|
||||
<style>
|
||||
.layui-form-label { width: 200px; }
|
||||
.bot-row { display: flex; flex-wrap: wrap; gap: 8px; }
|
||||
.bot-row .layui-form-item { display: flex; align-items: center; margin: 0; white-space: nowrap; flex: 1; min-width: 300px; }
|
||||
.bot-row .layui-form-label { width: auto; text-align: right; padding-right: 6px; font-size: 13px; }
|
||||
.bot-row .layui-input-inline { width: 70px; }
|
||||
.bot-row .layui-form-mid { font-size: 11px; color: #999; margin-left: 4px; }
|
||||
</style>
|
||||
|
||||
<div class="layui-card layadmin-header">
|
||||
<div class="layui-breadcrumb" lay-filter="breadcrumb">
|
||||
<a lay-href="">主页</a>
|
||||
<a><cite>游戏管理</cite></a>
|
||||
<a><cite>机器人AI配置</cite></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-fluid">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header">
|
||||
<div class="layui-btn-container">
|
||||
<button class="layui-btn layui-btn-sm layui-btn-normal" lay-filter="init-botai"><i class="layui-icon layui-icon-refresh"></i> 初始化配置</button>
|
||||
<button class="layui-btn layui-btn-sm" lay-filter="save-botai"><i class="layui-icon layui-icon-ok"></i> 保存配置</button>
|
||||
<button class="layui-btn layui-btn-sm layui-btn-warm" lay-filter="reload-botai"><i class="layui-icon layui-icon-refresh"></i> 刷新</button>
|
||||
<button class="layui-btn layui-btn-sm layui-btn-normal" onclick="saveSection('BotConfig')"><i class="layui-icon layui-icon-export"></i> 保存到初始化文件</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-card-body">
|
||||
<form class="layui-form" id="form-botai" style="padding: 20px;">
|
||||
|
||||
<fieldset class="layui-elem-field"><legend>全局设置</legend>
|
||||
<div class="layui-field-box">
|
||||
<div class="layui-form-item"><label class="layui-form-label">总开关</label><div class="layui-input-inline"><input type="checkbox" name="enabled" lay-skin="switch" lay-text="开启|关闭"></div><div class="layui-form-mid">机器人总开关</div></div>
|
||||
<div class="layui-form-item"><label class="layui-form-label">AI状态</label><div class="layui-input-inline"><input type="text" name="aiState" class="layui-input" style="width:120px;"></div><div class="layui-form-mid">active=正常 | idle=待机 | aggressive=激进</div></div>
|
||||
<div class="layui-form-item"><label class="layui-form-label">爆率因子</label><div class="layui-input-inline"><input type="number" name="luckFactor" class="layui-input" step="0.1"></div><div class="layui-form-mid">1.0=正常, >1容易杀, <1难杀</div></div>
|
||||
<div class="layui-form-item"><label class="layui-form-label">状态刷新间隔(秒)</label><div class="layui-input-inline"><input type="number" name="stateRefreshInterval" class="layui-input" step="0.1"></div><div class="layui-form-mid">0=不自动刷新</div></div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="layui-elem-field"><legend>技能配置</legend>
|
||||
<div class="layui-field-box">
|
||||
<div class="layui-form-item"><label class="layui-form-label">技能刷新间隔(秒)</label><div class="layui-input-inline"><input type="number" name="skillRefreshInterval" class="layui-input" step="0.1"></div><div class="layui-form-mid">每隔多少秒重新掷骰子决定技能组合</div></div>
|
||||
<div class="bot-row">
|
||||
<div class="layui-form-item"><label class="layui-form-label">锁定概率</label><div class="layui-input-inline"><input type="number" name="lockProb" class="layui-input" step="0.01"></div><div class="layui-form-mid">自动瞄准最近鱼</div></div>
|
||||
<div class="layui-form-item"><label class="layui-form-label">锁定Min</label><div class="layui-input-inline"><input type="number" name="lockMinTime" class="layui-input" step="0.1"></div></div>
|
||||
<div class="layui-form-item"><label class="layui-form-label">锁定Max</label><div class="layui-input-inline"><input type="number" name="lockMaxTime" class="layui-input" step="0.1"></div></div>
|
||||
</div>
|
||||
<div class="bot-row">
|
||||
<div class="layui-form-item"><label class="layui-form-label">狂暴概率</label><div class="layui-input-inline"><input type="number" name="frenzyProb" class="layui-input" step="0.01"></div><div class="layui-form-mid">开火速度翻倍</div></div>
|
||||
<div class="layui-form-item"><label class="layui-form-label">狂暴Min</label><div class="layui-input-inline"><input type="number" name="frenzyMinTime" class="layui-input" step="0.1"></div></div>
|
||||
<div class="layui-form-item"><label class="layui-form-label">狂暴Max</label><div class="layui-input-inline"><input type="number" name="frenzyMaxTime" class="layui-input" step="0.1"></div></div>
|
||||
</div>
|
||||
<div class="bot-row">
|
||||
<div class="layui-form-item"><label class="layui-form-label">三发散弹概率</label><div class="layui-input-inline"><input type="number" name="tripleProb" class="layui-input" step="0.01"></div><div class="layui-form-mid">一次3发</div></div>
|
||||
<div class="layui-form-item"><label class="layui-form-label">三发Min</label><div class="layui-input-inline"><input type="number" name="tripleMinTime" class="layui-input" step="0.1"></div></div>
|
||||
<div class="layui-form-item"><label class="layui-form-label">三发Max</label><div class="layui-input-inline"><input type="number" name="tripleMaxTime" class="layui-input" step="0.1"></div></div>
|
||||
</div>
|
||||
<div class="bot-row">
|
||||
<div class="layui-form-item"><label class="layui-form-label">双倍炮概率</label><div class="layui-input-inline"><input type="number" name="doubleProb" class="layui-input" step="0.01"></div><div class="layui-form-mid">消耗翻倍</div></div>
|
||||
<div class="layui-form-item"><label class="layui-form-label">双倍Min</label><div class="layui-input-inline"><input type="number" name="doubleMinTime" class="layui-input" step="0.1"></div></div>
|
||||
<div class="layui-form-item"><label class="layui-form-label">双倍Max</label><div class="layui-input-inline"><input type="number" name="doubleMaxTime" class="layui-input" step="0.1"></div></div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<!-- 行为模板 -->
|
||||
<!-- 三个行为模式共用同一套字段,使用前缀区分 -->
|
||||
<fieldset class="layui-elem-field"><legend>Active 行为参数</legend>
|
||||
<div class="layui-field-box">
|
||||
<div class="bot-row">
|
||||
<div class="layui-form-item"><label class="layui-form-label">开火间隔Min(秒)</label><div class="layui-input-inline"><input type="number" name="activeFireIntervalMin" class="layui-input" step="0.1"></div></div>
|
||||
<div class="layui-form-item"><label class="layui-form-label">开火间隔Max(秒)</label><div class="layui-input-inline"><input type="number" name="activeFireIntervalMax" class="layui-input" step="0.1"></div></div>
|
||||
<div class="layui-form-item"><label class="layui-form-label">开火概率</label><div class="layui-input-inline"><input type="number" name="activeFireProb" class="layui-input" step="0.01"></div><div class="layui-form-mid">0.9=经常开火偶尔停顿</div></div>
|
||||
</div>
|
||||
<div class="bot-row">
|
||||
<div class="layui-form-item"><label class="layui-form-label">旋转速度(度/秒)</label><div class="layui-input-inline"><input type="number" name="activeRotateSpeed" class="layui-input" step="1"></div></div>
|
||||
<div class="layui-form-item"><label class="layui-form-label">目标保持Min(秒)</label><div class="layui-input-inline"><input type="number" name="activeTargetHoldTimeMin" class="layui-input" step="0.1"></div></div>
|
||||
<div class="layui-form-item"><label class="layui-form-label">目标保持Max(秒)</label><div class="layui-input-inline"><input type="number" name="activeTargetHoldTimeMax" class="layui-input" step="0.1"></div></div>
|
||||
</div>
|
||||
<div class="bot-row">
|
||||
<div class="layui-form-item"><label class="layui-form-label">切换目标概率</label><div class="layui-input-inline"><input type="number" name="activeTargetSwitchChance" class="layui-input" step="0.01"></div></div>
|
||||
<div class="layui-form-item"><label class="layui-form-label">高分偏好</label><div class="layui-input-inline"><input type="number" name="activeTargetPreferHighScore" class="layui-input" step="0.1"></div><div class="layui-form-mid">0=纯距离, 1=纯分数</div></div>
|
||||
</div>
|
||||
<div class="bot-row">
|
||||
<div class="layui-form-item"><label class="layui-form-label">允许打Boss</label><div class="layui-input-inline"><input type="checkbox" name="activeCanTargetBoss" lay-skin="switch" lay-text="是|否"></div></div>
|
||||
<div class="layui-form-item"><label class="layui-form-label">允许打特殊鱼</label><div class="layui-input-inline"><input type="checkbox" name="activeCanTargetSpecial" lay-skin="switch" lay-text="是|否"></div></div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="layui-elem-field"><legend>Idle 行为参数</legend>
|
||||
<div class="layui-field-box">
|
||||
<div class="bot-row">
|
||||
<div class="layui-form-item"><label class="layui-form-label">开火间隔Min(秒)</label><div class="layui-input-inline"><input type="number" name="idleFireIntervalMin" class="layui-input" step="0.1"></div></div>
|
||||
<div class="layui-form-item"><label class="layui-form-label">开火间隔Max(秒)</label><div class="layui-input-inline"><input type="number" name="idleFireIntervalMax" class="layui-input" step="0.1"></div></div>
|
||||
<div class="layui-form-item"><label class="layui-form-label">开火概率</label><div class="layui-input-inline"><input type="number" name="idleFireProb" class="layui-input" step="0.01"></div></div>
|
||||
</div>
|
||||
<div class="bot-row">
|
||||
<div class="layui-form-item"><label class="layui-form-label">旋转速度(度/秒)</label><div class="layui-input-inline"><input type="number" name="idleRotateSpeed" class="layui-input" step="1"></div></div>
|
||||
<div class="layui-form-item"><label class="layui-form-label">目标保持Min(秒)</label><div class="layui-input-inline"><input type="number" name="idleTargetHoldTimeMin" class="layui-input" step="0.1"></div></div>
|
||||
<div class="layui-form-item"><label class="layui-form-label">目标保持Max(秒)</label><div class="layui-input-inline"><input type="number" name="idleTargetHoldTimeMax" class="layui-input" step="0.1"></div></div>
|
||||
</div>
|
||||
<div class="bot-row">
|
||||
<div class="layui-form-item"><label class="layui-form-label">切换目标概率</label><div class="layui-input-inline"><input type="number" name="idleTargetSwitchChance" class="layui-input" step="0.01"></div></div>
|
||||
<div class="layui-form-item"><label class="layui-form-label">高分偏好</label><div class="layui-input-inline"><input type="number" name="idleTargetPreferHighScore" class="layui-input" step="0.1"></div></div>
|
||||
</div>
|
||||
<div class="bot-row">
|
||||
<div class="layui-form-item"><label class="layui-form-label">允许打Boss</label><div class="layui-input-inline"><input type="checkbox" name="idleCanTargetBoss" lay-skin="switch" lay-text="是|否"></div></div>
|
||||
<div class="layui-form-item"><label class="layui-form-label">允许打特殊鱼</label><div class="layui-input-inline"><input type="checkbox" name="idleCanTargetSpecial" lay-skin="switch" lay-text="是|否"></div></div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="layui-elem-field"><legend>Aggressive 行为参数</legend>
|
||||
<div class="layui-field-box">
|
||||
<div class="bot-row">
|
||||
<div class="layui-form-item"><label class="layui-form-label">开火间隔Min(秒)</label><div class="layui-input-inline"><input type="number" name="aggFireIntervalMin" class="layui-input" step="0.1"></div></div>
|
||||
<div class="layui-form-item"><label class="layui-form-label">开火间隔Max(秒)</label><div class="layui-input-inline"><input type="number" name="aggFireIntervalMax" class="layui-input" step="0.1"></div></div>
|
||||
<div class="layui-form-item"><label class="layui-form-label">开火概率</label><div class="layui-input-inline"><input type="number" name="aggFireProb" class="layui-input" step="0.01"></div></div>
|
||||
</div>
|
||||
<div class="bot-row">
|
||||
<div class="layui-form-item"><label class="layui-form-label">旋转速度(度/秒)</label><div class="layui-input-inline"><input type="number" name="aggRotateSpeed" class="layui-input" step="1"></div></div>
|
||||
<div class="layui-form-item"><label class="layui-form-label">目标保持Min(秒)</label><div class="layui-input-inline"><input type="number" name="aggTargetHoldTimeMin" class="layui-input" step="0.1"></div></div>
|
||||
<div class="layui-form-item"><label class="layui-form-label">目标保持Max(秒)</label><div class="layui-input-inline"><input type="number" name="aggTargetHoldTimeMax" class="layui-input" step="0.1"></div></div>
|
||||
</div>
|
||||
<div class="bot-row">
|
||||
<div class="layui-form-item"><label class="layui-form-label">切换目标概率</label><div class="layui-input-inline"><input type="number" name="aggTargetSwitchChance" class="layui-input" step="0.01"></div></div>
|
||||
<div class="layui-form-item"><label class="layui-form-label">高分偏好</label><div class="layui-input-inline"><input type="number" name="aggTargetPreferHighScore" class="layui-input" step="0.1"></div></div>
|
||||
</div>
|
||||
<div class="bot-row">
|
||||
<div class="layui-form-item"><label class="layui-form-label">允许打Boss</label><div class="layui-input-inline"><input type="checkbox" name="aggCanTargetBoss" lay-skin="switch" lay-text="是|否"></div></div>
|
||||
<div class="layui-form-item"><label class="layui-form-label">允许打特殊鱼</label><div class="layui-input-inline"><input type="checkbox" name="aggCanTargetSpecial" lay-skin="switch" lay-text="是|否"></div></div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="layui-elem-field"><legend>倍数策略</legend>
|
||||
<div class="layui-field-box">
|
||||
<div class="bot-row">
|
||||
<div class="layui-form-item"><label class="layui-form-label">初始档位索引</label><div class="layui-input-inline"><input type="number" name="multiplierInitialLevelIndex" class="layui-input"></div><div class="layui-form-mid">对应炮倍列表下标,0=最低档</div></div>
|
||||
<div class="layui-form-item"><label class="layui-form-label">降档阈值</label><div class="layui-input-inline"><input type="number" name="multiplierDecreaseThreshold" class="layui-input" step="0.1"></div><div class="layui-form-mid">当前金币/进场金币 < 此值</div></div>
|
||||
<div class="layui-form-item"><label class="layui-form-label">升档阈值</label><div class="layui-input-inline"><input type="number" name="multiplierIncreaseThreshold" class="layui-input" step="0.1"></div><div class="layui-form-mid">当前金币/进场金币 > 此值</div></div>
|
||||
</div>
|
||||
<div class="bot-row">
|
||||
<div class="layui-form-item"><label class="layui-form-label">调整档位步数</label><div class="layui-input-inline"><input type="number" name="multiplierStep" class="layui-input"></div></div>
|
||||
<div class="layui-form-item"><label class="layui-form-label">检查间隔(秒)</label><div class="layui-input-inline"><input type="number" name="multiplierCheckInterval" class="layui-input" step="0.1"></div></div>
|
||||
<div class="layui-form-item"><label class="layui-form-label">最低档位</label><div class="layui-input-inline"><input type="number" name="multiplierMinLevelIndex" class="layui-input"></div><div class="layui-form-mid">不会降到低于此值</div></div>
|
||||
<div class="layui-form-item"><label class="layui-form-label">最高档位(-1不限)</label><div class="layui-input-inline"><input type="number" name="multiplierMaxLevelIndex" class="layui-input"></div></div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="layui-elem-field"><legend>Boss策略</legend>
|
||||
<div class="layui-field-box">
|
||||
<div class="bot-row">
|
||||
<div class="layui-form-item"><label class="layui-form-label">Boss鱼类型</label><div class="layui-input-inline"><input type="text" name="bossFishTypes" class="layui-input" style="width:120px;" placeholder="2,5"></div><div class="layui-form-mid">FishTypeEnum 的 int 值,逗号分隔</div></div>
|
||||
<div class="layui-form-item"><label class="layui-form-label">无Boss散射</label><div class="layui-input-inline"><input type="checkbox" name="scatterWhenNoBoss" lay-skin="switch" lay-text="开启|关闭"></div></div>
|
||||
<div class="layui-form-item"><label class="layui-form-label">三发概率</label><div class="layui-input-inline"><input type="number" name="tripleShotProb" class="layui-input" step="0.01"></div><div class="layui-form-mid">无Boss时定点三发概率</div></div>
|
||||
</div>
|
||||
<div class="bot-row">
|
||||
<div class="layui-form-item"><label class="layui-form-label">优先最大倍率Boss</label><div class="layui-input-inline"><input type="checkbox" name="preferMaxMultiplierBoss" lay-skin="switch" lay-text="是|否"></div></div>
|
||||
<div class="layui-form-item"><label class="layui-form-label">散射阈值</label><div class="layui-input-inline"><input type="number" name="scatterModeThreshold" class="layui-input"></div><div class="layui-form-mid">Boss数 > 此值开启散射</div></div>
|
||||
<div class="layui-form-item"><label class="layui-form-label">狂暴概率</label><div class="layui-input-inline"><input type="number" name="bossFrenzyProb" class="layui-input" step="0.01"></div><div class="layui-form-mid">Boss数 ≤ 散射阈值时的概率</div></div>
|
||||
</div>
|
||||
<div class="bot-row">
|
||||
<div class="layui-form-item"><label class="layui-form-label">狂暴Min</label><div class="layui-input-inline"><input type="number" name="bossFrenzyMinTime" class="layui-input" step="0.1"></div></div>
|
||||
<div class="layui-form-item"><label class="layui-form-label">狂暴Max</label><div class="layui-input-inline"><input type="number" name="bossFrenzyMaxTime" class="layui-input" step="0.1"></div></div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="layui-elem-field"><legend>退出条件</legend>
|
||||
<div class="layui-field-box">
|
||||
<div class="bot-row">
|
||||
<div class="layui-form-item"><label class="layui-form-label">盈利退出(金币)</label><div class="layui-input-inline"><input type="number" name="exitWhenGoldAbove" class="layui-input"></div><div class="layui-form-mid">超过此值时退出(盈利离场),0=不检测</div></div>
|
||||
<div class="layui-form-item"><label class="layui-form-label">亏损退出(金币)</label><div class="layui-input-inline"><input type="number" name="exitWhenGoldBelow" class="layui-input"></div><div class="layui-form-mid">低于此值时退出(输完离场)</div></div>
|
||||
<div class="layui-form-item"><label class="layui-form-label">检查间隔(秒)</label><div class="layui-input-inline"><input type="number" name="exitCheckInterval" class="layui-input" step="0.1"></div></div>
|
||||
</div>
|
||||
<div class="bot-row">
|
||||
<div class="layui-form-item"><label class="layui-form-label">发送离房消息</label><div class="layui-input-inline"><input type="checkbox" name="sendLeaveRoom" lay-skin="switch" lay-text="是|否"></div></div>
|
||||
<div class="layui-form-item"><label class="layui-form-label">退出后显示等待</label><div class="layui-input-inline"><input type="checkbox" name="showWaitingAfterExit" lay-skin="switch" lay-text="是|否"></div></div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
window.saveSection=function(n){layui.admin.req({method:'get',url:(layui.setter.baseurl||'/')+'adminapi/GameConfig/SaveSection/'+n,done:function(r){layui.layer.msg(r.info||(r.code===0?'保存成功':'保存失败'))}})};
|
||||
</script>
|
||||
<script>
|
||||
layui.use(['admin', 'form', 'layer'], function () {
|
||||
var $ = layui.$,
|
||||
admin = layui.admin,
|
||||
form = layui.form,
|
||||
layer = layui.layer,
|
||||
setter = layui.setter;
|
||||
|
||||
var gameConfig = null;
|
||||
setTimeout(function() { $('.layui-layer-shade').remove(); layer.closeAll(); }, 100);
|
||||
|
||||
function loadConfig() {
|
||||
admin.req({
|
||||
method: "get",
|
||||
url: setter.baseurl + "adminapi/GameConfig",
|
||||
done: function (res) {
|
||||
if (res.code === 0 && res.data) { gameConfig = res.data; fillForm(); }
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function bc() { if (!gameConfig.botConfig) gameConfig.botConfig = {}; return gameConfig.botConfig; }
|
||||
|
||||
function val(name, def) { var v = $('#form-botai input[name="'+name+'"]').val(); return v !== undefined && v !== '' ? v : def; }
|
||||
function chk(name) { return $('#form-botai input[name="'+name+'"]').prop('checked'); }
|
||||
function num(name, def) { return parseFloat(val(name, def)) || def; }
|
||||
function intv(name, def) { return parseInt(val(name, def)) || def; }
|
||||
|
||||
function fillBehavior(prefix, data) {
|
||||
if (!data) return;
|
||||
$('#form-botai input[name="'+prefix+'FireIntervalMin"]').val(data.fireIntervalMin ?? 0.3);
|
||||
$('#form-botai input[name="'+prefix+'FireIntervalMax"]').val(data.fireIntervalMax ?? 1.2);
|
||||
$('#form-botai input[name="'+prefix+'FireProb"]').val(data.fireProb ?? 0.9);
|
||||
$('#form-botai input[name="'+prefix+'RotateSpeed"]').val(data.rotateSpeed ?? 80);
|
||||
$('#form-botai input[name="'+prefix+'TargetHoldTimeMin"]').val(data.targetHoldTimeMin ?? 2);
|
||||
$('#form-botai input[name="'+prefix+'TargetHoldTimeMax"]').val(data.targetHoldTimeMax ?? 5);
|
||||
$('#form-botai input[name="'+prefix+'TargetSwitchChance"]').val(data.targetSwitchChance ?? 0.7);
|
||||
$('#form-botai input[name="'+prefix+'TargetPreferHighScore"]').val(data.targetPreferHighScore ?? 0);
|
||||
$('#form-botai input[name="'+prefix+'CanTargetBoss"]').prop('checked', data.canTargetBoss !== false);
|
||||
$('#form-botai input[name="'+prefix+'CanTargetSpecial"]').prop('checked', data.canTargetSpecial !== false);
|
||||
}
|
||||
|
||||
function readBehavior(prefix) {
|
||||
return {
|
||||
fireIntervalMin: num(prefix+'FireIntervalMin', 0.3),
|
||||
fireIntervalMax: num(prefix+'FireIntervalMax', 1.2),
|
||||
fireProb: num(prefix+'FireProb', 0.9),
|
||||
rotateSpeed: num(prefix+'RotateSpeed', 80),
|
||||
targetHoldTimeMin: num(prefix+'TargetHoldTimeMin', 2),
|
||||
targetHoldTimeMax: num(prefix+'TargetHoldTimeMax', 5),
|
||||
targetSwitchChance: num(prefix+'TargetSwitchChance', 0.7),
|
||||
targetPreferHighScore: num(prefix+'TargetPreferHighScore', 0),
|
||||
canTargetBoss: chk(prefix+'CanTargetBoss'),
|
||||
canTargetSpecial: chk(prefix+'CanTargetSpecial')
|
||||
};
|
||||
}
|
||||
|
||||
function fillForm() {
|
||||
var c = bc();
|
||||
$('#form-botai input[name="enabled"]').prop('checked', c.enabled !== false);
|
||||
$('#form-botai input[name="aiState"]').val(c.aiState || 'active');
|
||||
$('#form-botai input[name="luckFactor"]').val(c.luckFactor ?? 1);
|
||||
$('#form-botai input[name="stateRefreshInterval"]').val(c.stateRefreshInterval ?? 0);
|
||||
|
||||
var sk = c.skills || {};
|
||||
$('#form-botai input[name="skillRefreshInterval"]').val(sk.skillRefreshInterval ?? 10);
|
||||
$('#form-botai input[name="lockProb"]').val(sk.lockProb ?? 0);
|
||||
$('#form-botai input[name="lockMinTime"]').val(sk.lockMinTime ?? 5);
|
||||
$('#form-botai input[name="lockMaxTime"]').val(sk.lockMaxTime ?? 20);
|
||||
$('#form-botai input[name="frenzyProb"]').val(sk.frenzyProb ?? 0.3);
|
||||
$('#form-botai input[name="frenzyMinTime"]').val(sk.frenzyMinTime ?? 3);
|
||||
$('#form-botai input[name="frenzyMaxTime"]').val(sk.frenzyMaxTime ?? 8);
|
||||
$('#form-botai input[name="tripleProb"]').val(sk.tripleProb ?? 0.5);
|
||||
$('#form-botai input[name="tripleMinTime"]').val(sk.tripleMinTime ?? 4);
|
||||
$('#form-botai input[name="tripleMaxTime"]').val(sk.tripleMaxTime ?? 12);
|
||||
$('#form-botai input[name="doubleProb"]').val(sk.doubleProb ?? 0);
|
||||
$('#form-botai input[name="doubleMinTime"]').val(sk.doubleMinTime ?? 3);
|
||||
$('#form-botai input[name="doubleMaxTime"]').val(sk.doubleMaxTime ?? 6);
|
||||
|
||||
var mp = c.multiplier || {};
|
||||
$('#form-botai input[name="multiplierInitialLevelIndex"]').val(mp.initialLevelIndex ?? 0);
|
||||
$('#form-botai input[name="multiplierDecreaseThreshold"]').val(mp.decreaseThreshold ?? 0.5);
|
||||
$('#form-botai input[name="multiplierIncreaseThreshold"]').val(mp.increaseThreshold ?? 1.5);
|
||||
$('#form-botai input[name="multiplierStep"]').val(mp.step ?? 1);
|
||||
$('#form-botai input[name="multiplierCheckInterval"]').val(mp.checkInterval ?? 3);
|
||||
$('#form-botai input[name="multiplierMinLevelIndex"]').val(mp.minLevelIndex ?? 0);
|
||||
$('#form-botai input[name="multiplierMaxLevelIndex"]').val(mp.maxLevelIndex ?? -1);
|
||||
|
||||
var bs = c.bossStrategy || {};
|
||||
$('#form-botai input[name="bossFishTypes"]').val((bs.bossFishTypes || [2,5]).join(','));
|
||||
$('#form-botai input[name="scatterWhenNoBoss"]').prop('checked', bs.scatterWhenNoBoss !== false);
|
||||
$('#form-botai input[name="tripleShotProb"]').val(bs.tripleShotProb ?? 0.5);
|
||||
$('#form-botai input[name="preferMaxMultiplierBoss"]').prop('checked', bs.preferMaxMultiplierBoss !== false);
|
||||
$('#form-botai input[name="scatterModeThreshold"]').val(bs.scatterModeThreshold ?? 3);
|
||||
$('#form-botai input[name="bossFrenzyProb"]').val(bs.frenzyProb ?? 0.6);
|
||||
$('#form-botai input[name="bossFrenzyMinTime"]').val(bs.frenzyMinTime ?? 3);
|
||||
$('#form-botai input[name="bossFrenzyMaxTime"]').val(bs.frenzyMaxTime ?? 8);
|
||||
|
||||
var ex = c.exitCondition || {};
|
||||
$('#form-botai input[name="exitWhenGoldAbove"]').val(ex.exitWhenGoldAbove ?? 0);
|
||||
$('#form-botai input[name="exitWhenGoldBelow"]').val(ex.exitWhenGoldBelow ?? 100);
|
||||
$('#form-botai input[name="exitCheckInterval"]').val(ex.checkInterval ?? 5);
|
||||
$('#form-botai input[name="sendLeaveRoom"]').prop('checked', ex.sendLeaveRoom !== false);
|
||||
$('#form-botai input[name="showWaitingAfterExit"]').prop('checked', ex.showWaitingAfterExit !== false);
|
||||
|
||||
fillBehavior('active', c.activeBehavior);
|
||||
fillBehavior('idle', c.idleBehavior);
|
||||
fillBehavior('agg', c.aggressiveBehavior);
|
||||
|
||||
form.render();
|
||||
}
|
||||
|
||||
function collectConfig() {
|
||||
var c = bc();
|
||||
c.enabled = chk('enabled');
|
||||
c.aiState = val('aiState', 'active');
|
||||
c.luckFactor = num('luckFactor', 1);
|
||||
c.stateRefreshInterval = num('stateRefreshInterval', 0);
|
||||
|
||||
c.skills = {
|
||||
skillRefreshInterval: num('skillRefreshInterval', 10),
|
||||
lockProb: num('lockProb', 0), lockMinTime: num('lockMinTime', 5), lockMaxTime: num('lockMaxTime', 20),
|
||||
frenzyProb: num('frenzyProb', 0.3), frenzyMinTime: num('frenzyMinTime', 3), frenzyMaxTime: num('frenzyMaxTime', 8),
|
||||
tripleProb: num('tripleProb', 0.5), tripleMinTime: num('tripleMinTime', 4), tripleMaxTime: num('tripleMaxTime', 12),
|
||||
doubleProb: num('doubleProb', 0), doubleMinTime: num('doubleMinTime', 3), doubleMaxTime: num('doubleMaxTime', 6)
|
||||
};
|
||||
|
||||
c.multiplier = {
|
||||
initialLevelIndex: intv('multiplierInitialLevelIndex', 0),
|
||||
decreaseThreshold: num('multiplierDecreaseThreshold', 0.5),
|
||||
increaseThreshold: num('multiplierIncreaseThreshold', 1.5),
|
||||
step: intv('multiplierStep', 1),
|
||||
checkInterval: num('multiplierCheckInterval', 3),
|
||||
minLevelIndex: intv('multiplierMinLevelIndex', 0),
|
||||
maxLevelIndex: intv('multiplierMaxLevelIndex', -1)
|
||||
};
|
||||
|
||||
var bft = val('bossFishTypes', '2,5').split(',').map(function(s) { return parseInt(s.trim()) || 2; }).filter(function(n) { return n > 0; });
|
||||
c.bossStrategy = {
|
||||
bossFishTypes: bft.length > 0 ? bft : [2, 5],
|
||||
scatterWhenNoBoss: chk('scatterWhenNoBoss'),
|
||||
tripleShotProb: num('tripleShotProb', 0.5),
|
||||
preferMaxMultiplierBoss: chk('preferMaxMultiplierBoss'),
|
||||
scatterModeThreshold: intv('scatterModeThreshold', 3),
|
||||
frenzyProb: num('bossFrenzyProb', 0.6),
|
||||
frenzyMinTime: num('bossFrenzyMinTime', 3),
|
||||
frenzyMaxTime: num('bossFrenzyMaxTime', 8)
|
||||
};
|
||||
|
||||
c.exitCondition = {
|
||||
exitWhenGoldAbove: intv('exitWhenGoldAbove', 0),
|
||||
exitWhenGoldBelow: intv('exitWhenGoldBelow', 100),
|
||||
checkInterval: num('exitCheckInterval', 5),
|
||||
sendLeaveRoom: chk('sendLeaveRoom'),
|
||||
showWaitingAfterExit: chk('showWaitingAfterExit')
|
||||
};
|
||||
|
||||
c.activeBehavior = readBehavior('active');
|
||||
c.idleBehavior = readBehavior('idle');
|
||||
c.aggressiveBehavior = readBehavior('agg');
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
$(document).on('click', '[lay-filter="init-botai"]', function () {
|
||||
layer.confirm('确定要初始化机器人AI配置吗?此操作将覆盖现有数据!', function(idx) {
|
||||
layer.close(idx);
|
||||
admin.req({
|
||||
method: "get",
|
||||
url: setter.baseurl + "adminapi/GameConfig/InitSection/BotConfig",
|
||||
done: function (res) {
|
||||
layer.msg(res.info || (res.code === 0 ? '初始化成功' : '初始化失败'));
|
||||
if (res.code === 0 || res.code === 200) loadConfig();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
$(document).on('click', '[lay-filter="reload-botai"]', function () { loadConfig(); layer.msg('刷新成功'); });
|
||||
|
||||
$(document).on('click', '[lay-filter="save-botai"]', function () {
|
||||
collectConfig();
|
||||
admin.req({
|
||||
method: "post",
|
||||
contentType: "application/json;charset=UTF-8",
|
||||
url: setter.baseurl + "adminapi/GameConfig/Update",
|
||||
data: JSON.stringify(gameConfig),
|
||||
done: function (res) {
|
||||
if (res.code === 0 || res.code === 200) { layer.msg('保存成功'); loadConfig(); }
|
||||
else { layer.msg(res.info || '保存失败'); }
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
loadConfig();
|
||||
});
|
||||
</script>
|
||||