feat: deepen report metrics
This commit is contained in:
@@ -19,6 +19,16 @@ type ReportOverviewResponse struct {
|
||||
type ReportSummary struct {
|
||||
// TotalViews 内容累计曝光(全量累计值,用于粗略换算)。
|
||||
TotalViews int64 `json:"total_views"`
|
||||
// ContentCount 内容总量(当前快照)。
|
||||
ContentCount int64 `json:"content_count"`
|
||||
// ContentCreated 统计区间内新增内容数。
|
||||
ContentCreated int64 `json:"content_created"`
|
||||
// LikeActions 统计区间内新增点赞数(基于互动记录)。
|
||||
LikeActions int64 `json:"like_actions"`
|
||||
// FavoriteActions 统计区间内新增收藏数(基于互动记录)。
|
||||
FavoriteActions int64 `json:"favorite_actions"`
|
||||
// CommentCount 统计区间内新增评论数。
|
||||
CommentCount int64 `json:"comment_count"`
|
||||
// PaidOrders 统计区间内已支付订单数。
|
||||
PaidOrders int64 `json:"paid_orders"`
|
||||
// PaidAmount 统计区间内已支付金额(单位元)。
|
||||
@@ -27,6 +37,18 @@ type ReportSummary struct {
|
||||
RefundOrders int64 `json:"refund_orders"`
|
||||
// RefundAmount 统计区间内退款金额(单位元)。
|
||||
RefundAmount float64 `json:"refund_amount"`
|
||||
// WithdrawalApplyOrders 统计区间内提现申请订单数。
|
||||
WithdrawalApplyOrders int64 `json:"withdrawal_apply_orders"`
|
||||
// WithdrawalApplyAmount 统计区间内提现申请金额(单位元)。
|
||||
WithdrawalApplyAmount float64 `json:"withdrawal_apply_amount"`
|
||||
// WithdrawalPaidOrders 统计区间内提现完成订单数。
|
||||
WithdrawalPaidOrders int64 `json:"withdrawal_paid_orders"`
|
||||
// WithdrawalPaidAmount 统计区间内提现完成金额(单位元)。
|
||||
WithdrawalPaidAmount float64 `json:"withdrawal_paid_amount"`
|
||||
// WithdrawalFailedOrders 统计区间内提现失败订单数。
|
||||
WithdrawalFailedOrders int64 `json:"withdrawal_failed_orders"`
|
||||
// WithdrawalFailedAmount 统计区间内提现失败金额(单位元)。
|
||||
WithdrawalFailedAmount float64 `json:"withdrawal_failed_amount"`
|
||||
// ConversionRate 转化率(已支付订单数 / 累计曝光)。
|
||||
ConversionRate float64 `json:"conversion_rate"`
|
||||
}
|
||||
@@ -42,6 +64,26 @@ type ReportOverviewItem struct {
|
||||
RefundOrders int64 `json:"refund_orders"`
|
||||
// RefundAmount 当日退款金额(单位元)。
|
||||
RefundAmount float64 `json:"refund_amount"`
|
||||
// WithdrawalApplyOrders 当日提现申请订单数。
|
||||
WithdrawalApplyOrders int64 `json:"withdrawal_apply_orders"`
|
||||
// WithdrawalApplyAmount 当日提现申请金额(单位元)。
|
||||
WithdrawalApplyAmount float64 `json:"withdrawal_apply_amount"`
|
||||
// WithdrawalPaidOrders 当日提现完成订单数。
|
||||
WithdrawalPaidOrders int64 `json:"withdrawal_paid_orders"`
|
||||
// WithdrawalPaidAmount 当日提现完成金额(单位元)。
|
||||
WithdrawalPaidAmount float64 `json:"withdrawal_paid_amount"`
|
||||
// WithdrawalFailedOrders 当日提现失败订单数。
|
||||
WithdrawalFailedOrders int64 `json:"withdrawal_failed_orders"`
|
||||
// WithdrawalFailedAmount 当日提现失败金额(单位元)。
|
||||
WithdrawalFailedAmount float64 `json:"withdrawal_failed_amount"`
|
||||
// ContentCreated 当日新增内容数。
|
||||
ContentCreated int64 `json:"content_created"`
|
||||
// LikeActions 当日新增点赞数(基于互动记录)。
|
||||
LikeActions int64 `json:"like_actions"`
|
||||
// FavoriteActions 当日新增收藏数(基于互动记录)。
|
||||
FavoriteActions int64 `json:"favorite_actions"`
|
||||
// CommentCount 当日新增评论数。
|
||||
CommentCount int64 `json:"comment_count"`
|
||||
}
|
||||
|
||||
type ReportExportForm struct {
|
||||
|
||||
@@ -49,12 +49,48 @@ func (s *creator) ReportOverview(
|
||||
return nil, errorx.ErrDatabaseError.WithCause(err)
|
||||
}
|
||||
|
||||
// 订单仅统计内容购买类型,并按状态划分已支付/已退款。
|
||||
paidCount, paidAmount, err := s.orderAggregate(ctx, tid, consts.OrderStatusPaid, "paid_at", rg)
|
||||
// 内容规模与互动指标。
|
||||
contentCount, err := s.contentCount(ctx, tid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
refundCount, refundAmount, err := s.orderAggregate(ctx, tid, consts.OrderStatusRefunded, "updated_at", rg)
|
||||
contentCreated, err := s.contentCreatedAggregate(ctx, tid, rg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
likeActions, err := s.contentActionAggregate(ctx, tid, consts.UserContentActionTypeLike, rg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
favoriteActions, err := s.contentActionAggregate(ctx, tid, consts.UserContentActionTypeFavorite, rg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
commentCount, err := s.commentAggregate(ctx, tid, rg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 订单仅统计内容购买类型,并按状态划分已支付/已退款。
|
||||
paidCount, paidAmount, err := s.orderAggregate(ctx, tid, consts.OrderTypeContentPurchase, consts.OrderStatusPaid, "paid_at", rg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
refundCount, refundAmount, err := s.orderAggregate(ctx, tid, consts.OrderTypeContentPurchase, consts.OrderStatusRefunded, "updated_at", rg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 提现维度统计(申请/完成/失败)。
|
||||
withdrawApplyCount, withdrawApplyAmount, err := s.orderAggregate(ctx, tid, consts.OrderTypeWithdrawal, consts.OrderStatusCreated, "created_at", rg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
withdrawPaidCount, withdrawPaidAmount, err := s.orderAggregate(ctx, tid, consts.OrderTypeWithdrawal, consts.OrderStatusPaid, "paid_at", rg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
withdrawFailedCount, withdrawFailedAmount, err := s.orderAggregate(ctx, tid, consts.OrderTypeWithdrawal, consts.OrderStatusFailed, "updated_at", rg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -65,11 +101,39 @@ func (s *creator) ReportOverview(
|
||||
}
|
||||
|
||||
// 生成按日趋势序列。
|
||||
paidSeries, err := s.orderSeries(ctx, tid, consts.OrderStatusPaid, "paid_at", rg)
|
||||
paidSeries, err := s.orderSeries(ctx, tid, consts.OrderTypeContentPurchase, consts.OrderStatusPaid, "paid_at", rg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
refundSeries, err := s.orderSeries(ctx, tid, consts.OrderStatusRefunded, "updated_at", rg)
|
||||
refundSeries, err := s.orderSeries(ctx, tid, consts.OrderTypeContentPurchase, consts.OrderStatusRefunded, "updated_at", rg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
withdrawApplySeries, err := s.orderSeries(ctx, tid, consts.OrderTypeWithdrawal, consts.OrderStatusCreated, "created_at", rg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
withdrawPaidSeries, err := s.orderSeries(ctx, tid, consts.OrderTypeWithdrawal, consts.OrderStatusPaid, "paid_at", rg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
withdrawFailedSeries, err := s.orderSeries(ctx, tid, consts.OrderTypeWithdrawal, consts.OrderStatusFailed, "updated_at", rg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
contentCreatedSeries, err := s.contentCreatedSeries(ctx, tid, rg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
likeSeries, err := s.contentActionSeries(ctx, tid, consts.UserContentActionTypeLike, rg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
favoriteSeries, err := s.contentActionSeries(ctx, tid, consts.UserContentActionTypeFavorite, rg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
commentSeries, err := s.commentSeries(ctx, tid, rg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -79,23 +143,47 @@ func (s *creator) ReportOverview(
|
||||
key := day.Format("2006-01-02")
|
||||
paidItem := paidSeries[key]
|
||||
refundItem := refundSeries[key]
|
||||
withdrawApplyItem := withdrawApplySeries[key]
|
||||
withdrawPaidItem := withdrawPaidSeries[key]
|
||||
withdrawFailedItem := withdrawFailedSeries[key]
|
||||
items = append(items, creator_dto.ReportOverviewItem{
|
||||
Date: key,
|
||||
PaidOrders: paidItem.Count,
|
||||
PaidAmount: float64(paidItem.Amount) / 100.0,
|
||||
RefundOrders: refundItem.Count,
|
||||
RefundAmount: float64(refundItem.Amount) / 100.0,
|
||||
Date: key,
|
||||
PaidOrders: paidItem.Count,
|
||||
PaidAmount: float64(paidItem.Amount) / 100.0,
|
||||
RefundOrders: refundItem.Count,
|
||||
RefundAmount: float64(refundItem.Amount) / 100.0,
|
||||
WithdrawalApplyOrders: withdrawApplyItem.Count,
|
||||
WithdrawalApplyAmount: float64(withdrawApplyItem.Amount) / 100.0,
|
||||
WithdrawalPaidOrders: withdrawPaidItem.Count,
|
||||
WithdrawalPaidAmount: float64(withdrawPaidItem.Amount) / 100.0,
|
||||
WithdrawalFailedOrders: withdrawFailedItem.Count,
|
||||
WithdrawalFailedAmount: float64(withdrawFailedItem.Amount) / 100.0,
|
||||
ContentCreated: contentCreatedSeries[key],
|
||||
LikeActions: likeSeries[key],
|
||||
FavoriteActions: favoriteSeries[key],
|
||||
CommentCount: commentSeries[key],
|
||||
})
|
||||
}
|
||||
|
||||
return &creator_dto.ReportOverviewResponse{
|
||||
Summary: creator_dto.ReportSummary{
|
||||
TotalViews: totalViews,
|
||||
PaidOrders: paidCount,
|
||||
PaidAmount: float64(paidAmount) / 100.0,
|
||||
RefundOrders: refundCount,
|
||||
RefundAmount: float64(refundAmount) / 100.0,
|
||||
ConversionRate: conversionRate,
|
||||
TotalViews: totalViews,
|
||||
ContentCount: contentCount,
|
||||
ContentCreated: contentCreated,
|
||||
LikeActions: likeActions,
|
||||
FavoriteActions: favoriteActions,
|
||||
CommentCount: commentCount,
|
||||
PaidOrders: paidCount,
|
||||
PaidAmount: float64(paidAmount) / 100.0,
|
||||
RefundOrders: refundCount,
|
||||
RefundAmount: float64(refundAmount) / 100.0,
|
||||
WithdrawalApplyOrders: withdrawApplyCount,
|
||||
WithdrawalApplyAmount: float64(withdrawApplyAmount) / 100.0,
|
||||
WithdrawalPaidOrders: withdrawPaidCount,
|
||||
WithdrawalPaidAmount: float64(withdrawPaidAmount) / 100.0,
|
||||
WithdrawalFailedOrders: withdrawFailedCount,
|
||||
WithdrawalFailedAmount: float64(withdrawFailedAmount) / 100.0,
|
||||
ConversionRate: conversionRate,
|
||||
},
|
||||
Items: items,
|
||||
}, nil
|
||||
@@ -129,7 +217,7 @@ func (s *creator) ExportReport(
|
||||
}
|
||||
|
||||
builder := &strings.Builder{}
|
||||
builder.WriteString("date,paid_orders,paid_amount,refund_orders,refund_amount\n")
|
||||
builder.WriteString("date,paid_orders,paid_amount,refund_orders,refund_amount,withdrawal_apply_orders,withdrawal_apply_amount,withdrawal_paid_orders,withdrawal_paid_amount,withdrawal_failed_orders,withdrawal_failed_amount,content_created,like_actions,favorite_actions,comment_count\n")
|
||||
for _, item := range overview.Items {
|
||||
builder.WriteString(item.Date)
|
||||
builder.WriteString(",")
|
||||
@@ -140,6 +228,26 @@ func (s *creator) ExportReport(
|
||||
builder.WriteString(strconv.FormatInt(item.RefundOrders, 10))
|
||||
builder.WriteString(",")
|
||||
builder.WriteString(formatAmount(item.RefundAmount))
|
||||
builder.WriteString(",")
|
||||
builder.WriteString(strconv.FormatInt(item.WithdrawalApplyOrders, 10))
|
||||
builder.WriteString(",")
|
||||
builder.WriteString(formatAmount(item.WithdrawalApplyAmount))
|
||||
builder.WriteString(",")
|
||||
builder.WriteString(strconv.FormatInt(item.WithdrawalPaidOrders, 10))
|
||||
builder.WriteString(",")
|
||||
builder.WriteString(formatAmount(item.WithdrawalPaidAmount))
|
||||
builder.WriteString(",")
|
||||
builder.WriteString(strconv.FormatInt(item.WithdrawalFailedOrders, 10))
|
||||
builder.WriteString(",")
|
||||
builder.WriteString(formatAmount(item.WithdrawalFailedAmount))
|
||||
builder.WriteString(",")
|
||||
builder.WriteString(strconv.FormatInt(item.ContentCreated, 10))
|
||||
builder.WriteString(",")
|
||||
builder.WriteString(strconv.FormatInt(item.LikeActions, 10))
|
||||
builder.WriteString(",")
|
||||
builder.WriteString(strconv.FormatInt(item.FavoriteActions, 10))
|
||||
builder.WriteString(",")
|
||||
builder.WriteString(strconv.FormatInt(item.CommentCount, 10))
|
||||
builder.WriteString("\n")
|
||||
}
|
||||
|
||||
@@ -160,6 +268,7 @@ type reportAggRow struct {
|
||||
func (s *creator) orderAggregate(
|
||||
ctx context.Context,
|
||||
tenantID int64,
|
||||
orderType consts.OrderType,
|
||||
status consts.OrderStatus,
|
||||
timeField string,
|
||||
rg reportRange,
|
||||
@@ -173,7 +282,7 @@ func (s *creator) orderAggregate(
|
||||
Model(&models.Order{}).
|
||||
Select("count(*) as count, coalesce(sum(amount_paid), 0) as amount").
|
||||
Where("tenant_id = ? AND type = ? AND status = ? AND "+timeField+" >= ? AND "+timeField+" < ?",
|
||||
tenantID, consts.OrderTypeContentPurchase, status, rg.startDay, rg.endNext).
|
||||
tenantID, orderType, status, rg.startDay, rg.endNext).
|
||||
Scan(&total).Error
|
||||
if err != nil {
|
||||
return 0, 0, errorx.ErrDatabaseError.WithCause(err)
|
||||
@@ -184,6 +293,7 @@ func (s *creator) orderAggregate(
|
||||
func (s *creator) orderSeries(
|
||||
ctx context.Context,
|
||||
tenantID int64,
|
||||
orderType consts.OrderType,
|
||||
status consts.OrderStatus,
|
||||
timeField string,
|
||||
rg reportRange,
|
||||
@@ -194,7 +304,7 @@ func (s *creator) orderSeries(
|
||||
Model(&models.Order{}).
|
||||
Select("date_trunc('day', "+timeField+") as day, count(*) as count, coalesce(sum(amount_paid), 0) as amount").
|
||||
Where("tenant_id = ? AND type = ? AND status = ? AND "+timeField+" >= ? AND "+timeField+" < ?",
|
||||
tenantID, consts.OrderTypeContentPurchase, status, rg.startDay, rg.endNext).
|
||||
tenantID, orderType, status, rg.startDay, rg.endNext).
|
||||
Group("day").
|
||||
Scan(&rows).Error
|
||||
if err != nil {
|
||||
@@ -209,6 +319,127 @@ func (s *creator) orderSeries(
|
||||
return result, nil
|
||||
}
|
||||
|
||||
type reportCountRow struct {
|
||||
Day time.Time `gorm:"column:day"`
|
||||
Count int64 `gorm:"column:count"`
|
||||
}
|
||||
|
||||
func (s *creator) contentCount(ctx context.Context, tenantID int64) (int64, error) {
|
||||
tbl, q := models.ContentQuery.QueryContext(ctx)
|
||||
total, err := q.Where(tbl.TenantID.Eq(tenantID)).Count()
|
||||
if err != nil {
|
||||
return 0, errorx.ErrDatabaseError.WithCause(err)
|
||||
}
|
||||
return total, nil
|
||||
}
|
||||
|
||||
func (s *creator) contentCreatedAggregate(ctx context.Context, tenantID int64, rg reportRange) (int64, error) {
|
||||
tbl, q := models.ContentQuery.QueryContext(ctx)
|
||||
total, err := q.Where(
|
||||
tbl.TenantID.Eq(tenantID),
|
||||
tbl.CreatedAt.Gte(rg.startDay),
|
||||
tbl.CreatedAt.Lt(rg.endNext),
|
||||
).Count()
|
||||
if err != nil {
|
||||
return 0, errorx.ErrDatabaseError.WithCause(err)
|
||||
}
|
||||
return total, nil
|
||||
}
|
||||
|
||||
func (s *creator) contentCreatedSeries(ctx context.Context, tenantID int64, rg reportRange) (map[string]int64, error) {
|
||||
rows := make([]reportCountRow, 0)
|
||||
err := models.ContentQuery.WithContext(ctx).
|
||||
UnderlyingDB().
|
||||
Model(&models.Content{}).
|
||||
Select("date_trunc('day', created_at) as day, count(*) as count").
|
||||
Where("tenant_id = ? AND created_at >= ? AND created_at < ?", tenantID, rg.startDay, rg.endNext).
|
||||
Group("day").
|
||||
Scan(&rows).Error
|
||||
if err != nil {
|
||||
return nil, errorx.ErrDatabaseError.WithCause(err)
|
||||
}
|
||||
return buildCountSeries(rows), nil
|
||||
}
|
||||
|
||||
func (s *creator) contentActionAggregate(
|
||||
ctx context.Context,
|
||||
tenantID int64,
|
||||
actionType consts.UserContentActionType,
|
||||
rg reportRange,
|
||||
) (int64, error) {
|
||||
var total int64
|
||||
query := models.UserContentActionQuery.WithContext(ctx).
|
||||
UnderlyingDB().
|
||||
Model(&models.UserContentAction{}).
|
||||
Select("count(*)").
|
||||
Joins("join contents on contents.id = user_content_actions.content_id").
|
||||
Where("user_content_actions.type = ? AND user_content_actions.created_at >= ? AND user_content_actions.created_at < ? AND contents.tenant_id = ?",
|
||||
actionType, rg.startDay, rg.endNext, tenantID)
|
||||
if err := query.Scan(&total).Error; err != nil {
|
||||
return 0, errorx.ErrDatabaseError.WithCause(err)
|
||||
}
|
||||
return total, nil
|
||||
}
|
||||
|
||||
func (s *creator) contentActionSeries(
|
||||
ctx context.Context,
|
||||
tenantID int64,
|
||||
actionType consts.UserContentActionType,
|
||||
rg reportRange,
|
||||
) (map[string]int64, error) {
|
||||
rows := make([]reportCountRow, 0)
|
||||
err := models.UserContentActionQuery.WithContext(ctx).
|
||||
UnderlyingDB().
|
||||
Model(&models.UserContentAction{}).
|
||||
Select("date_trunc('day', user_content_actions.created_at) as day, count(*) as count").
|
||||
Joins("join contents on contents.id = user_content_actions.content_id").
|
||||
Where("user_content_actions.type = ? AND user_content_actions.created_at >= ? AND user_content_actions.created_at < ? AND contents.tenant_id = ?",
|
||||
actionType, rg.startDay, rg.endNext, tenantID).
|
||||
Group("day").
|
||||
Scan(&rows).Error
|
||||
if err != nil {
|
||||
return nil, errorx.ErrDatabaseError.WithCause(err)
|
||||
}
|
||||
return buildCountSeries(rows), nil
|
||||
}
|
||||
|
||||
func (s *creator) commentAggregate(ctx context.Context, tenantID int64, rg reportRange) (int64, error) {
|
||||
tbl, q := models.CommentQuery.QueryContext(ctx)
|
||||
total, err := q.Where(
|
||||
tbl.TenantID.Eq(tenantID),
|
||||
tbl.CreatedAt.Gte(rg.startDay),
|
||||
tbl.CreatedAt.Lt(rg.endNext),
|
||||
).Count()
|
||||
if err != nil {
|
||||
return 0, errorx.ErrDatabaseError.WithCause(err)
|
||||
}
|
||||
return total, nil
|
||||
}
|
||||
|
||||
func (s *creator) commentSeries(ctx context.Context, tenantID int64, rg reportRange) (map[string]int64, error) {
|
||||
rows := make([]reportCountRow, 0)
|
||||
err := models.CommentQuery.WithContext(ctx).
|
||||
UnderlyingDB().
|
||||
Model(&models.Comment{}).
|
||||
Select("date_trunc('day', created_at) as day, count(*) as count").
|
||||
Where("tenant_id = ? AND created_at >= ? AND created_at < ?", tenantID, rg.startDay, rg.endNext).
|
||||
Group("day").
|
||||
Scan(&rows).Error
|
||||
if err != nil {
|
||||
return nil, errorx.ErrDatabaseError.WithCause(err)
|
||||
}
|
||||
return buildCountSeries(rows), nil
|
||||
}
|
||||
|
||||
func buildCountSeries(rows []reportCountRow) map[string]int64 {
|
||||
result := make(map[string]int64, len(rows))
|
||||
for _, row := range rows {
|
||||
key := row.Day.Format("2006-01-02")
|
||||
result[key] = row.Count
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (s *creator) normalizeReportRange(filter *creator_dto.ReportOverviewFilter) (reportRange, error) {
|
||||
granularity := "day"
|
||||
if filter != nil && filter.Granularity != nil && strings.TrimSpace(*filter.Granularity) != "" {
|
||||
|
||||
@@ -394,6 +394,8 @@ func (s *CreatorTestSuite) Test_ReportOverview() {
|
||||
models.TableNameUser,
|
||||
models.TableNameContent,
|
||||
models.TableNameOrder,
|
||||
models.TableNameUserContentAction,
|
||||
models.TableNameComment,
|
||||
)
|
||||
|
||||
owner := &models.User{Username: "owner_r", Phone: "13900001011"}
|
||||
@@ -405,18 +407,23 @@ func (s *CreatorTestSuite) Test_ReportOverview() {
|
||||
}
|
||||
models.TenantQuery.WithContext(ctx).Create(tenant)
|
||||
|
||||
models.ContentQuery.WithContext(ctx).Create(&models.Content{
|
||||
content := &models.Content{
|
||||
TenantID: tenant.ID,
|
||||
UserID: owner.ID,
|
||||
Title: "Content A",
|
||||
Status: consts.ContentStatusPublished,
|
||||
Views: 100,
|
||||
})
|
||||
}
|
||||
models.ContentQuery.WithContext(ctx).Create(content)
|
||||
|
||||
now := time.Now()
|
||||
inRangePaidAt := now.Add(-12 * time.Hour)
|
||||
outRangePaidAt := now.Add(-10 * 24 * time.Hour)
|
||||
|
||||
likeAt := now.Add(-2 * time.Hour)
|
||||
favoriteAt := now.Add(-3 * time.Hour)
|
||||
commentAt := now.Add(-4 * time.Hour)
|
||||
|
||||
models.OrderQuery.WithContext(ctx).Create(
|
||||
&models.Order{
|
||||
TenantID: tenant.ID,
|
||||
@@ -442,8 +449,55 @@ func (s *CreatorTestSuite) Test_ReportOverview() {
|
||||
AmountPaid: 500,
|
||||
UpdatedAt: now.Add(-6 * time.Hour),
|
||||
},
|
||||
&models.Order{
|
||||
TenantID: tenant.ID,
|
||||
UserID: owner.ID,
|
||||
Type: consts.OrderTypeWithdrawal,
|
||||
Status: consts.OrderStatusCreated,
|
||||
AmountPaid: 300,
|
||||
CreatedAt: now.Add(-5 * time.Hour),
|
||||
},
|
||||
&models.Order{
|
||||
TenantID: tenant.ID,
|
||||
UserID: owner.ID,
|
||||
Type: consts.OrderTypeWithdrawal,
|
||||
Status: consts.OrderStatusPaid,
|
||||
AmountPaid: 800,
|
||||
PaidAt: now.Add(-3 * time.Hour),
|
||||
},
|
||||
&models.Order{
|
||||
TenantID: tenant.ID,
|
||||
UserID: owner.ID,
|
||||
Type: consts.OrderTypeWithdrawal,
|
||||
Status: consts.OrderStatusFailed,
|
||||
AmountPaid: 500,
|
||||
UpdatedAt: now.Add(-2 * time.Hour),
|
||||
},
|
||||
)
|
||||
|
||||
models.UserContentActionQuery.WithContext(ctx).Create(
|
||||
&models.UserContentAction{
|
||||
UserID: owner.ID,
|
||||
ContentID: content.ID,
|
||||
Type: string(consts.UserContentActionTypeLike),
|
||||
CreatedAt: likeAt,
|
||||
},
|
||||
&models.UserContentAction{
|
||||
UserID: owner.ID,
|
||||
ContentID: content.ID,
|
||||
Type: string(consts.UserContentActionTypeFavorite),
|
||||
CreatedAt: favoriteAt,
|
||||
},
|
||||
)
|
||||
|
||||
models.CommentQuery.WithContext(ctx).Create(&models.Comment{
|
||||
TenantID: tenant.ID,
|
||||
UserID: owner.ID,
|
||||
ContentID: content.ID,
|
||||
Content: "Nice",
|
||||
CreatedAt: commentAt,
|
||||
})
|
||||
|
||||
start := now.Add(-24 * time.Hour).Format(time.RFC3339)
|
||||
end := now.Format(time.RFC3339)
|
||||
report, err := Creator.ReportOverview(ctx, tenant.ID, owner.ID, &creator_dto.ReportOverviewFilter{
|
||||
@@ -452,10 +506,21 @@ func (s *CreatorTestSuite) Test_ReportOverview() {
|
||||
})
|
||||
So(err, ShouldBeNil)
|
||||
So(report.Summary.TotalViews, ShouldEqual, 100)
|
||||
So(report.Summary.ContentCount, ShouldEqual, 1)
|
||||
So(report.Summary.ContentCreated, ShouldEqual, 1)
|
||||
So(report.Summary.LikeActions, ShouldEqual, 1)
|
||||
So(report.Summary.FavoriteActions, ShouldEqual, 1)
|
||||
So(report.Summary.CommentCount, ShouldEqual, 1)
|
||||
So(report.Summary.PaidOrders, ShouldEqual, 1)
|
||||
So(report.Summary.PaidAmount, ShouldEqual, 10.0)
|
||||
So(report.Summary.RefundOrders, ShouldEqual, 1)
|
||||
So(report.Summary.RefundAmount, ShouldEqual, 5.0)
|
||||
So(report.Summary.WithdrawalApplyOrders, ShouldEqual, 1)
|
||||
So(report.Summary.WithdrawalApplyAmount, ShouldEqual, 3.0)
|
||||
So(report.Summary.WithdrawalPaidOrders, ShouldEqual, 1)
|
||||
So(report.Summary.WithdrawalPaidAmount, ShouldEqual, 8.0)
|
||||
So(report.Summary.WithdrawalFailedOrders, ShouldEqual, 1)
|
||||
So(report.Summary.WithdrawalFailedAmount, ShouldEqual, 5.0)
|
||||
|
||||
var paidSum, refundSum int64
|
||||
for _, item := range report.Items {
|
||||
@@ -475,6 +540,8 @@ func (s *CreatorTestSuite) Test_ExportReport() {
|
||||
models.TableNameUser,
|
||||
models.TableNameContent,
|
||||
models.TableNameOrder,
|
||||
models.TableNameUserContentAction,
|
||||
models.TableNameComment,
|
||||
)
|
||||
|
||||
owner := &models.User{Username: "owner_e", Phone: "13900001012"}
|
||||
@@ -507,6 +574,6 @@ func (s *CreatorTestSuite) Test_ExportReport() {
|
||||
resp, err := Creator.ExportReport(ctx, tenant.ID, owner.ID, form)
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.Filename, ShouldNotBeBlank)
|
||||
So(resp.Content, ShouldContainSubstring, "date,paid_orders,paid_amount,refund_orders,refund_amount")
|
||||
So(resp.Content, ShouldContainSubstring, "date,paid_orders,paid_amount,refund_orders,refund_amount,withdrawal_apply_orders,withdrawal_apply_amount,withdrawal_paid_orders,withdrawal_paid_amount,withdrawal_failed_orders,withdrawal_failed_amount,content_created,like_actions,favorite_actions,comment_count")
|
||||
})
|
||||
}
|
||||
|
||||
@@ -5553,12 +5553,48 @@ func (s *super) ReportOverview(ctx context.Context, filter *super_dto.SuperRepor
|
||||
return nil, errorx.ErrDatabaseError.WithCause(err)
|
||||
}
|
||||
|
||||
// 订单仅统计内容购买类型,并按状态划分已支付/已退款。
|
||||
paidCount, paidAmount, err := s.reportOrderAggregate(ctx, tenantID, consts.OrderStatusPaid, "paid_at", rg)
|
||||
// 内容规模与互动指标。
|
||||
contentCount, err := s.contentCount(ctx, tenantID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
refundCount, refundAmount, err := s.reportOrderAggregate(ctx, tenantID, consts.OrderStatusRefunded, "updated_at", rg)
|
||||
contentCreated, err := s.contentCreatedAggregate(ctx, tenantID, rg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
likeActions, err := s.contentActionAggregate(ctx, tenantID, consts.UserContentActionTypeLike, rg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
favoriteActions, err := s.contentActionAggregate(ctx, tenantID, consts.UserContentActionTypeFavorite, rg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
commentCount, err := s.commentAggregate(ctx, tenantID, rg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 订单仅统计内容购买类型,并按状态划分已支付/已退款。
|
||||
paidCount, paidAmount, err := s.reportOrderAggregate(ctx, tenantID, consts.OrderTypeContentPurchase, consts.OrderStatusPaid, "paid_at", rg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
refundCount, refundAmount, err := s.reportOrderAggregate(ctx, tenantID, consts.OrderTypeContentPurchase, consts.OrderStatusRefunded, "updated_at", rg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 提现维度统计(申请/完成/失败)。
|
||||
withdrawApplyCount, withdrawApplyAmount, err := s.reportOrderAggregate(ctx, tenantID, consts.OrderTypeWithdrawal, consts.OrderStatusCreated, "created_at", rg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
withdrawPaidCount, withdrawPaidAmount, err := s.reportOrderAggregate(ctx, tenantID, consts.OrderTypeWithdrawal, consts.OrderStatusPaid, "paid_at", rg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
withdrawFailedCount, withdrawFailedAmount, err := s.reportOrderAggregate(ctx, tenantID, consts.OrderTypeWithdrawal, consts.OrderStatusFailed, "updated_at", rg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -5568,11 +5604,39 @@ func (s *super) ReportOverview(ctx context.Context, filter *super_dto.SuperRepor
|
||||
conversionRate = float64(paidCount) / float64(totalViews)
|
||||
}
|
||||
|
||||
paidSeries, err := s.reportOrderSeries(ctx, tenantID, consts.OrderStatusPaid, "paid_at", rg)
|
||||
paidSeries, err := s.reportOrderSeries(ctx, tenantID, consts.OrderTypeContentPurchase, consts.OrderStatusPaid, "paid_at", rg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
refundSeries, err := s.reportOrderSeries(ctx, tenantID, consts.OrderStatusRefunded, "updated_at", rg)
|
||||
refundSeries, err := s.reportOrderSeries(ctx, tenantID, consts.OrderTypeContentPurchase, consts.OrderStatusRefunded, "updated_at", rg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
withdrawApplySeries, err := s.reportOrderSeries(ctx, tenantID, consts.OrderTypeWithdrawal, consts.OrderStatusCreated, "created_at", rg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
withdrawPaidSeries, err := s.reportOrderSeries(ctx, tenantID, consts.OrderTypeWithdrawal, consts.OrderStatusPaid, "paid_at", rg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
withdrawFailedSeries, err := s.reportOrderSeries(ctx, tenantID, consts.OrderTypeWithdrawal, consts.OrderStatusFailed, "updated_at", rg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
contentCreatedSeries, err := s.contentCreatedSeries(ctx, tenantID, rg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
likeSeries, err := s.contentActionSeries(ctx, tenantID, consts.UserContentActionTypeLike, rg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
favoriteSeries, err := s.contentActionSeries(ctx, tenantID, consts.UserContentActionTypeFavorite, rg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
commentSeries, err := s.commentSeries(ctx, tenantID, rg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -5582,23 +5646,47 @@ func (s *super) ReportOverview(ctx context.Context, filter *super_dto.SuperRepor
|
||||
key := day.Format("2006-01-02")
|
||||
paidItem := paidSeries[key]
|
||||
refundItem := refundSeries[key]
|
||||
withdrawApplyItem := withdrawApplySeries[key]
|
||||
withdrawPaidItem := withdrawPaidSeries[key]
|
||||
withdrawFailedItem := withdrawFailedSeries[key]
|
||||
items = append(items, v1_dto.ReportOverviewItem{
|
||||
Date: key,
|
||||
PaidOrders: paidItem.Count,
|
||||
PaidAmount: float64(paidItem.Amount) / 100.0,
|
||||
RefundOrders: refundItem.Count,
|
||||
RefundAmount: float64(refundItem.Amount) / 100.0,
|
||||
Date: key,
|
||||
PaidOrders: paidItem.Count,
|
||||
PaidAmount: float64(paidItem.Amount) / 100.0,
|
||||
RefundOrders: refundItem.Count,
|
||||
RefundAmount: float64(refundItem.Amount) / 100.0,
|
||||
WithdrawalApplyOrders: withdrawApplyItem.Count,
|
||||
WithdrawalApplyAmount: float64(withdrawApplyItem.Amount) / 100.0,
|
||||
WithdrawalPaidOrders: withdrawPaidItem.Count,
|
||||
WithdrawalPaidAmount: float64(withdrawPaidItem.Amount) / 100.0,
|
||||
WithdrawalFailedOrders: withdrawFailedItem.Count,
|
||||
WithdrawalFailedAmount: float64(withdrawFailedItem.Amount) / 100.0,
|
||||
ContentCreated: contentCreatedSeries[key],
|
||||
LikeActions: likeSeries[key],
|
||||
FavoriteActions: favoriteSeries[key],
|
||||
CommentCount: commentSeries[key],
|
||||
})
|
||||
}
|
||||
|
||||
return &v1_dto.ReportOverviewResponse{
|
||||
Summary: v1_dto.ReportSummary{
|
||||
TotalViews: totalViews,
|
||||
PaidOrders: paidCount,
|
||||
PaidAmount: float64(paidAmount) / 100.0,
|
||||
RefundOrders: refundCount,
|
||||
RefundAmount: float64(refundAmount) / 100.0,
|
||||
ConversionRate: conversionRate,
|
||||
TotalViews: totalViews,
|
||||
ContentCount: contentCount,
|
||||
ContentCreated: contentCreated,
|
||||
LikeActions: likeActions,
|
||||
FavoriteActions: favoriteActions,
|
||||
CommentCount: commentCount,
|
||||
PaidOrders: paidCount,
|
||||
PaidAmount: float64(paidAmount) / 100.0,
|
||||
RefundOrders: refundCount,
|
||||
RefundAmount: float64(refundAmount) / 100.0,
|
||||
WithdrawalApplyOrders: withdrawApplyCount,
|
||||
WithdrawalApplyAmount: float64(withdrawApplyAmount) / 100.0,
|
||||
WithdrawalPaidOrders: withdrawPaidCount,
|
||||
WithdrawalPaidAmount: float64(withdrawPaidAmount) / 100.0,
|
||||
WithdrawalFailedOrders: withdrawFailedCount,
|
||||
WithdrawalFailedAmount: float64(withdrawFailedAmount) / 100.0,
|
||||
ConversionRate: conversionRate,
|
||||
},
|
||||
Items: items,
|
||||
}, nil
|
||||
@@ -5627,7 +5715,7 @@ func (s *super) ExportReport(ctx context.Context, form *super_dto.SuperReportExp
|
||||
}
|
||||
|
||||
builder := &strings.Builder{}
|
||||
builder.WriteString("date,paid_orders,paid_amount,refund_orders,refund_amount\n")
|
||||
builder.WriteString("date,paid_orders,paid_amount,refund_orders,refund_amount,withdrawal_apply_orders,withdrawal_apply_amount,withdrawal_paid_orders,withdrawal_paid_amount,withdrawal_failed_orders,withdrawal_failed_amount,content_created,like_actions,favorite_actions,comment_count\n")
|
||||
for _, item := range overview.Items {
|
||||
builder.WriteString(item.Date)
|
||||
builder.WriteString(",")
|
||||
@@ -5638,6 +5726,26 @@ func (s *super) ExportReport(ctx context.Context, form *super_dto.SuperReportExp
|
||||
builder.WriteString(strconv.FormatInt(item.RefundOrders, 10))
|
||||
builder.WriteString(",")
|
||||
builder.WriteString(formatAmount(item.RefundAmount))
|
||||
builder.WriteString(",")
|
||||
builder.WriteString(strconv.FormatInt(item.WithdrawalApplyOrders, 10))
|
||||
builder.WriteString(",")
|
||||
builder.WriteString(formatAmount(item.WithdrawalApplyAmount))
|
||||
builder.WriteString(",")
|
||||
builder.WriteString(strconv.FormatInt(item.WithdrawalPaidOrders, 10))
|
||||
builder.WriteString(",")
|
||||
builder.WriteString(formatAmount(item.WithdrawalPaidAmount))
|
||||
builder.WriteString(",")
|
||||
builder.WriteString(strconv.FormatInt(item.WithdrawalFailedOrders, 10))
|
||||
builder.WriteString(",")
|
||||
builder.WriteString(formatAmount(item.WithdrawalFailedAmount))
|
||||
builder.WriteString(",")
|
||||
builder.WriteString(strconv.FormatInt(item.ContentCreated, 10))
|
||||
builder.WriteString(",")
|
||||
builder.WriteString(strconv.FormatInt(item.LikeActions, 10))
|
||||
builder.WriteString(",")
|
||||
builder.WriteString(strconv.FormatInt(item.FavoriteActions, 10))
|
||||
builder.WriteString(",")
|
||||
builder.WriteString(strconv.FormatInt(item.CommentCount, 10))
|
||||
builder.WriteString("\n")
|
||||
}
|
||||
|
||||
@@ -5649,9 +5757,126 @@ func (s *super) ExportReport(ctx context.Context, form *super_dto.SuperReportExp
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *super) contentCount(ctx context.Context, tenantID int64) (int64, error) {
|
||||
tbl, q := models.ContentQuery.QueryContext(ctx)
|
||||
if tenantID > 0 {
|
||||
q = q.Where(tbl.TenantID.Eq(tenantID))
|
||||
}
|
||||
total, err := q.Count()
|
||||
if err != nil {
|
||||
return 0, errorx.ErrDatabaseError.WithCause(err)
|
||||
}
|
||||
return total, nil
|
||||
}
|
||||
|
||||
func (s *super) contentCreatedAggregate(ctx context.Context, tenantID int64, rg reportRange) (int64, error) {
|
||||
tbl, q := models.ContentQuery.QueryContext(ctx)
|
||||
q = q.Where(tbl.CreatedAt.Gte(rg.startDay), tbl.CreatedAt.Lt(rg.endNext))
|
||||
if tenantID > 0 {
|
||||
q = q.Where(tbl.TenantID.Eq(tenantID))
|
||||
}
|
||||
total, err := q.Count()
|
||||
if err != nil {
|
||||
return 0, errorx.ErrDatabaseError.WithCause(err)
|
||||
}
|
||||
return total, nil
|
||||
}
|
||||
|
||||
func (s *super) contentCreatedSeries(ctx context.Context, tenantID int64, rg reportRange) (map[string]int64, error) {
|
||||
rows := make([]reportCountRow, 0)
|
||||
query := models.ContentQuery.WithContext(ctx).
|
||||
UnderlyingDB().
|
||||
Model(&models.Content{}).
|
||||
Select("date_trunc('day', created_at) as day, count(*) as count").
|
||||
Where("created_at >= ? AND created_at < ?", rg.startDay, rg.endNext)
|
||||
if tenantID > 0 {
|
||||
query = query.Where("tenant_id = ?", tenantID)
|
||||
}
|
||||
if err := query.Group("day").Scan(&rows).Error; err != nil {
|
||||
return nil, errorx.ErrDatabaseError.WithCause(err)
|
||||
}
|
||||
return buildCountSeries(rows), nil
|
||||
}
|
||||
|
||||
func (s *super) contentActionAggregate(
|
||||
ctx context.Context,
|
||||
tenantID int64,
|
||||
actionType consts.UserContentActionType,
|
||||
rg reportRange,
|
||||
) (int64, error) {
|
||||
var total int64
|
||||
query := models.UserContentActionQuery.WithContext(ctx).
|
||||
UnderlyingDB().
|
||||
Model(&models.UserContentAction{}).
|
||||
Select("count(*)").
|
||||
Where("user_content_actions.type = ? AND user_content_actions.created_at >= ? AND user_content_actions.created_at < ?",
|
||||
actionType, rg.startDay, rg.endNext)
|
||||
if tenantID > 0 {
|
||||
query = query.Joins("join contents on contents.id = user_content_actions.content_id").
|
||||
Where("contents.tenant_id = ?", tenantID)
|
||||
}
|
||||
if err := query.Scan(&total).Error; err != nil {
|
||||
return 0, errorx.ErrDatabaseError.WithCause(err)
|
||||
}
|
||||
return total, nil
|
||||
}
|
||||
|
||||
func (s *super) contentActionSeries(
|
||||
ctx context.Context,
|
||||
tenantID int64,
|
||||
actionType consts.UserContentActionType,
|
||||
rg reportRange,
|
||||
) (map[string]int64, error) {
|
||||
rows := make([]reportCountRow, 0)
|
||||
query := models.UserContentActionQuery.WithContext(ctx).
|
||||
UnderlyingDB().
|
||||
Model(&models.UserContentAction{}).
|
||||
Select("date_trunc('day', user_content_actions.created_at) as day, count(*) as count").
|
||||
Where("user_content_actions.type = ? AND user_content_actions.created_at >= ? AND user_content_actions.created_at < ?",
|
||||
actionType, rg.startDay, rg.endNext)
|
||||
if tenantID > 0 {
|
||||
query = query.Joins("join contents on contents.id = user_content_actions.content_id").
|
||||
Where("contents.tenant_id = ?", tenantID)
|
||||
}
|
||||
if err := query.Group("day").Scan(&rows).Error; err != nil {
|
||||
return nil, errorx.ErrDatabaseError.WithCause(err)
|
||||
}
|
||||
return buildCountSeries(rows), nil
|
||||
}
|
||||
|
||||
func (s *super) commentAggregate(ctx context.Context, tenantID int64, rg reportRange) (int64, error) {
|
||||
tbl, q := models.CommentQuery.QueryContext(ctx)
|
||||
q = q.Where(tbl.CreatedAt.Gte(rg.startDay), tbl.CreatedAt.Lt(rg.endNext))
|
||||
if tenantID > 0 {
|
||||
q = q.Where(tbl.TenantID.Eq(tenantID))
|
||||
}
|
||||
total, err := q.Count()
|
||||
if err != nil {
|
||||
return 0, errorx.ErrDatabaseError.WithCause(err)
|
||||
}
|
||||
return total, nil
|
||||
}
|
||||
|
||||
func (s *super) commentSeries(ctx context.Context, tenantID int64, rg reportRange) (map[string]int64, error) {
|
||||
rows := make([]reportCountRow, 0)
|
||||
query := models.CommentQuery.WithContext(ctx).
|
||||
UnderlyingDB().
|
||||
Model(&models.Comment{}).
|
||||
Select("date_trunc('day', created_at) as day, count(*) as count").
|
||||
Where("created_at >= ? AND created_at < ?", rg.startDay, rg.endNext)
|
||||
if tenantID > 0 {
|
||||
query = query.Where("tenant_id = ?", tenantID)
|
||||
}
|
||||
if err := query.Group("day").Scan(&rows).Error; err != nil {
|
||||
return nil, errorx.ErrDatabaseError.WithCause(err)
|
||||
}
|
||||
return buildCountSeries(rows), nil
|
||||
}
|
||||
|
||||
func (s *super) reportOrderAggregate(
|
||||
ctx context.Context,
|
||||
tenantID int64,
|
||||
orderType consts.OrderType,
|
||||
status consts.OrderStatus,
|
||||
timeField string,
|
||||
rg reportRange,
|
||||
@@ -5666,7 +5891,7 @@ func (s *super) reportOrderAggregate(
|
||||
Model(&models.Order{}).
|
||||
Select("count(*) as count, coalesce(sum(amount_paid), 0) as amount").
|
||||
Where("type = ? AND status = ? AND "+timeField+" >= ? AND "+timeField+" < ?",
|
||||
consts.OrderTypeContentPurchase, status, rg.startDay, rg.endNext)
|
||||
orderType, status, rg.startDay, rg.endNext)
|
||||
if tenantID > 0 {
|
||||
query = query.Where("tenant_id = ?", tenantID)
|
||||
}
|
||||
@@ -5680,6 +5905,7 @@ func (s *super) reportOrderAggregate(
|
||||
func (s *super) reportOrderSeries(
|
||||
ctx context.Context,
|
||||
tenantID int64,
|
||||
orderType consts.OrderType,
|
||||
status consts.OrderStatus,
|
||||
timeField string,
|
||||
rg reportRange,
|
||||
@@ -5690,7 +5916,7 @@ func (s *super) reportOrderSeries(
|
||||
Model(&models.Order{}).
|
||||
Select("date_trunc('day', "+timeField+") as day, count(*) as count, coalesce(sum(amount_paid), 0) as amount").
|
||||
Where("type = ? AND status = ? AND "+timeField+" >= ? AND "+timeField+" < ?",
|
||||
consts.OrderTypeContentPurchase, status, rg.startDay, rg.endNext)
|
||||
orderType, status, rg.startDay, rg.endNext)
|
||||
if tenantID > 0 {
|
||||
query = query.Where("tenant_id = ?", tenantID)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user