Fix Get agent balance

This commit is contained in:
2025-05-20 22:42:06 +07:00
parent e866ef3303
commit 2ee1322ba0
4 changed files with 59 additions and 25 deletions

View File

@@ -6,9 +6,10 @@ namespace Managing.Application.Abstractions.Services;
public interface IStatisticService public interface IStatisticService
{ {
Task<IList<AgentBalanceHistory>> GetAgentBalances(string agentName, DateTime start, DateTime? end = null); Task<AgentBalanceHistory> GetAgentBalances(string agentName, DateTime start, DateTime? end = null);
Task<(IList<AgentBalanceHistory> Agents, int TotalCount)> GetBestAgents(DateTime start, DateTime? end = null, int page = 1, Task<(IList<AgentBalanceHistory> Agents, int TotalCount)> GetBestAgents(DateTime start, DateTime? end = null,
int page = 1,
int pageSize = 10); int pageSize = 10);
List<Trader> GetBadTraders(); List<Trader> GetBadTraders();

View File

@@ -378,14 +378,14 @@ public class StatisticService : IStatisticService
await _messengerService.SendBadTraders(lastBadTrader); await _messengerService.SendBadTraders(lastBadTrader);
} }
public async Task<IList<AgentBalanceHistory>> GetAgentBalances(string agentName, DateTime start, public async Task<AgentBalanceHistory> GetAgentBalances(string agentName, DateTime start,
DateTime? end = null) DateTime? end = null)
{ {
var effectiveEnd = end ?? DateTime.UtcNow; var effectiveEnd = end ?? DateTime.UtcNow;
string cacheKey = $"AgentBalances_{agentName}_{start:yyyyMMdd}_{effectiveEnd:yyyyMMdd}"; string cacheKey = $"AgentBalances_{agentName}_{start:yyyyMMdd}_{effectiveEnd:yyyyMMdd}";
// Check if the balances are already cached // Check if the balances are already cached
var cachedBalances = _cacheService.GetValue<IList<AgentBalanceHistory>>(cacheKey); var cachedBalances = _cacheService.GetValue<AgentBalanceHistory>(cacheKey);
if (cachedBalances != null) if (cachedBalances != null)
{ {
@@ -393,15 +393,12 @@ public class StatisticService : IStatisticService
} }
var balances = await _agentBalanceRepository.GetAgentBalances(agentName, start, end); var balances = await _agentBalanceRepository.GetAgentBalances(agentName, start, end);
// Create a single AgentBalanceHistory with all balances // Create a single AgentBalanceHistory with all balances
var result = new List<AgentBalanceHistory> var result = new AgentBalanceHistory
{ {
new AgentBalanceHistory AgentName = agentName,
{ AgentBalances = balances.OrderBy(b => b.Time).ToList()
AgentName = agentName,
AgentBalances = balances.OrderBy(b => b.Time).ToList()
}
}; };
// Cache the results for 5 minutes // Cache the results for 5 minutes
@@ -434,7 +431,8 @@ public class StatisticService : IStatisticService
} }
// Get all agents with their balance history // Get all agents with their balance history
var (fetchedAgents, fetchedTotalCount) = await _agentBalanceRepository.GetAllAgentBalancesWithHistory(start, end); var (fetchedAgents, fetchedTotalCount) =
await _agentBalanceRepository.GetAllAgentBalancesWithHistory(start, end);
// Cache all results for 5 minutes // Cache all results for 5 minutes
_cacheService.SaveValue(cacheKey, (fetchedAgents, fetchedTotalCount), TimeSpan.FromMinutes(5)); _cacheService.SaveValue(cacheKey, (fetchedAgents, fetchedTotalCount), TimeSpan.FromMinutes(5));

View File

@@ -49,7 +49,8 @@ public class AgentBalanceRepository : IAgentBalanceRepository
$"|> range(start: {start:s}Z" + $"|> range(start: {start:s}Z" +
(end.HasValue ? $", stop: {end.Value:s}Z" : "") + (end.HasValue ? $", stop: {end.Value:s}Z" : "") +
$") " + $") " +
$"|> filter(fn: (r) => r[\"agent_name\"] == \"{agentName}\")"; $"|> filter(fn: (r) => r[\"agent_name\"] == \"{agentName}\") " +
$"|> pivot(rowKey: [\"_time\"], columnKey: [\"_field\"], valueColumn: \"_value\")";
var result = await query.QueryAsync<AgentBalanceDto>(flux, _influxDbRepository.Organization); var result = await query.QueryAsync<AgentBalanceDto>(flux, _influxDbRepository.Organization);

View File

@@ -14,6 +14,15 @@ export interface AgentBalanceWithBalances extends AgentBalanceHistory {
}>; }>;
} }
const FILTERS = [
{ label: '24H', value: '24H', days: 1 },
{ label: '3D', value: '3D', days: 3 },
{ label: '1W', value: '1W', days: 7 },
{ label: '1M', value: '1M', days: 30 },
{ label: '1Y', value: '1Y', days: 365 },
{ label: 'Total', value: 'Total', days: null },
]
function BestAgents() { function BestAgents() {
const { apiUrl } = useApiUrlStore() const { apiUrl } = useApiUrlStore()
const [data, setData] = useState<AgentBalanceWithBalances[]>([]) const [data, setData] = useState<AgentBalanceWithBalances[]>([])
@@ -21,37 +30,52 @@ function BestAgents() {
const [page, setPage] = useState(1) const [page, setPage] = useState(1)
const [pageSize, setPageSize] = useState(10) const [pageSize, setPageSize] = useState(10)
const [totalPages, setTotalPages] = useState(1) const [totalPages, setTotalPages] = useState(1)
const [expandedAgent, setExpandedAgent] = useState<string | null>(null) const [selectedFilter, setSelectedFilter] = useState('Total')
useEffect(() => { useEffect(() => {
setIsLoading(true) setIsLoading(true)
const client = new DataClient({}, apiUrl) const client = new DataClient({}, apiUrl)
const now = new Date() const now = new Date()
const startDate = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 30)
// Calculate start date based on selected filter
const filterObj = FILTERS.find(f => f.value === selectedFilter)
let startDate: Date
if (filterObj?.days) {
// Use the filter's days value to calculate start date
startDate = new Date(now.getTime() - filterObj.days * 24 * 60 * 60 * 1000)
} else {
// For 'Total', fetch from a far past date (e.g., 5 years ago)
startDate = new Date(now.getFullYear() - 5, now.getMonth(), now.getDate())
}
client.data_GetBestAgents(startDate, now, page, pageSize).then((res: BestAgentsResponse) => { client.data_GetBestAgents(startDate, now, page, pageSize).then((res: BestAgentsResponse) => {
setData(res.agents as AgentBalanceWithBalances[] ?? []) setData(res.agents as AgentBalanceWithBalances[] ?? [])
setTotalPages(res.totalPages ?? 1) setTotalPages(res.totalPages ?? 1)
console.log(res) console.log(res)
}).finally(() => setIsLoading(false)) }).finally(() => setIsLoading(false))
}, [apiUrl, page, pageSize]) }, [apiUrl, page, pageSize, selectedFilter])
// Type guard for agentBalances function filterBalancesByRange(agent: AgentBalanceWithBalances) {
function hasAgentBalances(agent: AgentBalanceWithBalances): agent is Required<AgentBalanceWithBalances> { if (!agent.agentBalances || selectedFilter === 'Total') return agent.agentBalances ?? []
return Array.isArray(agent.agentBalances) && agent.agentBalances.length > 0; const days = FILTERS.find(f => f.value === selectedFilter)?.days
if (!days) return agent.agentBalances ?? []
const now = new Date()
const cutoff = new Date(now.getTime() - days * 24 * 60 * 60 * 1000)
return agent.agentBalances.filter(b => b.time && new Date(b.time) >= cutoff)
} }
// Get the latest balance for each agent // Get the latest balance for each agent
const latestBalances = data.map(agent => { const latestBalances = data.map(agent => {
if (hasAgentBalances(agent)) { const filteredBalances = filterBalancesByRange(agent)
const lastBalance = agent.agentBalances[agent.agentBalances.length - 1] if (filteredBalances.length > 0) {
const lastBalance = filteredBalances[filteredBalances.length - 1]
return { return {
agentName: agent.agentName, agentName: agent.agentName,
originalAgent: agent, // Store the original agent for row details originalAgent: { ...agent, agentBalances: filteredBalances },
...lastBalance ...lastBalance
} }
} }
// fallback: just agentName return { agentName: agent.agentName, originalAgent: { ...agent, agentBalances: filteredBalances } }
return { agentName: agent.agentName, originalAgent: agent }
}) })
const columns = [ const columns = [
@@ -63,10 +87,20 @@ function BestAgents() {
{ Header: 'Last Update', accessor: 'time', Cell: ({ value }: any) => value ? new Date(value).toLocaleString() : '' }, { Header: 'Last Update', accessor: 'time', Cell: ({ value }: any) => value ? new Date(value).toLocaleString() : '' },
] ]
return ( return (
<div className="container mx-auto pt-6"> <div className="container mx-auto pt-6">
<GridTile title="Best Agents"> <GridTile title="Best Agents">
<div className="flex gap-2 mb-3">
{FILTERS.map(f => (
<button
key={f.value}
className={`px-2 py-0.5 text-xs rounded ${selectedFilter === f.value ? 'bg-primary text-primary-content' : 'bg-base-200'}`}
onClick={() => setSelectedFilter(f.value)}
>
{f.label}
</button>
))}
</div>
{isLoading ? ( {isLoading ? (
<progress className="progress progress-primary w-56"></progress> <progress className="progress progress-primary w-56"></progress>
) : ( ) : (