271 lines
9.4 KiB
C#
271 lines
9.4 KiB
C#
using Microsoft.Extensions.Logging;
|
|
using XiangYi.Application.DTOs.Requests;
|
|
using XiangYi.Application.DTOs.Responses;
|
|
using XiangYi.Application.Interfaces;
|
|
using XiangYi.Core.Constants;
|
|
using XiangYi.Core.Entities.Biz;
|
|
using XiangYi.Core.Exceptions;
|
|
using XiangYi.Core.Interfaces;
|
|
|
|
namespace XiangYi.Application.Services;
|
|
|
|
/// <summary>
|
|
/// 后台会员管理服务实现
|
|
/// </summary>
|
|
public class AdminMemberService : IAdminMemberService
|
|
{
|
|
private readonly IRepository<Member> _memberRepository;
|
|
private readonly IRepository<MemberFamily> _memberFamilyRepository;
|
|
private readonly IRepository<User> _userRepository;
|
|
private readonly IRepository<Order> _orderRepository;
|
|
private readonly ILogger<AdminMemberService> _logger;
|
|
|
|
/// <summary>
|
|
/// 会员等级名称
|
|
/// </summary>
|
|
private static readonly Dictionary<int, string> MemberLevelNames = new()
|
|
{
|
|
{ 1, "不限时会员" },
|
|
{ 2, "诚意会员" },
|
|
{ 3, "家庭版会员" },
|
|
{ 4, "限时会员" }
|
|
};
|
|
|
|
/// <summary>
|
|
/// 会员状态名称
|
|
/// </summary>
|
|
private static readonly Dictionary<int, string> MemberStatusNames = new()
|
|
{
|
|
{ 1, "生效中" },
|
|
{ 2, "已过期" }
|
|
};
|
|
|
|
public AdminMemberService(
|
|
IRepository<Member> memberRepository,
|
|
IRepository<MemberFamily> memberFamilyRepository,
|
|
IRepository<User> userRepository,
|
|
IRepository<Order> orderRepository,
|
|
ILogger<AdminMemberService> logger)
|
|
{
|
|
_memberRepository = memberRepository;
|
|
_memberFamilyRepository = memberFamilyRepository;
|
|
_userRepository = userRepository;
|
|
_orderRepository = orderRepository;
|
|
_logger = logger;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public async Task<PagedResult<AdminMemberListDto>> GetMemberListAsync(AdminMemberQueryRequest request)
|
|
{
|
|
// Get all members
|
|
var members = await _memberRepository.GetAllAsync();
|
|
var query = members.AsQueryable();
|
|
|
|
// Filter by member level
|
|
if (request.MemberLevel.HasValue)
|
|
{
|
|
query = query.Where(m => m.MemberLevel == request.MemberLevel.Value);
|
|
}
|
|
|
|
// Filter by status
|
|
if (request.Status.HasValue)
|
|
{
|
|
query = query.Where(m => m.Status == request.Status.Value);
|
|
}
|
|
|
|
// Filter by start time range
|
|
if (request.StartTimeBegin.HasValue)
|
|
{
|
|
query = query.Where(m => m.StartTime >= request.StartTimeBegin.Value);
|
|
}
|
|
if (request.StartTimeEnd.HasValue)
|
|
{
|
|
var endTime = request.StartTimeEnd.Value.Date.AddDays(1);
|
|
query = query.Where(m => m.StartTime < endTime);
|
|
}
|
|
|
|
// Get user IDs for keyword search
|
|
var memberList = query.ToList();
|
|
var userIds = memberList.Select(m => m.UserId).Distinct().ToList();
|
|
var users = await _userRepository.GetListAsync(u => userIds.Contains(u.Id));
|
|
var userDict = users.ToDictionary(u => u.Id);
|
|
|
|
// Filter by keyword (XiangQinNo, Nickname, Phone)
|
|
if (!string.IsNullOrWhiteSpace(request.Keyword))
|
|
{
|
|
var keyword = request.Keyword.Trim();
|
|
var matchedUserIds = users
|
|
.Where(u =>
|
|
(u.XiangQinNo != null && u.XiangQinNo.Contains(keyword)) ||
|
|
(u.Nickname != null && u.Nickname.Contains(keyword)) ||
|
|
(u.Phone != null && u.Phone.Contains(keyword)))
|
|
.Select(u => u.Id)
|
|
.ToHashSet();
|
|
memberList = memberList.Where(m => matchedUserIds.Contains(m.UserId)).ToList();
|
|
}
|
|
|
|
var total = memberList.Count;
|
|
|
|
// Order by CreateTime descending and paginate
|
|
var items = memberList
|
|
.OrderByDescending(m => m.CreateTime)
|
|
.Skip((request.PageIndex - 1) * request.PageSize)
|
|
.Take(request.PageSize)
|
|
.ToList();
|
|
|
|
// Get order IDs for order info lookup
|
|
var orderIds = items.Select(m => m.OrderId).Distinct().ToList();
|
|
var orders = await _orderRepository.GetListAsync(o => orderIds.Contains(o.Id));
|
|
var orderDict = orders.ToDictionary(o => o.Id);
|
|
|
|
// Get family bind counts for family members
|
|
var memberIds = items.Where(m => m.MemberLevel == 3).Select(m => m.Id).ToList();
|
|
var familyBinds = await _memberFamilyRepository.GetListAsync(f => memberIds.Contains(f.MemberId));
|
|
var familyBindCountDict = familyBinds.GroupBy(f => f.MemberId).ToDictionary(g => g.Key, g => g.Count());
|
|
|
|
// Map to DTOs - 过滤掉没有关联用户的会员记录
|
|
var dtos = items
|
|
.Where(m => userDict.ContainsKey(m.UserId)) // 只保留有关联用户的会员
|
|
.Select(m =>
|
|
{
|
|
userDict.TryGetValue(m.UserId, out var user);
|
|
orderDict.TryGetValue(m.OrderId, out var order);
|
|
familyBindCountDict.TryGetValue(m.Id, out var familyBindCount);
|
|
return MapToMemberListDto(m, user, order, familyBindCount);
|
|
}).ToList();
|
|
|
|
return new PagedResult<AdminMemberListDto>
|
|
{
|
|
Items = dtos,
|
|
Total = total,
|
|
PageIndex = request.PageIndex,
|
|
PageSize = request.PageSize
|
|
};
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public async Task<AdminMemberDetailDto> GetMemberDetailAsync(long memberId)
|
|
{
|
|
var member = await _memberRepository.GetByIdAsync(memberId);
|
|
if (member == null)
|
|
{
|
|
throw new BusinessException(ErrorCodes.UserNotFound, "会员记录不存在");
|
|
}
|
|
|
|
// Get user info
|
|
var user = await _userRepository.GetByIdAsync(member.UserId);
|
|
|
|
// Get order info
|
|
var order = await _orderRepository.GetByIdAsync(member.OrderId);
|
|
|
|
// Get family members if family version
|
|
List<AdminFamilyMemberDto>? familyMembers = null;
|
|
if (member.MemberLevel == 3)
|
|
{
|
|
var familyBinds = await _memberFamilyRepository.GetListAsync(f => f.MemberId == memberId);
|
|
if (familyBinds.Any())
|
|
{
|
|
var bindUserIds = familyBinds.Select(f => f.BindUserId).ToList();
|
|
var bindUsers = await _userRepository.GetListAsync(u => bindUserIds.Contains(u.Id));
|
|
var bindUserDict = bindUsers.ToDictionary(u => u.Id);
|
|
|
|
familyMembers = familyBinds.Select(f =>
|
|
{
|
|
bindUserDict.TryGetValue(f.BindUserId, out var bindUser);
|
|
return new AdminFamilyMemberDto
|
|
{
|
|
BindId = f.Id,
|
|
UserId = f.BindUserId,
|
|
XiangQinNo = bindUser?.XiangQinNo,
|
|
Nickname = bindUser?.Nickname,
|
|
Avatar = bindUser?.Avatar,
|
|
BindTime = f.CreateTime
|
|
};
|
|
}).ToList();
|
|
}
|
|
}
|
|
|
|
return MapToMemberDetailDto(member, user, order, familyMembers);
|
|
}
|
|
|
|
#region Private Helper Methods
|
|
|
|
private static AdminMemberListDto MapToMemberListDto(Member member, User? user, Order? order, int familyBindCount)
|
|
{
|
|
return new AdminMemberListDto
|
|
{
|
|
MemberId = member.Id,
|
|
UserId = member.UserId,
|
|
XiangQinNo = user?.XiangQinNo,
|
|
Nickname = user?.Nickname,
|
|
Avatar = user?.Avatar,
|
|
Phone = MaskPhone(user?.Phone),
|
|
MemberLevel = member.MemberLevel,
|
|
MemberLevelText = GetMemberLevelText(member.MemberLevel),
|
|
OrderId = member.OrderId,
|
|
OrderNo = order?.OrderNo,
|
|
StartTime = member.StartTime,
|
|
ExpireTime = member.ExpireTime,
|
|
Status = member.Status,
|
|
StatusText = GetMemberStatusText(member.Status),
|
|
FamilyBindCount = familyBindCount,
|
|
CreateTime = member.CreateTime
|
|
};
|
|
}
|
|
|
|
private static AdminMemberDetailDto MapToMemberDetailDto(
|
|
Member member,
|
|
User? user,
|
|
Order? order,
|
|
List<AdminFamilyMemberDto>? familyMembers)
|
|
{
|
|
return new AdminMemberDetailDto
|
|
{
|
|
MemberId = member.Id,
|
|
UserId = member.UserId,
|
|
XiangQinNo = user?.XiangQinNo,
|
|
Nickname = user?.Nickname,
|
|
Avatar = user?.Avatar,
|
|
Phone = MaskPhone(user?.Phone),
|
|
MemberLevel = member.MemberLevel,
|
|
MemberLevelText = GetMemberLevelText(member.MemberLevel),
|
|
OrderId = member.OrderId,
|
|
OrderNo = order?.OrderNo,
|
|
OrderAmount = order?.PayAmount,
|
|
PayTime = order?.PayTime,
|
|
StartTime = member.StartTime,
|
|
ExpireTime = member.ExpireTime,
|
|
Status = member.Status,
|
|
StatusText = GetMemberStatusText(member.Status),
|
|
FamilyMembers = familyMembers,
|
|
CreateTime = member.CreateTime
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// 手机号脱敏
|
|
/// </summary>
|
|
private static string? MaskPhone(string? phone)
|
|
{
|
|
return phone;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取会员等级文本
|
|
/// </summary>
|
|
private static string GetMemberLevelText(int memberLevel)
|
|
{
|
|
return MemberLevelNames.TryGetValue(memberLevel, out var name) ? name : "未知";
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取会员状态文本
|
|
/// </summary>
|
|
private static string GetMemberStatusText(int status)
|
|
{
|
|
return MemberStatusNames.TryGetValue(status, out var name) ? name : "未知";
|
|
}
|
|
|
|
#endregion
|
|
}
|