x
This commit is contained in:
parent
af5231747a
commit
5b238919f5
@ -122,10 +122,12 @@ type (
|
|||||||
GetAgentRealtimeRequest {}
|
GetAgentRealtimeRequest {}
|
||||||
// GetAgentRealtimeResponse - 代理链接实时数据响应
|
// GetAgentRealtimeResponse - 代理链接实时数据响应
|
||||||
GetAgentRealtimeResponse {
|
GetAgentRealtimeResponse {
|
||||||
Total int64 `json:"total"` // 访问总人数
|
Total int64 `json:"total"` // 访问总人数
|
||||||
Clicks int64 `json:"clicks"` // 点击量
|
Clicks int64 `json:"clicks"` // 点击量
|
||||||
Views int64 `json:"views"` // 浏览量
|
Views int64 `json:"views"` // 浏览量
|
||||||
PaidCount int64 `json:"paid_count"` // 付费数量
|
PaidCount int64 `json:"paid_count"` // 付费数量
|
||||||
|
GrowthRate string `json:"growth_rate"` // 访问量环比增长率(例如:"+10.5%"、"-5.2%"、"0%")
|
||||||
|
PaidGrowthRate string `json:"paid_growth_rate"` // 付费用户环比增长率(例如:"+20.0%"、"-10.0%"、"0%")
|
||||||
}
|
}
|
||||||
// GetUserInviteStatsRequest - 获取用户邀请统计
|
// GetUserInviteStatsRequest - 获取用户邀请统计
|
||||||
GetUserInviteStatsRequest {}
|
GetUserInviteStatsRequest {}
|
||||||
|
|||||||
@ -2,9 +2,9 @@ package user
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/perfect-panel/server/internal/model/user"
|
"github.com/perfect-panel/server/internal/model/user"
|
||||||
"github.com/perfect-panel/server/internal/svc"
|
"github.com/perfect-panel/server/internal/svc"
|
||||||
@ -85,17 +85,32 @@ func (l *GetAgentRealtimeLogic) GetAgentRealtime(req *types.GetAgentRealtimeRequ
|
|||||||
|
|
||||||
link, err := client.CreateShortLink(l.ctx, kuttReq)
|
link, err := client.CreateShortLink(l.ctx, kuttReq)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.Errorw("Failed to fetch Kutt stats",
|
l.Errorw("Failed to create/fetch Kutt link",
|
||||||
logger.Field("error", err.Error()),
|
logger.Field("error", err.Error()),
|
||||||
logger.Field("user_id", u.Id),
|
logger.Field("user_id", u.Id),
|
||||||
logger.Field("target", target))
|
logger.Field("target", target))
|
||||||
// Return 0 on error, don't block the UI
|
// Return 0 on error, don't block the UI
|
||||||
return &types.GetAgentRealtimeResponse{
|
return &types.GetAgentRealtimeResponse{
|
||||||
Total: 0,
|
Total: 0,
|
||||||
|
GrowthRate: "0%",
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6. Get paid user count
|
// 6. Get detailed stats for growth rate calculation
|
||||||
|
stats, err := client.GetLinkStats(l.ctx, link.ID)
|
||||||
|
var growthRate string
|
||||||
|
if err != nil {
|
||||||
|
l.Errorw("Failed to fetch Kutt detailed stats, using basic count only",
|
||||||
|
logger.Field("error", err.Error()),
|
||||||
|
logger.Field("link_id", link.ID))
|
||||||
|
growthRate = "N/A"
|
||||||
|
} else {
|
||||||
|
// Calculate month-over-month growth rate
|
||||||
|
// lastYear.views is an array of 12 months, last element is current month, second-to-last is previous month
|
||||||
|
growthRate = calculateGrowthRate(stats.LastYear.Views)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. Get paid user count
|
||||||
var paidCount int64
|
var paidCount int64
|
||||||
db := l.svcCtx.DB
|
db := l.svcCtx.DB
|
||||||
// Count users invited by me who have paid orders
|
// Count users invited by me who have paid orders
|
||||||
@ -115,11 +130,16 @@ func (l *GetAgentRealtimeLogic) GetAgentRealtime(req *types.GetAgentRealtimeRequ
|
|||||||
paidCount = 0
|
paidCount = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 8. Calculate paid user growth rate (month-over-month)
|
||||||
|
paidGrowthRate := l.calculatePaidGrowthRate(u.Id)
|
||||||
|
|
||||||
return &types.GetAgentRealtimeResponse{
|
return &types.GetAgentRealtimeResponse{
|
||||||
Total: int64(link.VisitCount),
|
Total: int64(link.VisitCount),
|
||||||
Clicks: int64(link.VisitCount),
|
Clicks: int64(link.VisitCount),
|
||||||
Views: int64(link.VisitCount),
|
Views: int64(link.VisitCount),
|
||||||
PaidCount: paidCount,
|
PaidCount: paidCount,
|
||||||
|
GrowthRate: growthRate,
|
||||||
|
PaidGrowthRate: paidGrowthRate,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,3 +168,78 @@ func (l *GetAgentRealtimeLogic) getDomain() string {
|
|||||||
}
|
}
|
||||||
return l.svcCtx.Config.Kutt.Domain
|
return l.svcCtx.Config.Kutt.Domain
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// calculatePaidGrowthRate 计算付费用户的环比增长率
|
||||||
|
func (l *GetAgentRealtimeLogic) calculatePaidGrowthRate(userId int64) string {
|
||||||
|
db := l.svcCtx.DB
|
||||||
|
|
||||||
|
// 获取本月第一天和上月第一天
|
||||||
|
now := time.Now()
|
||||||
|
currentMonthStart := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location())
|
||||||
|
lastMonthStart := currentMonthStart.AddDate(0, -1, 0)
|
||||||
|
|
||||||
|
// 查询本月付费用户数(本月有新订单的)
|
||||||
|
var currentMonthCount int64
|
||||||
|
err := db.Table("`order` o").
|
||||||
|
Joins("JOIN user u ON o.user_id = u.id").
|
||||||
|
Where("u.referer_id = ? AND o.status IN (?, ?) AND o.created_at >= ?",
|
||||||
|
userId, 2, 5, currentMonthStart).
|
||||||
|
Distinct("o.user_id").
|
||||||
|
Count(¤tMonthCount).Error
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
l.Errorw("Failed to count current month paid users",
|
||||||
|
logger.Field("error", err.Error()),
|
||||||
|
logger.Field("user_id", userId))
|
||||||
|
return "N/A"
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询上月付费用户数
|
||||||
|
var lastMonthCount int64
|
||||||
|
err = db.Table("`order` o").
|
||||||
|
Joins("JOIN user u ON o.user_id = u.id").
|
||||||
|
Where("u.referer_id = ? AND o.status IN (?, ?) AND o.created_at >= ? AND o.created_at < ?",
|
||||||
|
userId, 2, 5, lastMonthStart, currentMonthStart).
|
||||||
|
Distinct("o.user_id").
|
||||||
|
Count(&lastMonthCount).Error
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
l.Errorw("Failed to count last month paid users",
|
||||||
|
logger.Field("error", err.Error()),
|
||||||
|
logger.Field("user_id", userId))
|
||||||
|
return "N/A"
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算增长率
|
||||||
|
return calculateGrowthRate([]int{int(lastMonthCount), int(currentMonthCount)})
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculateGrowthRate 计算环比增长率
|
||||||
|
// views: 月份数据数组,最后一个是本月,倒数第二个是上月
|
||||||
|
func calculateGrowthRate(views []int) string {
|
||||||
|
if len(views) < 2 {
|
||||||
|
return "N/A"
|
||||||
|
}
|
||||||
|
|
||||||
|
currentMonth := views[len(views)-1]
|
||||||
|
lastMonth := views[len(views)-2]
|
||||||
|
|
||||||
|
// 如果上月是0,无法计算百分比
|
||||||
|
if lastMonth == 0 {
|
||||||
|
if currentMonth == 0 {
|
||||||
|
return "0%"
|
||||||
|
}
|
||||||
|
return "+100%"
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算增长率
|
||||||
|
growth := float64(currentMonth-lastMonth) / float64(lastMonth) * 100
|
||||||
|
|
||||||
|
// 格式化输出
|
||||||
|
if growth > 0 {
|
||||||
|
return fmt.Sprintf("+%.1f%%", growth)
|
||||||
|
} else if growth < 0 {
|
||||||
|
return fmt.Sprintf("%.1f%%", growth)
|
||||||
|
}
|
||||||
|
return "0%"
|
||||||
|
}
|
||||||
|
|||||||
@ -111,7 +111,7 @@ func (l *GetInviteSalesLogic) GetInviteSales(req *types.GetInviteSalesRequest) (
|
|||||||
}
|
}
|
||||||
|
|
||||||
list = append(list, types.InvitedUserSale{
|
list = append(list, types.InvitedUserSale{
|
||||||
Amount: order.Amount,
|
Amount: float64(order.Amount) / 100.0, // Convert cents to dollars
|
||||||
CreatedAt: order.CreatedAt,
|
CreatedAt: order.CreatedAt,
|
||||||
UserEmail: email,
|
UserEmail: email,
|
||||||
UserId: order.UserId,
|
UserId: order.UserId,
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package user
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
|
||||||
"github.com/perfect-panel/server/internal/model/user"
|
"github.com/perfect-panel/server/internal/model/user"
|
||||||
"github.com/perfect-panel/server/internal/svc"
|
"github.com/perfect-panel/server/internal/svc"
|
||||||
@ -36,21 +37,26 @@ func (l *GetUserInviteStatsLogic) GetUserInviteStats(req *types.GetUserInviteSta
|
|||||||
}
|
}
|
||||||
userId := u.Id
|
userId := u.Id
|
||||||
|
|
||||||
// 2. 获取有效邀请数 (FriendlyCount): 被邀请用户中至少有1个已支付订单 (status=2或5)
|
// 2. 获取历史邀请佣金 (FriendlyCount): 所有被邀请用户产生订单的佣金总和
|
||||||
var friendlyCount int64
|
// 注意:这里复用了 friendly_count 字段名,实际含义是佣金总额
|
||||||
|
var totalCommission sql.NullInt64
|
||||||
err = l.svcCtx.DB.WithContext(l.ctx).
|
err = l.svcCtx.DB.WithContext(l.ctx).
|
||||||
Table("user").
|
Table("`order` o").
|
||||||
Where("referer_id = ? AND EXISTS (SELECT 1 FROM `order` o WHERE o.user_id = user.id AND o.status IN (?, ?))", userId, 2, 5).
|
Select("COALESCE(SUM(o.commission), 0) as total").
|
||||||
Count(&friendlyCount).Error
|
Joins("JOIN user u ON o.user_id = u.id").
|
||||||
|
Where("u.referer_id = ? AND o.status IN (?, ?)", userId, 2, 5). // 只统计已支付和已完成的订单
|
||||||
|
Scan(&totalCommission).Error
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.Errorw("[GetUserInviteStats] count friendly users failed",
|
l.Errorw("[GetUserInviteStats] sum commission failed",
|
||||||
logger.Field("error", err.Error()),
|
logger.Field("error", err.Error()),
|
||||||
logger.Field("user_id", userId))
|
logger.Field("user_id", userId))
|
||||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError),
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError),
|
||||||
"count friendly users failed: %v", err.Error())
|
"sum commission failed: %v", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
friendlyCount := totalCommission.Int64
|
||||||
|
|
||||||
// 3. 获取历史邀请总数 (HistoryCount)
|
// 3. 获取历史邀请总数 (HistoryCount)
|
||||||
var historyCount int64
|
var historyCount int64
|
||||||
err = l.svcCtx.DB.WithContext(l.ctx).
|
err = l.svcCtx.DB.WithContext(l.ctx).
|
||||||
|
|||||||
@ -909,10 +909,12 @@ type GetGlobalConfigResponse struct {
|
|||||||
type GetAgentRealtimeRequest struct{}
|
type GetAgentRealtimeRequest struct{}
|
||||||
|
|
||||||
type GetAgentRealtimeResponse struct {
|
type GetAgentRealtimeResponse struct {
|
||||||
Total int64 `json:"total"` // 访问总人数
|
Total int64 `json:"total"` // 访问总人数
|
||||||
Clicks int64 `json:"clicks"` // 点击量
|
Clicks int64 `json:"clicks"` // 点击量
|
||||||
Views int64 `json:"views"` // 浏览量
|
Views int64 `json:"views"` // 浏览量
|
||||||
PaidCount int64 `json:"paid_count"` // 付费数量
|
PaidCount int64 `json:"paid_count"` // 付费数量
|
||||||
|
GrowthRate string `json:"growth_rate"` // 访问量环比增长率
|
||||||
|
PaidGrowthRate string `json:"paid_growth_rate"` // 付费用户环比增长率
|
||||||
}
|
}
|
||||||
|
|
||||||
type GetAgentDownloadsRequest struct{}
|
type GetAgentDownloadsRequest struct{}
|
||||||
@ -945,10 +947,10 @@ type GetInviteSalesResponse struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type InvitedUserSale struct {
|
type InvitedUserSale struct {
|
||||||
Amount int64 `json:"amount"`
|
Amount float64 `json:"amount"`
|
||||||
CreatedAt int64 `json:"created_at"`
|
CreatedAt int64 `json:"created_at"`
|
||||||
UserEmail string `json:"user_email"`
|
UserEmail string `json:"user_email"`
|
||||||
UserId int64 `json:"user_id"`
|
UserId int64 `json:"user_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type GetLoginLogRequest struct {
|
type GetLoginLogRequest struct {
|
||||||
|
|||||||
@ -161,3 +161,89 @@ func (c *Client) CreateInviteShortLink(ctx context.Context, baseURL, inviteCode,
|
|||||||
|
|
||||||
return link.Link, nil
|
return link.Link, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PeriodStats 时间段统计数据
|
||||||
|
type PeriodStats struct {
|
||||||
|
Total int `json:"total"` // 总访问量
|
||||||
|
Views []int `json:"views"` // 时间序列数据(按天/小时)
|
||||||
|
Stats StatsDetail `json:"stats"` // 详细统计
|
||||||
|
}
|
||||||
|
|
||||||
|
// StatsDetail 详细统计信息
|
||||||
|
type StatsDetail struct {
|
||||||
|
Browser []StatItem `json:"browser"` // 浏览器分布
|
||||||
|
OS []StatItem `json:"os"` // 操作系统分布
|
||||||
|
Country []StatItem `json:"country"` // 国家分布
|
||||||
|
Referrer []StatItem `json:"referrer"` // 来源分布
|
||||||
|
}
|
||||||
|
|
||||||
|
// StatItem 统计项
|
||||||
|
type StatItem struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Value int `json:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// LinkStatsResponse 链接详细统计响应
|
||||||
|
type LinkStatsResponse struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Address string `json:"address"`
|
||||||
|
Link string `json:"link"`
|
||||||
|
Target string `json:"target"`
|
||||||
|
VisitCount int `json:"visit_count"`
|
||||||
|
LastDay PeriodStats `json:"lastDay"`
|
||||||
|
LastWeek PeriodStats `json:"lastWeek"`
|
||||||
|
LastMonth PeriodStats `json:"lastMonth"`
|
||||||
|
LastYear PeriodStats `json:"lastYear"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLinkStats 获取链接的详细统计数据
|
||||||
|
//
|
||||||
|
// 参数:
|
||||||
|
// - ctx: 上下文
|
||||||
|
// - linkID: 链接的 UUID
|
||||||
|
//
|
||||||
|
// 返回:
|
||||||
|
// - *LinkStatsResponse: 详细统计数据
|
||||||
|
// - error: 错误信息
|
||||||
|
func (c *Client) GetLinkStats(ctx context.Context, linkID string) (*LinkStatsResponse, error) {
|
||||||
|
// 创建 HTTP 请求
|
||||||
|
url := fmt.Sprintf("%s/links/%s/stats", c.apiURL, linkID)
|
||||||
|
httpReq, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("create request failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置请求头
|
||||||
|
httpReq.Header.Set("X-API-KEY", c.apiKey)
|
||||||
|
httpReq.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
// 发送请求
|
||||||
|
resp, err := c.httpClient.Do(httpReq)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("send request failed: %w", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
// 读取响应体
|
||||||
|
respBody, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("read response failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查响应状态
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
var errResp ErrorResponse
|
||||||
|
if err := json.Unmarshal(respBody, &errResp); err == nil && errResp.Error != "" {
|
||||||
|
return nil, fmt.Errorf("kutt api error: %s - %s", errResp.Error, errResp.Message)
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("kutt api error: status %d, body: %s", resp.StatusCode, string(respBody))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析响应
|
||||||
|
var stats LinkStatsResponse
|
||||||
|
if err := json.Unmarshal(respBody, &stats); err != nil {
|
||||||
|
return nil, fmt.Errorf("unmarshal response failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &stats, nil
|
||||||
|
}
|
||||||
|
|||||||
@ -84,39 +84,39 @@ ON DUPLICATE KEY UPDATE `auth_identifier` = 'mock_user_005@test.com';
|
|||||||
-- status: 2 = Paid(已支付), 5 = Finished(已完成)
|
-- status: 2 = Paid(已支付), 5 = Finished(已完成)
|
||||||
-- ===================================================================
|
-- ===================================================================
|
||||||
|
|
||||||
-- 订单 1:用户 10001,已支付,金额 $9.99
|
-- 订单 1:用户 10001,已支付,金额 $9.99,佣金 $0.99
|
||||||
INSERT INTO `order` (
|
INSERT INTO `order` (
|
||||||
`user_id`, `order_no`, `type`, `status`, `amount`, `quantity`,
|
`user_id`, `order_no`, `type`, `status`, `amount`, `commission`, `quantity`,
|
||||||
`payment_id`, `created_at`, `updated_at`
|
`payment_id`, `created_at`, `updated_at`
|
||||||
) VALUES (
|
) VALUES (
|
||||||
10001, 'MOCK_ORDER_001', 1, 2, 999, 1,
|
10001, 'MOCK_ORDER_001', 1, 2, 999, 99, 1,
|
||||||
1, DATE_SUB(NOW(), INTERVAL 10 DAY), DATE_SUB(NOW(), INTERVAL 10 DAY)
|
1, DATE_SUB(NOW(), INTERVAL 10 DAY), DATE_SUB(NOW(), INTERVAL 10 DAY)
|
||||||
);
|
);
|
||||||
|
|
||||||
-- 订单 2:用户 10001,已支付,金额 $19.99
|
-- 订单 2:用户 10001,已支付,金额 $19.99,佣金 $1.99
|
||||||
INSERT INTO `order` (
|
INSERT INTO `order` (
|
||||||
`user_id`, `order_no`, `type`, `status`, `amount`, `quantity`,
|
`user_id`, `order_no`, `type`, `status`, `amount`, `commission`, `quantity`,
|
||||||
`payment_id`, `created_at`, `updated_at`
|
`payment_id`, `created_at`, `updated_at`
|
||||||
) VALUES (
|
) VALUES (
|
||||||
10001, 'MOCK_ORDER_002', 1, 2, 1999, 1,
|
10001, 'MOCK_ORDER_002', 1, 2, 1999, 199, 1,
|
||||||
1, DATE_SUB(NOW(), INTERVAL 5 DAY), DATE_SUB(NOW(), INTERVAL 5 DAY)
|
1, DATE_SUB(NOW(), INTERVAL 5 DAY), DATE_SUB(NOW(), INTERVAL 5 DAY)
|
||||||
);
|
);
|
||||||
|
|
||||||
-- 订单 3:用户 10002,已支付,金额 $29.99
|
-- 订单 3:用户 10002,已支付,金额 $29.99,佣金 $2.99
|
||||||
INSERT INTO `order` (
|
INSERT INTO `order` (
|
||||||
`user_id`, `order_no`, `type`, `status`, `amount`, `quantity`,
|
`user_id`, `order_no`, `type`, `status`, `amount`, `commission`, `quantity`,
|
||||||
`payment_id`, `created_at`, `updated_at`
|
`payment_id`, `created_at`, `updated_at`
|
||||||
) VALUES (
|
) VALUES (
|
||||||
10002, 'MOCK_ORDER_003', 1, 2, 2999, 1,
|
10002, 'MOCK_ORDER_003', 1, 2, 2999, 299, 1,
|
||||||
1, DATE_SUB(NOW(), INTERVAL 3 DAY), DATE_SUB(NOW(), INTERVAL 3 DAY)
|
1, DATE_SUB(NOW(), INTERVAL 3 DAY), DATE_SUB(NOW(), INTERVAL 3 DAY)
|
||||||
);
|
);
|
||||||
|
|
||||||
-- 订单 4:用户 10003,已完成,金额 $49.99
|
-- 订单 4:用户 10003,已完成,金额 $49.99,佣金 $4.99
|
||||||
INSERT INTO `order` (
|
INSERT INTO `order` (
|
||||||
`user_id`, `order_no`, `type`, `status`, `amount`, `quantity`,
|
`user_id`, `order_no`, `type`, `status`, `amount`, `commission`, `quantity`,
|
||||||
`payment_id`, `created_at`, `updated_at`
|
`payment_id`, `created_at`, `updated_at`
|
||||||
) VALUES (
|
) VALUES (
|
||||||
10003, 'MOCK_ORDER_004', 1, 5, 4999, 1,
|
10003, 'MOCK_ORDER_004', 1, 5, 4999, 499, 1,
|
||||||
1, DATE_SUB(NOW(), INTERVAL 1 DAY), DATE_SUB(NOW(), INTERVAL 1 DAY)
|
1, DATE_SUB(NOW(), INTERVAL 1 DAY), DATE_SUB(NOW(), INTERVAL 1 DAY)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user