120 lines
3.2 KiB
Go
120 lines
3.2 KiB
Go
// Package device implements user-service push token persistence.
|
|
package device
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"strings"
|
|
|
|
"hyapp/pkg/appcode"
|
|
userdomain "hyapp/services/user-service/internal/domain/user"
|
|
)
|
|
|
|
// Repository stores App push tokens in MySQL.
|
|
type Repository struct {
|
|
db *sql.DB
|
|
}
|
|
|
|
// New creates a device repository over the shared user-service connection pool.
|
|
func New(db *sql.DB) *Repository {
|
|
return &Repository{db: db}
|
|
}
|
|
|
|
// BindPushToken binds a push token to the current user/device and removes stale ownership of the same token.
|
|
func (r *Repository) BindPushToken(ctx context.Context, command userdomain.PushTokenCommand) error {
|
|
appCode := normalizedAppCode(ctx, command.AppCode)
|
|
tx, err := r.db.BeginTx(ctx, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// 同一个系统 push token 只能投递一次;重新登录其他账号时删除旧归属,避免离线消息双发。
|
|
if _, err := tx.ExecContext(ctx,
|
|
`DELETE FROM user_push_tokens
|
|
WHERE app_code = ? AND provider = ? AND push_token = ?
|
|
AND NOT (user_id = ? AND device_id = ?)`,
|
|
appCode,
|
|
command.Provider,
|
|
command.PushToken,
|
|
command.UserID,
|
|
command.DeviceID,
|
|
); err != nil {
|
|
_ = tx.Rollback()
|
|
return err
|
|
}
|
|
|
|
if _, err := tx.ExecContext(ctx,
|
|
`INSERT INTO user_push_tokens (
|
|
app_code, user_id, device_id, push_token, provider, platform, app_version, language, timezone,
|
|
status, bound_at_ms, updated_at_ms, last_request_id
|
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
ON DUPLICATE KEY UPDATE
|
|
push_token = VALUES(push_token),
|
|
provider = VALUES(provider),
|
|
platform = VALUES(platform),
|
|
app_version = VALUES(app_version),
|
|
language = VALUES(language),
|
|
timezone = VALUES(timezone),
|
|
status = VALUES(status),
|
|
updated_at_ms = VALUES(updated_at_ms),
|
|
last_request_id = VALUES(last_request_id)`,
|
|
appCode,
|
|
command.UserID,
|
|
command.DeviceID,
|
|
command.PushToken,
|
|
command.Provider,
|
|
command.Platform,
|
|
command.AppVersion,
|
|
command.Language,
|
|
command.Timezone,
|
|
userdomain.PushTokenStatusActive,
|
|
command.UpdatedAtMs,
|
|
command.UpdatedAtMs,
|
|
command.RequestID,
|
|
); err != nil {
|
|
_ = tx.Rollback()
|
|
return err
|
|
}
|
|
|
|
return tx.Commit()
|
|
}
|
|
|
|
// DeletePushToken marks one user's device token inactive.
|
|
func (r *Repository) DeletePushToken(ctx context.Context, command userdomain.DeletePushTokenCommand) (bool, error) {
|
|
appCode := normalizedAppCode(ctx, command.AppCode)
|
|
query := `UPDATE user_push_tokens
|
|
SET status = ?, updated_at_ms = ?, last_request_id = ?
|
|
WHERE app_code = ? AND user_id = ? AND device_id = ? AND status = ?`
|
|
args := []any{
|
|
userdomain.PushTokenStatusInactive,
|
|
command.UpdatedAtMs,
|
|
command.RequestID,
|
|
appCode,
|
|
command.UserID,
|
|
command.DeviceID,
|
|
userdomain.PushTokenStatusActive,
|
|
}
|
|
if strings.TrimSpace(command.PushToken) != "" {
|
|
query += ` AND push_token = ?`
|
|
args = append(args, command.PushToken)
|
|
}
|
|
|
|
result, err := r.db.ExecContext(ctx, query, args...)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
affected, err := result.RowsAffected()
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
return affected > 0, nil
|
|
}
|
|
|
|
func normalizedAppCode(ctx context.Context, value string) string {
|
|
value = strings.TrimSpace(value)
|
|
if value == "" {
|
|
return appcode.FromContext(ctx)
|
|
}
|
|
return appcode.Normalize(value)
|
|
}
|