Compare commits

...

4 Commits

8 changed files with 44 additions and 372 deletions

View File

@ -62,28 +62,4 @@ public interface IUserAnswerTaskService : IBaseService<JournalPageTaskUserAnswer
/// <param name="userId">用户Id</param> /// <param name="userId">用户Id</param>
/// <returns>批量领取结果</returns> /// <returns>批量领取结果</returns>
Task<BatchClaimPointsOutput> BatchClaimPointsAsync(BatchClaimPointsInput input, long userId); Task<BatchClaimPointsOutput> BatchClaimPointsAsync(BatchClaimPointsInput input, long userId);
/// <summary>
/// 获取杂志列表
/// </summary>
/// <param name="userId">用户Id</param>
/// <param name="queryType">查询类型Bound=已绑定Unbound=未绑定添加书籍用默认是Bound</param>
/// <returns>杂志列表</returns>
Task<List<JournalListOutput>> GetJournalListAsync(long userId, string? queryType);
/// <summary>
/// 添加用户与杂志关联(绑定杂志)
/// </summary>
/// <param name="journalId">杂志ID</param>
/// <param name="userId">用户ID</param>
/// <returns>是否绑定成功已绑定过返回false</returns>
Task<bool> AddUserJournalAsync(long journalId, long userId);
/// <summary>
/// 新增学生
/// </summary>
/// <param name="input">新增学生参数</param>
/// <param name="wxUserId">当前登录用户微信UserIdWxUserId</param>
/// <returns>新增学生结果</returns>
Task<bool> AddStudentAsync(AddStudentInput input, long wxUserId);
} }

View File

@ -1,18 +0,0 @@
namespace QYZH.InteractiveMagazine.Models.Dto.UserAnswerTaskService
{
/// <summary>
/// 新增学生输入
/// </summary>
public class AddStudentInput
{
/// <summary>
/// 昵称
/// </summary>
public string? Name { get; set; }
/// <summary>
/// 头像地址
/// </summary>
public string? AvatarUrl { get; set; }
}
}

View File

@ -1,43 +0,0 @@
namespace QYZH.InteractiveMagazine.Models.Dto.UserAnswerTaskService
{
/// <summary>
/// 新增学生输出
/// </summary>
public class AddStudentOutput
{
/// <summary>
/// 学生Id
/// </summary>
public long Id { get; set; }
/// <summary>
/// 关联微信用户Id
/// </summary>
public long WxUserId { get; set; }
/// <summary>
/// 昵称
/// </summary>
public string? Name { get; set; }
/// <summary>
/// 头像地址
/// </summary>
public string? AvatarUrl { get; set; }
/// <summary>
/// 用户类型
/// </summary>
public string Type { get; set; } = "Normal";
/// <summary>
/// 当前成长值
/// </summary>
public int GrowthPoints { get; set; }
/// <summary>
/// 积分余额
/// </summary>
public int Points { get; set; }
}
}

View File

@ -67,6 +67,11 @@ public class BatchClaimSuccessItem
/// </summary> /// </summary>
public string TaskName { get; set; } public string TaskName { get; set; }
/// <summary>
/// 任务编号(从 JournalPageTask.No 字段按 - 分割取索引2
/// </summary>
public string TaskNo { get; set; }
/// <summary> /// <summary>
/// 积分值 /// 积分值
/// </summary> /// </summary>

View File

@ -30,6 +30,11 @@ public class ClaimTaskPointsOutput
/// </summary> /// </summary>
public string TaskName { get; set; } public string TaskName { get; set; }
/// <summary>
/// 任务编号(从 JournalPageTask.No 字段按 - 分割取索引2
/// </summary>
public string TaskNo { get; set; }
/// <summary> /// <summary>
/// 题目得分(从 JournalPageTaskUserAnswer 表获取JournalPageTaskId == GroupId 的 Score /// 题目得分(从 JournalPageTaskUserAnswer 表获取JournalPageTaskId == GroupId 的 Score
/// </summary> /// </summary>

View File

@ -1,32 +0,0 @@
namespace QYZH.InteractiveMagazine.Models.Dto.UserAnswerTaskService;
/// <summary>
/// 杂志列表输出
/// </summary>
public class JournalListOutput
{
/// <summary>
/// 杂志Id
/// </summary>
public long Id { get; set; }
/// <summary>
/// 杂志名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 杂志标题
/// </summary>
public string Title { get; set; }
/// <summary>
/// 封面
/// </summary>
public string Cover { get; set; }
/// <summary>
/// 是否已绑定
/// </summary>
public bool IsBound { get; set; }
}

View File

@ -1258,6 +1258,22 @@ public class UserAnswerTaskService(
OperatorName = "系统" OperatorName = "系统"
}); });
// ============================================
// 【任务编号计算】
// 从 JournalPageTask.No 字段按 - 分割,取索引为 2 的部分
// No 最多出现索引为 3即最多4段0-3
// 例如:"A-B-C-D" → 分割后 ["A","B","C","D"] → 取索引2 = "C"
// ============================================
string taskNo = string.Empty;
if (!string.IsNullOrEmpty(mainTask.No))
{
var parts = mainTask.No.Split('-');
if (parts.Length >= 3)
{
taskNo = parts[2];
}
}
return new ClaimTaskPointsOutput return new ClaimTaskPointsOutput
{ {
RecordId = addPointsResult.RecordId, RecordId = addPointsResult.RecordId,
@ -1265,6 +1281,7 @@ public class UserAnswerTaskService(
NewBalance = addPointsResult.NewBalance, NewBalance = addPointsResult.NewBalance,
AddedAmount = addPointsResult.AddedAmount, AddedAmount = addPointsResult.AddedAmount,
TaskName = mainTask.Task ?? string.Empty, TaskName = mainTask.Task ?? string.Empty,
TaskNo = !string.IsNullOrWhiteSpace(taskNo) ? "任务:"+ taskNo : string.Empty,
Score = totalScore Score = totalScore
}; };
#endregion #endregion
@ -1484,6 +1501,22 @@ public class UserAnswerTaskService(
// 获取任务名称 // 获取任务名称
var taskName = mainTask.Task ?? string.Empty; var taskName = mainTask.Task ?? string.Empty;
// ============================================
// 【任务编号计算】
// 从 JournalPageTask.No 字段按 - 分割,取索引为 2 的部分
// No 最多出现索引为 3即最多4段0-3
// 例如:"A-B-C-D" → 分割后 ["A","B","C","D"] → 取索引2 = "C"
// ============================================
string taskNo = string.Empty;
if (!string.IsNullOrEmpty(mainTask.No))
{
var parts = mainTask.No.Split('-');
if (parts.Length >= 3)
{
taskNo = parts[2];
}
}
#endregion #endregion
#region 3.7 #region 3.7
@ -1509,6 +1542,7 @@ public class UserAnswerTaskService(
{ {
GroupId = groupId, GroupId = groupId,
TaskName = taskName, TaskName = taskName,
TaskNo = !string.IsNullOrWhiteSpace(taskNo) ? "任务:" + taskNo : string.Empty,
Points = points, Points = points,
GrowthPoints = growthPoints, GrowthPoints = growthPoints,
Score = score Score = score
@ -1536,163 +1570,4 @@ public class UserAnswerTaskService(
return result; return result;
#endregion #endregion
} }
/// <summary>
/// ============================================
/// 方法思路:获取杂志列表
/// 1. 查询所有已归档的杂志Status=Archive
/// 2. 查询用户已绑定的杂志ID列表
/// 3. 根据queryType过滤大小写不敏感
/// - "Bound":只返回已绑定的杂志
/// - "Unbound":只返回未绑定的杂志
/// - 其他:返回空集合
/// 4. 返回每个杂志的ID、名称、标题、封面、是否已绑定
/// ============================================
/// </summary>
/// <param name="userId">用户ID</param>
/// <param name="queryType">查询类型bound/unbound大小写不敏感其他返回空</param>
/// <returns>杂志列表</returns>
public async Task<List<JournalListOutput>> GetJournalListAsync(long userId, string? queryType = "Bound")
{
#region 1.
// 查询所有已归档且未删除的杂志
var allJournals = await journalRepository.Queryable()
.Where(j => !j.IsDeleted && j.Status == (int)JournalStatusEnum.Archive)
.OrderByDescending(j => j.CreatedAt)
.ToListAsync();
#endregion
#region 2.
// 查询用户已绑定的杂志ID列表
var userJournals = await userJournalRepository.Context.Queryable<UserJournal>()
.Where(uj => uj.UserId == userId && !uj.IsDeleted)
.Select(uj => uj.JournalId)
.ToListAsync();
var boundJournalIds = userJournals.ToHashSet();
#endregion
#region 3. queryType过滤
// ============================================
// 【大小写兼容处理 + 合法值校验】
// queryType 只接受两种值(大小写不敏感):
// - "Bound" / "bound" / "BOUND" → 返回已绑定杂志
// - "Unbound" / "unbound" / "UNBOUND" → 返回未绑定杂志
// - 空 / 其他值 → 返回空集合
// ============================================
var normalizedQueryType = queryType?.ToLowerInvariant() ?? string.Empty;
IEnumerable<Journal> filteredJournals;
if (normalizedQueryType == "bound")
{
filteredJournals = allJournals.Where(j => boundJournalIds.Contains(j.Id));
}
else if (normalizedQueryType == "unbound")
{
filteredJournals = allJournals.Where(j => !boundJournalIds.Contains(j.Id));
}
else
{
// 非 Bound/Unbound 类型,返回空集合
filteredJournals = Enumerable.Empty<Journal>();
}
#endregion
#region 4.
var result = filteredJournals.Select(j => new JournalListOutput
{
Id = j.Id,
Name = j.Name ?? string.Empty,
Title = j.Title ?? string.Empty,
Cover = j.Cover ?? string.Empty,
IsBound = boundJournalIds.Contains(j.Id)
}).ToList();
return result;
#endregion
}
/// <summary>
/// ============================================
/// 方法思路:添加用户与杂志关联(绑定杂志)
/// 1. 验证杂志是否存在
/// 2. 检查用户是否已绑定过该杂志
/// - 查询 UserJournal 表条件UserId + JournalId
/// 3. 已绑定过:返回 false
/// 4. 未绑定:插入 UserJournal 记录,返回 true
/// ============================================
/// </summary>
/// <param name="journalId">杂志ID</param>
/// <param name="userId">用户ID</param>
/// <returns>是否绑定成功已绑定过返回false</returns>
public async Task<bool> AddUserJournalAsync(long journalId, long userId)
{
#region 1.
var journal = await journalRepository.Queryable()
.Where(j => j.Id == journalId && !j.IsDeleted)
.FirstAsync();
if (journal == null)
{
throw new BusinessException("杂志不存在", 404);
}
#endregion
#region 2.
var exist = await userJournalRepository.Context.Queryable<UserJournal>()
.Where(uj => uj.UserId == userId && uj.JournalId == journalId && !uj.IsDeleted)
.AnyAsync();
if (exist)
{
// 已绑定过,返回 false
return false;
}
#endregion
#region 3. UserJournal
var userJournal = new UserJournal
{
UserId = userId,
JournalId = journalId,
Type = UserJournalTypeEnum.Subscribe
};
var result = await userJournalRepository.InsertAsync(userJournal);
return result;
#endregion
}
/// <summary>
/// ============================================
/// 方法思路:新增学生
/// 1. 创建新的Users记录
/// 2. WxUserId设置为当前登录人IDwxUserId参数传入
/// 3. 初始化积分和成长值为0
/// 4. 用户类型设置为普通用户Normal
/// 5. 插入数据库并返回结果
/// ============================================
/// </summary>
/// <param name="input">新增学生输入姓名、头像URL</param>
/// <param name="wxUserId">当前登录人ID微信用户ID</param>
/// <returns>是否插入成功</returns>
public async Task<bool> AddStudentAsync(AddStudentInput input, long wxUserId)
{
#region 1.
var student = new Users
{
WxUserId = wxUserId,
Name = input.Name,
AvatarUrl = input.AvatarUrl,
GrowthPoints = 0,
Points = 0,
Type = UsersTypeEnum.Normal,
IsLastOnline = false
};
#endregion
#region 2.
return await usersRepository.InsertAsync(student);
#endregion
}
} }

View File

@ -262,100 +262,4 @@ public class UserAnswerTaskController : WeChatBaseController
return BaseResponse<BatchClaimPointsOutput>.Fail("批量领取积分失败,请稍后重试"); return BaseResponse<BatchClaimPointsOutput>.Fail("批量领取积分失败,请稍后重试");
} }
} }
/// <summary>
/// 获取杂志列表
/// </summary>
/// <param name="queryType">查询类型bound=已绑定unbound=未绑定添加杂志用列表默认是bound</param>
/// <returns>杂志列表</returns>
[HttpGet("journal-list")]
public async Task<BaseResponse<List<JournalListOutput>>> GetJournalListAsync(string? queryType = "bound")
{
try
{
var userId = GetCurrentUserId();
if (userId == 0)
{
return BaseResponse<List<JournalListOutput>>.Fail(ResultCode.DENY, "未获取到用户信息");
}
var result = await _userAnswerTaskService.GetJournalListAsync(userId, queryType);
return Success(result);
}
catch (BusinessException ex)
{
_logger.LogWarning(ex, "查询杂志列表业务异常UserId: {UserId}", GetCurrentUserId());
return BaseResponse<List<JournalListOutput>>.Fail(ex.Message);
}
catch (Exception ex)
{
_logger.LogError(ex, "查询杂志列表失败UserId: {UserId}", GetCurrentUserId());
return BaseResponse<List<JournalListOutput>>.Fail("查询杂志列表失败,请稍后重试");
}
}
/// <summary>
/// 绑定杂志(添加用户与杂志关联)
/// </summary>
/// <param name="journalId">杂志ID</param>
/// <returns>是否绑定成功已绑定过返回false</returns>
[HttpPost("add-user-journal/{journalId:long}")]
public async Task<BaseResponse<bool>> AddUserJournalAsync([Required(ErrorMessage = "参数错误")] long journalId)
{
try
{
var userId = GetCurrentUserId();
if (userId == 0)
{
return BaseResponse<bool>.Fail(ResultCode.DENY, "未获取到用户信息");
}
var result = await _userAnswerTaskService.AddUserJournalAsync(journalId, userId);
return Success(result);
}
catch (BusinessException ex)
{
_logger.LogWarning(ex, "绑定杂志业务异常UserId: {UserId}, JournalId: {JournalId}", GetCurrentUserId(), journalId);
return BaseResponse<bool>.Fail(ex.Message);
}
catch (Exception ex)
{
_logger.LogError(ex, "绑定杂志失败UserId: {UserId}, JournalId: {JournalId}", GetCurrentUserId(), journalId);
return BaseResponse<bool>.Fail("绑定杂志失败,请稍后重试");
}
}
/// <summary>
/// 新增学生
/// </summary>
/// <param name="input">新增学生参数</param>
/// <returns>新增学生结果</returns>
[HttpPost("add-student")]
public async Task<BaseResponse<bool>> AddStudentAsync([FromBody] AddStudentInput input)
{
try
{
var userId = GetCurrentUserId();
if (userId == 0)
{
return BaseResponse<bool>.Fail(ResultCode.DENY, "未获取到用户信息");
}
if (GetCurrentWxUserId() == null)
{
return BaseResponse<bool>.Fail(ResultCode.DENY, "未获取到微信信息");
}
var result = await _userAnswerTaskService.AddStudentAsync(input, GetCurrentWxUserId()!.Value);
return Success(result);
}
catch (BusinessException ex)
{
_logger.LogWarning(ex, "新增学生业务异常UserId: {UserId}", GetCurrentUserId());
return BaseResponse<bool>.Fail(ex.Message);
}
catch (Exception ex)
{
_logger.LogError(ex, "新增学生失败UserId: {UserId}", GetCurrentUserId());
return BaseResponse<bool>.Fail("新增学生失败,请稍后重试");
}
}
} }