HaniBlindBox/server/HoneyBox/tests/HoneyBox.Tests/Services/UserManagementFrontendPropertyTests.cs
2026-01-17 17:48:43 +08:00

737 lines
27 KiB
C#

using FsCheck;
using FsCheck.Xunit;
using HoneyBox.Admin.Business.Models;
using HoneyBox.Admin.Business.Models.User;
using HoneyBox.Admin.Business.Services;
using HoneyBox.Model.Data;
using HoneyBox.Model.Entities;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.Extensions.Logging;
using Moq;
using Xunit;
namespace HoneyBox.Tests.Services;
/// <summary>
/// 用户管理前端模块属性测试
/// Feature: user-management-frontend
/// </summary>
public class UserManagementFrontendPropertyTests
{
private readonly Mock<ILogger<UserBusinessService>> _mockLogger = new();
#region Property 1:
/// <summary>
/// **Feature: user-management-frontend, Property 1: 搜索参数正确传递**
/// For any user list search request, when the admin inputs search conditions,
/// the API call query parameters should exactly match the user input search conditions.
/// **Validates: Requirements 1.2**
/// </summary>
[Property(MaxTest = 100)]
public bool SearchParameters_ShouldFilterCorrectly_ByNickname(PositiveInt seed)
{
var nicknames = new[] { "Alice", "Bob", "Charlie", "David", "Eve" };
var searchNickname = nicknames[seed.Get % nicknames.Length];
using var dbContext = CreateDbContext();
var service = new UserBusinessService(dbContext, _mockLogger.Object);
// Create users with different nicknames
foreach (var name in nicknames)
{
dbContext.Users.Add(CreateTestUser(name));
dbContext.Users.Add(CreateTestUser($"{name}Smith"));
}
dbContext.SaveChanges();
var request = new UserListRequest { Nickname = searchNickname };
var result = service.GetUserListAsync(request).GetAwaiter().GetResult();
// All returned users should contain the search nickname
return result.List.All(u => u.Nickname != null && u.Nickname.Contains(searchNickname));
}
/// <summary>
/// **Feature: user-management-frontend, Property 1: 搜索参数正确传递**
/// For any user list search request with mobile filter,
/// all returned users should have matching mobile numbers.
/// **Validates: Requirements 1.2**
/// </summary>
[Property(MaxTest = 100)]
public bool SearchParameters_ShouldFilterCorrectly_ByMobile(PositiveInt seed)
{
var mobilePrefix = $"138{(seed.Get % 100):D2}";
using var dbContext = CreateDbContext();
var service = new UserBusinessService(dbContext, _mockLogger.Object);
// Create users with different mobile numbers
for (int i = 0; i < 10; i++)
{
var user = CreateTestUser($"User{i}");
user.Mobile = i < 5 ? $"{mobilePrefix}{i:D6}" : $"139{i:D8}";
dbContext.Users.Add(user);
}
dbContext.SaveChanges();
var request = new UserListRequest { Mobile = mobilePrefix };
var result = service.GetUserListAsync(request).GetAwaiter().GetResult();
// All returned users should have mobile containing the prefix
return result.List.All(u => u.Mobile != null && u.Mobile.Contains(mobilePrefix));
}
/// <summary>
/// **Feature: user-management-frontend, Property 1: 搜索参数正确传递**
/// For any user list search request with parent ID filter,
/// all returned users should have the specified parent ID.
/// **Validates: Requirements 1.2**
/// </summary>
[Property(MaxTest = 100)]
public bool SearchParameters_ShouldFilterCorrectly_ByParentId(PositiveInt seed)
{
using var dbContext = CreateDbContext();
var service = new UserBusinessService(dbContext, _mockLogger.Object);
// Create parent user
var parent = CreateTestUser("Parent");
dbContext.Users.Add(parent);
dbContext.SaveChanges();
// Create subordinate users
var subordinateCount = (seed.Get % 5) + 1;
for (int i = 0; i < subordinateCount; i++)
{
var child = CreateTestUser($"Child{i}");
child.Pid = parent.Id;
dbContext.Users.Add(child);
}
// Create other users without parent
for (int i = 0; i < 3; i++)
{
dbContext.Users.Add(CreateTestUser($"Other{i}"));
}
dbContext.SaveChanges();
var request = new UserListRequest { ParentId = parent.Id };
var result = service.GetUserListAsync(request).GetAwaiter().GetResult();
// All returned users should have the specified parent ID
return result.Total == subordinateCount &&
result.List.All(u => u.ParentId == parent.Id);
}
#endregion
#region Property 2:
/// <summary>
/// **Feature: user-management-frontend, Property 2: 分页参数正确传递**
/// For any pagination request, the returned list should have at most pageSize items,
/// and the page and pageSize in response should match the request.
/// **Validates: Requirements 1.4**
/// </summary>
[Property(MaxTest = 100)]
public bool PaginationParameters_ShouldReturnCorrectPageSize(PositiveInt seed)
{
var userCount = (seed.Get % 30) + 10; // 10 to 39 users
var pageSize = (seed.Get % 10) + 1; // 1 to 10 per page
var page = (seed.Get % 5) + 1; // page 1 to 5
using var dbContext = CreateDbContext();
var service = new UserBusinessService(dbContext, _mockLogger.Object);
// Create test users
for (int i = 0; i < userCount; i++)
{
dbContext.Users.Add(CreateTestUser($"User{i}"));
}
dbContext.SaveChanges();
var request = new UserListRequest { Page = page, PageSize = pageSize };
var result = service.GetUserListAsync(request).GetAwaiter().GetResult();
// Verify pagination parameters are correctly passed
return result.Total == userCount &&
result.List.Count <= pageSize &&
result.Page == page &&
result.PageSize == pageSize;
}
/// <summary>
/// **Feature: user-management-frontend, Property 2: 分页参数正确传递**
/// The total count should remain consistent regardless of which page is requested.
/// **Validates: Requirements 1.4**
/// </summary>
[Property(MaxTest = 100)]
public bool PaginationParameters_TotalShouldBeConsistentAcrossPages(PositiveInt seed)
{
var userCount = (seed.Get % 20) + 15; // 15 to 34 users
var pageSize = 5;
using var dbContext = CreateDbContext();
var service = new UserBusinessService(dbContext, _mockLogger.Object);
// Create test users
for (int i = 0; i < userCount; i++)
{
dbContext.Users.Add(CreateTestUser($"User{i}"));
}
dbContext.SaveChanges();
// Get multiple pages
var page1 = service.GetUserListAsync(new UserListRequest { Page = 1, PageSize = pageSize }).GetAwaiter().GetResult();
var page2 = service.GetUserListAsync(new UserListRequest { Page = 2, PageSize = pageSize }).GetAwaiter().GetResult();
var page3 = service.GetUserListAsync(new UserListRequest { Page = 3, PageSize = pageSize }).GetAwaiter().GetResult();
// Total should be consistent across all pages
return page1.Total == page2.Total &&
page2.Total == page3.Total &&
page1.Total == userCount;
}
/// <summary>
/// **Feature: user-management-frontend, Property 2: 分页参数正确传递**
/// Different pages should return different users (no overlap).
/// **Validates: Requirements 1.4**
/// </summary>
[Property(MaxTest = 100)]
public bool PaginationParameters_DifferentPagesShouldNotOverlap(PositiveInt seed)
{
var userCount = (seed.Get % 15) + 20; // 20 to 34 users
var pageSize = 5;
using var dbContext = CreateDbContext();
var service = new UserBusinessService(dbContext, _mockLogger.Object);
// Create test users
for (int i = 0; i < userCount; i++)
{
dbContext.Users.Add(CreateTestUser($"User{i}"));
}
dbContext.SaveChanges();
// Get first two pages
var page1 = service.GetUserListAsync(new UserListRequest { Page = 1, PageSize = pageSize }).GetAwaiter().GetResult();
var page2 = service.GetUserListAsync(new UserListRequest { Page = 2, PageSize = pageSize }).GetAwaiter().GetResult();
// User IDs should not overlap between pages
var page1Ids = page1.List.Select(u => u.Id).ToHashSet();
var page2Ids = page2.List.Select(u => u.Id).ToHashSet();
return !page1Ids.Overlaps(page2Ids);
}
#endregion
#region Property 3:
/// <summary>
/// **Feature: user-management-frontend, Property 3: 资金变动参数验证**
/// When operation type is "subtract" and amount is greater than user's current balance,
/// the system should throw an exception instead of executing the subtraction.
/// **Validates: Requirements 2.3**
/// </summary>
[Property(MaxTest = 100)]
public bool MoneyChangeValidation_ShouldRejectInsufficientBalance(PositiveInt seed)
{
var initialBalance = (seed.Get % 100) + 10; // 10 to 109
var subtractAmount = initialBalance + (seed.Get % 50) + 1; // Always more than balance
using var dbContext = CreateDbContext();
var service = new UserBusinessService(dbContext, _mockLogger.Object);
// Create test user with limited balance
var user = CreateTestUser("TestUser");
user.Money = initialBalance;
dbContext.Users.Add(user);
dbContext.SaveChanges();
var request = new UserMoneyChangeRequest
{
Type = MoneyChangeType.Balance,
Amount = subtractAmount,
Operation = OperationType.Subtract,
Remark = "Property test - insufficient balance"
};
try
{
service.ChangeUserMoneyAsync(user.Id, request, 1).GetAwaiter().GetResult();
return false; // Should have thrown exception
}
catch (BusinessException ex)
{
// Verify user balance was not changed
var updatedUser = dbContext.Users.Find(user.Id);
return ex.Message.Contains("余额不足") && updatedUser!.Money == initialBalance;
}
}
/// <summary>
/// **Feature: user-management-frontend, Property 3: 资金变动参数验证**
/// When operation type is "add", the balance should increase by exactly the specified amount.
/// **Validates: Requirements 2.3**
/// </summary>
[Property(MaxTest = 100)]
public bool MoneyChangeValidation_AddShouldIncreaseBalanceExactly(PositiveInt seed)
{
var initialBalance = (seed.Get % 500) + 100; // 100 to 599
var addAmount = (seed.Get % 100) + 1; // 1 to 100
using var dbContext = CreateDbContext();
var service = new UserBusinessService(dbContext, _mockLogger.Object);
// Create test user
var user = CreateTestUser("TestUser");
user.Money = initialBalance;
dbContext.Users.Add(user);
dbContext.SaveChanges();
var request = new UserMoneyChangeRequest
{
Type = MoneyChangeType.Balance,
Amount = addAmount,
Operation = OperationType.Add,
Remark = "Property test - add balance"
};
service.ChangeUserMoneyAsync(user.Id, request, 1).GetAwaiter().GetResult();
// Refresh user from database
var updatedUser = dbContext.Users.Find(user.Id);
return updatedUser!.Money == initialBalance + addAmount;
}
/// <summary>
/// **Feature: user-management-frontend, Property 3: 资金变动参数验证**
/// When operation type is "subtract" and amount is less than or equal to balance,
/// the balance should decrease by exactly the specified amount.
/// **Validates: Requirements 2.3**
/// </summary>
[Property(MaxTest = 100)]
public bool MoneyChangeValidation_SubtractShouldDecreaseBalanceExactly(PositiveInt seed)
{
var initialBalance = (seed.Get % 500) + 200; // 200 to 699
var subtractAmount = (seed.Get % 100) + 1; // 1 to 100 (always less than balance)
using var dbContext = CreateDbContext();
var service = new UserBusinessService(dbContext, _mockLogger.Object);
// Create test user
var user = CreateTestUser("TestUser");
user.Money = initialBalance;
dbContext.Users.Add(user);
dbContext.SaveChanges();
var request = new UserMoneyChangeRequest
{
Type = MoneyChangeType.Balance,
Amount = subtractAmount,
Operation = OperationType.Subtract,
Remark = "Property test - subtract balance"
};
service.ChangeUserMoneyAsync(user.Id, request, 1).GetAwaiter().GetResult();
// Refresh user from database
var updatedUser = dbContext.Users.Find(user.Id);
return updatedUser!.Money == initialBalance - subtractAmount;
}
#endregion
#region Property 4:
/// <summary>
/// **Feature: user-management-frontend, Property 4: 用户状态切换一致性**
/// Ban operation should set status to 0, unban operation should set status to 1,
/// and the user list should display the correct status after the operation.
/// **Validates: Requirements 3.1, 3.2**
/// </summary>
[Property(MaxTest = 100)]
public bool UserStatusToggle_BanShouldSetStatusToZero(PositiveInt seed)
{
using var dbContext = CreateDbContext();
var service = new UserBusinessService(dbContext, _mockLogger.Object);
// Create test user with status 1 (active)
var user = CreateTestUser("TestUser");
user.Status = 1;
dbContext.Users.Add(user);
dbContext.SaveChanges();
// Ban the user (set status to 0)
var result = service.SetUserStatusAsync(user.Id, 0).GetAwaiter().GetResult();
if (!result) return false;
// Verify status was set to 0
var updatedUser = dbContext.Users.Find(user.Id);
return updatedUser!.Status == 0;
}
/// <summary>
/// **Feature: user-management-frontend, Property 4: 用户状态切换一致性**
/// Unban operation should set status to 1.
/// **Validates: Requirements 3.1, 3.2**
/// </summary>
[Property(MaxTest = 100)]
public bool UserStatusToggle_UnbanShouldSetStatusToOne(PositiveInt seed)
{
using var dbContext = CreateDbContext();
var service = new UserBusinessService(dbContext, _mockLogger.Object);
// Create test user with status 0 (banned)
var user = CreateTestUser("TestUser");
user.Status = 0;
dbContext.Users.Add(user);
dbContext.SaveChanges();
// Unban the user (set status to 1)
var result = service.SetUserStatusAsync(user.Id, 1).GetAwaiter().GetResult();
if (!result) return false;
// Verify status was set to 1
var updatedUser = dbContext.Users.Find(user.Id);
return updatedUser!.Status == 1;
}
/// <summary>
/// **Feature: user-management-frontend, Property 4: 用户状态切换一致性**
/// Status toggle should be idempotent - setting the same status multiple times
/// should result in the same final state.
/// **Validates: Requirements 3.1, 3.2**
/// </summary>
[Property(MaxTest = 100)]
public bool UserStatusToggle_ShouldBeIdempotent(PositiveInt seed)
{
var targetStatus = seed.Get % 2; // 0 or 1
using var dbContext = CreateDbContext();
var service = new UserBusinessService(dbContext, _mockLogger.Object);
// Create test user
var user = CreateTestUser("TestUser");
user.Status = (byte)(1 - targetStatus); // Start with opposite status
dbContext.Users.Add(user);
dbContext.SaveChanges();
// Set status multiple times
service.SetUserStatusAsync(user.Id, targetStatus).GetAwaiter().GetResult();
service.SetUserStatusAsync(user.Id, targetStatus).GetAwaiter().GetResult();
service.SetUserStatusAsync(user.Id, targetStatus).GetAwaiter().GetResult();
// Verify status is correct
var updatedUser = dbContext.Users.Find(user.Id);
return updatedUser!.Status == targetStatus;
}
/// <summary>
/// **Feature: user-management-frontend, Property 4: 用户状态切换一致性**
/// User list should reflect the correct status after status change.
/// **Validates: Requirements 3.1, 3.2**
/// </summary>
[Property(MaxTest = 100)]
public bool UserStatusToggle_ListShouldReflectCorrectStatus(PositiveInt seed)
{
var targetStatus = seed.Get % 2; // 0 or 1
using var dbContext = CreateDbContext();
var service = new UserBusinessService(dbContext, _mockLogger.Object);
// Create test user
var user = CreateTestUser("TestUser");
user.Status = (byte)(1 - targetStatus);
dbContext.Users.Add(user);
dbContext.SaveChanges();
// Change status
service.SetUserStatusAsync(user.Id, targetStatus).GetAwaiter().GetResult();
// Get user list and verify status
var request = new UserListRequest { UserId = user.Id };
var result = service.GetUserListAsync(request).GetAwaiter().GetResult();
return result.List.Count == 1 && result.List[0].Status == targetStatus;
}
#endregion
#region Property 5:
/// <summary>
/// **Feature: user-management-frontend, Property 5: 盈亏计算正确性**
/// Profit/loss amount should equal: User payment - Shipping amount - Backpack amount - Remaining DaDa coupons,
/// and profit status should correctly display based on whether the amount is positive or negative.
/// **Validates: Requirements 6.4**
/// </summary>
[Property(MaxTest = 100)]
public bool ProfitLossCalculation_ShouldBeCorrect(PositiveInt seed)
{
// Generate test data
var useMoney = (seed.Get % 1000) + 100m; // 100 to 1099
var fhMoney = (seed.Get % 500) + 50m; // 50 to 549
var bbMoney = (seed.Get % 300) + 20m; // 20 to 319
var syMoney = (seed.Get % 100) + 10m; // 10 to 109
// Calculate expected profit/loss
var expectedYueMoney = useMoney - fhMoney - bbMoney - syMoney;
var expectedStatus = expectedYueMoney >= 0 ? "盈利" : "亏损";
// Create a ProfitLossItem and verify calculation
var item = new ProfitLossItem
{
UserId = 1,
Uid = "TEST001",
Nickname = "TestUser",
UseMoney = useMoney,
FhMoney = fhMoney,
BbMoney = bbMoney,
SyMoney = syMoney,
YueMoney = expectedYueMoney,
ProfitStatus = expectedStatus
};
// Verify the calculation formula
var calculatedYueMoney = item.UseMoney - item.FhMoney - item.BbMoney - item.SyMoney;
var calculatedStatus = calculatedYueMoney >= 0 ? "盈利" : "亏损";
return item.YueMoney == calculatedYueMoney && item.ProfitStatus == calculatedStatus;
}
/// <summary>
/// **Feature: user-management-frontend, Property 5: 盈亏计算正确性**
/// When user payment equals total deductions, profit/loss should be zero and status should be "盈利".
/// **Validates: Requirements 6.4**
/// </summary>
[Property(MaxTest = 100)]
public bool ProfitLossCalculation_ZeroShouldBeProfit(PositiveInt seed)
{
var useMoney = (seed.Get % 1000) + 100m;
var fhMoney = useMoney / 3;
var bbMoney = useMoney / 3;
var syMoney = useMoney - fhMoney - bbMoney; // Make total equal to useMoney
var yueMoney = useMoney - fhMoney - bbMoney - syMoney;
var status = yueMoney >= 0 ? "盈利" : "亏损";
// Zero or positive should be "盈利"
return yueMoney == 0 && status == "盈利";
}
/// <summary>
/// **Feature: user-management-frontend, Property 5: 盈亏计算正确性**
/// When deductions exceed payment, profit/loss should be negative and status should be "亏损".
/// **Validates: Requirements 6.4**
/// </summary>
[Property(MaxTest = 100)]
public bool ProfitLossCalculation_NegativeShouldBeLoss(PositiveInt seed)
{
var useMoney = (seed.Get % 100) + 50m; // 50 to 149
var fhMoney = useMoney + (seed.Get % 50) + 10m; // Always more than useMoney
var bbMoney = 0m;
var syMoney = 0m;
var yueMoney = useMoney - fhMoney - bbMoney - syMoney;
var status = yueMoney >= 0 ? "盈利" : "亏损";
// Negative should be "亏损"
return yueMoney < 0 && status == "亏损";
}
#endregion
#region Property 6: API响应格式一致性
/// <summary>
/// **Feature: user-management-frontend, Property 6: API响应格式一致性**
/// For any backend API response, the response format should conform to the unified ApiResponse structure.
/// **Validates: Requirements 10.1-10.10**
/// </summary>
[Property(MaxTest = 100)]
public bool ApiResponseFormat_PagedResultShouldHaveConsistentStructure(PositiveInt seed)
{
var userCount = (seed.Get % 20) + 5;
var page = (seed.Get % 3) + 1;
var pageSize = (seed.Get % 10) + 5;
using var dbContext = CreateDbContext();
var service = new UserBusinessService(dbContext, _mockLogger.Object);
// Create test users
for (int i = 0; i < userCount; i++)
{
dbContext.Users.Add(CreateTestUser($"User{i}"));
}
dbContext.SaveChanges();
var request = new UserListRequest { Page = page, PageSize = pageSize };
var result = service.GetUserListAsync(request).GetAwaiter().GetResult();
// Verify PagedResult structure
return result != null &&
result.List != null &&
result.Total >= 0 &&
result.Page == page &&
result.PageSize == pageSize &&
result.TotalPages == (int)Math.Ceiling((double)result.Total / result.PageSize);
}
/// <summary>
/// **Feature: user-management-frontend, Property 6: API响应格式一致性**
/// User box API should return consistent PagedResult structure.
/// **Validates: Requirements 10.1**
/// </summary>
[Property(MaxTest = 100)]
public bool ApiResponseFormat_UserBoxShouldHaveConsistentStructure(PositiveInt seed)
{
var page = (seed.Get % 3) + 1;
var pageSize = (seed.Get % 10) + 5;
using var dbContext = CreateDbContext();
var service = new UserBusinessService(dbContext, _mockLogger.Object);
// Create test user
var user = CreateTestUser("TestUser");
dbContext.Users.Add(user);
dbContext.SaveChanges();
var query = new UserBoxQuery { Page = page, PageSize = pageSize };
var result = service.GetUserBoxAsync(user.Id, query).GetAwaiter().GetResult();
// Verify PagedResult structure
return result != null &&
result.List != null &&
result.Total >= 0 &&
result.Page == page &&
result.PageSize == pageSize;
}
/// <summary>
/// **Feature: user-management-frontend, Property 6: API响应格式一致性**
/// User orders API should return consistent PagedResult structure.
/// **Validates: Requirements 10.2**
/// </summary>
[Property(MaxTest = 100)]
public bool ApiResponseFormat_UserOrdersShouldHaveConsistentStructure(PositiveInt seed)
{
var page = (seed.Get % 3) + 1;
var pageSize = (seed.Get % 10) + 5;
using var dbContext = CreateDbContext();
var service = new UserBusinessService(dbContext, _mockLogger.Object);
// Create test user
var user = CreateTestUser("TestUser");
dbContext.Users.Add(user);
dbContext.SaveChanges();
var query = new UserOrderQuery { Page = page, PageSize = pageSize };
var result = service.GetUserOrdersAsync(user.Id, query).GetAwaiter().GetResult();
// Verify PagedResult structure
return result != null &&
result.List != null &&
result.Total >= 0 &&
result.Page == page &&
result.PageSize == pageSize;
}
/// <summary>
/// **Feature: user-management-frontend, Property 6: API响应格式一致性**
/// Money detail API should return consistent PagedResult structure.
/// **Validates: Requirements 10.3**
/// </summary>
[Property(MaxTest = 100)]
public bool ApiResponseFormat_MoneyDetailShouldHaveConsistentStructure(PositiveInt seed)
{
var page = (seed.Get % 3) + 1;
var pageSize = (seed.Get % 10) + 5;
using var dbContext = CreateDbContext();
var service = new UserBusinessService(dbContext, _mockLogger.Object);
// Create test user
var user = CreateTestUser("TestUser");
dbContext.Users.Add(user);
dbContext.SaveChanges();
var query = new MoneyDetailQuery { Page = page, PageSize = pageSize };
var result = service.GetUserMoneyDetailAsync(user.Id, query).GetAwaiter().GetResult();
// Verify PagedResult structure
return result != null &&
result.List != null &&
result.Total >= 0 &&
result.Page == page &&
result.PageSize == pageSize;
}
/// <summary>
/// **Feature: user-management-frontend, Property 6: API响应格式一致性**
/// Login stats API should return consistent LoginStatsResponse structure.
/// **Validates: Requirements 10.6**
/// </summary>
[Property(MaxTest = 100)]
public bool ApiResponseFormat_LoginStatsShouldHaveConsistentStructure(PositiveInt seed)
{
var types = new[] { "day", "week", "month" };
var type = types[seed.Get % types.Length];
using var dbContext = CreateDbContext();
var service = new UserBusinessService(dbContext, _mockLogger.Object);
var query = new LoginStatsQuery { Type = type };
var result = service.GetLoginStatsAsync(query).GetAwaiter().GetResult();
// Verify LoginStatsResponse structure
return result != null &&
result.Labels != null &&
result.Values != null &&
result.Labels.Count == result.Values.Count &&
result.TotalLogins >= 0;
}
#endregion
#region Helper Methods
private HoneyBoxDbContext CreateDbContext()
{
var options = new DbContextOptionsBuilder<HoneyBoxDbContext>()
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
.ConfigureWarnings(w => w.Ignore(InMemoryEventId.TransactionIgnoredWarning))
.Options;
return new HoneyBoxDbContext(options);
}
private User CreateTestUser(string nickname)
{
return new User
{
OpenId = Guid.NewGuid().ToString("N"),
Uid = $"UID{DateTime.Now.Ticks}{Guid.NewGuid():N}".Substring(0, 20),
Nickname = nickname,
HeadImg = "https://example.com/avatar.png",
Mobile = $"138{new Random().Next(10000000, 99999999)}",
Money = 100,
Integral = 50,
Score = 30,
Status = 1,
IsTest = 0,
Pid = 0,
Vip = 1,
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now
};
}
#endregion
}