feat: live module and jenkins pipeline to harbor deploy

This commit is contained in:
Yvan
2026-09-23 17:42:54 +08:00
parent e376ff02aa
commit 53e8599bdd
145 changed files with 8984 additions and 269 deletions
+36
View File
@@ -0,0 +1,36 @@
package analytics
import (
"context"
biz "kra/internal/biz/analytics"
"kra/internal/service/dto"
"time"
)
type Service struct{ uc *biz.Usecase }
func NewService(uc *biz.Usecase) *Service { return &Service{uc: uc} }
func (s *Service) Get(ctx context.Context, days int) (*dto.DashboardResponse, error) {
if days != 0 && days != 1 && days != 7 && days != 30 {
days = 1
}
now := time.Now()
today := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
from, to := today.AddDate(0, 0, -days+1), now
if days == 0 {
from = today.AddDate(0, 0, -1)
to = today.Add(-time.Nanosecond)
}
v, e := s.uc.Get(ctx, from, to)
if e != nil {
return nil, e
}
out := &dto.DashboardResponse{RoomTotal: v.RoomTotal, RoomLive: v.RoomLive, RechargeCents: v.RechargeCents, PaidOrderCount: v.PaidOrderCount, GiftDiamonds: v.GiftDiamonds, GiftSendCount: v.GiftSendCount, DiamondRateCents: v.DiamondRateCents}
for _, x := range v.RechargeTrend {
out.RechargeTrend = append(out.RechargeTrend, dto.DashboardTrend{Date: x.Date, AmountCents: x.AmountCents})
}
for _, x := range v.GiftRanking {
out.GiftRanking = append(out.GiftRanking, dto.DashboardRank{RoomID: x.RoomID, RoomName: x.RoomName, AnchorName: x.AnchorName, Diamonds: x.Diamonds})
}
return out, nil
}
+5
View File
@@ -0,0 +1,5 @@
package analytics
import "github.com/google/wire"
var ProviderSet = wire.NewSet(NewService)
+68
View File
@@ -0,0 +1,68 @@
package asset
import (
"context"
biz "kra/internal/biz/asset"
"kra/internal/service/dto"
"time"
)
type Service struct{ uc *biz.Usecase }
func NewService(uc *biz.Usecase) *Service { return &Service{uc: uc} }
func dr(a, b string) []time.Time {
if a == "" || b == "" {
return nil
}
x, e1 := time.ParseInLocation("2006-01-02", a, time.Local)
y, e2 := time.ParseInLocation("2006-01-02", b, time.Local)
if e1 != nil || e2 != nil {
return nil
}
return []time.Time{x, y.Add(24*time.Hour - time.Nanosecond)}
}
func (s *Service) List(ctx context.Context, in *dto.AssetUserListRequest) ([]*dto.AssetUserResponse, int64, error) {
p, z := in.Page, in.PageSize
if p < 1 {
p = 1
}
if z < 1 {
z = 10
}
items, total, e := s.uc.List(ctx, biz.Filter{Keyword: in.Keyword, UserID: in.UserID, Enabled: in.Enabled, CreatedAt: dr(in.StartedAt, in.EndedAt)}, biz.ListOptions{Offset: (p - 1) * z, Limit: z, OrderBy: in.OrderBy})
if e != nil {
return nil, 0, e
}
out := make([]*dto.AssetUserResponse, 0, len(items))
for _, v := range items {
out = append(out, user(v))
}
return out, total, nil
}
func (s *Service) Get(ctx context.Context, id uint) (*dto.AssetUserResponse, error) {
v, e := s.uc.Get(ctx, id)
if e != nil {
return nil, e
}
return user(v), nil
}
func (s *Service) SetEnabled(ctx context.Context, actor, id uint, enabled bool) error {
return s.uc.SetEnabled(ctx, actor, id, enabled)
}
func (s *Service) Adjust(ctx context.Context, actor, id uint, in *dto.AssetAdjustmentRequest) (*dto.AssetUserResponse, error) {
v, e := s.uc.Adjust(ctx, &biz.Adjustment{UserID: id, OperatorUserID: actor, Type: in.Type, Amount: in.Amount, Reason: in.Reason})
if e != nil {
return nil, e
}
return user(v), nil
}
func user(v *biz.User) *dto.AssetUserResponse {
out := &dto.AssetUserResponse{UserID: v.UserID, Username: v.Username, Nickname: v.Nickname, Avatar: v.Avatar, Enabled: v.Enabled, DiamondBalance: v.DiamondBalance, TotalRechargeCents: v.TotalRechargeCents, TotalSpentDiamonds: v.TotalSpentDiamonds, CreatedAt: v.CreatedAt, LastLoginAt: v.LastLoginAt}
for _, x := range v.RecentOrders {
out.RecentOrders = append(out.RecentOrders, dto.AssetOrderRecord{OrderNo: x.OrderNo, Diamonds: x.Diamonds, AmountCents: x.AmountCents, Status: x.Status, CreatedAt: x.CreatedAt})
}
for _, x := range v.RecentSends {
out.RecentSends = append(out.RecentSends, dto.AssetSendRecord{SerialNo: x.SerialNo, RoomName: x.RoomName, GiftName: x.GiftName, GiftIcon: x.GiftIcon, DiamondTotal: x.DiamondTotal, CreatedAt: x.CreatedAt})
}
return out
}
+5
View File
@@ -0,0 +1,5 @@
package asset
import "github.com/google/wire"
var ProviderSet = wire.NewSet(NewService)
+23
View File
@@ -0,0 +1,23 @@
package dto
type DashboardResponse struct {
RoomTotal int64 `json:"roomTotal"`
RoomLive int64 `json:"roomLive"`
RechargeCents int64 `json:"rechargeCents"`
PaidOrderCount int64 `json:"paidOrderCount"`
GiftDiamonds int64 `json:"giftDiamonds"`
GiftSendCount int64 `json:"giftSendCount"`
DiamondRateCents int64 `json:"diamondRateCents"`
RechargeTrend []DashboardTrend `json:"rechargeTrend"`
GiftRanking []DashboardRank `json:"giftRanking"`
}
type DashboardTrend struct {
Date string `json:"date"`
AmountCents int64 `json:"amountCents"`
}
type DashboardRank struct {
RoomID uint `json:"roomId"`
RoomName string `json:"roomName"`
AnchorName string `json:"anchorName"`
Diamonds int64 `json:"diamonds"`
}
+46
View File
@@ -0,0 +1,46 @@
package dto
import "time"
type AssetUserListRequest struct {
LiveListRequest
Keyword string `form:"keyword"`
UserID uint `form:"userId"`
Enabled *bool `form:"enabled"`
StartedAt string `form:"startedAt"`
EndedAt string `form:"endedAt"`
}
type AssetAdjustmentRequest struct {
Type string `json:"type" binding:"required"`
Amount int64 `json:"amount" binding:"required"`
Reason string `json:"reason" binding:"required"`
}
type AssetOrderRecord struct {
OrderNo string `json:"orderNo"`
Diamonds int64 `json:"diamonds"`
AmountCents int64 `json:"amountCents"`
Status string `json:"status"`
CreatedAt time.Time `json:"createdAt"`
}
type AssetSendRecord struct {
SerialNo string `json:"serialNo"`
RoomName string `json:"roomName"`
GiftName string `json:"giftName"`
GiftIcon string `json:"giftIcon"`
DiamondTotal int64 `json:"diamondTotal"`
CreatedAt time.Time `json:"createdAt"`
}
type AssetUserResponse struct {
UserID uint `json:"userId"`
Username string `json:"username"`
Nickname string `json:"nickname"`
Avatar string `json:"avatar"`
Enabled bool `json:"enabled"`
DiamondBalance int64 `json:"diamondBalance"`
TotalRechargeCents int64 `json:"totalRechargeCents"`
TotalSpentDiamonds int64 `json:"totalSpentDiamonds"`
CreatedAt time.Time `json:"createdAt"`
LastLoginAt *time.Time `json:"lastLoginAt"`
RecentOrders []AssetOrderRecord `json:"recentOrders,omitempty"`
RecentSends []AssetSendRecord `json:"recentSends,omitempty"`
}
+71
View File
@@ -0,0 +1,71 @@
package dto
import "time"
type GiftListRequest struct {
LiveListRequest
Name string `form:"name"`
MinPrice int64 `form:"minPrice"`
MaxPrice int64 `form:"maxPrice"`
Enabled *bool `form:"enabled"`
}
type GiftRequest struct {
Name string `json:"name" binding:"required"`
Icon string `json:"icon" binding:"required"`
Price int64 `json:"price" binding:"required"`
Description string `json:"description"`
Tiers []int `json:"tiers" binding:"required"`
Sort int `json:"sort"`
Enabled bool `json:"enabled"`
}
type GiftResponse struct {
ID uint `json:"id"`
Name string `json:"name"`
Icon string `json:"icon"`
Price int64 `json:"price"`
Description string `json:"description"`
Tiers []int `json:"tiers"`
Sort int `json:"sort"`
Enabled bool `json:"enabled"`
SendCount int64 `json:"sendCount"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
type GiftSendListRequest struct {
LiveListRequest
RoomName string `form:"roomName"`
AnchorName string `form:"anchorName"`
UserKeyword string `form:"userKeyword"`
GiftID uint `form:"giftId"`
StartedAt string `form:"startedAt"`
EndedAt string `form:"endedAt"`
}
type GiftSendRequest struct {
GiftID uint `json:"giftId" binding:"required"`
Quantity int `json:"quantity" binding:"required"`
}
type GiftSendResponse struct {
ID uint `json:"id"`
SerialNo string `json:"serialNo"`
UserID uint `json:"userId"`
Username string `json:"username"`
Nickname string `json:"nickname"`
RoomID *uint `json:"roomId"`
RoomName string `json:"roomName"`
RoomDeleted bool `json:"roomDeleted"`
AnchorUserID uint `json:"anchorUserId"`
AnchorName string `json:"anchorName"`
GiftID *uint `json:"giftId"`
GiftName string `json:"giftName"`
GiftIcon string `json:"giftIcon"`
GiftPrice int64 `json:"giftPrice"`
Quantity int `json:"quantity"`
DiamondTotal int64 `json:"diamondTotal"`
EquivalentCents int64 `json:"equivalentCents"`
CreatedAt time.Time `json:"createdAt"`
}
type GiftSendSummary struct {
Count int64 `json:"count"`
Diamonds int64 `json:"diamonds"`
AmountCents int64 `json:"amountCents"`
}
+82
View File
@@ -0,0 +1,82 @@
package dto
import "time"
type LiveStreamResponse struct {
StreamID string `json:"streamId"`
PublisherOnline bool `json:"publisherOnline"`
SubscriberCount int `json:"subscriberCount"`
}
type LiveListRequest struct {
Page int `form:"page" json:"page"`
PageSize int `form:"pageSize" json:"pageSize"`
OrderBy string `form:"orderBy" json:"orderBy"`
}
type LiveCategoryListRequest struct {
LiveListRequest
Name string `form:"name"`
Enabled *bool `form:"enabled"`
}
type LiveCategoryRequest struct {
Name string `json:"name" binding:"required"`
Icon string `json:"icon"`
Sort int `json:"sort"`
Enabled bool `json:"enabled"`
}
type LiveCategoryResponse struct {
ID uint `json:"id"`
Name string `json:"name"`
Icon string `json:"icon"`
Sort int `json:"sort"`
Enabled bool `json:"enabled"`
RoomCount int64 `json:"roomCount"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
type LiveRoomListRequest struct {
LiveListRequest
Name string `form:"name"`
AnchorName string `form:"anchorName"`
CategoryID uint `form:"categoryId"`
Status string `form:"status"`
Enabled *bool `form:"enabled"`
StartedAt string `form:"startedAt"`
EndedAt string `form:"endedAt"`
}
type LiveRoomRequest struct {
Name string `json:"name" binding:"required"`
CategoryID uint `json:"categoryId" binding:"required"`
CoverURL string `json:"coverUrl" binding:"required"`
Intro string `json:"intro"`
Sort int `json:"sort"`
AnchorUserID uint `json:"anchorUserId" binding:"required"`
StreamURL string `json:"streamUrl"`
ReplayURL string `json:"replayUrl"`
Enabled bool `json:"enabled"`
}
type LiveRoomResponse struct {
ID uint `json:"id"`
StreamID string `json:"streamId"`
Name string `json:"name"`
CategoryID uint `json:"categoryId"`
CategoryName string `json:"categoryName"`
CoverURL string `json:"coverUrl"`
Intro string `json:"intro"`
Sort int `json:"sort"`
AnchorUserID uint `json:"anchorUserId"`
AnchorName string `json:"anchorName"`
AnchorAvatar string `json:"anchorAvatar"`
StreamURL string `json:"streamUrl"`
ReplayURL string `json:"replayUrl"`
Enabled bool `json:"enabled"`
Status string `json:"status"`
ViewerCount int `json:"viewerCount"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
type LiveStatusRequest struct {
Enabled bool `json:"enabled"`
}
type LiveSortRequest struct {
Values map[uint]int `json:"values" binding:"required"`
}
+24
View File
@@ -0,0 +1,24 @@
package dto
import "time"
type LiveChatHistoryRequest struct {
Limit int `form:"limit"`
}
type LiveChatMessageResponse struct {
ID uint `json:"id"`
RoomID uint `json:"roomId"`
Type string `json:"type"`
UserID uint `json:"userId"`
Username string `json:"username"`
Nickname string `json:"nickname"`
Avatar string `json:"avatar"`
Content string `json:"content,omitempty"`
GiftID uint `json:"giftId,omitempty"`
GiftName string `json:"giftName,omitempty"`
GiftIcon string `json:"giftIcon,omitempty"`
Quantity int `json:"quantity,omitempty"`
DiamondTotal int64 `json:"diamondTotal,omitempty"`
CreatedAt time.Time `json:"createdAt"`
}
+41
View File
@@ -0,0 +1,41 @@
package dto
import "time"
type LiveClientLoginRequest struct {
Username string `json:"username" binding:"required"`
Password string `json:"password" binding:"required"`
}
type LiveClientRegisterRequest struct {
Username string `json:"username" binding:"required"`
Password string `json:"password" binding:"required"`
ConfirmPassword string `json:"confirmPassword" binding:"required"`
}
type LiveClientProfileRequest struct {
Nickname string `json:"nickname"`
Avatar string `json:"avatar"`
}
type LiveClientPasswordRequest struct {
OldPassword string `json:"oldPassword" binding:"required"`
NewPassword string `json:"newPassword" binding:"required"`
ConfirmPassword string `json:"confirmPassword" binding:"required"`
}
type LiveClientAccountResponse struct {
UserID uint `json:"userId"`
Username string `json:"username"`
Nickname string `json:"nickname"`
Avatar string `json:"avatar"`
Enabled bool `json:"enabled"`
DiamondBalance int64 `json:"diamondBalance"`
CreatedAt time.Time `json:"createdAt"`
}
type LiveClientAuthResponse struct {
User *LiveClientAccountResponse `json:"user"`
Token string `json:"token"`
ExpiresAt int64 `json:"expiresAt"`
}
+5
View File
@@ -0,0 +1,5 @@
package dto
type PlatformSetting struct {
DiamondRateCents int64 `json:"diamondRateCents"`
}
+58
View File
@@ -0,0 +1,58 @@
package dto
import "time"
type RechargePlan struct {
ID uint `json:"id"`
Sort int `json:"sort"`
Diamonds int64 `json:"diamonds"`
AmountCents int64 `json:"amountCents"`
Enabled bool `json:"enabled"`
}
type RechargePlansRequest struct {
Plans []*RechargePlan `json:"plans" binding:"required"`
}
type RechargeOrderListRequest struct {
LiveListRequest
OrderNo string `form:"orderNo"`
UserKeyword string `form:"userKeyword"`
PaymentStatus string `form:"paymentStatus"`
Provider string `form:"provider"`
StartedAt string `form:"startedAt"`
EndedAt string `form:"endedAt"`
}
type RechargeOrderRequest struct {
PlanID uint `json:"planId" binding:"required"`
Provider string `json:"provider" binding:"required"`
NotifyURL string `json:"notifyUrl"`
ReturnURL string `json:"returnUrl"`
Extra map[string]any `json:"extra"`
ClientIP string `json:"-"`
UserAgent string `json:"-"`
DeviceID string `json:"-"`
}
type RechargeOrderResponse struct {
ID uint `json:"id"`
OrderNo string `json:"orderNo"`
UserID uint `json:"userId"`
Username string `json:"username"`
Nickname string `json:"nickname"`
Diamonds int64 `json:"diamonds"`
AmountCents int64 `json:"amountCents"`
Provider string `json:"provider"`
ProviderTradeNo string `json:"providerTradeNo"`
PaymentStatus string `json:"paymentStatus"`
FulfillmentStatus string `json:"fulfillmentStatus"`
RefundStatus string `json:"refundStatus"`
CreatedAt time.Time `json:"createdAt"`
CreditedAt *time.Time `json:"creditedAt"`
}
type RechargeCheckoutResponse struct {
Order *RechargeOrderResponse `json:"order"`
Payment *PaymentResultResponse `json:"payment"`
}
type RechargeSummary struct {
Count int64 `json:"count"`
PaidCount int64 `json:"paidCount"`
AmountCents int64 `json:"amountCents"`
}
+87
View File
@@ -0,0 +1,87 @@
package gift
import (
"context"
biz "kra/internal/biz/gift"
"kra/internal/service/dto"
"time"
)
type Service struct{ uc *biz.Usecase }
func NewService(uc *biz.Usecase) *Service { return &Service{uc: uc} }
func rangeOf(a, b string) []time.Time {
if a == "" || b == "" {
return nil
}
x, e1 := time.ParseInLocation("2006-01-02", a, time.Local)
y, e2 := time.ParseInLocation("2006-01-02", b, time.Local)
if e1 != nil || e2 != nil {
return nil
}
return []time.Time{x, y.Add(24*time.Hour - time.Nanosecond)}
}
func opts(v dto.LiveListRequest) biz.ListOptions {
p, s := v.Page, v.PageSize
if p < 1 {
p = 1
}
if s < 1 {
s = 10
}
return biz.ListOptions{Offset: (p - 1) * s, Limit: s, OrderBy: v.OrderBy}
}
func (s *Service) List(ctx context.Context, in *dto.GiftListRequest) ([]*dto.GiftResponse, int64, error) {
items, total, err := s.uc.List(ctx, biz.Filter{Name: in.Name, MinPrice: in.MinPrice, MaxPrice: in.MaxPrice, Enabled: in.Enabled}, opts(in.LiveListRequest))
if err != nil {
return nil, 0, err
}
out := make([]*dto.GiftResponse, 0, len(items))
for _, v := range items {
out = append(out, gift(v))
}
return out, total, nil
}
func (s *Service) Get(ctx context.Context, id uint) (*dto.GiftResponse, error) {
v, e := s.uc.Get(ctx, id)
if e != nil {
return nil, e
}
return gift(v), nil
}
func (s *Service) Save(ctx context.Context, id uint, in *dto.GiftRequest) (*dto.GiftResponse, error) {
v, e := s.uc.Save(ctx, &biz.Gift{ID: id, Name: in.Name, Icon: in.Icon, Price: in.Price, Description: in.Description, Tiers: in.Tiers, Sort: in.Sort, Enabled: in.Enabled})
if e != nil {
return nil, e
}
return gift(v), nil
}
func (s *Service) Delete(ctx context.Context, id uint) error { return s.uc.Delete(ctx, id) }
func (s *Service) SetEnabled(ctx context.Context, id uint, v bool) error {
return s.uc.SetEnabled(ctx, id, v)
}
func (s *Service) SaveSort(ctx context.Context, v map[uint]int) error { return s.uc.SaveSort(ctx, v) }
func (s *Service) ListSends(ctx context.Context, in *dto.GiftSendListRequest) ([]*dto.GiftSendResponse, int64, *dto.GiftSendSummary, error) {
items, total, sum, err := s.uc.ListSends(ctx, biz.SendFilter{RoomName: in.RoomName, AnchorName: in.AnchorName, UserKeyword: in.UserKeyword, GiftID: in.GiftID, CreatedAt: rangeOf(in.StartedAt, in.EndedAt)}, opts(in.LiveListRequest))
if err != nil {
return nil, 0, nil, err
}
out := make([]*dto.GiftSendResponse, 0, len(items))
for _, v := range items {
out = append(out, send(v))
}
return out, total, &dto.GiftSendSummary{Count: sum.Count, Diamonds: sum.Diamonds, AmountCents: sum.AmountCents}, nil
}
func (s *Service) Send(ctx context.Context, userID, roomID uint, in *dto.GiftSendRequest) (*dto.GiftSendResponse, error) {
v, e := s.uc.Send(ctx, userID, roomID, in.GiftID, in.Quantity)
if e != nil {
return nil, e
}
return send(v), nil
}
func gift(v *biz.Gift) *dto.GiftResponse {
return &dto.GiftResponse{ID: v.ID, Name: v.Name, Icon: v.Icon, Price: v.Price, Description: v.Description, Tiers: v.Tiers, Sort: v.Sort, Enabled: v.Enabled, SendCount: v.SendCount, CreatedAt: v.CreatedAt, UpdatedAt: v.UpdatedAt}
}
func send(v *biz.Send) *dto.GiftSendResponse {
return &dto.GiftSendResponse{ID: v.ID, SerialNo: v.SerialNo, UserID: v.UserID, Username: v.Username, Nickname: v.Nickname, RoomID: v.RoomID, RoomName: v.RoomName, RoomDeleted: v.RoomDeleted, AnchorUserID: v.AnchorUserID, AnchorName: v.AnchorName, GiftID: v.GiftID, GiftName: v.GiftName, GiftIcon: v.GiftIcon, GiftPrice: v.GiftPrice, Quantity: v.Quantity, DiamondTotal: v.DiamondTotal, EquivalentCents: v.EquivalentCents, CreatedAt: v.CreatedAt}
}
+5
View File
@@ -0,0 +1,5 @@
package gift
import "github.com/google/wire"
var ProviderSet = wire.NewSet(NewService)
+117
View File
@@ -0,0 +1,117 @@
package live
import (
"context"
"fmt"
bizlive "kra/internal/biz/live"
"kra/internal/service/dto"
"time"
)
type CatalogService struct {
categories *bizlive.CategoryUsecase
rooms *bizlive.RoomUsecase
}
func NewCatalogService(categories *bizlive.CategoryUsecase, rooms *bizlive.RoomUsecase) *CatalogService {
return &CatalogService{categories: categories, rooms: rooms}
}
func liveListOptions(input dto.LiveListRequest) bizlive.ListOptions {
page, size := input.Page, input.PageSize
if page < 1 {
page = 1
}
if size < 1 {
size = 10
}
return bizlive.ListOptions{Offset: (page - 1) * size, Limit: size, OrderBy: input.OrderBy}
}
func liveDateRange(start, end string) []time.Time {
if start == "" || end == "" {
return nil
}
from, e1 := time.ParseInLocation("2006-01-02", start, time.Local)
to, e2 := time.ParseInLocation("2006-01-02", end, time.Local)
if e1 != nil || e2 != nil {
return nil
}
return []time.Time{from, to.Add(24*time.Hour - time.Nanosecond)}
}
func (s *CatalogService) ListCategories(ctx context.Context, input *dto.LiveCategoryListRequest) ([]*dto.LiveCategoryResponse, int64, error) {
items, total, err := s.categories.List(ctx, bizlive.CategoryFilter{Name: input.Name, Enabled: input.Enabled}, liveListOptions(input.LiveListRequest))
if err != nil {
return nil, 0, err
}
result := make([]*dto.LiveCategoryResponse, 0, len(items))
for _, item := range items {
result = append(result, categoryResponse(item))
}
return result, total, nil
}
func (s *CatalogService) CreateCategory(ctx context.Context, input *dto.LiveCategoryRequest) (*dto.LiveCategoryResponse, error) {
item, err := s.categories.Create(ctx, &bizlive.Category{Name: input.Name, Icon: input.Icon, Sort: input.Sort, Enabled: input.Enabled})
if err != nil {
return nil, err
}
return categoryResponse(item), nil
}
func (s *CatalogService) UpdateCategory(ctx context.Context, id uint, input *dto.LiveCategoryRequest) (*dto.LiveCategoryResponse, error) {
item, err := s.categories.Update(ctx, &bizlive.Category{ID: id, Name: input.Name, Icon: input.Icon, Sort: input.Sort, Enabled: input.Enabled})
if err != nil {
return nil, err
}
return categoryResponse(item), nil
}
func (s *CatalogService) DeleteCategory(ctx context.Context, id uint) error {
return s.categories.Delete(ctx, id)
}
func (s *CatalogService) ListRooms(ctx context.Context, input *dto.LiveRoomListRequest) ([]*dto.LiveRoomResponse, int64, error) {
items, total, err := s.rooms.List(ctx, bizlive.RoomFilter{Name: input.Name, AnchorName: input.AnchorName, CategoryID: input.CategoryID, Status: input.Status, Enabled: input.Enabled, CreatedAt: liveDateRange(input.StartedAt, input.EndedAt)}, liveListOptions(input.LiveListRequest))
if err != nil {
return nil, 0, err
}
result := make([]*dto.LiveRoomResponse, 0, len(items))
for _, item := range items {
result = append(result, roomResponse(item))
}
return result, total, nil
}
func (s *CatalogService) GetRoom(ctx context.Context, id uint) (*dto.LiveRoomResponse, error) {
item, err := s.rooms.Get(ctx, id)
if err != nil {
return nil, err
}
return roomResponse(item), nil
}
func liveRoomDO(id uint, input *dto.LiveRoomRequest) *bizlive.Room {
return &bizlive.Room{ID: id, Name: input.Name, CategoryID: input.CategoryID, CoverURL: input.CoverURL, Intro: input.Intro, Sort: input.Sort, AnchorUserID: input.AnchorUserID, StreamURL: input.StreamURL, ReplayURL: input.ReplayURL, Enabled: input.Enabled}
}
func (s *CatalogService) CreateRoom(ctx context.Context, input *dto.LiveRoomRequest) (*dto.LiveRoomResponse, error) {
item, err := s.rooms.Create(ctx, liveRoomDO(0, input))
if err != nil {
return nil, err
}
return roomResponse(item), nil
}
func (s *CatalogService) UpdateRoom(ctx context.Context, id uint, input *dto.LiveRoomRequest) (*dto.LiveRoomResponse, error) {
item, err := s.rooms.Update(ctx, liveRoomDO(id, input))
if err != nil {
return nil, err
}
return roomResponse(item), nil
}
func (s *CatalogService) DeleteRoom(ctx context.Context, id uint) error {
return s.rooms.Delete(ctx, id)
}
func (s *CatalogService) SetRoomEnabled(ctx context.Context, id uint, enabled bool) error {
return s.rooms.SetEnabled(ctx, id, enabled)
}
func (s *CatalogService) SaveRoomSort(ctx context.Context, values map[uint]int) error {
return s.rooms.SaveSort(ctx, values)
}
func categoryResponse(item *bizlive.Category) *dto.LiveCategoryResponse {
return &dto.LiveCategoryResponse{ID: item.ID, Name: item.Name, Icon: item.Icon, Sort: item.Sort, Enabled: item.Enabled, RoomCount: item.RoomCount, CreatedAt: item.CreatedAt, UpdatedAt: item.UpdatedAt}
}
func roomResponse(item *bizlive.Room) *dto.LiveRoomResponse {
return &dto.LiveRoomResponse{ID: item.ID, StreamID: fmt.Sprintf("room-%d", item.ID), Name: item.Name, CategoryID: item.CategoryID, CategoryName: item.CategoryName, CoverURL: item.CoverURL, Intro: item.Intro, Sort: item.Sort, AnchorUserID: item.AnchorUserID, AnchorName: item.AnchorName, AnchorAvatar: item.AnchorAvatar, StreamURL: item.StreamURL, ReplayURL: item.ReplayURL, Enabled: item.Enabled, Status: item.Status, ViewerCount: item.ViewerCount, CreatedAt: item.CreatedAt, UpdatedAt: item.UpdatedAt}
}
+5
View File
@@ -0,0 +1,5 @@
package live
import "github.com/google/wire"
var ProviderSet = wire.NewSet(NewStreamService, NewCatalogService)
+32
View File
@@ -0,0 +1,32 @@
package live
import (
"context"
bizlive "kra/internal/biz/live"
"kra/internal/service/dto"
)
type StreamService struct{ uc *bizlive.StreamUsecase }
func NewStreamService(uc *bizlive.StreamUsecase) *StreamService { return &StreamService{uc: uc} }
func (s *StreamService) List(ctx context.Context) ([]*dto.LiveStreamResponse, error) {
streams, err := s.uc.List(ctx)
if err != nil {
return nil, err
}
result := make([]*dto.LiveStreamResponse, 0, len(streams))
for _, stream := range streams {
result = append(result, &dto.LiveStreamResponse{StreamID: stream.StreamID, PublisherOnline: stream.PublisherOnline, SubscriberCount: stream.SubscriberCount})
}
return result, nil
}
func (s *StreamService) Get(ctx context.Context, streamID string) (*dto.LiveStreamResponse, error) {
stream, err := s.uc.Get(ctx, streamID)
if err != nil {
return nil, err
}
return &dto.LiveStreamResponse{StreamID: stream.StreamID, PublisherOnline: stream.PublisherOnline, SubscriberCount: stream.SubscriberCount}, nil
}
+27
View File
@@ -0,0 +1,27 @@
package livechat
import (
"context"
biz "kra/internal/biz/livechat"
"kra/internal/service/dto"
)
type Service struct{ uc *biz.Usecase }
func NewService(uc *biz.Usecase) *Service { return &Service{uc: uc} }
func (s *Service) History(ctx context.Context, roomID uint, limit int) ([]*dto.LiveChatMessageResponse, error) {
items, err := s.uc.ListRecent(ctx, roomID, limit)
if err != nil {
return nil, err
}
out := make([]*dto.LiveChatMessageResponse, 0, len(items))
for _, item := range items {
out = append(out, MessageResponse(item))
}
return out, nil
}
func MessageResponse(item *biz.Message) *dto.LiveChatMessageResponse {
return &dto.LiveChatMessageResponse{ID: item.ID, RoomID: item.RoomID, Type: item.Type, UserID: item.UserID, Username: item.Username, Nickname: item.Nickname, Avatar: item.Avatar, Content: item.Content, GiftID: item.GiftID, GiftName: item.GiftName, GiftIcon: item.GiftIcon, Quantity: item.Quantity, DiamondTotal: item.DiamondTotal, CreatedAt: item.CreatedAt}
}
+5
View File
@@ -0,0 +1,5 @@
package livechat
import "github.com/google/wire"
var ProviderSet = wire.NewSet(NewService)
+60
View File
@@ -0,0 +1,60 @@
package liveclient
import (
"context"
biz "kra/internal/biz/liveclient"
"kra/internal/service/dto"
)
type Service struct{ uc *biz.Usecase }
func NewService(uc *biz.Usecase) *Service { return &Service{uc: uc} }
func (s *Service) Register(ctx context.Context, in *dto.LiveClientRegisterRequest, ip, agent string) (*dto.LiveClientAuthResponse, error) {
result, err := s.uc.Register(ctx, &biz.RegisterInput{Username: in.Username, Password: in.Password, ConfirmPassword: in.ConfirmPassword}, ip, agent)
if err != nil {
return nil, err
}
return authResponse(result), nil
}
func (s *Service) Login(ctx context.Context, in *dto.LiveClientLoginRequest, ip, agent string) (*dto.LiveClientAuthResponse, error) {
result, err := s.uc.Login(ctx, &biz.LoginInput{Username: in.Username, Password: in.Password, IP: ip, Agent: agent})
if err != nil {
return nil, err
}
return authResponse(result), nil
}
func (s *Service) Me(ctx context.Context, userID uint) (*dto.LiveClientAccountResponse, error) {
account, err := s.uc.Get(ctx, userID)
if err != nil {
return nil, err
}
return accountResponse(account), nil
}
func (s *Service) UpdateProfile(ctx context.Context, userID uint, in *dto.LiveClientProfileRequest) (*dto.LiveClientAccountResponse, error) {
account, err := s.uc.UpdateProfile(ctx, &biz.ProfileInput{UserID: userID, Nickname: in.Nickname, Avatar: in.Avatar})
if err != nil {
return nil, err
}
return accountResponse(account), nil
}
func (s *Service) ChangePassword(ctx context.Context, userID uint, in *dto.LiveClientPasswordRequest, ip, agent string) (*dto.LiveClientAuthResponse, error) {
result, err := s.uc.ChangePassword(ctx, &biz.PasswordInput{UserID: userID, OldPassword: in.OldPassword, NewPassword: in.NewPassword, ConfirmPassword: in.ConfirmPassword, IP: ip, Agent: agent})
if err != nil {
return nil, err
}
return authResponse(result), nil
}
func accountResponse(account *biz.Account) *dto.LiveClientAccountResponse {
return &dto.LiveClientAccountResponse{UserID: account.UserID, Username: account.Username, Nickname: account.Nickname, Avatar: account.Avatar, Enabled: account.Enabled, DiamondBalance: account.DiamondBalance, CreatedAt: account.CreatedAt}
}
func authResponse(result *biz.AuthResult) *dto.LiveClientAuthResponse {
return &dto.LiveClientAuthResponse{User: accountResponse(result.Account), Token: result.Auth.Token, ExpiresAt: result.Auth.ExpiresAt.UnixMilli()}
}
+5
View File
@@ -0,0 +1,5 @@
package liveclient
import "github.com/google/wire"
var ProviderSet = wire.NewSet(NewService)
@@ -0,0 +1,5 @@
package platformsetting
import "github.com/google/wire"
var ProviderSet = wire.NewSet(NewService)
@@ -0,0 +1,21 @@
package platformsetting
import (
"context"
biz "kra/internal/biz/platformsetting"
"kra/internal/service/dto"
)
type Service struct{ uc *biz.Usecase }
func NewService(uc *biz.Usecase) *Service { return &Service{uc: uc} }
func (s *Service) Get(ctx context.Context) (*dto.PlatformSetting, error) {
v, e := s.uc.Get(ctx)
if e != nil {
return nil, e
}
return &dto.PlatformSetting{DiamondRateCents: v.DiamondRateCents}, nil
}
func (s *Service) Save(ctx context.Context, v *dto.PlatformSetting) error {
return s.uc.Save(ctx, &biz.Setting{DiamondRateCents: v.DiamondRateCents})
}
+5
View File
@@ -0,0 +1,5 @@
package recharge
import "github.com/google/wire"
var ProviderSet = wire.NewSet(NewService)
+110
View File
@@ -0,0 +1,110 @@
package recharge
import (
"context"
"time"
bizpayment "kra/internal/biz/payment"
biz "kra/internal/biz/recharge"
"kra/internal/service/dto"
)
type Service struct{ uc *biz.Usecase }
func NewService(uc *biz.Usecase) *Service { return &Service{uc: uc} }
func dates(a, b string) []time.Time {
if a == "" || b == "" {
return nil
}
x, e1 := time.ParseInLocation("2006-01-02", a, time.Local)
y, e2 := time.ParseInLocation("2006-01-02", b, time.Local)
if e1 != nil || e2 != nil {
return nil
}
return []time.Time{x, y.Add(24*time.Hour - time.Nanosecond)}
}
func options(v dto.LiveListRequest) biz.ListOptions {
p, size := v.Page, v.PageSize
if p < 1 {
p = 1
}
if size < 1 {
size = 10
}
return biz.ListOptions{Offset: (p - 1) * size, Limit: size, OrderBy: v.OrderBy}
}
func (s *Service) Plans(ctx context.Context) ([]*dto.RechargePlan, error) {
items, err := s.uc.Plans(ctx)
if err != nil {
return nil, err
}
out := make([]*dto.RechargePlan, 0, len(items))
for _, v := range items {
out = append(out, &dto.RechargePlan{ID: v.ID, Sort: v.Sort, Diamonds: v.Diamonds, AmountCents: v.AmountCents, Enabled: v.Enabled})
}
return out, nil
}
func (s *Service) SavePlans(ctx context.Context, in *dto.RechargePlansRequest) error {
plans := make([]*biz.Plan, 0, len(in.Plans))
for _, v := range in.Plans {
plans = append(plans, &biz.Plan{ID: v.ID, Sort: v.Sort, Diamonds: v.Diamonds, AmountCents: v.AmountCents, Enabled: v.Enabled})
}
return s.uc.SavePlans(ctx, plans)
}
func (s *Service) List(ctx context.Context, in *dto.RechargeOrderListRequest) ([]*dto.RechargeOrderResponse, int64, *dto.RechargeSummary, error) {
items, total, sum, err := s.uc.List(ctx, biz.Filter{OrderNo: in.OrderNo, UserKeyword: in.UserKeyword, PaymentStatus: in.PaymentStatus, Provider: in.Provider, CreatedAt: dates(in.StartedAt, in.EndedAt)}, options(in.LiveListRequest))
if err != nil {
return nil, 0, nil, err
}
out := make([]*dto.RechargeOrderResponse, 0, len(items))
for _, v := range items {
out = append(out, order(v))
}
return out, total, &dto.RechargeSummary{Count: sum.Count, PaidCount: sum.PaidCount, AmountCents: sum.AmountCents}, nil
}
func (s *Service) Get(ctx context.Context, no string) (*dto.RechargeOrderResponse, error) {
v, err := s.uc.Get(ctx, no)
if err != nil {
return nil, err
}
return order(v), nil
}
func (s *Service) Create(ctx context.Context, userID uint, in *dto.RechargeOrderRequest) (*dto.RechargeCheckoutResponse, error) {
v, result, err := s.uc.Create(ctx, userID, in.PlanID, biz.Checkout{Provider: in.Provider, NotifyURL: in.NotifyURL, ReturnURL: in.ReturnURL, ClientIP: in.ClientIP, UserAgent: in.UserAgent, DeviceID: in.DeviceID, Extra: in.Extra})
if err != nil {
return nil, err
}
return &dto.RechargeCheckoutResponse{Order: order(v), Payment: paymentResult(result)}, nil
}
func order(v *biz.Order) *dto.RechargeOrderResponse {
return &dto.RechargeOrderResponse{
ID: v.ID, OrderNo: v.OrderNo, UserID: v.UserID, Username: v.Username, Nickname: v.Nickname,
Diamonds: v.Diamonds, AmountCents: v.AmountCents, Provider: v.Provider, ProviderTradeNo: v.ProviderTradeNo,
PaymentStatus: v.PaymentStatus, FulfillmentStatus: v.FulfillmentStatus, RefundStatus: v.RefundStatus,
CreatedAt: v.CreatedAt, CreditedAt: v.CreditedAt,
}
}
func paymentResult(result *bizpayment.PaymentResult) *dto.PaymentResultResponse {
if result == nil {
return nil
}
return &dto.PaymentResultResponse{
Provider: result.Provider, Status: result.Status, TradeNo: result.TradeNo,
ProviderTradeNo: result.ProviderTradeNo, Amount: result.Amount,
PayerPaidAmount: result.PayerPaidAmount, CashPaidAmount: result.CashPaidAmount,
PointPaidAmount: result.PointPaidAmount, DiscountAmount: result.DiscountAmount,
ProviderDiscountAmount: result.ProviderDiscountAmount, MerchantDiscountAmount: result.MerchantDiscountAmount,
SettlementAmount: result.SettlementAmount, Currency: result.Currency, PayerCurrency: result.PayerCurrency,
AmountBreakdownKnown: result.AmountBreakdownKnown, Duplicate: result.Duplicate, Payload: result.Payload,
OrderStatus: result.OrderStatus, FulfillmentStatus: result.FulfillmentStatus, RefundStatus: result.RefundStatus,
}
}
+16
View File
@@ -1,8 +1,16 @@
package service
import (
analyticsservice "kra/internal/service/analytics"
assetservice "kra/internal/service/asset"
giftservice "kra/internal/service/gift"
integrationservice "kra/internal/service/integration"
liveservice "kra/internal/service/live"
livechatservice "kra/internal/service/livechat"
liveclientservice "kra/internal/service/liveclient"
paymentservice "kra/internal/service/payment"
platformsettingservice "kra/internal/service/platformsetting"
rechargeservice "kra/internal/service/recharge"
systemservice "kra/internal/service/system"
taskservice "kra/internal/service/task"
@@ -14,5 +22,13 @@ var ProviderSet = wire.NewSet(
systemservice.ProviderSet,
paymentservice.ProviderSet,
integrationservice.ProviderSet,
liveservice.ProviderSet,
livechatservice.ProviderSet,
liveclientservice.ProviderSet,
giftservice.ProviderSet,
rechargeservice.ProviderSet,
assetservice.ProviderSet,
platformsettingservice.ProviderSet,
analyticsservice.ProviderSet,
taskservice.ProviderSet,
)