Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
陈曦
sub2api
Commits
f51ad2e1
Commit
f51ad2e1
authored
Dec 25, 2025
by
Forest
Browse files
refactor: 删除 ports 目录
parent
f57f12c6
Changes
66
Hide whitespace changes
Inline
Side-by-side
backend/internal/repository/account_repo.go
View file @
f51ad2e1
...
...
@@ -3,10 +3,11 @@ package repository
import
(
"context"
"errors"
"time"
"github.com/Wei-Shaw/sub2api/internal/model"
"github.com/Wei-Shaw/sub2api/internal/pkg/pagination"
"github.com/Wei-Shaw/sub2api/internal/service/ports"
"time"
"github.com/Wei-Shaw/sub2api/internal/service"
"gorm.io/gorm"
"gorm.io/gorm/clause"
...
...
@@ -357,7 +358,7 @@ func (r *AccountRepository) UpdateExtra(ctx context.Context, id int64, updates m
// BulkUpdate updates multiple accounts with the provided fields.
// It merges credentials/extra JSONB fields instead of overwriting them.
func
(
r
*
AccountRepository
)
BulkUpdate
(
ctx
context
.
Context
,
ids
[]
int64
,
updates
ports
.
AccountBulkUpdate
)
(
int64
,
error
)
{
func
(
r
*
AccountRepository
)
BulkUpdate
(
ctx
context
.
Context
,
ids
[]
int64
,
updates
service
.
AccountBulkUpdate
)
(
int64
,
error
)
{
if
len
(
ids
)
==
0
{
return
0
,
nil
}
...
...
backend/internal/repository/account_repo_integration_test.go
View file @
f51ad2e1
...
...
@@ -9,7 +9,7 @@ import (
"github.com/Wei-Shaw/sub2api/internal/model"
"github.com/Wei-Shaw/sub2api/internal/pkg/pagination"
"github.com/Wei-Shaw/sub2api/internal/service
/ports
"
"github.com/Wei-Shaw/sub2api/internal/service"
"github.com/stretchr/testify/suite"
"gorm.io/gorm"
)
...
...
@@ -513,7 +513,7 @@ func (s *AccountRepoSuite) TestBulkUpdate() {
a2
:=
mustCreateAccount
(
s
.
T
(),
s
.
db
,
&
model
.
Account
{
Name
:
"bulk2"
,
Priority
:
1
})
newPriority
:=
99
affected
,
err
:=
s
.
repo
.
BulkUpdate
(
s
.
ctx
,
[]
int64
{
a1
.
ID
,
a2
.
ID
},
ports
.
AccountBulkUpdate
{
affected
,
err
:=
s
.
repo
.
BulkUpdate
(
s
.
ctx
,
[]
int64
{
a1
.
ID
,
a2
.
ID
},
service
.
AccountBulkUpdate
{
Priority
:
&
newPriority
,
})
s
.
Require
()
.
NoError
(
err
)
...
...
@@ -531,7 +531,7 @@ func (s *AccountRepoSuite) TestBulkUpdate_MergeCredentials() {
Credentials
:
model
.
JSONB
{
"existing"
:
"value"
},
})
_
,
err
:=
s
.
repo
.
BulkUpdate
(
s
.
ctx
,
[]
int64
{
a1
.
ID
},
ports
.
AccountBulkUpdate
{
_
,
err
:=
s
.
repo
.
BulkUpdate
(
s
.
ctx
,
[]
int64
{
a1
.
ID
},
service
.
AccountBulkUpdate
{
Credentials
:
model
.
JSONB
{
"new_key"
:
"new_value"
},
})
s
.
Require
()
.
NoError
(
err
)
...
...
@@ -547,7 +547,7 @@ func (s *AccountRepoSuite) TestBulkUpdate_MergeExtra() {
Extra
:
model
.
JSONB
{
"existing"
:
"val"
},
})
_
,
err
:=
s
.
repo
.
BulkUpdate
(
s
.
ctx
,
[]
int64
{
a1
.
ID
},
ports
.
AccountBulkUpdate
{
_
,
err
:=
s
.
repo
.
BulkUpdate
(
s
.
ctx
,
[]
int64
{
a1
.
ID
},
service
.
AccountBulkUpdate
{
Extra
:
model
.
JSONB
{
"new_key"
:
"new_val"
},
})
s
.
Require
()
.
NoError
(
err
)
...
...
@@ -558,7 +558,7 @@ func (s *AccountRepoSuite) TestBulkUpdate_MergeExtra() {
}
func
(
s
*
AccountRepoSuite
)
TestBulkUpdate_EmptyIDs
()
{
affected
,
err
:=
s
.
repo
.
BulkUpdate
(
s
.
ctx
,
[]
int64
{},
ports
.
AccountBulkUpdate
{})
affected
,
err
:=
s
.
repo
.
BulkUpdate
(
s
.
ctx
,
[]
int64
{},
service
.
AccountBulkUpdate
{})
s
.
Require
()
.
NoError
(
err
)
s
.
Require
()
.
Zero
(
affected
)
}
...
...
@@ -566,7 +566,7 @@ func (s *AccountRepoSuite) TestBulkUpdate_EmptyIDs() {
func
(
s
*
AccountRepoSuite
)
TestBulkUpdate_EmptyUpdates
()
{
a1
:=
mustCreateAccount
(
s
.
T
(),
s
.
db
,
&
model
.
Account
{
Name
:
"bulk-empty"
})
affected
,
err
:=
s
.
repo
.
BulkUpdate
(
s
.
ctx
,
[]
int64
{
a1
.
ID
},
ports
.
AccountBulkUpdate
{})
affected
,
err
:=
s
.
repo
.
BulkUpdate
(
s
.
ctx
,
[]
int64
{
a1
.
ID
},
service
.
AccountBulkUpdate
{})
s
.
Require
()
.
NoError
(
err
)
s
.
Require
()
.
Zero
(
affected
)
}
...
...
backend/internal/repository/api_key_cache.go
View file @
f51ad2e1
...
...
@@ -5,8 +5,7 @@ import (
"fmt"
"time"
"github.com/Wei-Shaw/sub2api/internal/service/ports"
"github.com/Wei-Shaw/sub2api/internal/service"
"github.com/redis/go-redis/v9"
)
...
...
@@ -19,7 +18,7 @@ type apiKeyCache struct {
rdb
*
redis
.
Client
}
func
NewApiKeyCache
(
rdb
*
redis
.
Client
)
ports
.
ApiKeyCache
{
func
NewApiKeyCache
(
rdb
*
redis
.
Client
)
service
.
ApiKeyCache
{
return
&
apiKeyCache
{
rdb
:
rdb
}
}
...
...
backend/internal/repository/billing_cache.go
View file @
f51ad2e1
...
...
@@ -8,8 +8,7 @@ import (
"strconv"
"time"
"github.com/Wei-Shaw/sub2api/internal/service/ports"
"github.com/Wei-Shaw/sub2api/internal/service"
"github.com/redis/go-redis/v9"
)
...
...
@@ -58,7 +57,7 @@ type billingCache struct {
rdb
*
redis
.
Client
}
func
NewBillingCache
(
rdb
*
redis
.
Client
)
ports
.
BillingCache
{
func
NewBillingCache
(
rdb
*
redis
.
Client
)
service
.
BillingCache
{
return
&
billingCache
{
rdb
:
rdb
}
}
...
...
@@ -90,7 +89,7 @@ func (c *billingCache) InvalidateUserBalance(ctx context.Context, userID int64)
return
c
.
rdb
.
Del
(
ctx
,
key
)
.
Err
()
}
func
(
c
*
billingCache
)
GetSubscriptionCache
(
ctx
context
.
Context
,
userID
,
groupID
int64
)
(
*
ports
.
SubscriptionCacheData
,
error
)
{
func
(
c
*
billingCache
)
GetSubscriptionCache
(
ctx
context
.
Context
,
userID
,
groupID
int64
)
(
*
service
.
SubscriptionCacheData
,
error
)
{
key
:=
fmt
.
Sprintf
(
"%s%d:%d"
,
billingSubKeyPrefix
,
userID
,
groupID
)
result
,
err
:=
c
.
rdb
.
HGetAll
(
ctx
,
key
)
.
Result
()
if
err
!=
nil
{
...
...
@@ -102,8 +101,8 @@ func (c *billingCache) GetSubscriptionCache(ctx context.Context, userID, groupID
return
c
.
parseSubscriptionCache
(
result
)
}
func
(
c
*
billingCache
)
parseSubscriptionCache
(
data
map
[
string
]
string
)
(
*
ports
.
SubscriptionCacheData
,
error
)
{
result
:=
&
ports
.
SubscriptionCacheData
{}
func
(
c
*
billingCache
)
parseSubscriptionCache
(
data
map
[
string
]
string
)
(
*
service
.
SubscriptionCacheData
,
error
)
{
result
:=
&
service
.
SubscriptionCacheData
{}
result
.
Status
=
data
[
subFieldStatus
]
if
result
.
Status
==
""
{
...
...
@@ -136,7 +135,7 @@ func (c *billingCache) parseSubscriptionCache(data map[string]string) (*ports.Su
return
result
,
nil
}
func
(
c
*
billingCache
)
SetSubscriptionCache
(
ctx
context
.
Context
,
userID
,
groupID
int64
,
data
*
ports
.
SubscriptionCacheData
)
error
{
func
(
c
*
billingCache
)
SetSubscriptionCache
(
ctx
context
.
Context
,
userID
,
groupID
int64
,
data
*
service
.
SubscriptionCacheData
)
error
{
if
data
==
nil
{
return
nil
}
...
...
backend/internal/repository/billing_cache_integration_test.go
View file @
f51ad2e1
...
...
@@ -8,7 +8,7 @@ import (
"testing"
"time"
"github.com/Wei-Shaw/sub2api/internal/service
/ports
"
"github.com/Wei-Shaw/sub2api/internal/service"
"github.com/redis/go-redis/v9"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
...
...
@@ -21,18 +21,18 @@ type BillingCacheSuite struct {
func
(
s
*
BillingCacheSuite
)
TestUserBalance
()
{
tests
:=
[]
struct
{
name
string
fn
func
(
ctx
context
.
Context
,
rdb
*
redis
.
Client
,
cache
ports
.
BillingCache
)
fn
func
(
ctx
context
.
Context
,
rdb
*
redis
.
Client
,
cache
service
.
BillingCache
)
}{
{
name
:
"missing_key_returns_redis_nil"
,
fn
:
func
(
ctx
context
.
Context
,
rdb
*
redis
.
Client
,
cache
ports
.
BillingCache
)
{
fn
:
func
(
ctx
context
.
Context
,
rdb
*
redis
.
Client
,
cache
service
.
BillingCache
)
{
_
,
err
:=
cache
.
GetUserBalance
(
ctx
,
1
)
require
.
ErrorIs
(
s
.
T
(),
err
,
redis
.
Nil
,
"expected redis.Nil for missing balance key"
)
},
},
{
name
:
"deduct_on_nonexistent_is_noop"
,
fn
:
func
(
ctx
context
.
Context
,
rdb
*
redis
.
Client
,
cache
ports
.
BillingCache
)
{
fn
:
func
(
ctx
context
.
Context
,
rdb
*
redis
.
Client
,
cache
service
.
BillingCache
)
{
userID
:=
int64
(
1
)
balanceKey
:=
fmt
.
Sprintf
(
"%s%d"
,
billingBalanceKeyPrefix
,
userID
)
...
...
@@ -44,7 +44,7 @@ func (s *BillingCacheSuite) TestUserBalance() {
},
{
name
:
"set_and_get_with_ttl"
,
fn
:
func
(
ctx
context
.
Context
,
rdb
*
redis
.
Client
,
cache
ports
.
BillingCache
)
{
fn
:
func
(
ctx
context
.
Context
,
rdb
*
redis
.
Client
,
cache
service
.
BillingCache
)
{
userID
:=
int64
(
2
)
balanceKey
:=
fmt
.
Sprintf
(
"%s%d"
,
billingBalanceKeyPrefix
,
userID
)
...
...
@@ -61,7 +61,7 @@ func (s *BillingCacheSuite) TestUserBalance() {
},
{
name
:
"deduct_reduces_balance"
,
fn
:
func
(
ctx
context
.
Context
,
rdb
*
redis
.
Client
,
cache
ports
.
BillingCache
)
{
fn
:
func
(
ctx
context
.
Context
,
rdb
*
redis
.
Client
,
cache
service
.
BillingCache
)
{
userID
:=
int64
(
3
)
require
.
NoError
(
s
.
T
(),
cache
.
SetUserBalance
(
ctx
,
userID
,
10.5
),
"SetUserBalance"
)
...
...
@@ -74,7 +74,7 @@ func (s *BillingCacheSuite) TestUserBalance() {
},
{
name
:
"invalidate_removes_key"
,
fn
:
func
(
ctx
context
.
Context
,
rdb
*
redis
.
Client
,
cache
ports
.
BillingCache
)
{
fn
:
func
(
ctx
context
.
Context
,
rdb
*
redis
.
Client
,
cache
service
.
BillingCache
)
{
userID
:=
int64
(
100
)
balanceKey
:=
fmt
.
Sprintf
(
"%s%d"
,
billingBalanceKeyPrefix
,
userID
)
...
...
@@ -96,7 +96,7 @@ func (s *BillingCacheSuite) TestUserBalance() {
},
{
name
:
"deduct_refreshes_ttl"
,
fn
:
func
(
ctx
context
.
Context
,
rdb
*
redis
.
Client
,
cache
ports
.
BillingCache
)
{
fn
:
func
(
ctx
context
.
Context
,
rdb
*
redis
.
Client
,
cache
service
.
BillingCache
)
{
userID
:=
int64
(
103
)
balanceKey
:=
fmt
.
Sprintf
(
"%s%d"
,
billingBalanceKeyPrefix
,
userID
)
...
...
@@ -133,11 +133,11 @@ func (s *BillingCacheSuite) TestUserBalance() {
func
(
s
*
BillingCacheSuite
)
TestSubscriptionCache
()
{
tests
:=
[]
struct
{
name
string
fn
func
(
ctx
context
.
Context
,
rdb
*
redis
.
Client
,
cache
ports
.
BillingCache
)
fn
func
(
ctx
context
.
Context
,
rdb
*
redis
.
Client
,
cache
service
.
BillingCache
)
}{
{
name
:
"missing_key_returns_redis_nil"
,
fn
:
func
(
ctx
context
.
Context
,
rdb
*
redis
.
Client
,
cache
ports
.
BillingCache
)
{
fn
:
func
(
ctx
context
.
Context
,
rdb
*
redis
.
Client
,
cache
service
.
BillingCache
)
{
userID
:=
int64
(
10
)
groupID
:=
int64
(
20
)
...
...
@@ -147,7 +147,7 @@ func (s *BillingCacheSuite) TestSubscriptionCache() {
},
{
name
:
"update_usage_on_nonexistent_is_noop"
,
fn
:
func
(
ctx
context
.
Context
,
rdb
*
redis
.
Client
,
cache
ports
.
BillingCache
)
{
fn
:
func
(
ctx
context
.
Context
,
rdb
*
redis
.
Client
,
cache
service
.
BillingCache
)
{
userID
:=
int64
(
11
)
groupID
:=
int64
(
21
)
subKey
:=
fmt
.
Sprintf
(
"%s%d:%d"
,
billingSubKeyPrefix
,
userID
,
groupID
)
...
...
@@ -161,12 +161,12 @@ func (s *BillingCacheSuite) TestSubscriptionCache() {
},
{
name
:
"set_and_get_with_ttl"
,
fn
:
func
(
ctx
context
.
Context
,
rdb
*
redis
.
Client
,
cache
ports
.
BillingCache
)
{
fn
:
func
(
ctx
context
.
Context
,
rdb
*
redis
.
Client
,
cache
service
.
BillingCache
)
{
userID
:=
int64
(
12
)
groupID
:=
int64
(
22
)
subKey
:=
fmt
.
Sprintf
(
"%s%d:%d"
,
billingSubKeyPrefix
,
userID
,
groupID
)
data
:=
&
ports
.
SubscriptionCacheData
{
data
:=
&
service
.
SubscriptionCacheData
{
Status
:
"active"
,
ExpiresAt
:
time
.
Now
()
.
Add
(
1
*
time
.
Hour
),
DailyUsage
:
1.0
,
...
...
@@ -189,11 +189,11 @@ func (s *BillingCacheSuite) TestSubscriptionCache() {
},
{
name
:
"update_usage_increments_all_fields"
,
fn
:
func
(
ctx
context
.
Context
,
rdb
*
redis
.
Client
,
cache
ports
.
BillingCache
)
{
fn
:
func
(
ctx
context
.
Context
,
rdb
*
redis
.
Client
,
cache
service
.
BillingCache
)
{
userID
:=
int64
(
13
)
groupID
:=
int64
(
23
)
data
:=
&
ports
.
SubscriptionCacheData
{
data
:=
&
service
.
SubscriptionCacheData
{
Status
:
"active"
,
ExpiresAt
:
time
.
Now
()
.
Add
(
1
*
time
.
Hour
),
DailyUsage
:
1.0
,
...
...
@@ -214,12 +214,12 @@ func (s *BillingCacheSuite) TestSubscriptionCache() {
},
{
name
:
"invalidate_removes_key"
,
fn
:
func
(
ctx
context
.
Context
,
rdb
*
redis
.
Client
,
cache
ports
.
BillingCache
)
{
fn
:
func
(
ctx
context
.
Context
,
rdb
*
redis
.
Client
,
cache
service
.
BillingCache
)
{
userID
:=
int64
(
101
)
groupID
:=
int64
(
10
)
subKey
:=
fmt
.
Sprintf
(
"%s%d:%d"
,
billingSubKeyPrefix
,
userID
,
groupID
)
data
:=
&
ports
.
SubscriptionCacheData
{
data
:=
&
service
.
SubscriptionCacheData
{
Status
:
"active"
,
ExpiresAt
:
time
.
Now
()
.
Add
(
1
*
time
.
Hour
),
DailyUsage
:
1.0
,
...
...
@@ -245,7 +245,7 @@ func (s *BillingCacheSuite) TestSubscriptionCache() {
},
{
name
:
"missing_status_returns_parsing_error"
,
fn
:
func
(
ctx
context
.
Context
,
rdb
*
redis
.
Client
,
cache
ports
.
BillingCache
)
{
fn
:
func
(
ctx
context
.
Context
,
rdb
*
redis
.
Client
,
cache
service
.
BillingCache
)
{
userID
:=
int64
(
102
)
groupID
:=
int64
(
11
)
subKey
:=
fmt
.
Sprintf
(
"%s%d:%d"
,
billingSubKeyPrefix
,
userID
,
groupID
)
...
...
backend/internal/repository/concurrency_cache.go
View file @
f51ad2e1
...
...
@@ -5,8 +5,7 @@ import (
"fmt"
"time"
"github.com/Wei-Shaw/sub2api/internal/service/ports"
"github.com/Wei-Shaw/sub2api/internal/service"
"github.com/redis/go-redis/v9"
)
...
...
@@ -107,7 +106,7 @@ type concurrencyCache struct {
rdb
*
redis
.
Client
}
func
NewConcurrencyCache
(
rdb
*
redis
.
Client
)
ports
.
ConcurrencyCache
{
func
NewConcurrencyCache
(
rdb
*
redis
.
Client
)
service
.
ConcurrencyCache
{
return
&
concurrencyCache
{
rdb
:
rdb
}
}
...
...
backend/internal/repository/concurrency_cache_integration_test.go
View file @
f51ad2e1
...
...
@@ -8,7 +8,7 @@ import (
"testing"
"time"
"github.com/Wei-Shaw/sub2api/internal/service
/ports
"
"github.com/Wei-Shaw/sub2api/internal/service"
"github.com/redis/go-redis/v9"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
...
...
@@ -16,7 +16,7 @@ import (
type
ConcurrencyCacheSuite
struct
{
IntegrationRedisSuite
cache
ports
.
ConcurrencyCache
cache
service
.
ConcurrencyCache
}
func
(
s
*
ConcurrencyCacheSuite
)
SetupTest
()
{
...
...
backend/internal/repository/email_cache.go
View file @
f51ad2e1
...
...
@@ -5,8 +5,7 @@ import (
"encoding/json"
"time"
"github.com/Wei-Shaw/sub2api/internal/service/ports"
"github.com/Wei-Shaw/sub2api/internal/service"
"github.com/redis/go-redis/v9"
)
...
...
@@ -16,24 +15,24 @@ type emailCache struct {
rdb
*
redis
.
Client
}
func
NewEmailCache
(
rdb
*
redis
.
Client
)
ports
.
EmailCache
{
func
NewEmailCache
(
rdb
*
redis
.
Client
)
service
.
EmailCache
{
return
&
emailCache
{
rdb
:
rdb
}
}
func
(
c
*
emailCache
)
GetVerificationCode
(
ctx
context
.
Context
,
email
string
)
(
*
ports
.
VerificationCodeData
,
error
)
{
func
(
c
*
emailCache
)
GetVerificationCode
(
ctx
context
.
Context
,
email
string
)
(
*
service
.
VerificationCodeData
,
error
)
{
key
:=
verifyCodeKeyPrefix
+
email
val
,
err
:=
c
.
rdb
.
Get
(
ctx
,
key
)
.
Result
()
if
err
!=
nil
{
return
nil
,
err
}
var
data
ports
.
VerificationCodeData
var
data
service
.
VerificationCodeData
if
err
:=
json
.
Unmarshal
([]
byte
(
val
),
&
data
);
err
!=
nil
{
return
nil
,
err
}
return
&
data
,
nil
}
func
(
c
*
emailCache
)
SetVerificationCode
(
ctx
context
.
Context
,
email
string
,
data
*
ports
.
VerificationCodeData
,
ttl
time
.
Duration
)
error
{
func
(
c
*
emailCache
)
SetVerificationCode
(
ctx
context
.
Context
,
email
string
,
data
*
service
.
VerificationCodeData
,
ttl
time
.
Duration
)
error
{
key
:=
verifyCodeKeyPrefix
+
email
val
,
err
:=
json
.
Marshal
(
data
)
if
err
!=
nil
{
...
...
backend/internal/repository/email_cache_integration_test.go
View file @
f51ad2e1
...
...
@@ -7,7 +7,7 @@ import (
"testing"
"time"
"github.com/Wei-Shaw/sub2api/internal/service
/ports
"
"github.com/Wei-Shaw/sub2api/internal/service"
"github.com/redis/go-redis/v9"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
...
...
@@ -15,7 +15,7 @@ import (
type
EmailCacheSuite
struct
{
IntegrationRedisSuite
cache
ports
.
EmailCache
cache
service
.
EmailCache
}
func
(
s
*
EmailCacheSuite
)
SetupTest
()
{
...
...
@@ -31,7 +31,7 @@ func (s *EmailCacheSuite) TestGetVerificationCode_Missing() {
func
(
s
*
EmailCacheSuite
)
TestSetAndGetVerificationCode
()
{
email
:=
"a@example.com"
emailTTL
:=
2
*
time
.
Minute
data
:=
&
ports
.
VerificationCodeData
{
Code
:
"123456"
,
Attempts
:
1
,
CreatedAt
:
time
.
Now
()}
data
:=
&
service
.
VerificationCodeData
{
Code
:
"123456"
,
Attempts
:
1
,
CreatedAt
:
time
.
Now
()}
require
.
NoError
(
s
.
T
(),
s
.
cache
.
SetVerificationCode
(
s
.
ctx
,
email
,
data
,
emailTTL
),
"SetVerificationCode"
)
...
...
@@ -44,7 +44,7 @@ func (s *EmailCacheSuite) TestSetAndGetVerificationCode() {
func
(
s
*
EmailCacheSuite
)
TestVerificationCode_TTL
()
{
email
:=
"ttl@example.com"
emailTTL
:=
2
*
time
.
Minute
data
:=
&
ports
.
VerificationCodeData
{
Code
:
"654321"
,
Attempts
:
0
,
CreatedAt
:
time
.
Now
()}
data
:=
&
service
.
VerificationCodeData
{
Code
:
"654321"
,
Attempts
:
0
,
CreatedAt
:
time
.
Now
()}
require
.
NoError
(
s
.
T
(),
s
.
cache
.
SetVerificationCode
(
s
.
ctx
,
email
,
data
,
emailTTL
),
"SetVerificationCode"
)
...
...
@@ -56,7 +56,7 @@ func (s *EmailCacheSuite) TestVerificationCode_TTL() {
func
(
s
*
EmailCacheSuite
)
TestDeleteVerificationCode
()
{
email
:=
"delete@example.com"
data
:=
&
ports
.
VerificationCodeData
{
Code
:
"999999"
,
Attempts
:
0
,
CreatedAt
:
time
.
Now
()}
data
:=
&
service
.
VerificationCodeData
{
Code
:
"999999"
,
Attempts
:
0
,
CreatedAt
:
time
.
Now
()}
require
.
NoError
(
s
.
T
(),
s
.
cache
.
SetVerificationCode
(
s
.
ctx
,
email
,
data
,
2
*
time
.
Minute
),
"SetVerificationCode"
)
...
...
backend/internal/repository/gateway_cache.go
View file @
f51ad2e1
...
...
@@ -4,8 +4,7 @@ import (
"context"
"time"
"github.com/Wei-Shaw/sub2api/internal/service/ports"
"github.com/Wei-Shaw/sub2api/internal/service"
"github.com/redis/go-redis/v9"
)
...
...
@@ -15,7 +14,7 @@ type gatewayCache struct {
rdb
*
redis
.
Client
}
func
NewGatewayCache
(
rdb
*
redis
.
Client
)
ports
.
GatewayCache
{
func
NewGatewayCache
(
rdb
*
redis
.
Client
)
service
.
GatewayCache
{
return
&
gatewayCache
{
rdb
:
rdb
}
}
...
...
backend/internal/repository/gateway_cache_integration_test.go
View file @
f51ad2e1
...
...
@@ -7,7 +7,7 @@ import (
"testing"
"time"
"github.com/Wei-Shaw/sub2api/internal/service
/ports
"
"github.com/Wei-Shaw/sub2api/internal/service"
"github.com/redis/go-redis/v9"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
...
...
@@ -15,7 +15,7 @@ import (
type
GatewayCacheSuite
struct
{
IntegrationRedisSuite
cache
ports
.
GatewayCache
cache
service
.
GatewayCache
}
func
(
s
*
GatewayCacheSuite
)
SetupTest
()
{
...
...
backend/internal/repository/http_upstream.go
View file @
f51ad2e1
...
...
@@ -6,7 +6,7 @@ import (
"time"
"github.com/Wei-Shaw/sub2api/internal/config"
"github.com/Wei-Shaw/sub2api/internal/service
/ports
"
"github.com/Wei-Shaw/sub2api/internal/service"
)
// httpUpstreamService is a generic HTTP upstream service that can be used for
...
...
@@ -17,7 +17,7 @@ type httpUpstreamService struct {
}
// NewHTTPUpstream creates a new generic HTTP upstream service
func
NewHTTPUpstream
(
cfg
*
config
.
Config
)
ports
.
HTTPUpstream
{
func
NewHTTPUpstream
(
cfg
*
config
.
Config
)
service
.
HTTPUpstream
{
responseHeaderTimeout
:=
time
.
Duration
(
cfg
.
Gateway
.
ResponseHeaderTimeout
)
*
time
.
Second
if
responseHeaderTimeout
==
0
{
responseHeaderTimeout
=
300
*
time
.
Second
...
...
backend/internal/repository/identity_cache.go
View file @
f51ad2e1
...
...
@@ -6,8 +6,7 @@ import (
"fmt"
"time"
"github.com/Wei-Shaw/sub2api/internal/service/ports"
"github.com/Wei-Shaw/sub2api/internal/service"
"github.com/redis/go-redis/v9"
)
...
...
@@ -20,24 +19,24 @@ type identityCache struct {
rdb
*
redis
.
Client
}
func
NewIdentityCache
(
rdb
*
redis
.
Client
)
ports
.
IdentityCache
{
func
NewIdentityCache
(
rdb
*
redis
.
Client
)
service
.
IdentityCache
{
return
&
identityCache
{
rdb
:
rdb
}
}
func
(
c
*
identityCache
)
GetFingerprint
(
ctx
context
.
Context
,
accountID
int64
)
(
*
ports
.
Fingerprint
,
error
)
{
func
(
c
*
identityCache
)
GetFingerprint
(
ctx
context
.
Context
,
accountID
int64
)
(
*
service
.
Fingerprint
,
error
)
{
key
:=
fmt
.
Sprintf
(
"%s%d"
,
fingerprintKeyPrefix
,
accountID
)
val
,
err
:=
c
.
rdb
.
Get
(
ctx
,
key
)
.
Result
()
if
err
!=
nil
{
return
nil
,
err
}
var
fp
ports
.
Fingerprint
var
fp
service
.
Fingerprint
if
err
:=
json
.
Unmarshal
([]
byte
(
val
),
&
fp
);
err
!=
nil
{
return
nil
,
err
}
return
&
fp
,
nil
}
func
(
c
*
identityCache
)
SetFingerprint
(
ctx
context
.
Context
,
accountID
int64
,
fp
*
ports
.
Fingerprint
)
error
{
func
(
c
*
identityCache
)
SetFingerprint
(
ctx
context
.
Context
,
accountID
int64
,
fp
*
service
.
Fingerprint
)
error
{
key
:=
fmt
.
Sprintf
(
"%s%d"
,
fingerprintKeyPrefix
,
accountID
)
val
,
err
:=
json
.
Marshal
(
fp
)
if
err
!=
nil
{
...
...
backend/internal/repository/identity_cache_integration_test.go
View file @
f51ad2e1
...
...
@@ -8,7 +8,7 @@ import (
"testing"
"time"
"github.com/Wei-Shaw/sub2api/internal/service
/ports
"
"github.com/Wei-Shaw/sub2api/internal/service"
"github.com/redis/go-redis/v9"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
...
...
@@ -30,7 +30,7 @@ func (s *IdentityCacheSuite) TestGetFingerprint_Missing() {
}
func
(
s
*
IdentityCacheSuite
)
TestSetAndGetFingerprint
()
{
fp
:=
&
ports
.
Fingerprint
{
ClientID
:
"c1"
,
UserAgent
:
"ua"
}
fp
:=
&
service
.
Fingerprint
{
ClientID
:
"c1"
,
UserAgent
:
"ua"
}
require
.
NoError
(
s
.
T
(),
s
.
cache
.
SetFingerprint
(
s
.
ctx
,
1
,
fp
),
"SetFingerprint"
)
gotFP
,
err
:=
s
.
cache
.
GetFingerprint
(
s
.
ctx
,
1
)
require
.
NoError
(
s
.
T
(),
err
,
"GetFingerprint"
)
...
...
@@ -39,7 +39,7 @@ func (s *IdentityCacheSuite) TestSetAndGetFingerprint() {
}
func
(
s
*
IdentityCacheSuite
)
TestFingerprint_TTL
()
{
fp
:=
&
ports
.
Fingerprint
{
ClientID
:
"c1"
,
UserAgent
:
"ua"
}
fp
:=
&
service
.
Fingerprint
{
ClientID
:
"c1"
,
UserAgent
:
"ua"
}
require
.
NoError
(
s
.
T
(),
s
.
cache
.
SetFingerprint
(
s
.
ctx
,
2
,
fp
))
fpKey
:=
fmt
.
Sprintf
(
"%s%d"
,
fingerprintKeyPrefix
,
2
)
...
...
backend/internal/repository/openai_oauth_service.go
View file @
f51ad2e1
...
...
@@ -7,13 +7,12 @@ import (
"time"
"github.com/Wei-Shaw/sub2api/internal/pkg/openai"
"github.com/Wei-Shaw/sub2api/internal/service/ports"
"github.com/Wei-Shaw/sub2api/internal/service"
"github.com/imroc/req/v3"
)
// NewOpenAIOAuthClient creates a new OpenAI OAuth client
func
NewOpenAIOAuthClient
()
ports
.
OpenAIOAuthClient
{
func
NewOpenAIOAuthClient
()
service
.
OpenAIOAuthClient
{
return
&
openaiOAuthService
{
tokenURL
:
openai
.
TokenURL
}
}
...
...
backend/internal/repository/redeem_cache.go
View file @
f51ad2e1
...
...
@@ -5,8 +5,7 @@ import (
"fmt"
"time"
"github.com/Wei-Shaw/sub2api/internal/service/ports"
"github.com/Wei-Shaw/sub2api/internal/service"
"github.com/redis/go-redis/v9"
)
...
...
@@ -20,7 +19,7 @@ type redeemCache struct {
rdb
*
redis
.
Client
}
func
NewRedeemCache
(
rdb
*
redis
.
Client
)
ports
.
RedeemCache
{
func
NewRedeemCache
(
rdb
*
redis
.
Client
)
service
.
RedeemCache
{
return
&
redeemCache
{
rdb
:
rdb
}
}
...
...
backend/internal/repository/update_cache.go
View file @
f51ad2e1
...
...
@@ -4,8 +4,7 @@ import (
"context"
"time"
"github.com/Wei-Shaw/sub2api/internal/service/ports"
"github.com/Wei-Shaw/sub2api/internal/service"
"github.com/redis/go-redis/v9"
)
...
...
@@ -15,7 +14,7 @@ type updateCache struct {
rdb
*
redis
.
Client
}
func
NewUpdateCache
(
rdb
*
redis
.
Client
)
ports
.
UpdateCache
{
func
NewUpdateCache
(
rdb
*
redis
.
Client
)
service
.
UpdateCache
{
return
&
updateCache
{
rdb
:
rdb
}
}
...
...
backend/internal/repository/wire.go
View file @
f51ad2e1
package
repository
import
(
"github.com/Wei-Shaw/sub2api/internal/service/ports"
"github.com/Wei-Shaw/sub2api/internal/service"
"github.com/google/wire"
)
...
...
@@ -40,13 +39,13 @@ var ProviderSet = wire.NewSet(
NewOpenAIOAuthClient
,
// Bind concrete repositories to service port interfaces
wire
.
Bind
(
new
(
ports
.
UserRepository
),
new
(
*
UserRepository
)),
wire
.
Bind
(
new
(
ports
.
ApiKeyRepository
),
new
(
*
ApiKeyRepository
)),
wire
.
Bind
(
new
(
ports
.
GroupRepository
),
new
(
*
GroupRepository
)),
wire
.
Bind
(
new
(
ports
.
AccountRepository
),
new
(
*
AccountRepository
)),
wire
.
Bind
(
new
(
ports
.
ProxyRepository
),
new
(
*
ProxyRepository
)),
wire
.
Bind
(
new
(
ports
.
RedeemCodeRepository
),
new
(
*
RedeemCodeRepository
)),
wire
.
Bind
(
new
(
ports
.
UsageLogRepository
),
new
(
*
UsageLogRepository
)),
wire
.
Bind
(
new
(
ports
.
SettingRepository
),
new
(
*
SettingRepository
)),
wire
.
Bind
(
new
(
ports
.
UserSubscriptionRepository
),
new
(
*
UserSubscriptionRepository
)),
wire
.
Bind
(
new
(
service
.
UserRepository
),
new
(
*
UserRepository
)),
wire
.
Bind
(
new
(
service
.
ApiKeyRepository
),
new
(
*
ApiKeyRepository
)),
wire
.
Bind
(
new
(
service
.
GroupRepository
),
new
(
*
GroupRepository
)),
wire
.
Bind
(
new
(
service
.
AccountRepository
),
new
(
*
AccountRepository
)),
wire
.
Bind
(
new
(
service
.
ProxyRepository
),
new
(
*
ProxyRepository
)),
wire
.
Bind
(
new
(
service
.
RedeemCodeRepository
),
new
(
*
RedeemCodeRepository
)),
wire
.
Bind
(
new
(
service
.
UsageLogRepository
),
new
(
*
UsageLogRepository
)),
wire
.
Bind
(
new
(
service
.
SettingRepository
),
new
(
*
SettingRepository
)),
wire
.
Bind
(
new
(
service
.
UserSubscriptionRepository
),
new
(
*
UserSubscriptionRepository
)),
)
backend/internal/service/account_service.go
View file @
f51ad2e1
...
...
@@ -4,10 +4,10 @@ import (
"context"
"errors"
"fmt"
"time"
"github.com/Wei-Shaw/sub2api/internal/model"
"github.com/Wei-Shaw/sub2api/internal/pkg/pagination"
"github.com/Wei-Shaw/sub2api/internal/service/ports"
"gorm.io/gorm"
)
...
...
@@ -15,6 +15,51 @@ var (
ErrAccountNotFound
=
errors
.
New
(
"account not found"
)
)
type
AccountRepository
interface
{
Create
(
ctx
context
.
Context
,
account
*
model
.
Account
)
error
GetByID
(
ctx
context
.
Context
,
id
int64
)
(
*
model
.
Account
,
error
)
// GetByCRSAccountID finds an account previously synced from CRS.
// Returns (nil, nil) if not found.
GetByCRSAccountID
(
ctx
context
.
Context
,
crsAccountID
string
)
(
*
model
.
Account
,
error
)
Update
(
ctx
context
.
Context
,
account
*
model
.
Account
)
error
Delete
(
ctx
context
.
Context
,
id
int64
)
error
List
(
ctx
context
.
Context
,
params
pagination
.
PaginationParams
)
([]
model
.
Account
,
*
pagination
.
PaginationResult
,
error
)
ListWithFilters
(
ctx
context
.
Context
,
params
pagination
.
PaginationParams
,
platform
,
accountType
,
status
,
search
string
)
([]
model
.
Account
,
*
pagination
.
PaginationResult
,
error
)
ListByGroup
(
ctx
context
.
Context
,
groupID
int64
)
([]
model
.
Account
,
error
)
ListActive
(
ctx
context
.
Context
)
([]
model
.
Account
,
error
)
ListByPlatform
(
ctx
context
.
Context
,
platform
string
)
([]
model
.
Account
,
error
)
UpdateLastUsed
(
ctx
context
.
Context
,
id
int64
)
error
SetError
(
ctx
context
.
Context
,
id
int64
,
errorMsg
string
)
error
SetSchedulable
(
ctx
context
.
Context
,
id
int64
,
schedulable
bool
)
error
BindGroups
(
ctx
context
.
Context
,
accountID
int64
,
groupIDs
[]
int64
)
error
ListSchedulable
(
ctx
context
.
Context
)
([]
model
.
Account
,
error
)
ListSchedulableByGroupID
(
ctx
context
.
Context
,
groupID
int64
)
([]
model
.
Account
,
error
)
ListSchedulableByPlatform
(
ctx
context
.
Context
,
platform
string
)
([]
model
.
Account
,
error
)
ListSchedulableByGroupIDAndPlatform
(
ctx
context
.
Context
,
groupID
int64
,
platform
string
)
([]
model
.
Account
,
error
)
SetRateLimited
(
ctx
context
.
Context
,
id
int64
,
resetAt
time
.
Time
)
error
SetOverloaded
(
ctx
context
.
Context
,
id
int64
,
until
time
.
Time
)
error
ClearRateLimit
(
ctx
context
.
Context
,
id
int64
)
error
UpdateSessionWindow
(
ctx
context
.
Context
,
id
int64
,
start
,
end
*
time
.
Time
,
status
string
)
error
UpdateExtra
(
ctx
context
.
Context
,
id
int64
,
updates
map
[
string
]
any
)
error
BulkUpdate
(
ctx
context
.
Context
,
ids
[]
int64
,
updates
AccountBulkUpdate
)
(
int64
,
error
)
}
// AccountBulkUpdate describes the fields that can be updated in a bulk operation.
// Nil pointers mean "do not change".
type
AccountBulkUpdate
struct
{
Name
*
string
ProxyID
*
int64
Concurrency
*
int
Priority
*
int
Status
*
string
Credentials
map
[
string
]
any
Extra
map
[
string
]
any
}
// CreateAccountRequest 创建账号请求
type
CreateAccountRequest
struct
{
Name
string
`json:"name"`
...
...
@@ -42,12 +87,12 @@ type UpdateAccountRequest struct {
// AccountService 账号管理服务
type
AccountService
struct
{
accountRepo
ports
.
AccountRepository
groupRepo
ports
.
GroupRepository
accountRepo
AccountRepository
groupRepo
GroupRepository
}
// NewAccountService 创建账号服务实例
func
NewAccountService
(
accountRepo
ports
.
AccountRepository
,
groupRepo
ports
.
GroupRepository
)
*
AccountService
{
func
NewAccountService
(
accountRepo
AccountRepository
,
groupRepo
GroupRepository
)
*
AccountService
{
return
&
AccountService
{
accountRepo
:
accountRepo
,
groupRepo
:
groupRepo
,
...
...
backend/internal/service/account_test_service.go
View file @
f51ad2e1
...
...
@@ -17,8 +17,6 @@ import (
"github.com/Wei-Shaw/sub2api/internal/model"
"github.com/Wei-Shaw/sub2api/internal/pkg/claude"
"github.com/Wei-Shaw/sub2api/internal/pkg/openai"
"github.com/Wei-Shaw/sub2api/internal/service/ports"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
)
...
...
@@ -40,14 +38,14 @@ type TestEvent struct {
// AccountTestService handles account testing operations
type
AccountTestService
struct
{
accountRepo
ports
.
AccountRepository
accountRepo
AccountRepository
oauthService
*
OAuthService
openaiOAuthService
*
OpenAIOAuthService
httpUpstream
ports
.
HTTPUpstream
httpUpstream
HTTPUpstream
}
// NewAccountTestService creates a new AccountTestService
func
NewAccountTestService
(
accountRepo
ports
.
AccountRepository
,
oauthService
*
OAuthService
,
openaiOAuthService
*
OpenAIOAuthService
,
httpUpstream
ports
.
HTTPUpstream
)
*
AccountTestService
{
func
NewAccountTestService
(
accountRepo
AccountRepository
,
oauthService
*
OAuthService
,
openaiOAuthService
*
OpenAIOAuthService
,
httpUpstream
HTTPUpstream
)
*
AccountTestService
{
return
&
AccountTestService
{
accountRepo
:
accountRepo
,
oauthService
:
oauthService
,
...
...
Prev
1
2
3
4
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment