first commit

This commit is contained in:
2026-09-11 01:47:20 +08:00
commit 303207d3cc
74 changed files with 8348 additions and 0 deletions
+188
View File
@@ -0,0 +1,188 @@
package service
import (
"context"
"time"
"git.lzcwy.cn/zhengchiliu/zt-closet-manage-backend/global"
"git.lzcwy.cn/zhengchiliu/zt-closet-manage-backend/internal/dto"
"git.lzcwy.cn/zhengchiliu/zt-closet-manage-backend/internal/mysqldao"
"git.lzcwy.cn/zhengchiliu/zt-closet-manage-backend/pkg/errcode"
)
// DashboardService 仪表盘统计服务。
type DashboardService struct {
statsDao *mysqldao.StatsDao
}
// NewDashboardService 创建仪表盘服务。
func NewDashboardService() *DashboardService {
return &DashboardService{statsDao: mysqldao.NewStatsDao()}
}
// Summary 聚合仪表盘核心指标:用户/订单/营收/待办。
func (s *DashboardService) Summary(ctx context.Context) (*dto.DashboardResponse, *errcode.Error) {
ctx, span := global.Tracer.Start(ctx, "svc.DashboardSummary")
defer span.End()
todayStart := todayStartUnix()
userStats, err := s.statsDao.UserStatsGet(ctx, todayStart)
if err != nil {
return nil, errcode.DatabaseError
}
userStatusMap, err := s.statsDao.UserStatsGetByStatus(ctx)
if err != nil {
return nil, errcode.DatabaseError
}
orderStats, err := s.statsDao.OrderStatsGet(ctx)
if err != nil {
return nil, errcode.DatabaseError
}
revenueStats, err := s.statsDao.RevenueStatsGet(ctx, todayStart)
if err != nil {
return nil, errcode.DatabaseError
}
todoStats, err := s.statsDao.TodoStatsGet(ctx)
if err != nil {
return nil, errcode.DatabaseError
}
contentStats, err := s.statsDao.ContentStatsGet(ctx)
if err != nil {
return nil, errcode.DatabaseError
}
now := nowUnix()
vipStats, err := s.statsDao.VIPStatsGet(ctx, now, now+7*86400)
if err != nil {
return nil, errcode.DatabaseError
}
goodsStats, err := s.statsDao.GoodsStatsGet(ctx)
if err != nil {
return nil, errcode.DatabaseError
}
usageStats, err := s.statsDao.UsageStatsGet(ctx, todayStatDate())
if err != nil {
return nil, errcode.DatabaseError
}
socialStats, err := s.statsDao.SocialStatsGet(ctx)
if err != nil {
return nil, errcode.DatabaseError
}
return &dto.DashboardResponse{
Users: dto.DashboardUsers{
Total: userStats.Total,
TodayNew: userStats.TodayNew,
Normal: userStatusMap[UserStatusNormal],
TempBan: userStatusMap[UserStatusTempBan],
PermBan: userStatusMap[UserStatusPermBan],
Deactivated: userStatusMap[UserStatusDeactivated],
},
Orders: dto.DashboardOrders{
Total: orderStats.Total,
Pending: orderStats.Pending,
Paid: orderStats.Paid,
Cancelled: orderStats.Cancelled,
Expired: orderStats.Expired,
Refunded: orderStats.Refunded,
},
Revenue: dto.DashboardRevenue{
TotalRevenue: revenueStats.TotalRevenue,
TodayRevenue: revenueStats.TodayRevenue,
RefundTotal: revenueStats.RefundTotal,
},
Todo: dto.DashboardTodo{
RefundPending: todoStats.RefundPending,
OutfitPendingAudit: todoStats.OutfitPendingAudit,
GoodsOnShelf: todoStats.GoodsOnShelf,
},
Content: dto.DashboardContent{
Outfit: dto.DashboardOutfitStats{
Total: contentStats.OutfitTotal,
Pending: contentStats.OutfitPending,
Passed: contentStats.OutfitPassed,
Rejected: contentStats.OutfitRejected,
Banned: contentStats.OutfitBanned,
},
Clothes: dto.DashboardClothesStats{
Total: contentStats.ClothesTotal,
Processing: contentStats.ClothesProcessing,
Success: contentStats.ClothesSuccess,
Failed: contentStats.ClothesFailed,
},
Share: dto.DashboardShareStats{
Total: contentStats.ShareTotal,
Normal: contentStats.ShareNormal,
Cancelled: contentStats.ShareCancelled,
Expired: contentStats.ShareExpired,
Banned: contentStats.ShareBanned,
},
},
VIP: dto.DashboardVIP{
MemberTotal: vipStats.MemberTotal,
Monthly: vipStats.Monthly,
Yearly: vipStats.Yearly,
Trial: vipStats.Trial,
ExpiringSoon: vipStats.ExpiringSoon,
},
Goods: dto.DashboardGoods{
Total: goodsStats.Total,
OnShelf: goodsStats.OnShelf,
OffShelf: goodsStats.OffShelf,
ByType: buildGoodsTypeCounts(goodsStats.ByType),
},
Usage: dto.DashboardUsage{
AIRecommend: usageStats.AIRecommend,
ClosetShare: usageStats.ClosetShare,
OutfitShare: usageStats.OutfitShare,
},
Social: dto.DashboardSocial{
CosImageCount: socialStats.CosImageCount,
LikeCount: socialStats.LikeCount,
CollectCount: socialStats.CollectCount,
},
}, nil
}
// buildGoodsTypeCounts 商品类型数量 DAO → DTO。
func buildGoodsTypeCounts(in []mysqldao.GoodsTypeCount) []dto.GoodsTypeCount {
out := make([]dto.GoodsTypeCount, 0, len(in))
for _, v := range in {
out = append(out, dto.GoodsTypeCount{Type: v.Type, Count: v.Count})
}
return out
}
// Trends 返回近 N 天趋势(新增用户/已支付订单量/营收,按天补零对齐)。
func (s *DashboardService) Trends(ctx context.Context, days int) (*dto.TrendResponse, *errcode.Error) {
ctx, span := global.Tracer.Start(ctx, "svc.DashboardTrends")
defer span.End()
if days <= 0 {
days = 30
}
if days > 90 {
days = 90
}
startTs := todayStartUnix() - int64(days-1)*86400
stats, err := s.statsDao.TrendStatsGet(ctx, startTs)
if err != nil {
return nil, errcode.DatabaseError
}
loc := time.Now().Location()
startDay := time.Unix(startTs, 0).In(loc)
points := make([]dto.TrendPoint, 0, days)
for i := 0; i < days; i++ {
date := startDay.AddDate(0, 0, i).Format("2006-01-02")
points = append(points, dto.TrendPoint{
Date: date,
NewUsers: stats.NewUsers[date],
Orders: stats.Orders[date],
Revenue: stats.Revenue[date],
})
}
return &dto.TrendResponse{Points: points}, nil
}