1. 新增WxUserName字段到UsersOutput Dto用于存储微信用户名 2. 关联WxUser表查询以获取微信用户名数据 3. 将用户相关的GET接口修改为POST接口,使用FromBody接收参数
322 lines
12 KiB
C#
322 lines
12 KiB
C#
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)
|
||
{
|
||
RefAsync<int> totalNumber = 0;
|
||
var list = await Queryable()
|
||
.LeftJoin<WxUser>((u, w) => u.WxUserId == w.Id && !w.IsDeleted)
|
||
.WhereIF(!string.IsNullOrEmpty(input.WxUserId), (u, w) => u.WxUserId.ToString() == input.WxUserId)
|
||
.OrderBy((u, w) => u.Id, OrderByType.Desc)
|
||
.Select((u, w) => new UsersOutput
|
||
{
|
||
Id = u.Id,
|
||
WxUserId = u.WxUserId.ToString(),
|
||
WxUserName = w.Name,
|
||
Name = u.Name,
|
||
AvatarUrl = u.AvatarUrl,
|
||
Points = u.Points,
|
||
Type = u.Type.ToString(),
|
||
Status = u.Status.ToString(),
|
||
GrowthPoints = u.GrowthPoints,
|
||
UploadDomain = u.UploadDomain
|
||
})
|
||
.ToPageListAsync(input.PageIndex, input.PageSize, totalNumber);
|
||
|
||
var page = new PageListModel<UsersOutput>(list, input.PageIndex, input.PageSize, totalNumber);
|
||
|
||
return BaseResponse<PageListModel<UsersOutput>>.Success(page);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取用户详情(仅基本信息)
|
||
/// </summary>
|
||
public async Task<BaseResponse<UserDetailOutput>> GetDetailAsync(long id)
|
||
{
|
||
var user = await Queryable()
|
||
.LeftJoin<WxUser>((u, w) => u.WxUserId == w.Id && !w.IsDeleted)
|
||
.Where((u, w) => u.Id == id)
|
||
.Select((u, w) => new UsersOutput
|
||
{
|
||
Id = u.Id,
|
||
WxUserId = u.WxUserId.ToString(),
|
||
WxUserName = w.Name,
|
||
Name = u.Name,
|
||
AvatarUrl = u.AvatarUrl,
|
||
Points = u.Points,
|
||
Type = u.Type.ToString(),
|
||
Status = u.Status.ToString(),
|
||
GrowthPoints = u.GrowthPoints,
|
||
UploadDomain = u.UploadDomain
|
||
})
|
||
.FirstAsync();
|
||
if (user == null)
|
||
{
|
||
return BaseResponse<UserDetailOutput>.Fail("用户不存在");
|
||
}
|
||
|
||
return BaseResponse<UserDetailOutput>.Success(new UserDetailOutput
|
||
{
|
||
BasicInfo = user
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// 分页查询用户积分记录
|
||
/// </summary>
|
||
public async Task<BaseResponse<PageListModel<PointsRecordOutput>>> GetUserPointsRecordsAsync(long userId, PointsRecordQueryInput input)
|
||
{
|
||
input.UserId = userId;
|
||
var result = await pointsService.GetPointsRecordsAsync(input);
|
||
return BaseResponse<PageListModel<PointsRecordOutput>>.Success(result);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 分页查询用户签到记录
|
||
/// </summary>
|
||
public async Task<BaseResponse<PageListModel<CheckInRecordOutput>>> GetUserCheckInRecordsAsync(long userId, PageQueryModel input)
|
||
{
|
||
RefAsync<int> total = 0;
|
||
var records = await Context.Queryable<CheckInRecord>()
|
||
.Where(r => r.UserId == userId && !r.IsDeleted)
|
||
.OrderBy(r => r.CheckInDate, OrderByType.Desc)
|
||
.Select(r => new CheckInRecordOutput
|
||
{
|
||
Id = (long)r.Id,
|
||
CheckInDate = r.CheckInDate,
|
||
CreatedAt = r.CreatedAt,
|
||
ConsecutiveDays = r.ConsecutiveDays,
|
||
PointsAwarded = r.PointsAwarded,
|
||
GrowthPointsAwarded = r.GrowthPointsAwarded,
|
||
Type = r.Type.ToString(),
|
||
Status = r.Status.ToString()
|
||
})
|
||
.ToPageListAsync(input.PageIndex, input.PageSize, total);
|
||
|
||
var page = new PageListModel<CheckInRecordOutput>(records, input.PageIndex, input.PageSize, total);
|
||
return BaseResponse<PageListModel<CheckInRecordOutput>>.Success(page);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 分页查询用户补偿任务
|
||
/// </summary>
|
||
public async Task<BaseResponse<PageListModel<CompensationTaskOutput>>> GetUserCompensationTasksAsync(long userId, CompensationTaskQueryInput input)
|
||
{
|
||
RefAsync<int> total = 0;
|
||
var tasks = await Context.Queryable<CompensationTask>()
|
||
.Where(t => t.UserId == userId && !t.IsDeleted)
|
||
.WhereIF(input.Status.HasValue, t => t.Status == (int)input.Status)
|
||
.WhereIF(input.TaskType.HasValue, t => t.TaskType == (int)input.TaskType)
|
||
.WhereIF(!string.IsNullOrEmpty(input.BusinessSource), t => t.BusinessSource == input.BusinessSource)
|
||
.OrderBy(t => t.CreatedAt, OrderByType.Desc)
|
||
.Select(t => new CompensationTaskOutput
|
||
{
|
||
Id = t.Id,
|
||
TaskType = (CompensationTaskTypeEnum)t.TaskType,
|
||
BusinessSource = t.BusinessSource,
|
||
BusinessId = t.BusinessId,
|
||
UserId = t.UserId,
|
||
Payload = t.Payload,
|
||
ErrorMessage = t.ErrorMessage,
|
||
ErrorSource = t.ErrorSource,
|
||
RetryCount = t.RetryCount,
|
||
MaxRetries = t.MaxRetries,
|
||
Status = (CompensationTaskStatusEnum)t.Status,
|
||
ProcessedAt = t.ProcessedAt,
|
||
ScheduledAt = t.ScheduledAt,
|
||
ResultMessage = t.ResultMessage,
|
||
CreatedAt = t.CreatedAt
|
||
})
|
||
.ToPageListAsync(input.PageIndex, input.PageSize, total);
|
||
|
||
var page = new PageListModel<CompensationTaskOutput>(tasks, input.PageIndex, input.PageSize, total);
|
||
return BaseResponse<PageListModel<CompensationTaskOutput>>.Success(page);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 分页查询用户期刊列表(含期刊详情)
|
||
/// </summary>
|
||
public async Task<BaseResponse<PageListModel<UserJournalItemOutput>>> GetUserJournalsAsync(long userId, UserJournalQueryInput input)
|
||
{
|
||
RefAsync<int> total = 0;
|
||
var items = await Context.Queryable<UserJournal>()
|
||
.Where(uj => uj.UserId == userId && !uj.IsDeleted)
|
||
.WhereIF(!string.IsNullOrWhiteSpace(input.Type), uj => uj.Type.ToString() == input.Type)
|
||
.OrderByDescending(uj => uj.CreatedAt)
|
||
.Select(uj => new UserJournalItemOutput
|
||
{
|
||
BindId = uj.Id,
|
||
JournalId = uj.JournalId,
|
||
Type = uj.Type.ToString(),
|
||
Status = uj.Status.ToString(),
|
||
CreatedAt = uj.CreatedAt
|
||
})
|
||
.ToPageListAsync(input.PageIndex, input.PageSize, total);
|
||
|
||
// 填充期刊详情(标题、封面)
|
||
var journalIds = items.Select(i => i.JournalId).Distinct().ToList();
|
||
if (journalIds.Count > 0)
|
||
{
|
||
var journals = await Context.Queryable<Journal>()
|
||
.Where(j => journalIds.Contains(j.Id) && !j.IsDeleted)
|
||
.ToListAsync();
|
||
var journalDict = journals.ToDictionary(j => j.Id);
|
||
foreach (var item in items)
|
||
{
|
||
if (journalDict.TryGetValue(item.JournalId, out var journal))
|
||
{
|
||
item.Name = journal.Name;
|
||
item.CoverImageUrl = journal.Cover;
|
||
}
|
||
}
|
||
}
|
||
|
||
var page = new PageListModel<UserJournalItemOutput>(items, input.PageIndex, input.PageSize, total);
|
||
return BaseResponse<PageListModel<UserJournalItemOutput>>.Success(page);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新用户状态
|
||
/// </summary>
|
||
public async Task<BaseResponse> UpdateStatusAsync(long id)
|
||
{
|
||
var exists = await usersRepository.GetByIdAsync(id);
|
||
if (exists == null)
|
||
{
|
||
return BaseResponse.Fail("用户不存在");
|
||
}
|
||
var statusValue = exists.Status == (int)UserStatusEnum.Active ? UserStatusEnum.Disabled : UserStatusEnum.Active;
|
||
var result = await UpdateAsync(
|
||
u => new Users { Status = (int)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("用户不存在", ResultCode.NOT_FOUND);
|
||
|
||
// 调用积分服务增加积分
|
||
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("用户不存在", ResultCode.NOT_FOUND);
|
||
|
||
// 调用积分服务扣除积分
|
||
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
|
||
};
|
||
}
|
||
}
|