商城c端前端

This commit is contained in:
richjohnrambo
2026-10-12 23:26:36 +08:00
parent 8f3a97a54d
commit dd729cb679
17 changed files with 744 additions and 41 deletions
+90
View File
@@ -23,6 +23,8 @@ type Repository interface {
ActiveMemberLevels(context.Context, int64) ([]store.MemberLevel, error)
ActiveMemberNotices(context.Context, int64, int64, int) ([]store.MemberNotice, error)
MemberTaskSummary(context.Context, int64, int64) (*store.TaskSummary, error)
TaskLeaderboard(context.Context, int64, int) ([]store.LeaderboardMember, error)
ActiveCustomerServices(context.Context, int64) ([]store.CustomerService, error)
}
type Service struct {
@@ -95,6 +97,26 @@ type MemberLevel struct {
Version int `json:"version"`
}
type LeaderboardEntry struct {
Rank int `json:"rank"`
Username string `json:"username"`
AvatarURL string `json:"avatar_url"`
CompletedCount int64 `json:"completed_count"`
Status string `json:"status"`
}
type CustomerService struct {
ID int64 `json:"id"`
ServiceCode string `json:"service_code"`
Name string `json:"name"`
AvatarURL string `json:"avatar_url"`
WorkTime string `json:"work_time"`
OnlineStatus string `json:"online_status"`
ServiceURL string `json:"service_url"`
BackupContact string `json:"backup_contact"`
PrivacyNotice string `json:"privacy_notice"`
}
func New(repository Repository) *Service {
return &Service{repository: repository}
}
@@ -183,6 +205,51 @@ func (s *Service) MemberLevels(ctx context.Context, tenantID int64) ([]MemberLev
return items, nil
}
func (s *Service) Leaderboard(ctx context.Context, tenantID int64) ([]LeaderboardEntry, error) {
rows, err := s.repository.TaskLeaderboard(ctx, tenantID, 50)
if err != nil {
return nil, err
}
items := make([]LeaderboardEntry, 0, len(rows))
for index, row := range rows {
items = append(items, LeaderboardEntry{Rank: index + 1, Username: maskedUsername(row.Username),
AvatarURL: row.AvatarURL, CompletedCount: row.CompletedCount, Status: "领取成功"})
}
return items, nil
}
func (s *Service) CustomerServices(ctx context.Context, tenantID int64) ([]CustomerService, error) {
rows, err := s.repository.ActiveCustomerServices(ctx, tenantID)
if err != nil {
return nil, err
}
items := make([]CustomerService, 0, len(rows))
for _, row := range rows {
items = append(items, CustomerService{
ID: row.ID, ServiceCode: row.ServiceCode, Name: row.Name,
AvatarURL: safeExternalURL(row.AvatarURL), WorkTime: row.WorkTime,
OnlineStatus: customerServiceStatus(row.OnlineStatus), ServiceURL: safeExternalURL(row.ServiceURL),
BackupContact: row.BackupContact,
PrivacyNotice: "请勿在客服会话中发送登录密码、支付密码或短信验证码",
})
}
return items, nil
}
func maskedUsername(value string) string {
runes := []rune(strings.TrimSpace(value))
switch len(runes) {
case 0:
return "会员"
case 1:
return string(runes[0]) + "*"
case 2:
return string(runes[0]) + "*"
default:
return string(runes[0]) + "***" + string(runes[len(runes)-1])
}
}
func parseFeatures(raw json.RawMessage) Features {
var config map[string]any
if json.Unmarshal(raw, &config) != nil {
@@ -241,6 +308,29 @@ func bannerTarget(kind int, value string) (string, string) {
return "none", ""
}
func safeExternalURL(value string) string {
value = strings.TrimSpace(value)
if value == "" {
return ""
}
parsed, err := url.Parse(value)
if err != nil || parsed.Scheme != "https" || parsed.Host == "" || parsed.User != nil {
return ""
}
return parsed.String()
}
func customerServiceStatus(status int) string {
switch status {
case 1:
return "online"
case 3:
return "busy"
default:
return "offline"
}
}
func noticeSourceName(source int) string {
if source == 2 {
return "operational_simulation"