Files
ai-agent/internal/services/wxwork_login_service.go
T

247 lines
7.2 KiB
Go

package services
import (
"agent-desk/internal/models"
"agent-desk/internal/pkg/config"
"agent-desk/internal/pkg/dto/response"
"agent-desk/internal/pkg/enums"
"agent-desk/internal/pkg/errorsx"
"agent-desk/internal/repositories"
"agent-desk/internal/wxwork"
"strings"
"time"
"github.com/mlogclub/simple/common/jsons"
"github.com/mlogclub/simple/common/strs"
"github.com/mlogclub/simple/sqls"
"gorm.io/gorm"
)
var WxWorkLoginService = &wxWorkLoginService{}
type wxWorkLoginService struct {
}
func (s *wxWorkLoginService) BuildWxWorkLoginURL(next string) (string, error) {
if !wxwork.Enabled() {
return "", errorsx.BusinessErrorI18n(1, "error.wxwork.loginDisabled")
}
state, err := wxwork.CreateState(next)
if err != nil {
return "", err
}
return wxwork.BuildLoginURL(state)
}
func (s *wxWorkLoginService) BuildWxWorkQRCodeLoginURL(next string) (string, error) {
if !wxwork.Enabled() {
return "", errorsx.BusinessErrorI18n(1, "error.wxwork.loginDisabled")
}
state, err := wxwork.CreateState(next)
if err != nil {
return "", err
}
return wxwork.BuildQRCodeLoginURL(state)
}
func (s *wxWorkLoginService) LoginByWxWork(code, state string, authCfg config.AuthConfig, clientIP, userAgent string) (string, string, error) {
next, err := wxwork.ParseState(state)
if err != nil {
return "", "", errorsx.UnauthorizedI18n("error.e0111")
}
profile, err := wxwork.GetUserDetail(code)
if err != nil {
return "", "", err
}
loginResp, err := s.loginWithWxWorkProfile(profile, authCfg, clientIP, userAgent)
if err != nil {
return "", "", err
}
ticket, err := wxwork.IssueLoginTicket(loginResp)
if err != nil {
return "", "", err
}
return ticket, next, nil
}
func (s *wxWorkLoginService) ExchangeWxWorkLoginTicket(ticket string) (*response.LoginResponse, error) {
return wxwork.ConsumeLoginTicket(ticket)
}
func (s *wxWorkLoginService) loginWithWxWorkProfile(profile *wxwork.LoginUser, authCfg config.AuthConfig, clientIP, userAgent string) (*response.LoginResponse, error) {
if profile == nil || strings.TrimSpace(profile.UserID) == "" {
return nil, errorsx.BusinessErrorI18n(2, "error.wxwork.profileMissing")
}
var ret *response.LoginResponse
err := sqls.WithTransaction(func(ctx *sqls.TxContext) error {
var (
identity = repositories.UserIdentityRepository.GetBy(ctx.Tx, enums.ThirdProviderWxWork, profile.CorpID, profile.UserID)
user *models.User
err error
)
if identity == nil {
user, identity, err = s.createWxWorkUser(ctx, profile)
if err != nil {
return err
}
} else {
if identity.Status != enums.StatusOk {
return errorsx.BusinessErrorI18n(3, "error.wxwork.bindingDisabled")
}
user = repositories.UserRepository.Get(ctx.Tx, identity.UserID)
if user == nil {
return errorsx.BusinessErrorI18n(4, "error.wxwork.boundUserMissing")
}
}
if user.Status != enums.StatusOk {
return errorsx.UnauthorizedI18n("error.e0200")
}
if err = repositories.UserRepository.Updates(ctx.Tx, user.ID, map[string]any{
"nickname": s.resolveWxWorkNickname(user.Nickname, profile),
"avatar": s.resolveWxWorkAvatar(user.Avatar, profile),
"last_login_at": time.Now(),
"last_login_ip": clientIP,
"update_user_id": user.ID,
"update_user_name": user.Username,
"updated_at": time.Now(),
}); err != nil {
return err
}
if err = repositories.UserIdentityRepository.Updates(ctx.Tx, identity.ID, map[string]any{
"raw_profile": jsons.ToJsonStr(profile),
"last_auth_at": time.Now(),
"status": enums.StatusOk,
"update_user_id": user.ID,
"update_user_name": user.Username,
"updated_at": time.Now(),
}); err != nil {
return err
}
ret, err = AuthService.issueTokens(ctx, user, clientIP, userAgent, authCfg)
if err != nil {
return err
}
return nil
})
if err != nil {
return nil, err
}
return ret, nil
}
func (s *wxWorkLoginService) createWxWorkUser(ctx *sqls.TxContext, profile *wxwork.LoginUser) (*models.User, *models.UserIdentity, error) {
username := strings.TrimSpace(profile.UserID)
mobile := strings.TrimSpace(profile.Mobile)
email := strings.TrimSpace(s.firstNonEmpty(profile.Email, profile.BizMail))
now := time.Now()
if err := s.checkWxWorkProfile(ctx.Tx, username, mobile, email); err != nil {
return nil, nil, err
}
user := &models.User{
Username: username,
Nickname: s.resolveWxWorkNickname("", profile),
Avatar: s.resolveWxWorkAvatar("", profile),
Password: "",
PasswordSalt: "",
Status: enums.StatusOk,
AuditFields: models.AuditFields{
CreatedAt: now,
CreateUserID: 0,
CreateUserName: enums.GetThirdProviderLabel(enums.ThirdProviderWxWork),
UpdatedAt: now,
UpdateUserID: 0,
UpdateUserName: enums.GetThirdProviderLabel(enums.ThirdProviderWxWork),
},
}
if err := repositories.UserRepository.Create(ctx.Tx, user); err != nil {
return nil, nil, err
}
identity := &models.UserIdentity{
UserID: user.ID,
Provider: enums.ThirdProviderWxWork,
ProviderUserID: strings.TrimSpace(profile.UserID),
ProviderCorpID: strings.TrimSpace(profile.CorpID),
ProviderName: enums.GetThirdProviderLabel(enums.ThirdProviderWxWork),
RawProfile: jsons.ToJsonStr(profile),
Status: enums.StatusOk,
LastAuthAt: &now,
AuditFields: models.AuditFields{
CreatedAt: now,
CreateUserID: user.ID,
CreateUserName: user.Username,
UpdatedAt: now,
UpdateUserID: user.ID,
UpdateUserName: user.Username,
},
}
if unionID := strings.TrimSpace(profile.OpenID); unionID != "" {
identity.ProviderUnionID = &unionID
}
if err := repositories.UserIdentityRepository.Create(ctx.Tx, identity); err != nil {
return nil, nil, err
}
return user, identity, nil
}
func (s *wxWorkLoginService) resolveWxWorkNickname(current string, profile *wxwork.LoginUser) string {
if profile != nil {
if name := strings.TrimSpace(profile.Name); name != "" {
return name
}
}
if current = strings.TrimSpace(current); current != "" {
return current
}
if profile != nil {
return strings.TrimSpace(profile.UserID)
}
return ""
}
func (s *wxWorkLoginService) resolveWxWorkAvatar(current string, profile *wxwork.LoginUser) string {
if profile != nil {
if avatar := strings.TrimSpace(profile.Avatar); avatar != "" {
return avatar
}
}
return strings.TrimSpace(current)
}
func (s *wxWorkLoginService) checkWxWorkProfile(tx *gorm.DB, username, mobile string, email string) error {
if strs.IsBlank(username) {
return errorsx.BusinessErrorI18n(5, "error.wxwork.userIDMissing")
}
if existing := repositories.UserRepository.GetByUsername(tx, username); existing != nil {
return errorsx.BusinessErrorI18n(5, "error.wxwork.userIDTaken")
}
if strs.IsNotBlank(mobile) {
if repositories.UserRepository.GetByMobile(tx, mobile) != nil {
return errorsx.BusinessErrorI18n(6, "error.wxwork.mobileTaken")
}
}
if strs.IsNotBlank(email) {
if repositories.UserRepository.GetByEmail(tx, email) != nil {
return errorsx.BusinessErrorI18n(7, "error.wxwork.emailTaken")
}
}
return nil
}
func (s *wxWorkLoginService) firstNonEmpty(values ...string) string {
for _, value := range values {
if value = strings.TrimSpace(value); value != "" {
return value
}
}
return ""
}