142 lines
3.4 KiB
Go
142 lines
3.4 KiB
Go
package service
|
||
|
||
import (
|
||
"context"
|
||
"crypto/rand"
|
||
"encoding/hex"
|
||
"errors"
|
||
"fmt"
|
||
"strings"
|
||
"time"
|
||
|
||
"pay-bridge/internal/errcode"
|
||
"pay-bridge/internal/model"
|
||
"pay-bridge/internal/repository"
|
||
"pay-bridge/pkg/crypto"
|
||
)
|
||
|
||
// AppService 应用服务
|
||
type AppService struct {
|
||
repo *repository.AppRepository
|
||
encKey string
|
||
}
|
||
|
||
func NewAppService(repo *repository.AppRepository, encKey string) *AppService {
|
||
return &AppService{repo: repo, encKey: encKey}
|
||
}
|
||
|
||
// GetAppSecret 获取 appSecret(用于鉴权中间件)
|
||
func (s *AppService) GetAppSecret(ctx context.Context, appID string) (string, error) {
|
||
app, err := s.repo.GetByAppID(ctx, appID)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
if app == nil {
|
||
return "", errors.New(errcode.ErrAppNotFound)
|
||
}
|
||
|
||
secret, err := crypto.Decrypt(app.AppSecret, s.encKey)
|
||
if err != nil {
|
||
return "", fmt.Errorf("decrypt app secret: %w", err)
|
||
}
|
||
return secret, nil
|
||
}
|
||
|
||
// GetApp 获取应用信息
|
||
func (s *AppService) GetApp(ctx context.Context, appID string) (*model.App, error) {
|
||
return s.repo.GetByAppID(ctx, appID)
|
||
}
|
||
|
||
// CreateAppResult 创建应用的返回,包含明文 secret(仅展示一次)
|
||
type CreateAppResult struct {
|
||
App *model.App
|
||
PlainSecret string
|
||
}
|
||
|
||
// CreateApp 创建应用,自动生成 app_id 和 app_secret
|
||
func (s *AppService) CreateApp(ctx context.Context, appName string) (*CreateAppResult, error) {
|
||
appID := generateAppID()
|
||
plainSecret := generateSecret()
|
||
|
||
encSecret, err := crypto.Encrypt(plainSecret, s.encKey)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
app := &model.App{
|
||
AppID: appID,
|
||
AppSecret: encSecret,
|
||
AppName: appName,
|
||
Status: 1,
|
||
}
|
||
if err := s.repo.Create(ctx, app); err != nil {
|
||
return nil, err
|
||
}
|
||
return &CreateAppResult{App: app, PlainSecret: plainSecret}, nil
|
||
}
|
||
|
||
// ListApps 分页查询应用列表
|
||
func (s *AppService) ListApps(ctx context.Context, limit, offset int) ([]*model.App, error) {
|
||
return s.repo.List(ctx, limit, offset)
|
||
}
|
||
|
||
// DisableApp 禁用应用
|
||
func (s *AppService) DisableApp(ctx context.Context, appID string) error {
|
||
app, err := s.repo.GetByAppIDUnscoped(ctx, appID)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if app == nil {
|
||
return errors.New(errcode.ErrAppNotFound)
|
||
}
|
||
return s.repo.UpdateStatus(ctx, appID, 0)
|
||
}
|
||
|
||
// EnableApp 启用应用
|
||
func (s *AppService) EnableApp(ctx context.Context, appID string) error {
|
||
app, err := s.repo.GetByAppIDUnscoped(ctx, appID)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if app == nil {
|
||
return errors.New(errcode.ErrAppNotFound)
|
||
}
|
||
return s.repo.UpdateStatus(ctx, appID, 1)
|
||
}
|
||
|
||
// ResetSecret 重置应用密钥,返回新的明文 secret(仅此一次)
|
||
func (s *AppService) ResetSecret(ctx context.Context, appID string) (string, error) {
|
||
app, err := s.repo.GetByAppIDUnscoped(ctx, appID)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
if app == nil {
|
||
return "", errors.New(errcode.ErrAppNotFound)
|
||
}
|
||
|
||
plainSecret := generateSecret()
|
||
encSecret, err := crypto.Encrypt(plainSecret, s.encKey)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
if err := s.repo.UpdateSecret(ctx, appID, encSecret); err != nil {
|
||
return "", err
|
||
}
|
||
return plainSecret, nil
|
||
}
|
||
|
||
// generateAppID 生成 app_id:app_ + yyMMdd + 8位随机hex
|
||
func generateAppID() string {
|
||
b := make([]byte, 4)
|
||
_, _ = rand.Read(b)
|
||
date := time.Now().Format("060102")
|
||
return "app_" + date + hex.EncodeToString(b)
|
||
}
|
||
|
||
// generateSecret 生成 32 字节随机 secret(64位hex)
|
||
func generateSecret() string {
|
||
b := make([]byte, 32)
|
||
_, _ = rand.Read(b)
|
||
return strings.ToUpper(hex.EncodeToString(b))
|
||
}
|