HaniBlindBox/server/HoneyBox/src/HoneyBox.Api/Controllers/UserController.cs
2026-01-25 15:44:07 +08:00

575 lines
18 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 System.Security.Claims;
using HoneyBox.Core.Interfaces;
using HoneyBox.Model.Base;
using HoneyBox.Model.Models.Asset;
using HoneyBox.Model.Models.Auth;
using HoneyBox.Model.Models.Vip;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace HoneyBox.Api.Controllers;
/// <summary>
/// 用户控制器 - 处理用户信息、资产和VIP相关功能
/// </summary>
/// <remarks>
/// 提供用户信息查询和更新、资产明细查询、VIP信息查询等功能
/// </remarks>
[ApiController]
[Route("api")]
public class UserController : ControllerBase
{
private readonly IUserService _userService;
private readonly IAuthService _authService;
private readonly IAssetService _assetService;
private readonly IVipService _vipService;
private readonly IQuanYiService _quanYiService;
private readonly ILogger<UserController> _logger;
public UserController(
IUserService userService,
IAuthService authService,
IAssetService assetService,
IVipService vipService,
IQuanYiService quanYiService,
ILogger<UserController> logger)
{
_userService = userService;
_authService = authService;
_assetService = assetService;
_vipService = vipService;
_quanYiService = quanYiService;
_logger = logger;
}
/// <summary>
/// 获取用户简要信息GET方式
/// </summary>
/// <remarks>
/// GET /api/userInfo
///
/// 获取当前登录用户的简要信息直接返回用户数据不嵌套在userinfo对象中
/// 用于前端 getUserInfo() 调用
/// </remarks>
/// <returns>用户信息数据</returns>
[HttpGet("userInfo")]
[Authorize]
[ProducesResponseType(typeof(ApiResponse<UserInfoDto>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ApiResponse<UserInfoDto>), StatusCodes.Status401Unauthorized)]
public async Task<ApiResponse<UserInfoDto>> GetUserInfoSimple()
{
var userId = GetCurrentUserId();
if (userId == null)
{
return ApiResponse<UserInfoDto>.Unauthorized();
}
try
{
var userInfo = await _userService.GetUserInfoAsync(userId.Value);
if (userInfo == null)
{
_logger.LogWarning("User not found: UserId={UserId}", userId);
return ApiResponse<UserInfoDto>.Fail("用户不存在");
}
return ApiResponse<UserInfoDto>.Success(userInfo);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to get user info: UserId={UserId}", userId);
return ApiResponse<UserInfoDto>.Fail("获取用户信息失败");
}
}
/// <summary>
/// 获取用户完整信息POST方式
/// </summary>
/// <remarks>
/// POST /api/user
///
/// 获取当前登录用户的详细信息包含余额、积分、VIP等级等
/// 返回数据嵌套在 userinfo 对象中,用于前端 getUser() 调用
/// Requirements: 4.1-4.5
/// </remarks>
/// <returns>用户信息数据</returns>
[HttpPost("user")]
[Authorize]
[ProducesResponseType(typeof(ApiResponse<UserInfoResponse>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ApiResponse<UserInfoResponse>), StatusCodes.Status401Unauthorized)]
public async Task<ApiResponse<UserInfoResponse>> GetUserInfo()
{
var userId = GetCurrentUserId();
if (userId == null)
{
return ApiResponse<UserInfoResponse>.Unauthorized();
}
try
{
var userInfo = await _userService.GetUserInfoAsync(userId.Value);
if (userInfo == null)
{
_logger.LogWarning("User not found: UserId={UserId}", userId);
return ApiResponse<UserInfoResponse>.Fail("用户不存在");
}
var response = new UserInfoResponse
{
Userinfo = userInfo,
Other = new OtherConfigDto(),
TaskList = new List<TaskDto>()
};
return ApiResponse<UserInfoResponse>.Success(response);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to get user info: UserId={UserId}", userId);
return ApiResponse<UserInfoResponse>.Fail("获取用户信息失败");
}
}
/// <summary>
/// 更新用户信息
/// POST /api/update_userinfo
/// Requirements: 4.2, 4.3
/// </summary>
[HttpPost("update_userinfo")]
[Authorize]
public async Task<ApiResponse> UpdateUserInfo([FromBody] UpdateUserInfoRequest request)
{
var userId = GetCurrentUserId();
if (userId == null)
{
return ApiResponse.Unauthorized();
}
if (request == null)
{
return ApiResponse.Fail("请求参数不能为空");
}
try
{
var updateDto = new UpdateUserDto();
var hasUpdate = false;
// 处理昵称更新
if (!string.IsNullOrWhiteSpace(request.Nickname))
{
updateDto.Nickname = request.Nickname;
hasUpdate = true;
}
// 处理头像更新
if (!string.IsNullOrWhiteSpace(request.Imagebase))
{
// Base64图片上传到腾讯云COS
var headimgUrl = await UploadBase64ImageAsync(request.Imagebase, userId.Value);
if (!string.IsNullOrWhiteSpace(headimgUrl))
{
updateDto.Headimg = headimgUrl;
hasUpdate = true;
}
}
else if (!string.IsNullOrWhiteSpace(request.Headimg))
{
// 直接使用传入的头像URL
updateDto.Headimg = request.Headimg;
hasUpdate = true;
}
if (!hasUpdate)
{
return ApiResponse.Fail("没有需要更新的内容");
}
await _userService.UpdateUserAsync(userId.Value, updateDto);
_logger.LogInformation("User info updated: UserId={UserId}", userId);
return ApiResponse.Success("更新成功");
}
catch (InvalidOperationException ex)
{
_logger.LogWarning("Update user info failed: UserId={UserId}, Error={Error}", userId, ex.Message);
return ApiResponse.Fail(ex.Message);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to update user info: UserId={UserId}", userId);
return ApiResponse.Fail("更新用户信息失败");
}
}
/// <summary>
/// 账号注销
/// POST /api/user_log_off
/// Requirements: 7.1-7.3
/// </summary>
[HttpPost("user_log_off")]
[Authorize]
public async Task<ApiResponse> LogOff([FromBody] LogOffRequest? request)
{
var userId = GetCurrentUserId();
if (userId == null)
{
return ApiResponse.Unauthorized();
}
try
{
var type = request?.Type ?? 0;
await _authService.LogOffAsync(userId.Value, type);
var message = type == 0 ? "注销成功" : "取消注销成功";
_logger.LogInformation("User log off: UserId={UserId}, Type={Type}", userId, type);
return ApiResponse.Success(message);
}
catch (InvalidOperationException ex)
{
_logger.LogWarning("Log off failed: UserId={UserId}, Error={Error}", userId, ex.Message);
return ApiResponse.Fail(ex.Message);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to log off: UserId={UserId}", userId);
return ApiResponse.Fail("操作失败");
}
}
#region Asset Endpoints
/// <summary>
/// 获取余额明细
/// POST /api/profitMoney
/// Requirements: 1.1
/// </summary>
[HttpPost("profitMoney")]
[Authorize]
public async Task<ApiResponse<AssetRecordPageResponse>> GetMoneyRecords([FromBody] AssetRecordRequest? request)
{
var userId = GetCurrentUserId();
if (userId == null)
{
return ApiResponse<AssetRecordPageResponse>.Unauthorized();
}
try
{
var type = request?.Type ?? 0;
var page = request?.Page ?? 1;
var limit = request?.Limit ?? 15;
if (page < 1) page = 1;
if (limit < 1) limit = 15;
var result = await _assetService.GetMoneyRecordsAsync(userId.Value, type, page, limit);
return ApiResponse<AssetRecordPageResponse>.Success(result);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to get money records: UserId={UserId}", userId);
return ApiResponse<AssetRecordPageResponse>.Fail("获取余额明细失败");
}
}
/// <summary>
/// 获取吧唧币明细
/// POST /api/profitIntegral
/// Requirements: 1.2
/// </summary>
[HttpPost("profitIntegral")]
[Authorize]
public async Task<ApiResponse<AssetRecordPageResponse>> GetIntegralRecords([FromBody] AssetRecordRequest? request)
{
var userId = GetCurrentUserId();
if (userId == null)
{
return ApiResponse<AssetRecordPageResponse>.Unauthorized();
}
try
{
var type = request?.Type ?? 0;
var page = request?.Page ?? 1;
var limit = request?.Limit ?? 15;
if (page < 1) page = 1;
if (limit < 1) limit = 15;
var result = await _assetService.GetIntegralRecordsAsync(userId.Value, type, page, limit);
return ApiResponse<AssetRecordPageResponse>.Success(result);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to get integral records: UserId={UserId}", userId);
return ApiResponse<AssetRecordPageResponse>.Fail("获取吧唧币明细失败");
}
}
/// <summary>
/// 获取积分明细
/// POST /api/profitScore
/// Requirements: 1.3
/// </summary>
[HttpPost("profitScore")]
[Authorize]
public async Task<ApiResponse<AssetRecordPageResponse>> GetScoreRecords([FromBody] AssetRecordRequest? request)
{
var userId = GetCurrentUserId();
if (userId == null)
{
return ApiResponse<AssetRecordPageResponse>.Unauthorized();
}
try
{
var type = request?.Type ?? 0;
var page = request?.Page ?? 1;
var limit = request?.Limit ?? 15;
if (page < 1) page = 1;
if (limit < 1) limit = 15;
var result = await _assetService.GetScoreRecordsAsync(userId.Value, type, page, limit);
return ApiResponse<AssetRecordPageResponse>.Success(result);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to get score records: UserId={UserId}", userId);
return ApiResponse<AssetRecordPageResponse>.Fail("获取积分明细失败");
}
}
/// <summary>
/// 获取支付记录
/// POST /api/profitPay
/// Requirements: 1.4
/// </summary>
[HttpPost("profitPay")]
[Authorize]
public async Task<ApiResponse<AssetRecordPageResponse>> GetPayRecords([FromBody] AssetRecordRequest? request)
{
var userId = GetCurrentUserId();
if (userId == null)
{
return ApiResponse<AssetRecordPageResponse>.Unauthorized();
}
try
{
var page = request?.Page ?? 1;
var limit = request?.Limit ?? 15;
if (page < 1) page = 1;
if (limit < 1) limit = 15;
var result = await _assetService.GetPayRecordsAsync(userId.Value, page, limit);
return ApiResponse<AssetRecordPageResponse>.Success(result);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to get pay records: UserId={UserId}", userId);
return ApiResponse<AssetRecordPageResponse>.Fail("获取支付记录失败");
}
}
#endregion
#region VIP Endpoints
/// <summary>
/// 获取VIP信息
/// </summary>
/// <remarks>
/// POST /api/vip_list
///
/// 获取当前用户的VIP等级信息和权益列表
/// Requirements: 2.1-2.5
/// </remarks>
/// <returns>VIP信息数据</returns>
[HttpPost("vip_list")]
[Authorize]
[ProducesResponseType(typeof(ApiResponse<VipInfoResponse>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ApiResponse<VipInfoResponse>), StatusCodes.Status401Unauthorized)]
public async Task<ApiResponse<VipInfoResponse>> GetVipInfo()
{
var userId = GetCurrentUserId();
if (userId == null)
{
return ApiResponse<VipInfoResponse>.Unauthorized();
}
try
{
var result = await _vipService.GetVipInfoAsync(userId.Value);
return ApiResponse<VipInfoResponse>.Success(result);
}
catch (InvalidOperationException ex)
{
_logger.LogWarning("Get VIP info failed: UserId={UserId}, Error={Error}", userId, ex.Message);
return ApiResponse<VipInfoResponse>.Fail(ex.Message);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to get VIP info: UserId={UserId}", userId);
return ApiResponse<VipInfoResponse>.Fail("获取VIP信息失败");
}
}
#endregion
#region QuanYi (VIP Rights) Endpoints
/// <summary>
/// 获取权益中心信息
/// </summary>
/// <remarks>
/// Get /api/quan_yi
///
/// 获取当前用户的权益中心信息,包含等级列表和可领取奖品
/// Requirements: 8.1
/// </remarks>
/// <returns>权益中心数据</returns>
[HttpGet("quan_yi")]
[Authorize]
[ProducesResponseType(typeof(ApiResponse<QuanYiResponse>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ApiResponse<QuanYiResponse>), StatusCodes.Status401Unauthorized)]
public async Task<ApiResponse<QuanYiResponse>> GetQuanYi()
{
var userId = GetCurrentUserId();
if (userId == null)
{
return ApiResponse<QuanYiResponse>.Unauthorized();
}
try
{
var result = await _quanYiService.GetQuanYiAsync(userId.Value);
return ApiResponse<QuanYiResponse>.Success(result);
}
catch (InvalidOperationException ex)
{
_logger.LogWarning("Get QuanYi failed: UserId={UserId}, Error={Error}", userId, ex.Message);
return ApiResponse<QuanYiResponse>.Fail(ex.Message);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to get QuanYi: UserId={UserId}", userId);
return ApiResponse<QuanYiResponse>.Fail("获取权益信息失败");
}
}
/// <summary>
/// 领取权益奖品
/// </summary>
/// <remarks>
/// POST /api/quan_yi_ling
///
/// 领取指定等级的权益奖品
/// Requirements: 8.2
/// </remarks>
/// <param name="request">领取请求</param>
/// <returns>领取结果</returns>
[HttpPost("quan_yi_ling")]
[Authorize]
[ProducesResponseType(typeof(ApiResponse<QuanYiLingResponse>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ApiResponse<QuanYiLingResponse>), StatusCodes.Status401Unauthorized)]
public async Task<ApiResponse<QuanYiLingResponse>> ClaimQuanYi([FromBody] QuanYiLingRequest request)
{
var userId = GetCurrentUserId();
if (userId == null)
{
return ApiResponse<QuanYiLingResponse>.Unauthorized();
}
if (request == null || request.Id <= 0)
{
return ApiResponse<QuanYiLingResponse>.Fail("请选择要领取的等级");
}
try
{
var result = await _quanYiService.ClaimQuanYiAsync(userId.Value, request.Id);
return ApiResponse<QuanYiLingResponse>.Success(result, "领取成功");
}
catch (InvalidOperationException ex)
{
_logger.LogWarning("Claim QuanYi failed: UserId={UserId}, LevelId={LevelId}, Error={Error}",
userId, request.Id, ex.Message);
return ApiResponse<QuanYiLingResponse>.Fail(ex.Message);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to claim QuanYi: UserId={UserId}, LevelId={LevelId}",
userId, request.Id);
return ApiResponse<QuanYiLingResponse>.Fail("领取失败");
}
}
#endregion
#region Private Helper Methods
/// <summary>
/// 获取当前登录用户ID
/// </summary>
private int? GetCurrentUserId()
{
var userIdClaim = User.FindFirst(ClaimTypes.NameIdentifier);
if (userIdClaim == null || !int.TryParse(userIdClaim.Value, out var userId))
{
return null;
}
return userId;
}
/// <summary>
/// 上传Base64图片到腾讯云COS
/// </summary>
/// <param name="base64Image">Base64编码的图片数据</param>
/// <param name="userId">用户ID</param>
/// <returns>上传后的图片URL</returns>
private async Task<string?> UploadBase64ImageAsync(string base64Image, int userId)
{
try
{
// 移除Base64前缀如果有
var base64Data = base64Image;
if (base64Image.Contains(","))
{
base64Data = base64Image.Split(',')[1];
}
// 解码Base64数据
var imageBytes = Convert.FromBase64String(base64Data);
// TODO: 实现腾讯云COS上传
// 目前返回一个占位URL实际应该上传到COS并返回真实URL
// 可以在后续添加ICosUploadService接口和实现
// 临时方案:将图片保存到本地并返回相对路径
var fileName = $"avatar_{userId}_{DateTime.UtcNow:yyyyMMddHHmmss}.png";
var uploadPath = Path.Combine("wwwroot", "uploads", "avatars");
if (!Directory.Exists(uploadPath))
{
Directory.CreateDirectory(uploadPath);
}
var filePath = Path.Combine(uploadPath, fileName);
await System.IO.File.WriteAllBytesAsync(filePath, imageBytes);
// 返回相对URL
return $"/uploads/avatars/{fileName}";
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to upload base64 image for user: {UserId}", userId);
return null;
}
}
#endregion
}