AssetManager.API/AssetManager.API/Controllers/PortfolioController.cs
OpenClaw Agent 74e5f85579 refactor: 移除 calculateDailyNav 接口
前端暂不需要,后续有需求再加
2026-03-15 14:43:35 +00:00

341 lines
10 KiB
C#
Executable File
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 AssetManager.Models.DTOs;
using AssetManager.Models;
using AssetManager.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System.Security.Claims;
namespace AssetManager.API.Controllers;
[ApiController]
[Route("api/v1/portfolio")]
[Authorize]
public class PortfolioController : ControllerBase
{
private readonly ILogger<PortfolioController> _logger;
private readonly IPortfolioFacade _portfolioFacade;
public PortfolioController(
ILogger<PortfolioController> logger,
IPortfolioFacade portfolioFacade)
{
_logger = logger;
_portfolioFacade = portfolioFacade;
}
/// <summary>
/// 获取当前登录用户ID
/// </summary>
private string GetCurrentUserId()
{
return User.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? throw new Exception("User not authenticated");
}
/// <summary>
/// 创建投资组合(账本)
/// </summary>
[HttpPost]
public async Task<ActionResult<ApiResponse<CreatePortfolioResponse>>> CreatePortfolio([FromBody] CreatePortfolioRequest request)
{
var userId = GetCurrentUserId();
try
{
var response = await _portfolioFacade.CreatePortfolioWithValidationAsync(request, userId);
return Ok(new ApiResponse<CreatePortfolioResponse>
{
code = Models.StatusCodes.Success,
data = response,
message = "创建成功"
});
}
catch (ArgumentException ex)
{
return BadRequest(new ApiResponse<CreatePortfolioResponse>
{
code = Models.StatusCodes.BadRequest,
data = null,
message = ex.Message
});
}
}
/// <summary>
/// 获取投资组合列表
/// </summary>
[HttpGet]
public async Task<ActionResult<ApiResponse<GetPortfoliosResponse>>> GetPortfolios()
{
var userId = GetCurrentUserId();
var portfolios = await _portfolioFacade.GetPortfolioListAsync(userId);
return Ok(new ApiResponse<GetPortfoliosResponse>
{
code = Models.StatusCodes.Success,
data = new GetPortfoliosResponse
{
Items = portfolios
},
message = "success"
});
}
/// <summary>
/// 获取用户总资产
/// </summary>
[HttpGet("assets")]
public async Task<ActionResult<ApiResponse<TotalAssetsResponse>>> GetTotalAssets()
{
var userId = GetCurrentUserId();
var totalAssets = await _portfolioFacade.GetTotalAssetsAsync(userId);
return Ok(new ApiResponse<TotalAssetsResponse>
{
code = Models.StatusCodes.Success,
data = totalAssets,
message = "success"
});
}
/// <summary>
/// 获取交易记录(通过 query parameter
/// </summary>
[HttpGet("transactions")]
public async Task<ActionResult<ApiResponse<GetTransactionsResponse>>> GetTransactionsByQuery(
[FromQuery] string portfolioId,
[FromQuery] int limit = 20,
[FromQuery] int offset = 0)
{
var userId = GetCurrentUserId();
try
{
var request = new GetTransactionsRequest
{
PortfolioId = portfolioId,
Limit = limit,
Offset = offset
};
var transactions = await _portfolioFacade.GetTransactionsAsync(portfolioId, request, userId);
return Ok(new ApiResponse<GetTransactionsResponse>
{
code = Models.StatusCodes.Success,
data = new GetTransactionsResponse
{
Items = transactions,
Total = transactions.Count
},
message = "success"
});
}
catch (UnauthorizedAccessException)
{
return NotFound(new ApiResponse<GetTransactionsResponse>
{
code = Models.StatusCodes.NotFound,
data = null,
message = "组合不存在"
});
}
}
/// <summary>
/// 获取投资组合详情
/// </summary>
[HttpGet("{id}")]
public async Task<ActionResult<ApiResponse<PortfolioDetailResponse>>> GetPortfolio(string id)
{
var userId = GetCurrentUserId();
try
{
var portfolio = await _portfolioFacade.GetPortfolioDetailAsync(id, userId);
return Ok(new ApiResponse<PortfolioDetailResponse>
{
code = Models.StatusCodes.Success,
data = portfolio,
message = "success"
});
}
catch (UnauthorizedAccessException)
{
return NotFound(new ApiResponse<PortfolioDetailResponse>
{
code = Models.StatusCodes.NotFound,
data = null,
message = "组合不存在"
});
}
}
/// <summary>
/// 删除投资组合
/// </summary>
[HttpDelete("{id}")]
public async Task<ActionResult<ApiResponse<object>>> DeletePortfolio(string id)
{
var userId = GetCurrentUserId();
var success = await _portfolioFacade.DeletePortfolioAsync(id, userId);
if (!success)
{
return NotFound(new ApiResponse<object>
{
code = Models.StatusCodes.NotFound,
data = null,
message = "组合不存在"
});
}
return Ok(new ApiResponse<object>
{
code = Models.StatusCodes.Success,
data = null,
message = "删除成功"
});
}
/// <summary>
/// 获取净值历史(收益曲线)
/// </summary>
[HttpGet("{id}/nav-history")]
public async Task<ActionResult<ApiResponse<NavHistoryResponse>>> GetNavHistory(
string id,
[FromQuery] DateTime? startDate,
[FromQuery] DateTime? endDate,
[FromQuery] string? interval)
{
var userId = GetCurrentUserId();
var request = new NavHistoryRequest
{
StartDate = startDate,
EndDate = endDate,
Interval = interval ?? "daily"
};
try
{
var response = await _portfolioFacade.GetNavHistoryAsync(id, request, userId);
return Ok(new ApiResponse<NavHistoryResponse>
{
code = Models.StatusCodes.Success,
data = response,
message = "success"
});
}
catch (UnauthorizedAccessException)
{
return NotFound(new ApiResponse<NavHistoryResponse>
{
code = Models.StatusCodes.NotFound,
data = null,
message = "组合不存在"
});
}
}
/// <summary>
/// 手动回填净值历史
/// </summary>
[HttpPost("{id}/nav-history/backfill")]
public async Task<ActionResult<ApiResponse<BackfillNavResponse>>> BackfillNavHistory(
string id,
[FromQuery] bool force = false)
{
var userId = GetCurrentUserId();
try
{
var response = await _portfolioFacade.BackfillNavHistoryAsync(id, userId, force);
return Ok(new ApiResponse<BackfillNavResponse>
{
code = Models.StatusCodes.Success,
data = response,
message = "success"
});
}
catch (UnauthorizedAccessException)
{
return NotFound(new ApiResponse<BackfillNavResponse>
{
code = Models.StatusCodes.NotFound,
data = null,
message = "组合不存在"
});
}
}
/// <summary>
/// 创建交易(买入/卖出)
/// </summary>
[HttpPost("transactions")]
public async Task<ActionResult<ApiResponse<TransactionItem>>> CreateTransaction(
[FromBody] CreateTransactionRequest request)
{
var userId = GetCurrentUserId();
if (string.IsNullOrEmpty(request.PortfolioId))
{
return BadRequest(new ApiResponse<TransactionItem>
{
code = Models.StatusCodes.BadRequest,
data = null,
message = "portfolioId 不能为空"
});
}
try
{
var transaction = await _portfolioFacade.CreateTransactionAsync(request.PortfolioId, request, userId);
return Ok(new ApiResponse<TransactionItem>
{
code = Models.StatusCodes.Success,
data = transaction,
message = "交易创建成功"
});
}
catch (UnauthorizedAccessException)
{
return NotFound(new ApiResponse<TransactionItem>
{
code = Models.StatusCodes.NotFound,
data = null,
message = "组合不存在"
});
}
}
/// <summary>
/// 获取策略信号
/// </summary>
[HttpGet("{id}/signal")]
public async Task<ActionResult<ApiResponse<StrategySignalResponse>>> GetStrategySignal(string id)
{
var userId = GetCurrentUserId();
try
{
var signal = await _portfolioFacade.GetStrategySignalAsync(id, userId);
return Ok(new ApiResponse<StrategySignalResponse>
{
code = Models.StatusCodes.Success,
data = signal,
message = "success"
});
}
catch (UnauthorizedAccessException)
{
return NotFound(new ApiResponse<StrategySignalResponse>
{
code = Models.StatusCodes.NotFound,
data = null,
message = "组合不存在"
});
}
}
}