feat: 实现前端卡密管理界面
- 卡密列表展示与分页功能 - 单个/批量创建卡密 - 卡密删除与批量删除 - 卡密导出功能 (file-saver) - 启用/禁用状态切换 - 状态判断 (有效/已使用/已失效) - Toast 通知系统 (vue-sonner) - 登录页面错误提示优化 - 后端登录错误消息中文化
This commit is contained in:
120
backend/internal/repository/admin_repo.go
Normal file
120
backend/internal/repository/admin_repo.go
Normal file
@@ -0,0 +1,120 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"gpt-manager-go/internal/models"
|
||||
)
|
||||
|
||||
// AdminRepository 管理员仓储
|
||||
type AdminRepository struct {
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
// NewAdminRepository 创建管理员仓储
|
||||
func NewAdminRepository(db *sql.DB) *AdminRepository {
|
||||
return &AdminRepository{db: db}
|
||||
}
|
||||
|
||||
// FindByUsername 根据用户名查找管理员
|
||||
func (r *AdminRepository) FindByUsername(username string) (*models.Admin, error) {
|
||||
admin := &models.Admin{}
|
||||
err := r.db.QueryRow(`
|
||||
SELECT id, username, email, password_hash, is_super_admin, is_active, created_at, last_login
|
||||
FROM admins WHERE username = $1
|
||||
`, username).Scan(
|
||||
&admin.ID,
|
||||
&admin.Username,
|
||||
&admin.Email,
|
||||
&admin.PasswordHash,
|
||||
&admin.IsSuperAdmin,
|
||||
&admin.IsActive,
|
||||
&admin.CreatedAt,
|
||||
&admin.LastLogin,
|
||||
)
|
||||
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return admin, nil
|
||||
}
|
||||
|
||||
// FindByEmail 根据邮箱查找管理员
|
||||
func (r *AdminRepository) FindByEmail(email string) (*models.Admin, error) {
|
||||
admin := &models.Admin{}
|
||||
err := r.db.QueryRow(`
|
||||
SELECT id, username, email, password_hash, is_super_admin, is_active, created_at, last_login
|
||||
FROM admins WHERE email = $1
|
||||
`, email).Scan(
|
||||
&admin.ID,
|
||||
&admin.Username,
|
||||
&admin.Email,
|
||||
&admin.PasswordHash,
|
||||
&admin.IsSuperAdmin,
|
||||
&admin.IsActive,
|
||||
&admin.CreatedAt,
|
||||
&admin.LastLogin,
|
||||
)
|
||||
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return admin, nil
|
||||
}
|
||||
|
||||
// FindByID 根据 ID 查找管理员
|
||||
func (r *AdminRepository) FindByID(id int) (*models.Admin, error) {
|
||||
admin := &models.Admin{}
|
||||
err := r.db.QueryRow(`
|
||||
SELECT id, username, email, password_hash, is_super_admin, is_active, created_at, last_login
|
||||
FROM admins WHERE id = $1
|
||||
`, id).Scan(
|
||||
&admin.ID,
|
||||
&admin.Username,
|
||||
&admin.Email,
|
||||
&admin.PasswordHash,
|
||||
&admin.IsSuperAdmin,
|
||||
&admin.IsActive,
|
||||
&admin.CreatedAt,
|
||||
&admin.LastLogin,
|
||||
)
|
||||
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return admin, nil
|
||||
}
|
||||
|
||||
// Create 创建管理员
|
||||
func (r *AdminRepository) Create(admin *models.Admin) error {
|
||||
return r.db.QueryRow(`
|
||||
INSERT INTO admins (username, email, password_hash, is_super_admin, is_active, created_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6)
|
||||
RETURNING id
|
||||
`,
|
||||
admin.Username,
|
||||
admin.Email,
|
||||
admin.PasswordHash,
|
||||
admin.IsSuperAdmin,
|
||||
admin.IsActive,
|
||||
time.Now(),
|
||||
).Scan(&admin.ID)
|
||||
}
|
||||
|
||||
// UpdateLastLogin 更新最后登录时间
|
||||
func (r *AdminRepository) UpdateLastLogin(id int) error {
|
||||
_, err := r.db.Exec(`
|
||||
UPDATE admins SET last_login = $1 WHERE id = $2
|
||||
`, time.Now(), id)
|
||||
return err
|
||||
}
|
||||
149
backend/internal/repository/card_key_repo.go
Normal file
149
backend/internal/repository/card_key_repo.go
Normal file
@@ -0,0 +1,149 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"gpt-manager-go/internal/models"
|
||||
|
||||
"github.com/lib/pq"
|
||||
)
|
||||
|
||||
// CardKeyRepository 卡密仓储
|
||||
type CardKeyRepository struct {
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
// NewCardKeyRepository 创建仓储
|
||||
func NewCardKeyRepository(db *sql.DB) *CardKeyRepository {
|
||||
return &CardKeyRepository{db: db}
|
||||
}
|
||||
|
||||
// FindByKey 根据卡密查找
|
||||
func (r *CardKeyRepository) FindByKey(key string) (*models.CardKey, error) {
|
||||
cardKey := &models.CardKey{}
|
||||
err := r.db.QueryRow(`
|
||||
SELECT id, key, max_uses, used_count, validity_type, expires_at, is_active, created_by_id, created_at
|
||||
FROM card_keys WHERE key = $1
|
||||
`, key).Scan(
|
||||
&cardKey.ID, &cardKey.Key, &cardKey.MaxUses, &cardKey.UsedCount,
|
||||
&cardKey.ValidityType, &cardKey.ExpiresAt, &cardKey.IsActive,
|
||||
&cardKey.CreatedByID, &cardKey.CreatedAt,
|
||||
)
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
}
|
||||
return cardKey, err
|
||||
}
|
||||
|
||||
// IncrementUsedCount 增加使用次数,如果达到最大次数则设为不活跃
|
||||
func (r *CardKeyRepository) IncrementUsedCount(id int) error {
|
||||
_, err := r.db.Exec(`
|
||||
UPDATE card_keys
|
||||
SET used_count = used_count + 1,
|
||||
is_active = CASE WHEN used_count + 1 >= max_uses THEN false ELSE is_active END
|
||||
WHERE id = $1
|
||||
`, id)
|
||||
return err
|
||||
}
|
||||
|
||||
// Create 创建卡密
|
||||
func (r *CardKeyRepository) Create(cardKey *models.CardKey) error {
|
||||
return r.db.QueryRow(`
|
||||
INSERT INTO card_keys (key, max_uses, used_count, validity_type, expires_at, is_active, created_by_id, created_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
||||
RETURNING id
|
||||
`,
|
||||
cardKey.Key,
|
||||
cardKey.MaxUses,
|
||||
cardKey.UsedCount,
|
||||
cardKey.ValidityType,
|
||||
cardKey.ExpiresAt,
|
||||
cardKey.IsActive,
|
||||
cardKey.CreatedByID,
|
||||
cardKey.CreatedAt,
|
||||
).Scan(&cardKey.ID)
|
||||
}
|
||||
|
||||
// FindAll 获取所有卡密
|
||||
func (r *CardKeyRepository) FindAll() ([]*models.CardKey, error) {
|
||||
rows, err := r.db.Query(`
|
||||
SELECT id, key, max_uses, used_count, validity_type, expires_at, is_active, created_by_id, created_at
|
||||
FROM card_keys ORDER BY created_at DESC
|
||||
`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var cardKeys []*models.CardKey
|
||||
for rows.Next() {
|
||||
ck := &models.CardKey{}
|
||||
if err := rows.Scan(
|
||||
&ck.ID, &ck.Key, &ck.MaxUses, &ck.UsedCount,
|
||||
&ck.ValidityType, &ck.ExpiresAt, &ck.IsActive,
|
||||
&ck.CreatedByID, &ck.CreatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cardKeys = append(cardKeys, ck)
|
||||
}
|
||||
return cardKeys, nil
|
||||
}
|
||||
|
||||
// FindAllPaginated 分页获取卡密
|
||||
func (r *CardKeyRepository) FindAllPaginated(page, pageSize int) ([]*models.CardKey, error) {
|
||||
offset := (page - 1) * pageSize
|
||||
rows, err := r.db.Query(`
|
||||
SELECT id, key, max_uses, used_count, validity_type, expires_at, is_active, created_by_id, created_at
|
||||
FROM card_keys ORDER BY created_at DESC
|
||||
LIMIT $1 OFFSET $2
|
||||
`, pageSize, offset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var cardKeys []*models.CardKey
|
||||
for rows.Next() {
|
||||
ck := &models.CardKey{}
|
||||
if err := rows.Scan(
|
||||
&ck.ID, &ck.Key, &ck.MaxUses, &ck.UsedCount,
|
||||
&ck.ValidityType, &ck.ExpiresAt, &ck.IsActive,
|
||||
&ck.CreatedByID, &ck.CreatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cardKeys = append(cardKeys, ck)
|
||||
}
|
||||
return cardKeys, nil
|
||||
}
|
||||
|
||||
// Count 获取卡密总数
|
||||
func (r *CardKeyRepository) Count() (int, error) {
|
||||
var count int
|
||||
err := r.db.QueryRow(`SELECT COUNT(*) FROM card_keys`).Scan(&count)
|
||||
return count, err
|
||||
}
|
||||
|
||||
// Delete 删除单个卡密
|
||||
func (r *CardKeyRepository) Delete(id int) error {
|
||||
_, err := r.db.Exec(`DELETE FROM card_keys WHERE id = $1`, id)
|
||||
return err
|
||||
}
|
||||
|
||||
// BatchDelete 批量删除卡密
|
||||
func (r *CardKeyRepository) BatchDelete(ids []int) error {
|
||||
if len(ids) == 0 {
|
||||
return nil
|
||||
}
|
||||
// 构建 IN 子句
|
||||
query := `DELETE FROM card_keys WHERE id = ANY($1)`
|
||||
_, err := r.db.Exec(query, pq.Array(ids))
|
||||
return err
|
||||
}
|
||||
|
||||
// ToggleActive 切换卡密激活状态
|
||||
func (r *CardKeyRepository) ToggleActive(id int, isActive bool) error {
|
||||
_, err := r.db.Exec(`UPDATE card_keys SET is_active = $1 WHERE id = $2`, isActive, id)
|
||||
return err
|
||||
}
|
||||
245
backend/internal/repository/chatgpt_account_repo.go
Normal file
245
backend/internal/repository/chatgpt_account_repo.go
Normal file
@@ -0,0 +1,245 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"gpt-manager-go/internal/models"
|
||||
)
|
||||
|
||||
// ChatGPTAccountRepository ChatGPT 账号仓储
|
||||
type ChatGPTAccountRepository struct {
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
// NewChatGPTAccountRepository 创建仓储
|
||||
func NewChatGPTAccountRepository(db *sql.DB) *ChatGPTAccountRepository {
|
||||
return &ChatGPTAccountRepository{db: db}
|
||||
}
|
||||
|
||||
// Create 创建账号
|
||||
func (r *ChatGPTAccountRepository) Create(account *models.ChatGPTAccount) error {
|
||||
return r.db.QueryRow(`
|
||||
INSERT INTO chatgpt_accounts (name, auth_token, team_account_id, seats_in_use, seats_entitled,
|
||||
active_start, active_until, is_active, consecutive_failures, created_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
||||
RETURNING id
|
||||
`,
|
||||
account.Name,
|
||||
account.AuthToken,
|
||||
account.TeamAccountID,
|
||||
account.SeatsInUse,
|
||||
account.SeatsEntitled,
|
||||
account.ActiveStart,
|
||||
account.ActiveUntil,
|
||||
account.IsActive,
|
||||
account.ConsecutiveFailures,
|
||||
time.Now(),
|
||||
).Scan(&account.ID)
|
||||
}
|
||||
|
||||
// Update 更新账号
|
||||
func (r *ChatGPTAccountRepository) Update(account *models.ChatGPTAccount) error {
|
||||
_, err := r.db.Exec(`
|
||||
UPDATE chatgpt_accounts SET
|
||||
name = $1, auth_token = $2, seats_in_use = $3, seats_entitled = $4,
|
||||
active_start = $5, active_until = $6, is_active = $7,
|
||||
consecutive_failures = $8, last_check = $9, updated_at = $10
|
||||
WHERE id = $11
|
||||
`,
|
||||
account.Name,
|
||||
account.AuthToken,
|
||||
account.SeatsInUse,
|
||||
account.SeatsEntitled,
|
||||
account.ActiveStart,
|
||||
account.ActiveUntil,
|
||||
account.IsActive,
|
||||
account.ConsecutiveFailures,
|
||||
account.LastCheck,
|
||||
time.Now(),
|
||||
account.ID,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
// FindByID 根据 ID 查找
|
||||
func (r *ChatGPTAccountRepository) FindByID(id int) (*models.ChatGPTAccount, error) {
|
||||
account := &models.ChatGPTAccount{}
|
||||
err := r.db.QueryRow(`
|
||||
SELECT id, name, auth_token, team_account_id, seats_in_use, seats_entitled,
|
||||
active_start, active_until, is_active, consecutive_failures,
|
||||
last_check, last_used, created_at, updated_at
|
||||
FROM chatgpt_accounts WHERE id = $1
|
||||
`, id).Scan(
|
||||
&account.ID, &account.Name, &account.AuthToken, &account.TeamAccountID,
|
||||
&account.SeatsInUse, &account.SeatsEntitled, &account.ActiveStart, &account.ActiveUntil,
|
||||
&account.IsActive, &account.ConsecutiveFailures, &account.LastCheck,
|
||||
&account.LastUsed, &account.CreatedAt, &account.UpdatedAt,
|
||||
)
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
}
|
||||
return account, err
|
||||
}
|
||||
|
||||
// FindByTeamAccountID 根据 Team Account ID 查找
|
||||
func (r *ChatGPTAccountRepository) FindByTeamAccountID(teamAccountID string) (*models.ChatGPTAccount, error) {
|
||||
account := &models.ChatGPTAccount{}
|
||||
err := r.db.QueryRow(`
|
||||
SELECT id, name, auth_token, team_account_id, seats_in_use, seats_entitled,
|
||||
active_start, active_until, is_active, consecutive_failures,
|
||||
last_check, last_used, created_at, updated_at
|
||||
FROM chatgpt_accounts WHERE team_account_id = $1
|
||||
`, teamAccountID).Scan(
|
||||
&account.ID, &account.Name, &account.AuthToken, &account.TeamAccountID,
|
||||
&account.SeatsInUse, &account.SeatsEntitled, &account.ActiveStart, &account.ActiveUntil,
|
||||
&account.IsActive, &account.ConsecutiveFailures, &account.LastCheck,
|
||||
&account.LastUsed, &account.CreatedAt, &account.UpdatedAt,
|
||||
)
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
}
|
||||
return account, err
|
||||
}
|
||||
|
||||
// FindAll 获取所有账号
|
||||
func (r *ChatGPTAccountRepository) FindAll() ([]*models.ChatGPTAccount, error) {
|
||||
rows, err := r.db.Query(`
|
||||
SELECT id, name, auth_token, team_account_id, seats_in_use, seats_entitled,
|
||||
active_start, active_until, is_active, consecutive_failures,
|
||||
last_check, last_used, created_at, updated_at
|
||||
FROM chatgpt_accounts ORDER BY created_at DESC
|
||||
`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var accounts []*models.ChatGPTAccount
|
||||
for rows.Next() {
|
||||
account := &models.ChatGPTAccount{}
|
||||
if err := rows.Scan(
|
||||
&account.ID, &account.Name, &account.AuthToken, &account.TeamAccountID,
|
||||
&account.SeatsInUse, &account.SeatsEntitled, &account.ActiveStart, &account.ActiveUntil,
|
||||
&account.IsActive, &account.ConsecutiveFailures, &account.LastCheck,
|
||||
&account.LastUsed, &account.CreatedAt, &account.UpdatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
accounts = append(accounts, account)
|
||||
}
|
||||
return accounts, nil
|
||||
}
|
||||
|
||||
// FindActive 获取所有激活的账号
|
||||
func (r *ChatGPTAccountRepository) FindActive() ([]*models.ChatGPTAccount, error) {
|
||||
rows, err := r.db.Query(`
|
||||
SELECT id, name, auth_token, team_account_id, seats_in_use, seats_entitled,
|
||||
active_start, active_until, is_active, consecutive_failures,
|
||||
last_check, last_used, created_at, updated_at
|
||||
FROM chatgpt_accounts WHERE is_active = true ORDER BY created_at DESC
|
||||
`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var accounts []*models.ChatGPTAccount
|
||||
for rows.Next() {
|
||||
account := &models.ChatGPTAccount{}
|
||||
if err := rows.Scan(
|
||||
&account.ID, &account.Name, &account.AuthToken, &account.TeamAccountID,
|
||||
&account.SeatsInUse, &account.SeatsEntitled, &account.ActiveStart, &account.ActiveUntil,
|
||||
&account.IsActive, &account.ConsecutiveFailures, &account.LastCheck,
|
||||
&account.LastUsed, &account.CreatedAt, &account.UpdatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
accounts = append(accounts, account)
|
||||
}
|
||||
return accounts, nil
|
||||
}
|
||||
|
||||
// Delete 删除账号
|
||||
func (r *ChatGPTAccountRepository) Delete(id int) error {
|
||||
_, err := r.db.Exec(`DELETE FROM chatgpt_accounts WHERE id = $1`, id)
|
||||
return err
|
||||
}
|
||||
|
||||
// FindActiveWithAvailableSeats 查找激活且有可用席位的账号
|
||||
func (r *ChatGPTAccountRepository) FindActiveWithAvailableSeats() ([]*models.ChatGPTAccount, error) {
|
||||
rows, err := r.db.Query(`
|
||||
SELECT id, name, auth_token, team_account_id, seats_in_use, seats_entitled,
|
||||
active_start, active_until, is_active, consecutive_failures,
|
||||
last_check, last_used, created_at, updated_at
|
||||
FROM chatgpt_accounts
|
||||
WHERE is_active = true AND seats_entitled > seats_in_use
|
||||
ORDER BY (seats_entitled - seats_in_use) DESC, last_used ASC NULLS FIRST
|
||||
`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var accounts []*models.ChatGPTAccount
|
||||
for rows.Next() {
|
||||
account := &models.ChatGPTAccount{}
|
||||
if err := rows.Scan(
|
||||
&account.ID, &account.Name, &account.AuthToken, &account.TeamAccountID,
|
||||
&account.SeatsInUse, &account.SeatsEntitled, &account.ActiveStart, &account.ActiveUntil,
|
||||
&account.IsActive, &account.ConsecutiveFailures, &account.LastCheck,
|
||||
&account.LastUsed, &account.CreatedAt, &account.UpdatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
accounts = append(accounts, account)
|
||||
}
|
||||
return accounts, nil
|
||||
}
|
||||
|
||||
// UpdateLastUsed 更新账号最后使用时间和席位
|
||||
func (r *ChatGPTAccountRepository) UpdateLastUsed(id int) error {
|
||||
_, err := r.db.Exec(`
|
||||
UPDATE chatgpt_accounts SET last_used = $1, seats_in_use = seats_in_use + 1, updated_at = $1
|
||||
WHERE id = $2
|
||||
`, time.Now(), id)
|
||||
return err
|
||||
}
|
||||
|
||||
// FindAllPaginated 分页获取账号
|
||||
func (r *ChatGPTAccountRepository) FindAllPaginated(page, pageSize int) ([]*models.ChatGPTAccount, error) {
|
||||
offset := (page - 1) * pageSize
|
||||
rows, err := r.db.Query(`
|
||||
SELECT id, name, auth_token, team_account_id, seats_in_use, seats_entitled,
|
||||
active_start, active_until, is_active, consecutive_failures,
|
||||
last_check, last_used, created_at, updated_at
|
||||
FROM chatgpt_accounts ORDER BY created_at DESC
|
||||
LIMIT $1 OFFSET $2
|
||||
`, pageSize, offset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var accounts []*models.ChatGPTAccount
|
||||
for rows.Next() {
|
||||
account := &models.ChatGPTAccount{}
|
||||
if err := rows.Scan(
|
||||
&account.ID, &account.Name, &account.AuthToken, &account.TeamAccountID,
|
||||
&account.SeatsInUse, &account.SeatsEntitled, &account.ActiveStart, &account.ActiveUntil,
|
||||
&account.IsActive, &account.ConsecutiveFailures, &account.LastCheck,
|
||||
&account.LastUsed, &account.CreatedAt, &account.UpdatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
accounts = append(accounts, account)
|
||||
}
|
||||
return accounts, nil
|
||||
}
|
||||
|
||||
// Count 获取账号总数
|
||||
func (r *ChatGPTAccountRepository) Count() (int, error) {
|
||||
var count int
|
||||
err := r.db.QueryRow(`SELECT COUNT(*) FROM chatgpt_accounts`).Scan(&count)
|
||||
return count, err
|
||||
}
|
||||
109
backend/internal/repository/invitation_repo.go
Normal file
109
backend/internal/repository/invitation_repo.go
Normal file
@@ -0,0 +1,109 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"gpt-manager-go/internal/models"
|
||||
)
|
||||
|
||||
// InvitationRepository 邀请记录仓储
|
||||
type InvitationRepository struct {
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
// NewInvitationRepository 创建仓储
|
||||
func NewInvitationRepository(db *sql.DB) *InvitationRepository {
|
||||
return &InvitationRepository{db: db}
|
||||
}
|
||||
|
||||
// Create 创建邀请记录
|
||||
func (r *InvitationRepository) Create(invitation *models.Invitation) error {
|
||||
return r.db.QueryRow(`
|
||||
INSERT INTO invitations (card_key_id, account_id, invited_email, status, error_message, expires_at, created_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||
RETURNING id
|
||||
`,
|
||||
invitation.CardKeyID,
|
||||
invitation.AccountID,
|
||||
invitation.InvitedEmail,
|
||||
invitation.Status,
|
||||
invitation.ErrorMessage,
|
||||
invitation.ExpiresAt,
|
||||
time.Now(),
|
||||
).Scan(&invitation.ID)
|
||||
}
|
||||
|
||||
// FindByEmail 根据邮箱查找邀请记录
|
||||
func (r *InvitationRepository) FindByEmail(email string) ([]*models.Invitation, error) {
|
||||
rows, err := r.db.Query(`
|
||||
SELECT id, card_key_id, account_id, invited_email, status, error_message, expires_at, created_at, updated_at
|
||||
FROM invitations WHERE invited_email = $1 ORDER BY created_at DESC
|
||||
`, email)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var invitations []*models.Invitation
|
||||
for rows.Next() {
|
||||
inv := &models.Invitation{}
|
||||
if err := rows.Scan(
|
||||
&inv.ID, &inv.CardKeyID, &inv.AccountID, &inv.InvitedEmail,
|
||||
&inv.Status, &inv.ErrorMessage, &inv.ExpiresAt,
|
||||
&inv.CreatedAt, &inv.UpdatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
invitations = append(invitations, inv)
|
||||
}
|
||||
return invitations, nil
|
||||
}
|
||||
|
||||
// UpdateStatus 更新邀请状态
|
||||
func (r *InvitationRepository) UpdateStatus(id int, status models.InvitationStatus, errMsg string) error {
|
||||
var errMsgSQL sql.NullString
|
||||
if errMsg != "" {
|
||||
errMsgSQL = sql.NullString{String: errMsg, Valid: true}
|
||||
}
|
||||
|
||||
_, err := r.db.Exec(`
|
||||
UPDATE invitations SET status = $1, error_message = $2, updated_at = $3
|
||||
WHERE id = $4
|
||||
`, status, errMsgSQL, time.Now(), id)
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteByEmailAndAccountID 根据邮箱和账号ID删除邀请记录
|
||||
func (r *InvitationRepository) DeleteByEmailAndAccountID(email string, accountID int) error {
|
||||
_, err := r.db.Exec(`
|
||||
DELETE FROM invitations WHERE invited_email = $1 AND account_id = $2
|
||||
`, email, accountID)
|
||||
return err
|
||||
}
|
||||
|
||||
// FindByAccountID 根据账号ID查找邀请记录
|
||||
func (r *InvitationRepository) FindByAccountID(accountID int) ([]*models.Invitation, error) {
|
||||
rows, err := r.db.Query(`
|
||||
SELECT id, card_key_id, account_id, invited_email, status, error_message, expires_at, created_at, updated_at
|
||||
FROM invitations WHERE account_id = $1 ORDER BY created_at DESC
|
||||
`, accountID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var invitations []*models.Invitation
|
||||
for rows.Next() {
|
||||
inv := &models.Invitation{}
|
||||
if err := rows.Scan(
|
||||
&inv.ID, &inv.CardKeyID, &inv.AccountID, &inv.InvitedEmail,
|
||||
&inv.Status, &inv.ErrorMessage, &inv.ExpiresAt,
|
||||
&inv.CreatedAt, &inv.UpdatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
invitations = append(invitations, inv)
|
||||
}
|
||||
return invitations, nil
|
||||
}
|
||||
Reference in New Issue
Block a user