refactor: shorten model and table prefixes

This commit is contained in:
2026-07-23 14:30:17 +08:00
parent 6e7be2f3a4
commit 0ca2898ac0
71 changed files with 707 additions and 638 deletions

View File

@@ -14,8 +14,8 @@ type ExpertFilter struct {
Query string
}
func (s *SessionService) ListExperts(filter ExpertFilter) ([]models.SenlinAgentAIExpertItem, error) {
query := models.DBService.Model(&models.SenlinAgentAIExpertItem{}).Where("enabled = ?", true)
func (s *SessionService) ListExperts(filter ExpertFilter) ([]models.SaAIExpertItem, error) {
query := models.DBService.Model(&models.SaAIExpertItem{}).Where("enabled = ?", true)
if category := strings.TrimSpace(filter.Category); category != "" {
query = query.Where("category = ?", category)
}
@@ -26,18 +26,18 @@ func (s *SessionService) ListExperts(filter ExpertFilter) ([]models.SenlinAgentA
like, like, like,
)
}
var experts []models.SenlinAgentAIExpertItem
var experts []models.SaAIExpertItem
if err := query.Order("category asc, id asc").Find(&experts).Error; err != nil {
return nil, err
}
if experts == nil {
experts = []models.SenlinAgentAIExpertItem{}
experts = []models.SaAIExpertItem{}
}
return experts, nil
}
func (s *SessionService) GetExpert(identity string) (*models.SenlinAgentAIExpertItem, error) {
var expert models.SenlinAgentAIExpertItem
func (s *SessionService) GetExpert(identity string) (*models.SaAIExpertItem, error) {
var expert models.SaAIExpertItem
if err := models.DBService.Where("identity = ? AND enabled = ?", strings.TrimSpace(identity), true).First(&expert).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, ErrExpertNotFound
@@ -47,11 +47,11 @@ func (s *SessionService) GetExpert(identity string) (*models.SenlinAgentAIExpert
return &expert, nil
}
func findExpertByIdentity(tx *gorm.DB, identity string) (*models.SenlinAgentAIExpertItem, error) {
func findExpertByIdentity(tx *gorm.DB, identity string) (*models.SaAIExpertItem, error) {
if strings.TrimSpace(identity) == "" {
return nil, nil
}
var expert models.SenlinAgentAIExpertItem
var expert models.SaAIExpertItem
if err := tx.Where("identity = ? AND enabled = ?", identity, true).First(&expert).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, ErrExpertNotFound

View File

@@ -45,7 +45,7 @@ func (g *Gateway) SaveUserKey(userID uint, provider string, apiKey string) error
if err != nil {
return err
}
key := models.SenlinAgentAIKey{UserID: userID, Provider: provider, EncryptedAPIKey: encrypted}
key := models.SaAIKey{UserID: userID, Provider: provider, EncryptedAPIKey: encrypted}
return models.DBService.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "user_id"}},
DoUpdates: clause.AssignmentColumns([]string{"provider", "encrypted_api_key", "updated_at"}),
@@ -53,7 +53,7 @@ func (g *Gateway) SaveUserKey(userID uint, provider string, apiKey string) error
}
func (g *Gateway) SelectKey(userID uint) (SelectedKey, error) {
var userKey models.SenlinAgentAIKey
var userKey models.SaAIKey
err := models.DBService.Where("user_id = ?", userID).First(&userKey).Error
if err == nil {
selected := SelectedKey{Provider: userKey.Provider, KeyType: "user"}
@@ -77,7 +77,7 @@ func (g *Gateway) RecordCall(database *gorm.DB, userID uint, provider string, us
if database == nil {
database = models.DBService
}
return database.Create(&models.SenlinAgentAICallLog{
return database.Create(&models.SaAICallLog{
UserID: userID,
Provider: provider,
UsedKeyType: usedKeyType,
@@ -98,17 +98,17 @@ func (g *Gateway) ReserveRateLimit(userID uint, action string, limit int, window
currentTime = g.now().UTC()
}
windowStart := currentTime.Truncate(window)
bucket := models.SenlinAgentAIRateBucket{
bucket := models.SaAIRateBucket{
UserID: userID, Action: action, WindowStart: windowStart, Count: 1,
}
result := models.DBService.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "user_id"}, {Name: "action"}, {Name: "window_start"}},
DoUpdates: clause.Assignments(map[string]any{
"count": gorm.Expr("senlin_agent_ai_rate_buckets.count + 1"),
"count": gorm.Expr("sa_ai_rate_buckets.count + 1"),
"updated_at": currentTime,
}),
Where: clause.Where{Exprs: []clause.Expression{
clause.Lt{Column: clause.Column{Table: "senlin_agent_ai_rate_buckets", Name: "count"}, Value: limit},
clause.Lt{Column: clause.Column{Table: "sa_ai_rate_buckets", Name: "count"}, Value: limit},
}},
}).Create(&bucket)
if result.Error != nil {

View File

@@ -14,7 +14,7 @@ import (
func TestSelectKeyPrefersUserKey(t *testing.T) {
database := newTestDB(t)
require.NoError(t, database.Create(&models.SenlinAgentAIKey{UserID: 3, Provider: "openai", EncryptedAPIKey: "user-key"}).Error)
require.NoError(t, database.Create(&models.SaAIKey{UserID: 3, Provider: "openai", EncryptedAPIKey: "user-key"}).Error)
gateway := NewGateway("system-key")
selected, err := gateway.SelectKey(3)
@@ -30,7 +30,7 @@ func TestSaveUserKeyEncryptsStoredKey(t *testing.T) {
require.NoError(t, gateway.SaveUserKey(3, "openai", "user-key"))
var stored models.SenlinAgentAIKey
var stored models.SaAIKey
require.NoError(t, database.Where("user_id = ?", 3).First(&stored).Error)
require.NotEqual(t, "user-key", stored.EncryptedAPIKey)
require.Contains(t, stored.EncryptedAPIKey, "v1:")
@@ -65,7 +65,7 @@ func TestRecordCallStoresAuditFields(t *testing.T) {
require.NoError(t, gateway.RecordCall(database, 3, "openai", "system", "inbox_analyze", "failed", "rate limited"))
var log models.SenlinAgentAICallLog
var log models.SaAICallLog
require.NoError(t, database.First(&log).Error)
require.Equal(t, uint(3), log.UserID)
require.Equal(t, "openai", log.Provider)
@@ -77,7 +77,7 @@ func TestRecordCallStoresAuditFields(t *testing.T) {
func TestReserveRateLimitRejectsCallsOverWindow(t *testing.T) {
database := newTestDB(t)
require.NoError(t, database.Create(&models.SenlinAgentUser{Email: "rate@example.com", DisplayName: "Rate", PasswordHash: "hash"}).Error)
require.NoError(t, database.Create(&models.SaUser{Email: "rate@example.com", DisplayName: "Rate", PasswordHash: "hash"}).Error)
gateway := NewGateway("system-key")
require.NoError(t, gateway.ReserveRateLimit(1, "inbox_analyze", 1, time.Hour))
@@ -88,9 +88,9 @@ func TestReserveRateLimitRejectsCallsOverWindow(t *testing.T) {
func TestCreateAISession(t *testing.T) {
database := newTestDB(t)
user := models.SenlinAgentUser{Email: "session@example.com", DisplayName: "Session User", PasswordHash: "hash"}
user := models.SaUser{Email: "session@example.com", DisplayName: "Session User", PasswordHash: "hash"}
require.NoError(t, database.Create(&user).Error)
project := models.SenlinAgentProject{OwnerID: user.ID, Name: "Session Project", Identifier: "SESSION"}
project := models.SaProject{OwnerID: user.ID, Name: "Session Project", Identifier: "SESSION"}
require.NoError(t, database.Create(&project).Error)
service := NewSessionService(NewGateway("system-key"))

View File

@@ -142,7 +142,7 @@ func aiRequestContext(c *gin.Context) (uint, string, bool) {
return userID, projectIdentity, true
}
func sessionDTO(session models.SenlinAgentAISession) SessionDTO {
func sessionDTO(session models.SaAISession) SessionDTO {
dto := SessionDTO{
ID: session.Identity,
ProjectID: session.ProjectIdentity,
@@ -158,12 +158,12 @@ func sessionDTO(session models.SenlinAgentAISession) SessionDTO {
return dto
}
func expertSummaryPointer(expert models.SenlinAgentAIExpertItem) *ExpertSummaryDTO {
func expertSummaryPointer(expert models.SaAIExpertItem) *ExpertSummaryDTO {
dto := expertSummaryDTO(expert)
return &dto
}
func expertSummaryDTO(expert models.SenlinAgentAIExpertItem) ExpertSummaryDTO {
func expertSummaryDTO(expert models.SaAIExpertItem) ExpertSummaryDTO {
return ExpertSummaryDTO{
ID: expert.Identity, Slug: expert.Slug, Category: expert.Category,
CategoryName: expert.CategoryName, Name: expert.Name, Description: expert.Description,

View File

@@ -46,7 +46,7 @@ func TestAISessionHandlersRequireOwnedProject(t *testing.T) {
}
var count int64
require.NoError(t, database.Model(&models.SenlinAgentAISession{}).Count(&count).Error)
require.NoError(t, database.Model(&models.SaAISession{}).Count(&count).Error)
require.Zero(t, count)
require.Empty(t, gateway.steps)
}
@@ -55,7 +55,7 @@ func TestAIExpertLibraryListsDetailsAndCreatesAssociatedSession(t *testing.T) {
database := newAIHandlerTestDB(t)
owner := createAIHandlerUser(t, database, "expert-owner@example.com")
project := createAIHandlerProject(t, database, owner.ID, "EXPERTS")
var expert models.SenlinAgentAIExpertItem
var expert models.SaAIExpertItem
require.NoError(t, database.Order("id asc").First(&expert).Error)
router := aiHandlerTestRouter(owner.ID, NewGatewayWithSecret("system-key", "test-encryption-secret"))
@@ -87,7 +87,7 @@ func TestAIExpertLibraryListsDetailsAndCreatesAssociatedSession(t *testing.T) {
responseExpert := createPayload["expert"].(map[string]any)
require.Equal(t, expert.Identity, responseExpert["id"])
require.Equal(t, expert.Name, responseExpert["name"])
var session models.SenlinAgentAISession
var session models.SaAISession
require.NoError(t, database.First(&session).Error)
require.NotNil(t, session.ExpertID)
require.Equal(t, expert.ID, *session.ExpertID)
@@ -131,7 +131,7 @@ func TestCreateAISessionReturnsRateLimitBeforeMissingKeyAndAuditsFailure(t *test
require.NoError(t, json.Unmarshal(recorder.Body.Bytes(), &payload))
require.Equal(t, "ai_rate_limited", payload.Error.Code)
require.Equal(t, "AI 请求过于频繁,请稍后重试", payload.Error.Message)
var latest models.SenlinAgentAICallLog
var latest models.SaAICallLog
require.NoError(t, database.Order("id desc").First(&latest).Error)
require.Equal(t, "none", latest.Provider)
require.Equal(t, "none", latest.UsedKeyType)
@@ -157,16 +157,16 @@ func TestCreateAISessionWithoutKeyReturnsAuditedErrorAndCreatesNoFormalObjects(t
require.Equal(t, "ai_key_missing", payload.Error.Code)
require.Equal(t, "尚未配置可用的 AI 密钥", payload.Error.Message)
for _, model := range []any{
&models.SenlinAgentAISession{},
&models.SenlinAgentTask{},
&models.SenlinAgentNote{},
&models.SenlinAgentSource{},
&models.SaAISession{},
&models.SaTask{},
&models.SaNote{},
&models.SaSource{},
} {
var count int64
require.NoError(t, database.Model(model).Count(&count).Error)
require.Zero(t, count)
}
var call models.SenlinAgentAICallLog
var call models.SaAICallLog
require.NoError(t, database.First(&call).Error)
require.Equal(t, owner.ID, call.UserID)
require.Equal(t, "none", call.Provider)
@@ -174,7 +174,7 @@ func TestCreateAISessionWithoutKeyReturnsAuditedErrorAndCreatesNoFormalObjects(t
require.Equal(t, "ai_session_create", call.Action)
require.Equal(t, "failed", call.Status)
require.Equal(t, "ai_key_missing", call.Error)
var bucket models.SenlinAgentAIRateBucket
var bucket models.SaAIRateBucket
require.NoError(t, database.Where("user_id = ? AND action = ?", owner.ID, aiSessionCreateAction).First(&bucket).Error)
require.Equal(t, 1, bucket.Count)
}
@@ -186,7 +186,7 @@ func TestCreateAISessionRollsBackSessionWhenReadyAuditWriteFails(t *testing.T) {
injectedError := errors.New("injected ready audit failure")
callbackName := "test:fail_ready_ai_audit"
require.NoError(t, database.Callback().Create().Before("gorm:create").Register(callbackName, func(tx *gorm.DB) {
call, ok := tx.Statement.Dest.(*models.SenlinAgentAICallLog)
call, ok := tx.Statement.Dest.(*models.SaAICallLog)
if ok && call.Status == defaultSessionStatus {
tx.AddError(injectedError)
}
@@ -201,9 +201,9 @@ func TestCreateAISessionRollsBackSessionWhenReadyAuditWriteFails(t *testing.T) {
require.Equal(t, http.StatusInternalServerError, recorder.Code)
var sessionCount int64
require.NoError(t, database.Model(&models.SenlinAgentAISession{}).Where("project_id = ?", project.ID).Count(&sessionCount).Error)
require.NoError(t, database.Model(&models.SaAISession{}).Where("project_id = ?", project.ID).Count(&sessionCount).Error)
require.Zero(t, sessionCount)
var calls []models.SenlinAgentAICallLog
var calls []models.SaAICallLog
require.NoError(t, database.Where("user_id = ? AND action = ?", owner.ID, aiSessionCreateAction).Find(&calls).Error)
require.Len(t, calls, 1)
require.Equal(t, "openai", calls[0].Provider)
@@ -216,7 +216,7 @@ func TestCreateAISessionAuditsKnownProviderMetadataWhenUserKeyDecryptFails(t *te
database := newAIHandlerTestDB(t)
owner := createAIHandlerUser(t, database, "decrypt-failure@example.com")
project := createAIHandlerProject(t, database, owner.ID, "DECRYPT_FAILURE")
require.NoError(t, database.Create(&models.SenlinAgentAIKey{
require.NoError(t, database.Create(&models.SaAIKey{
UserID: owner.ID, Provider: "deepseek", EncryptedAPIKey: "v1:not-valid-base64",
}).Error)
router := aiHandlerTestRouter(owner.ID, NewGatewayWithSecret("system-key", "test-encryption-secret"))
@@ -227,7 +227,7 @@ func TestCreateAISessionAuditsKnownProviderMetadataWhenUserKeyDecryptFails(t *te
}))
require.Equal(t, http.StatusInternalServerError, recorder.Code)
var call models.SenlinAgentAICallLog
var call models.SaAICallLog
require.NoError(t, database.Where("user_id = ? AND action = ?", owner.ID, aiSessionCreateAction).First(&call).Error)
require.Equal(t, "deepseek", call.Provider)
require.Equal(t, "user", call.UsedKeyType)
@@ -235,7 +235,7 @@ func TestCreateAISessionAuditsKnownProviderMetadataWhenUserKeyDecryptFails(t *te
require.Equal(t, "provider_selection_failed", call.Error)
require.NotContains(t, call.Error, "not-valid-base64")
var sessionCount int64
require.NoError(t, database.Model(&models.SenlinAgentAISession{}).Count(&sessionCount).Error)
require.NoError(t, database.Model(&models.SaAISession{}).Count(&sessionCount).Error)
require.Zero(t, sessionCount)
}
@@ -266,14 +266,14 @@ func TestCreateAISessionReturnsIdentityDTOAndCompleteAuditWithoutAutomaticObject
require.NotContains(t, payload, forbidden)
}
var call models.SenlinAgentAICallLog
var call models.SaAICallLog
require.NoError(t, database.First(&call).Error)
require.Equal(t, "openai", call.Provider)
require.Equal(t, "system", call.UsedKeyType)
require.Equal(t, "ai_session_create", call.Action)
require.Equal(t, "ready", call.Status)
require.Empty(t, call.Error)
for _, model := range []any{&models.SenlinAgentTask{}, &models.SenlinAgentNote{}, &models.SenlinAgentSource{}} {
for _, model := range []any{&models.SaTask{}, &models.SaNote{}, &models.SaSource{}} {
var count int64
require.NoError(t, database.Model(model).Count(&count).Error)
require.Zero(t, count)
@@ -285,10 +285,10 @@ func TestListAISessionsReturnsOnlyOwnedProjectIdentityDTOs(t *testing.T) {
owner := createAIHandlerUser(t, database, "owner@example.com")
project := createAIHandlerProject(t, database, owner.ID, "LIST")
otherProject := createAIHandlerProject(t, database, owner.ID, "OTHER")
require.NoError(t, database.Create(&models.SenlinAgentAISession{
require.NoError(t, database.Create(&models.SaAISession{
ProjectID: project.ID, CreatedBy: owner.ID, Title: "目标会话", Context: "项目上下文",
}).Error)
require.NoError(t, database.Create(&models.SenlinAgentAISession{
require.NoError(t, database.Create(&models.SaAISession{
ProjectID: otherProject.ID, CreatedBy: owner.ID, Title: "其他会话", Context: "不得混入",
}).Error)
router := aiHandlerTestRouter(owner.ID, &recordingSessionGateway{})
@@ -339,16 +339,16 @@ func newAIHandlerTestDB(t *testing.T) *gorm.DB {
return database
}
func createAIHandlerUser(t *testing.T, database *gorm.DB, email string) models.SenlinAgentUser {
func createAIHandlerUser(t *testing.T, database *gorm.DB, email string) models.SaUser {
t.Helper()
user := models.SenlinAgentUser{Email: email, DisplayName: email, PasswordHash: "hash"}
user := models.SaUser{Email: email, DisplayName: email, PasswordHash: "hash"}
require.NoError(t, database.Create(&user).Error)
return user
}
func createAIHandlerProject(t *testing.T, database *gorm.DB, ownerID uint, identifier string) models.SenlinAgentProject {
func createAIHandlerProject(t *testing.T, database *gorm.DB, ownerID uint, identifier string) models.SaProject {
t.Helper()
project := models.SenlinAgentProject{OwnerID: ownerID, Name: identifier, Identifier: identifier}
project := models.SaProject{OwnerID: ownerID, Name: identifier, Identifier: identifier}
require.NoError(t, database.Create(&project).Error)
return project
}

View File

@@ -28,7 +28,7 @@ func TestPostgresReserveRateLimitIsAtomicAcrossConcurrentConnections(t *testing.
user := createAIRateTestUser(t, database, "postgres-rate-"+suffix+"@example.com")
action := "postgres_concurrent_" + suffix
t.Cleanup(func() {
database.Where("user_id = ?", user.ID).Delete(&models.SenlinAgentAIRateBucket{})
database.Where("user_id = ?", user.ID).Delete(&models.SaAIRateBucket{})
database.Delete(&user)
})
gateway := NewGatewayWithSecret("system-key", "test-encryption-secret")
@@ -66,7 +66,7 @@ func TestPostgresReserveRateLimitIsAtomicAcrossConcurrentConnections(t *testing.
}
require.Equal(t, limit, allowed)
require.Equal(t, attempts-limit, limited)
var bucket models.SenlinAgentAIRateBucket
var bucket models.SaAIRateBucket
require.NoError(t, database.Where("user_id = ? AND action = ?", user.ID, action).First(&bucket).Error)
require.Equal(t, limit, bucket.Count)
}

View File

@@ -53,7 +53,7 @@ func TestReserveRateLimitIsAtomicUnderConcurrentSQLiteRequests(t *testing.T) {
}
require.Equal(t, limit, allowed)
require.Equal(t, attempts-limit, limited)
var buckets []models.SenlinAgentAIRateBucket
var buckets []models.SaAIRateBucket
require.NoError(t, database.Where("user_id = ? AND action = ?", user.ID, "concurrent_session_create").Find(&buckets).Error)
require.Len(t, buckets, 1)
require.Equal(t, limit, buckets[0].Count)
@@ -72,7 +72,7 @@ func TestReserveRateLimitUsesFixedWindowsAndCountsFailedAttempts(t *testing.T) {
current = current.Add(time.Hour)
require.NoError(t, gateway.ReserveRateLimit(user.ID, "windowed_session_create", 2, time.Hour))
var buckets []models.SenlinAgentAIRateBucket
var buckets []models.SaAIRateBucket
require.NoError(t, database.Where("user_id = ? AND action = ?", user.ID, "windowed_session_create").Order("window_start asc").Find(&buckets).Error)
require.Len(t, buckets, 2)
require.Equal(t, []int{2, 1}, []int{buckets[0].Count, buckets[1].Count})
@@ -93,9 +93,9 @@ func newConcurrentAIRateTestDB(t *testing.T) *gorm.DB {
return database
}
func createAIRateTestUser(t *testing.T, database *gorm.DB, email string) models.SenlinAgentUser {
func createAIRateTestUser(t *testing.T, database *gorm.DB, email string) models.SaUser {
t.Helper()
user := models.SenlinAgentUser{Email: email, DisplayName: email, PasswordHash: "hash"}
user := models.SaUser{Email: email, DisplayName: email, PasswordHash: "hash"}
require.NoError(t, database.Create(&user).Error)
return user
}

View File

@@ -38,28 +38,28 @@ func NewSessionService(gateway sessionGateway) *SessionService {
}
// List 在项目 owner 校验后返回该项目的会话,内部自增 ID 不离开服务边界。
func (s *SessionService) List(userID uint, projectIdentity string) ([]models.SenlinAgentAISession, error) {
func (s *SessionService) List(userID uint, projectIdentity string) ([]models.SaAISession, error) {
project, err := projects.FindOwnedProject(userID, projectIdentity)
if err != nil {
return nil, err
}
var sessions []models.SenlinAgentAISession
var sessions []models.SaAISession
if err := models.DBService.Preload("Expert").Where("project_id = ?", project.ID).Order("updated_at desc, id desc").Find(&sessions).Error; err != nil {
return nil, err
}
if sessions == nil {
sessions = []models.SenlinAgentAISession{}
sessions = []models.SaAISession{}
}
return sessions, nil
}
// Create 先校验项目,再限流,最后才选择 provider/key会话创建不会生成任何正式业务对象。
func (s *SessionService) Create(userID uint, projectIdentity, title, context string) (*models.SenlinAgentAISession, error) {
func (s *SessionService) Create(userID uint, projectIdentity, title, context string) (*models.SaAISession, error) {
return s.CreateWithExpert(userID, projectIdentity, title, context, "")
}
// CreateWithExpert 创建带本地专家角色的项目会话。
func (s *SessionService) CreateWithExpert(userID uint, projectIdentity, title, context, expertIdentity string) (*models.SenlinAgentAISession, error) {
func (s *SessionService) CreateWithExpert(userID uint, projectIdentity, title, context, expertIdentity string) (*models.SaAISession, error) {
title = strings.TrimSpace(title)
context = strings.TrimSpace(context)
if title == "" {
@@ -103,7 +103,7 @@ func (s *SessionService) CreateWithExpert(userID uint, projectIdentity, title, c
return nil, err
}
session := models.SenlinAgentAISession{
session := models.SaAISession{
ProjectID: project.ID,
CreatedBy: userID,
Title: title,
@@ -149,7 +149,7 @@ func selectedAuditMetadata(selected SelectedKey) (string, string) {
return provider, keyType
}
func aiSessionStatus(session models.SenlinAgentAISession) string {
func aiSessionStatus(session models.SaAISession) string {
if status := strings.TrimSpace(session.Status); status != "" {
return status
}