package auth import ( "context" "database/sql" "hyapp/pkg/appcode" "hyapp/pkg/xerr" authdomain "hyapp/services/user-service/internal/domain/auth" invitedomain "hyapp/services/user-service/internal/domain/invite" userdomain "hyapp/services/user-service/internal/domain/user" invitestorage "hyapp/services/user-service/internal/storage/mysql/invite" "hyapp/services/user-service/internal/storage/mysql/shared" userstorage "hyapp/services/user-service/internal/storage/mysql/user" ) func (r *Repository) FindThirdPartyIdentity(ctx context.Context, provider string, providerSubject string) (authdomain.ThirdPartyIdentity, error) { var identity authdomain.ThirdPartyIdentity err := r.db.QueryRowContext(ctx, ` SELECT app_code, user_id, provider, provider_subject, COALESCE(provider_union_id, ''), created_at_ms, updated_at_ms FROM third_party_identities WHERE app_code = ? AND provider = ? AND provider_subject = ? `, appcode.FromContext(ctx), provider, providerSubject).Scan(&identity.AppCode, &identity.UserID, &identity.Provider, &identity.ProviderSubject, &identity.ProviderUnionID, &identity.CreatedAtMs, &identity.UpdatedAtMs) if err == sql.ErrNoRows { // 未绑定三方身份时由 service 转入注册路径。 return authdomain.ThirdPartyIdentity{}, xerr.New(xerr.NotFound, "third-party identity not found") } if err != nil { return authdomain.ThirdPartyIdentity{}, err } return identity, nil } func (r *Repository) FindUserIDByRegisterDeviceID(ctx context.Context, appCode string, deviceID string) (int64, error) { var userID int64 err := r.db.QueryRowContext(ctx, ` SELECT user_id FROM users WHERE app_code = ? AND register_device_id = ? LIMIT 1 `, appcode.Normalize(appCode), deviceID).Scan(&userID) if err == sql.ErrNoRows { // 未查到占用者时,service 才允许新三方身份走注册事务。 return 0, xerr.New(xerr.NotFound, "registered device not found") } if err != nil { return 0, err } return userID, nil } // CreateThirdPartyUser 原子创建用户、默认短号、三方身份和首个 session。 func (r *Repository) CreateThirdPartyUser(ctx context.Context, user userdomain.User, identity userdomain.Identity, thirdParty authdomain.ThirdPartyIdentity, session authdomain.Session, inviteBind invitedomain.BindCommand) error { // 三方首次登录注册必须同时写用户、默认短号、三方绑定和首个 session。 tx, err := r.db.BeginTx(ctx, nil) if err != nil { return err } defer tx.Rollback() if err := userstorage.InsertUserIdentity(ctx, tx, user, identity); err != nil { // 默认短号冲突由 service 重试。 return mapAuthDuplicateError(err) } if _, err := invitestorage.EnsurePrimaryCodeForUser(ctx, tx, user.AppCode, user.UserID, user.CreatedAtMs); err != nil { return err } if _, err := invitestorage.BindRelation(ctx, tx, inviteBind); err != nil { return err } _, err = tx.ExecContext(ctx, ` INSERT INTO third_party_identities (app_code, user_id, provider, provider_subject, provider_union_id, created_at_ms, updated_at_ms) VALUES (?, ?, ?, ?, ?, ?, ?) `, appcode.Normalize(thirdParty.AppCode), thirdParty.UserID, thirdParty.Provider, thirdParty.ProviderSubject, shared.NullableString(thirdParty.ProviderUnionID), thirdParty.CreatedAtMs, thirdParty.UpdatedAtMs) if err != nil { return mapAuthDuplicateError(err) } if err := insertSession(ctx, tx, session); err != nil { // session 插入失败会回滚用户和三方绑定。 return err } return tx.Commit() } // RecordLoginAudit 写登录审计;调用方不会让审计失败影响主链路。