refactor(mcp): apply Part 1 review I1+I3+I4+I5

- Rename MCPToken -> Token (mcp.Token reads cleaner; zero external callers yet)
- IssueSystemToken: guard against nil ACPSessionID / UserID (early input validation)
- TokenService: inject now func for clock injection (parity with RateLimiter)
- Repository CHECK constraint tests: assert wrapped CodeInternal

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-08 10:28:49 +08:00
parent 980c6fcf0e
commit a00f5a9efd
7 changed files with 106 additions and 65 deletions
+16 -16
View File
@@ -18,10 +18,10 @@ import (
// Repository 是 mcp 模块对 PG 的全部依赖。Service 单测通过手写 fakeRepo 满足。
type Repository interface {
// CRUD
Create(ctx context.Context, t *MCPToken) (*MCPToken, error)
GetByHash(ctx context.Context, hash []byte) (*MCPToken, error)
GetByID(ctx context.Context, id uuid.UUID) (*MCPToken, error)
List(ctx context.Context, callerUserID *uuid.UUID, activeOnly bool) ([]*MCPToken, error)
Create(ctx context.Context, t *Token) (*Token, error)
GetByHash(ctx context.Context, hash []byte) (*Token, error)
GetByID(ctx context.Context, id uuid.UUID) (*Token, error)
List(ctx context.Context, callerUserID *uuid.UUID, activeOnly bool) ([]*Token, error)
// 状态变更
UpdateLastUsed(ctx context.Context, id uuid.UUID) error
@@ -99,13 +99,13 @@ func scopeFromJSON(raw []byte) (Scope, error) {
return s, nil
}
// rowToMCPToken 把 sqlc 生成的 row 翻译为业务结构。
func rowToMCPToken(r mcpsqlc.McpToken) (*MCPToken, error) {
// rowToToken 把 sqlc 生成的 row 翻译为业务结构。
func rowToToken(r mcpsqlc.McpToken) (*Token, error) {
scope, err := scopeFromJSON(r.Scope)
if err != nil {
return nil, errs.Wrap(err, errs.CodeInternal, "decode mcp_token.scope")
}
return &MCPToken{
return &Token{
ID: fromPgUUID(r.ID),
TokenHash: r.TokenHash,
UserID: fromPgUUID(r.UserID),
@@ -131,7 +131,7 @@ func pgxNoRowsToErrNotFound(err error, code errs.Code, msg string) error {
// ===== Create =====
func (r *pgRepo) Create(ctx context.Context, t *MCPToken) (*MCPToken, error) {
func (r *pgRepo) Create(ctx context.Context, t *Token) (*Token, error) {
scopeJSON, err := scopeToJSON(t.Scope)
if err != nil {
return nil, errs.Wrap(err, errs.CodeInternal, "encode mcp_token.scope")
@@ -150,30 +150,30 @@ func (r *pgRepo) Create(ctx context.Context, t *MCPToken) (*MCPToken, error) {
if err != nil {
return nil, errs.Wrap(err, errs.CodeInternal, "insert mcp_token")
}
return rowToMCPToken(row)
return rowToToken(row)
}
// ===== Get =====
func (r *pgRepo) GetByHash(ctx context.Context, hash []byte) (*MCPToken, error) {
func (r *pgRepo) GetByHash(ctx context.Context, hash []byte) (*Token, error) {
row, err := r.q.GetMcpTokenByHash(ctx, hash)
if err != nil {
return nil, pgxNoRowsToErrNotFound(err, errs.CodeMcpTokenInvalid, "mcp token not found")
}
return rowToMCPToken(row)
return rowToToken(row)
}
func (r *pgRepo) GetByID(ctx context.Context, id uuid.UUID) (*MCPToken, error) {
func (r *pgRepo) GetByID(ctx context.Context, id uuid.UUID) (*Token, error) {
row, err := r.q.GetMcpTokenByID(ctx, toPgUUID(id))
if err != nil {
return nil, pgxNoRowsToErrNotFound(err, errs.CodeMcpTokenNotFound, "mcp token not found")
}
return rowToMCPToken(row)
return rowToToken(row)
}
// ===== Stub methods (implemented in subsequent tasks) =====
func (r *pgRepo) List(ctx context.Context, callerUserID *uuid.UUID, activeOnly bool) ([]*MCPToken, error) {
func (r *pgRepo) List(ctx context.Context, callerUserID *uuid.UUID, activeOnly bool) ([]*Token, error) {
rows, err := r.q.ListMcpTokens(ctx, mcpsqlc.ListMcpTokensParams{
CallerUserID: toPgUUIDPtr(callerUserID),
ActiveOnly: activeOnly,
@@ -181,9 +181,9 @@ func (r *pgRepo) List(ctx context.Context, callerUserID *uuid.UUID, activeOnly b
if err != nil {
return nil, errs.Wrap(err, errs.CodeInternal, "list mcp_tokens")
}
out := make([]*MCPToken, 0, len(rows))
out := make([]*Token, 0, len(rows))
for _, row := range rows {
t, err := rowToMCPToken(row)
t, err := rowToToken(row)
if err != nil {
return nil, err
}