91 lines
3.1 KiB
Go
91 lines
3.1 KiB
Go
package app
|
|
|
|
import "yingce/backend/internal/model"
|
|
|
|
type AnalyticsFinance struct {
|
|
SettledOrders int `json:"settledOrders"`
|
|
CostedOrders int `json:"costedOrders"`
|
|
RevenueMicrocredits int64 `json:"revenueMicrocredits"`
|
|
CostMicrocredits int64 `json:"costMicrocredits"`
|
|
ProfitMicrocredits *int64 `json:"profitMicrocredits"`
|
|
ProfitMargin *float64 `json:"profitMargin"`
|
|
}
|
|
|
|
type analyticsFinancialRecord struct {
|
|
Log model.ApiCallLog
|
|
Revenue int64
|
|
Cost *int64
|
|
}
|
|
|
|
// 一个订单的重试、轮询不能重复计入收入;只归属实际结算渠道。
|
|
// 历史订单使用下单价格快照,缺失成本时不反查当前价格。
|
|
func (s *Service) analyticsFinancialRecords(logs []model.ApiCallLog) (map[string]analyticsFinancialRecord, error) {
|
|
ids := make([]string, 0, len(logs))
|
|
for _, log := range logs {
|
|
if log.Billable && log.BillingOrderID != "" {
|
|
ids = append(ids, log.BillingOrderID)
|
|
}
|
|
}
|
|
orders, err := s.repo.BillingOrdersByIDs(uniqueNonEmpty(ids))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
byOrder := make(map[string]model.ApiCallLog)
|
|
for _, log := range logs {
|
|
order, exists := orders[log.BillingOrderID]
|
|
if !log.Billable || !exists || order.UserID != log.UserID || order.ChannelID != log.ChannelID || order.Status != model.BillingStatusSettled {
|
|
continue
|
|
}
|
|
switch log.RequestKind {
|
|
case "poll", "download", "upload", "workflow-schema", "cancel-query", "cancel":
|
|
continue
|
|
}
|
|
previous, exists := byOrder[order.ID]
|
|
if !exists || log.CreatedAt.Before(previous.CreatedAt) || (log.CreatedAt.Equal(previous.CreatedAt) && log.ID < previous.ID) {
|
|
byOrder[order.ID] = log
|
|
}
|
|
}
|
|
records := make(map[string]analyticsFinancialRecord, len(byOrder))
|
|
for id, log := range byOrder {
|
|
order := orders[id]
|
|
cost, err := billingCreditCost(order)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
records[log.ID] = analyticsFinancialRecord{Log: log, Revenue: order.ActualAmountMicrocredits, Cost: cost}
|
|
}
|
|
return records, nil
|
|
}
|
|
|
|
func (finance *AnalyticsFinance) add(record analyticsFinancialRecord) {
|
|
finance.SettledOrders++
|
|
finance.RevenueMicrocredits += record.Revenue
|
|
if record.Cost != nil {
|
|
finance.CostedOrders++
|
|
finance.CostMicrocredits += *record.Cost
|
|
}
|
|
finance.ProfitMicrocredits, finance.ProfitMargin = nil, nil
|
|
if finance.CostedOrders == finance.SettledOrders {
|
|
profit := finance.RevenueMicrocredits - finance.CostMicrocredits
|
|
finance.ProfitMicrocredits = &profit
|
|
if finance.RevenueMicrocredits > 0 {
|
|
margin := float64(profit) * 100 / float64(finance.RevenueMicrocredits)
|
|
finance.ProfitMargin = &margin
|
|
}
|
|
}
|
|
}
|
|
|
|
func applyAnalyticsFinance(overview *AnalyticsOverview, records map[string]analyticsFinancialRecord) {
|
|
rows := make(map[[2]string]*AnalyticsFinance, len(overview.Models))
|
|
for index := range overview.Models {
|
|
row := &overview.Models[index]
|
|
rows[[2]string{row.Model, row.Capability}] = &row.Finance
|
|
}
|
|
for _, record := range records {
|
|
overview.KPI.Finance.add(record)
|
|
if row := rows[[2]string{firstNonEmpty(record.Log.Model, "未识别"), record.Log.Capability}]; row != nil {
|
|
row.add(record)
|
|
}
|
|
}
|
|
}
|