spark-server/internal/module/client/logic/developer_apps.go
2026-09-29 10:22:28 +08:00

169 lines
6.0 KiB
Go

package logic
import (
"context"
"strings"
"spark-server/internal/module/client/entity"
"spark-server/internal/pkg/errs"
)
const (
appCodeSpark = "spark"
appCodeSleepHeart = "sleepheart"
)
// DeveloperApps returns enabled apps for the given source app (client wall).
func (s *Service) DeveloperApps(ctx context.Context, sourceAppCode, excludePackage string) (*entity.DeveloperAppsPayload, error) {
sourceAppCode = normalizeAppCode(sourceAppCode)
excludePackage = strings.TrimSpace(excludePackage)
rows := s.loadDeveloperApps(ctx, sourceAppCode, true)
// SleepHeart 可回退 spark 共用墙
if len(rows) == 0 && sourceAppCode == appCodeSleepHeart {
rows = s.loadDeveloperApps(ctx, appCodeSpark, true)
}
items := make([]entity.DeveloperAppItem, 0, len(rows))
for i := range rows {
row := &rows[i]
if excludePackage != "" && strings.EqualFold(row.TargetPackageName, excludePackage) {
continue
}
items = append(items, toDeveloperAppItem(row))
}
return &entity.DeveloperAppsPayload{Items: items}, nil
}
// ListDeveloperAppsAdmin returns all rows for popadmin (including disabled).
func (s *Service) ListDeveloperAppsAdmin(ctx context.Context, sourceAppCode string) ([]entity.DeveloperApp, error) {
return s.loadDeveloperApps(ctx, normalizeAppCode(sourceAppCode), false), nil
}
// CreateDeveloperAppAdmin inserts or updates one recommendation.
func (s *Service) CreateDeveloperAppAdmin(ctx context.Context, in *entity.DeveloperApp) (*entity.DeveloperApp, error) {
if s.repo == nil {
return nil, errs.ErrAvailable
}
if in == nil {
return nil, errs.ErrInvalidArgument
}
in.SourceAppCode = normalizeAppCode(in.SourceAppCode)
in.TargetAppName = strings.TrimSpace(in.TargetAppName)
in.TargetPackageName = strings.TrimSpace(in.TargetPackageName)
in.Subtitle = strings.TrimSpace(in.Subtitle)
in.IconURL = strings.TrimSpace(in.IconURL)
in.StoreURL = strings.TrimSpace(in.StoreURL)
in.FallbackURL = strings.TrimSpace(in.FallbackURL)
in.SchemaURL = strings.TrimSpace(in.SchemaURL)
if in.TargetAppName == "" {
return nil, errs.ErrInvalidArgument.WithMsg("targetAppName required")
}
if in.TargetPackageName == "" {
in.TargetPackageName = "pkg_" + strings.ToLower(strings.ReplaceAll(in.TargetAppName, " ", "_"))
}
if in.StoreURL == "" && in.FallbackURL != "" {
in.StoreURL = in.FallbackURL
}
in.Enabled = true
if err := s.repo.UpsertDeveloperApp(ctx, in); err != nil {
return nil, err
}
return in, nil
}
// DeleteDeveloperAppAdmin removes one row by id.
func (s *Service) DeleteDeveloperAppAdmin(ctx context.Context, id int64) error {
if s.repo == nil {
return errs.ErrAvailable
}
if id <= 0 {
return errs.ErrInvalidArgument
}
return s.repo.DeleteDeveloperApp(ctx, id)
}
// SyncWebsiteIconsAdmin upserts catalog apps + default icons from wyrs365.com.
func (s *Service) SyncWebsiteIconsAdmin(ctx context.Context) (int, error) {
if s.repo == nil {
return 0, errs.ErrAvailable
}
n := 0
for _, src := range []string{appCodeSpark, appCodeSleepHeart} {
catalog := websiteDeveloperCatalog(src)
for i := range catalog {
if err := s.repo.UpsertDeveloperApp(ctx, &catalog[i]); err != nil {
return n, err
}
n++
}
}
return n, nil
}
func (s *Service) loadDeveloperApps(ctx context.Context, sourceAppCode string, enabledOnly bool) []entity.DeveloperApp {
if s.repo != nil {
rows, err := s.repo.ListDeveloperApps(ctx, sourceAppCode, enabledOnly)
if err == nil && len(rows) > 0 {
return rows
}
}
return defaultDeveloperApps(sourceAppCode)
}
func toDeveloperAppItem(a *entity.DeveloperApp) entity.DeveloperAppItem {
store := a.StoreURL
if store == "" {
store = a.FallbackURL
}
return entity.DeveloperAppItem{
ID: a.ID, Name: a.TargetAppName, Subtitle: a.Subtitle, IconURL: a.IconURL,
StoreURL: store, SchemaURL: a.SchemaURL, PackageName: a.TargetPackageName, SortOrder: a.SortOrder,
}
}
func normalizeAppCode(code string) string {
code = strings.ToLower(strings.TrimSpace(code))
switch code {
case "", "pop":
return appCodeSpark
case "health", "healthios", "sleep", "sleep-heart":
return appCodeSleepHeart
default:
return code
}
}
func defaultDeveloperApps(sourceAppCode string) []entity.DeveloperApp {
return websiteDeveloperCatalog(sourceAppCode)
}
func websiteDeveloperCatalog(sourceAppCode string) []entity.DeveloperApp {
mk := func(name, pkg, sub, icon, page string, order int) entity.DeveloperApp {
if icon == "" {
icon = "https://www.wyrs365.com/assets/logo-piggy-account.png"
}
return entity.DeveloperApp{
SourceAppCode: sourceAppCode, TargetAppName: name, TargetPackageName: pkg,
Subtitle: sub, IconURL: icon, StoreURL: page, FallbackURL: page,
SortOrder: order, Enabled: true,
}
}
return []entity.DeveloperApp{
mk("Flipclock桌面翻页时钟", "com.wyrs.flipclock", "桌面翻页时钟小组件",
"https://www.wyrs365.com/assets/logo-flipclock.png", "https://www.wyrs365.com/flipclock", 10),
mk("手指测心率", "com.wyrs.heartrate", "指尖 PPG 测心率",
"https://www.wyrs365.com/assets/logo-heartrate.png", "https://www.wyrs365.com/heartrate", 20),
mk("猪咪记账记事本", "com.wyrs.piggy", "记账 · 倒数日 · 小组件",
"https://www.wyrs365.com/assets/logo-piggy-account.png", "https://www.wyrs365.com/piggy-account", 30),
mk("鼾声分贝仪", "com.wyrs.snore", "鼾声检测与分贝测量",
"https://www.wyrs365.com/assets/logo-piggy-account.png", "https://www.wyrs365.com/decibel", 40),
mk("微雨LedBanner", "com.wyrs.ledbanner", "LED 滚动灯牌",
"https://www.wyrs365.com/assets/logo-piggy-account.png", "https://www.wyrs365.com/led-banner", 50),
mk("微雨PDF扫描转换器", "com.wyrs.wypdf", "PDF 扫描转换",
"https://www.wyrs365.com/assets/logo-piggy-account.png", "https://www.wyrs365.com/pdf-scanner", 60),
mk("RunSteps", "com.wyrs.runsteps", "步数与运动",
"https://www.wyrs365.com/assets/logo-piggy-account.png", "https://www.wyrs365.com/runsteps", 70),
mk("火花", "com.wyrs.spark", "即时通讯 · 火花",
"https://www.wyrs365.com/assets/logo-piggy-account.png", "https://www.wyrs365.com/", 80),
}
}