260 lines
7.9 KiB
C#
260 lines
7.9 KiB
C#
using HoneyBox.Core.Interfaces;
|
||
using HoneyBox.Model.Data;
|
||
using HoneyBox.Model.Models.Invitation;
|
||
using Microsoft.EntityFrameworkCore;
|
||
using Microsoft.Extensions.Logging;
|
||
using System.Text.Json;
|
||
|
||
namespace HoneyBox.Core.Services;
|
||
|
||
/// <summary>
|
||
/// 推荐服务实现
|
||
/// </summary>
|
||
public class InvitationService : IInvitationService
|
||
{
|
||
private readonly HoneyBoxDbContext _dbContext;
|
||
private readonly ILogger<InvitationService> _logger;
|
||
private const int PageSize = 15;
|
||
|
||
public InvitationService(HoneyBoxDbContext dbContext, ILogger<InvitationService> logger)
|
||
{
|
||
_dbContext = dbContext;
|
||
_logger = logger;
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task<InvitationInfoResponse> GetInvitationInfoAsync(int userId, int page)
|
||
{
|
||
// 获取被邀请用户列表(pid = userId 且 status = 1)
|
||
var query = _dbContext.Users
|
||
.Where(u => u.Pid == userId && u.Status == 1)
|
||
.OrderByDescending(u => u.Id);
|
||
|
||
// 获取总数
|
||
var totalCount = await query.CountAsync();
|
||
|
||
// 计算分页
|
||
var totalPages = (int)Math.Ceiling((double)totalCount / PageSize);
|
||
var skip = (page - 1) * PageSize;
|
||
|
||
// 获取当前页数据
|
||
var invitedUsers = await query
|
||
.Skip(skip)
|
||
.Take(PageSize)
|
||
.Select(u => new
|
||
{
|
||
u.Id,
|
||
u.Nickname,
|
||
u.HeadImg,
|
||
u.Pid,
|
||
u.CreatedAt
|
||
})
|
||
.ToListAsync();
|
||
|
||
// 计算每个被邀请用户带来的佣金
|
||
var invitationRecords = new List<InvitationRecordDto>();
|
||
foreach (var user in invitedUsers)
|
||
{
|
||
var commissionMoney = await CalculateCommissionMoneyAsync(userId, user.Id);
|
||
invitationRecords.Add(new InvitationRecordDto
|
||
{
|
||
Id = user.Id,
|
||
Nickname = user.Nickname,
|
||
Headimg = user.HeadImg,
|
||
Pid = user.Pid,
|
||
Addtime = user.CreatedAt.ToString("yyyy-MM-dd HH:mm:ss"),
|
||
CommissionMoney = commissionMoney
|
||
});
|
||
}
|
||
|
||
// 计算总奖励金额
|
||
var totalMoney = await CalculateTotalRewardAsync(userId);
|
||
|
||
// 获取分享配置
|
||
var shareTitle = await GetShareTitleAsync();
|
||
var shareImage = string.Empty; // 海报图片需要单独生成,这里暂时返回空
|
||
|
||
return new InvitationInfoResponse
|
||
{
|
||
ShareTitle = shareTitle,
|
||
ShareImage = shareImage,
|
||
Count = totalCount,
|
||
Money = totalMoney,
|
||
Data = invitationRecords,
|
||
LastPage = totalPages
|
||
};
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task<BindInviteCodeResponse> BindInviteCodeAsync(int userId, string inviteCode)
|
||
{
|
||
// 获取当前用户
|
||
var user = await _dbContext.Users.FindAsync(userId);
|
||
if (user == null)
|
||
{
|
||
return new BindInviteCodeResponse
|
||
{
|
||
Success = false,
|
||
Message = "用户不存在"
|
||
};
|
||
}
|
||
|
||
// 检查是否已绑定推荐人
|
||
if (user.Pid > 0)
|
||
{
|
||
return new BindInviteCodeResponse
|
||
{
|
||
Success = false,
|
||
Message = "您已绑定过邀请码"
|
||
};
|
||
}
|
||
|
||
// 检查是否在注册24小时内
|
||
var registerTime = user.CreatedAt;
|
||
var deadline = registerTime.AddHours(24);
|
||
if (DateTime.Now > deadline)
|
||
{
|
||
return new BindInviteCodeResponse
|
||
{
|
||
Success = false,
|
||
Message = "新用户注册24小时内才可以绑定邀请码"
|
||
};
|
||
}
|
||
|
||
// 检查是否绑定自己
|
||
if (user.Uid == inviteCode)
|
||
{
|
||
return new BindInviteCodeResponse
|
||
{
|
||
Success = false,
|
||
Message = "不能绑定自己"
|
||
};
|
||
}
|
||
|
||
// 查找邀请人
|
||
var inviter = await _dbContext.Users
|
||
.FirstOrDefaultAsync(u => u.Uid == inviteCode);
|
||
|
||
if (inviter == null)
|
||
{
|
||
return new BindInviteCodeResponse
|
||
{
|
||
Success = false,
|
||
Message = "邀请码不存在"
|
||
};
|
||
}
|
||
|
||
// 绑定邀请码
|
||
user.Pid = inviter.Id;
|
||
user.UpdatedAt = DateTime.Now;
|
||
await _dbContext.SaveChangesAsync();
|
||
|
||
_logger.LogInformation("用户 {UserId} 成功绑定邀请码 {InviteCode},推荐人ID: {InviterId}",
|
||
userId, inviteCode, inviter.Id);
|
||
|
||
return new BindInviteCodeResponse
|
||
{
|
||
Success = true,
|
||
Message = "绑定成功"
|
||
};
|
||
}
|
||
|
||
#region 私有方法
|
||
|
||
/// <summary>
|
||
/// 计算某个被邀请用户带来的佣金
|
||
/// 佣金来源:type=5 的 ProfitMoney 和 ProfitIntegral 记录
|
||
/// </summary>
|
||
/// <param name="inviterId">邀请人ID</param>
|
||
/// <param name="invitedUserId">被邀请用户ID</param>
|
||
/// <returns>佣金金额</returns>
|
||
private async Task<decimal> CalculateCommissionMoneyAsync(int inviterId, int invitedUserId)
|
||
{
|
||
// 从 ProfitMoney 获取佣金(type=5, share_uid=被邀请用户ID, change_money>0)
|
||
var moneyCommission = await _dbContext.ProfitMoneys
|
||
.Where(p => p.UserId == inviterId)
|
||
.Where(p => p.Type == 5)
|
||
.Where(p => p.ShareUid == invitedUserId)
|
||
.Where(p => p.ChangeMoney > 0)
|
||
.SumAsync(p => p.ChangeMoney);
|
||
|
||
// 从 ProfitIntegral 获取佣金(type=5, share_uid=被邀请用户ID, change_money>0)
|
||
var integralCommission = await _dbContext.ProfitIntegrals
|
||
.Where(p => p.UserId == inviterId)
|
||
.Where(p => p.Type == 5)
|
||
.Where(p => p.ShareUid == invitedUserId)
|
||
.Where(p => p.ChangeMoney > 0)
|
||
.SumAsync(p => p.ChangeMoney);
|
||
|
||
// 积分转换为金额(除以100)
|
||
if (integralCommission > 0)
|
||
{
|
||
moneyCommission += Math.Round(integralCommission / 100, 2);
|
||
}
|
||
|
||
return moneyCommission;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 计算用户的总推荐奖励金额
|
||
/// </summary>
|
||
/// <param name="userId">用户ID</param>
|
||
/// <returns>总奖励金额</returns>
|
||
private async Task<decimal> CalculateTotalRewardAsync(int userId)
|
||
{
|
||
// 从 ProfitMoney 获取总佣金(type=5, change_money>0)
|
||
var moneyReward = await _dbContext.ProfitMoneys
|
||
.Where(p => p.UserId == userId)
|
||
.Where(p => p.Type == 5)
|
||
.Where(p => p.ChangeMoney > 0)
|
||
.SumAsync(p => p.ChangeMoney);
|
||
|
||
// 从 ProfitIntegral 获取总佣金(type=5, change_money>0)
|
||
var integralReward = await _dbContext.ProfitIntegrals
|
||
.Where(p => p.UserId == userId)
|
||
.Where(p => p.Type == 5)
|
||
.Where(p => p.ChangeMoney > 0)
|
||
.SumAsync(p => p.ChangeMoney);
|
||
|
||
// 积分转换为金额(除以100)
|
||
if (integralReward > 0)
|
||
{
|
||
moneyReward += Math.Round(integralReward / 100, 2);
|
||
}
|
||
|
||
return moneyReward;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取分享标题配置
|
||
/// </summary>
|
||
/// <returns>分享标题</returns>
|
||
private async Task<string> GetShareTitleAsync()
|
||
{
|
||
var config = await _dbContext.Configs
|
||
.FirstOrDefaultAsync(c => c.ConfigKey == "base");
|
||
|
||
if (config?.ConfigValue == null)
|
||
{
|
||
return string.Empty;
|
||
}
|
||
|
||
try
|
||
{
|
||
var jsonDoc = JsonSerializer.Deserialize<JsonElement>(config.ConfigValue);
|
||
if (jsonDoc.TryGetProperty("share_title", out var shareTitle))
|
||
{
|
||
return shareTitle.GetString() ?? string.Empty;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogWarning(ex, "解析分享配置失败");
|
||
}
|
||
|
||
return string.Empty;
|
||
}
|
||
|
||
#endregion
|
||
}
|