Files
QYZH.InteractiveMagazine/QYZH.InteractiveMagazine.Service/UsersService.cs
glz 78b686f9e5 refactor: 批量新增枚举类型并完成实体、DTO、服务层枚举替换
- 新增30+业务枚举类型覆盖用户、宠物、商城、社区、积分等模块
- 完成实体类、DTO、服务层的字符串枚举替换为强类型枚举
- 修复用户状态枚举名称变更,将Frozen改为Disabled
- 新增批量发布社区消息接口与控制器实现
- 新增操作日志、积分管理、补偿任务相关服务与DTO
2026-06-08 16:57:44 +08:00

290 lines
10 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using QYZH.InteractiveMagazine.IService;
using QYZH.InteractiveMagazine.Models.Common;
using QYZH.InteractiveMagazine.Models.Dto;
using QYZH.InteractiveMagazine.Models.Dto.CheckIn;
using QYZH.InteractiveMagazine.Models.Dto.Compensation;
using QYZH.InteractiveMagazine.Models.Dto.Points;
using QYZH.InteractiveMagazine.Models.Entity;
using QYZH.InteractiveMagazine.Models.Enum;
using QYZH.InteractiveMagazine.Repository;
using SqlSugar;
namespace QYZH.InteractiveMagazine.Service;
public class UsersService(
BaseRepository<Users> usersRepository,
ILogger<UsersService> _logger,
IPointsService pointsService,
ICheckInService checkInService,
ICompensationTaskService compensationTaskService,
IUserJournalService userJournalService,
IOperationLogService operationLogService) : BaseRepository<Users>, IUsersService
{
/// <summary>
/// 分页查询用户列表
/// </summary>
public async Task<BaseResponse<PageListModel<UsersOutput>>> GetListAsync(UsersQueryInput input)
{
var page = Queryable()
.WhereIF(!string.IsNullOrEmpty(input.WxUserId), u => u.OpenId == input.WxUserId)
.OrderBy(u => u.Id, OrderByType.Desc)
.ToPage<Users, UsersOutput>(input);
return BaseResponse<PageListModel<UsersOutput>>.Success(page);
}
/// <summary>
/// 获取用户详情(包含积分记录、签到记录、补偿任务、期刊列表)
/// </summary>
public async Task<BaseResponse<UserDetailOutput>> GetDetailAsync(long id)
{
var user = await GetByIdAsync<UsersOutput>(u => u.Id == id);
if (user == null)
{
return BaseResponse<UserDetailOutput>.Fail("用户不存在");
}
// 并行查询关联数据
var pointsTask = GetPointsRecordsAsync(id);
var checkInTask = GetCheckInRecordsAsync(id);
var compensationTask = GetFailedCompensationTasksAsync(id);
var journalsTask = GetUserJournalsWithDetailAsync(id);
await Task.WhenAll(pointsTask, checkInTask, compensationTask, journalsTask);
return BaseResponse<UserDetailOutput>.Success(new UserDetailOutput
{
BasicInfo = user,
PointsRecords = await pointsTask,
CheckInRecords = await checkInTask,
FailedCompensationTasks = await compensationTask,
Journals = await journalsTask
});
}
/// <summary>
/// 获取用户积分记录最近20条
/// </summary>
private async Task<List<PointsRecordOutput>> GetPointsRecordsAsync(long userId)
{
try
{
var result = await pointsService.GetPointsRecordsAsync(new PointsRecordQueryInput
{
UserId = userId,
PageIndex = 1,
PageSize = 20
});
return result.Result ?? [];
}
catch (Exception ex)
{
_logger.LogWarning(ex, "获取用户积分记录失败UserId: {UserId}", userId);
return [];
}
}
/// <summary>
/// 获取用户签到记录
/// </summary>
private async Task<List<CheckInRecordOutput>> GetCheckInRecordsAsync(long userId)
{
try
{
var info = await checkInService.GetCheckInInfoAsync(userId);
return info.RecentRecords ?? [];
}
catch (Exception ex)
{
_logger.LogWarning(ex, "获取用户签到记录失败UserId: {UserId}", userId);
return [];
}
}
/// <summary>
/// 获取用户失败的补偿任务(需要手动处理)
/// </summary>
private async Task<List<CompensationTaskOutput>> GetFailedCompensationTasksAsync(long userId)
{
try
{
var tasks = await compensationTaskService.GetTasksAsync(new GetCompensationTasksInput
{
Status = CompensationTaskStatusEnum.Failed,
Limit = 50
});
return tasks.Where(t => t.UserId == userId).ToList();
}
catch (Exception ex)
{
_logger.LogWarning(ex, "获取用户失败补偿任务失败UserId: {UserId}", userId);
return [];
}
}
/// <summary>
/// 获取用户拥有的期刊列表(含期刊详情)
/// </summary>
private async Task<List<UserJournalItemOutput>> GetUserJournalsWithDetailAsync(long userId)
{
try
{
var userJournals = await Context.Queryable<UserJournal>()
.Where(uj => uj.UserId == userId && !uj.IsDeleted)
.OrderByDescending(uj => uj.CreatedAt)
.ToListAsync();
var result = new List<UserJournalItemOutput>();
foreach (var uj in userJournals)
{
var journal = await Context.Queryable<Journal>()
.Where(j => j.Id == uj.JournalId && !j.IsDeleted)
.FirstAsync();
if (journal != null)
{
result.Add(new UserJournalItemOutput
{
BindId = uj.Id,
JournalId = uj.JournalId,
JournalTitle = journal.Title,
CoverImageUrl = journal.CoverImageUrl,
Type = uj.Type.ToString(),
Status = uj.Status.ToString(),
CreatedAt = uj.CreatedAt
});
}
}
return result;
}
catch (Exception ex)
{
_logger.LogWarning(ex, "获取用户期刊列表失败UserId: {UserId}", userId);
return [];
}
}
/// <summary>
/// 更新用户状态
/// </summary>
public async Task<BaseResponse> UpdateStatusAsync(long id, UpdateUserStatusInput input)
{
var exists = await Queryable().AnyAsync(u => u.Id == id);
if (!exists)
{
return BaseResponse.Fail("用户不存在");
}
var statusValue = input.Status == 1 ? UserStatusEnum.Active : UserStatusEnum.Disabled;
var result = await UpdateAsync(
u => new Users { Status = statusValue },
u => u.Id == id
);
return result ? BaseResponse.Success() : BaseResponse.Fail("更新失败");
}
/// <summary>
/// 手动增加用户积分
/// </summary>
public async Task<ManualPointsOutput> ManualAddPointsAsync(long userId, ManualAddPointsInput input, long operatorId, string operatorName, string? ipAddress = null)
{
_logger.LogInformation("管理员手动增加积分UserId: {UserId}, Amount: {Amount}, Operator: {Operator}",
userId, input.Amount, operatorName);
// 校验用户是否存在
var user = await Queryable().Where(u => u.Id == userId && !u.IsDeleted).FirstAsync();
if (user == null)
throw new BusinessException("用户不存在", 404);
// 调用积分服务增加积分
var result = await pointsService.AddPointsAsync(new AddPointsInput
{
UserId = userId,
Amount = input.Amount,
ChangeType = PointsChangeTypeEnum.ManualAdjust,
Description = $"管理员手动增加: {input.Reason}",
OperatorName = operatorName
});
// 记录操作日志
var detail = JsonConvert.SerializeObject(new
{
Amount = input.Amount,
Reason = input.Reason,
PreviousBalance = result.PreviousBalance,
NewBalance = result.NewBalance,
RecordId = result.RecordId
});
await operationLogService.LogAsync(
operatorId, operatorName,
OperationLogActionType.ManualAddPoints,
OperationLogTargetType.User,
userId, user.Name, detail, ipAddress);
return new ManualPointsOutput
{
RecordId = result.RecordId,
PreviousBalance = result.PreviousBalance,
NewBalance = result.NewBalance,
ChangeAmount = input.Amount,
OperatorName = operatorName,
OperatedAt = DateTime.Now
};
}
/// <summary>
/// 手动扣除用户积分
/// </summary>
public async Task<ManualPointsOutput> ManualDeductPointsAsync(long userId, ManualDeductPointsInput input, long operatorId, string operatorName, string? ipAddress = null)
{
_logger.LogInformation("管理员手动扣除积分UserId: {UserId}, Amount: {Amount}, Operator: {Operator}",
userId, input.Amount, operatorName);
// 校验用户是否存在
var user = await Queryable().Where(u => u.Id == userId && !u.IsDeleted).FirstAsync();
if (user == null)
throw new BusinessException("用户不存在", 404);
// 调用积分服务扣除积分
var result = await pointsService.DeductPointsAsync(new DeductPointsInput
{
UserId = userId,
Amount = input.Amount,
ChangeType = PointsChangeTypeEnum.ManualAdjust,
Description = $"管理员手动扣除: {input.Reason}",
OperatorName = operatorName
});
// 记录操作日志
var detail = JsonConvert.SerializeObject(new
{
Amount = input.Amount,
Reason = input.Reason,
PreviousBalance = result.PreviousBalance,
NewBalance = result.NewBalance,
RecordId = result.RecordId
});
await operationLogService.LogAsync(
operatorId, operatorName,
OperationLogActionType.ManualDeductPoints,
OperationLogTargetType.User,
userId, user.Name, detail, ipAddress);
return new ManualPointsOutput
{
RecordId = result.RecordId,
PreviousBalance = result.PreviousBalance,
NewBalance = result.NewBalance,
ChangeAmount = -input.Amount,
OperatorName = operatorName,
OperatedAt = DateTime.Now
};
}
}