Files
likedream/internal/application/cpdaily/login/sms.go
T
sugarBecomer 98eb15f4e4 ✨ 手机号登录
2025-12-06 02:16:20 +08:00

281 lines
8.3 KiB
Go

package login
import (
"bytes"
"context"
"encoding/base64"
"fmt"
"likedream-goframe/internal/application/cookiejar"
"likedream-goframe/internal/application/cpdaily/cryptoutil"
"likedream-goframe/internal/application/cpdaily/school"
"likedream-goframe/internal/application/httpclient"
"likedream-goframe/internal/common/cst"
"likedream-goframe/internal/common/errors"
"likedream-goframe/internal/utils"
"net/http"
"net/url"
"github.com/bytedance/sonic"
"github.com/samber/oops"
"resty.dev/v3"
)
type cpdailySmsCommonResponse[T any] struct {
ErrCode int `json:"errCode"`
ErrMsg string `json:"errMsg"`
Data T `json:"data"`
}
func (c *cpdailySmsCommonResponse[T]) EnsureSuccess() {
if c.ErrCode != 0 {
utils.OkOrPanic(errors.NewBusinessError(503, c.ErrMsg))
}
}
type CpdailySmsSendSmsData struct {
Countdown int `json:"countdown"`
TipMsg string `json:"tipMsg"`
Status int `json:"status"`
CookieJar *cookiejar.PersistCookieJar `json:"-"`
}
type CpdailySmsLoginByCodeData struct {
AuthID string `json:"authId"`
AuthStatus string `json:"authStatus"`
CompleteUserInfoStatus string `json:"completeUserInfoStatus"`
DeviceExceptionMsg string `json:"deviceExceptionMsg"`
DeviceStatus string `json:"deviceStatus"`
FirstLogin bool `json:"firstLogin"`
Mobile string `json:"mobile"`
MsgTip string `json:"msgTip"`
Name string `json:"name"`
OpenID string `json:"openId"`
PersonID string `json:"personId"`
SessionToken string `json:"sessionToken"`
Status string `json:"status"`
TenantID string `json:"tenantId"`
TenantName string `json:"tenantName"`
Tgc string `json:"tgc"`
TgcStatus string `json:"tgcStatus"`
UserID string `json:"userId"`
UserType string `json:"userType"`
}
func NewSms(deviceID string) *SmsLoginHandler {
return &SmsLoginHandler{
deviceID: deviceID,
}
}
func NewSmsWithoutDeviceID() *SmsLoginHandler {
return &SmsLoginHandler{
deviceID: "",
}
}
type SmsLoginHandler struct {
deviceID string
client *resty.Client
}
func (s *SmsLoginHandler) SendSms(ctx context.Context, phone string) *CpdailySmsSendSmsData {
jar := cookiejar.NewPersistCookieJar()
phoneEncrypted := base64.StdEncoding.EncodeToString(cryptoutil.AesEncrypt([]byte(phone)))
s.client = httpclient.NewCpdailyClient()
req := s.client.
SetCookieJar(jar).
R().
SetContext(utils.NewCpdailySmsContextBuilder().
WithDeviceID(s.deviceID).
Build(ctx)).
SetBody(map[string]string{
"a": phoneEncrypted,
"b": cryptoutil.EncryptVersion,
})
setSmsRequestHeaders(ctx, req)
resp, e := req.
Post("https://mobile.campushoy.com/app/auth/authentication/mobile/messageCode/v-8222")
utils.OkOrPanic(e, oops.WithContext(ctx).Wrap)
respJson := utils.ReadJson[cpdailySmsCommonResponse[CpdailySmsSendSmsData]](resp.Body)
respJson.EnsureSuccess()
respJson.Data.CookieJar = jar
return &respJson.Data
}
func (s *SmsLoginHandler) LoginByCode(ctx context.Context, phone, code string, jar http.CookieJar) *CpdailySmsLoginByCodeData {
payload := map[string]any{
"d": code,
"e": "CPDAILY",
"c": phone,
"f": "86",
}
payloadString, e := sonic.Marshal(payload)
utils.OkOrPanic(e, oops.WithContext(ctx).Wrap)
encryptedPayload := cryptoutil.AesEncrypt(payloadString)
encodedPayload := base64.StdEncoding.EncodeToString(encryptedPayload)
payload = map[string]any{
"a": encodedPayload,
"b": cryptoutil.EncryptVersion,
}
s.client = httpclient.NewCpdailyClient()
s.client.SetCookieJar(jar)
req := s.client.R().
SetBody(payload).
SetContext(utils.NewCpdailySmsContextBuilder().
WithDeviceID(s.deviceID).
Build(ctx))
setSmsRequestHeaders(ctx, req)
resp, e := req.
Post("https://mobile.campushoy.com/app/auth/authentication/mobileLogin/v-8222")
utils.OkOrPanic(e, oops.WithContext(ctx).Wrap)
respJson := utils.ReadJson[cpdailySmsCommonResponse[string]](resp.Body)
respJson.EnsureSuccess()
data := decodeData[CpdailySmsLoginByCodeData](respJson.Data)
return &data
}
func (s *SmsLoginHandler) SetLoginCookie(ctx context.Context, jar http.CookieJar, loginData *CpdailySmsLoginByCodeData) {
detail := school.GetSchoolDetail(ctx, loginData.TenantID)
getCookieUrl := fmt.Sprintf("%s%s", detail.WecloudUrl, "/wec-im-group/group/imUserToken/getImUserToken")
cookieMap := map[string]any{
"AMP2": []map[string]any{
{
"path": "/",
"value": loginData.SessionToken,
"domain": "",
"name": "sessionToken",
},
},
"AMP1": []map[string]any{
{
"path": "/",
"value": loginData.SessionToken,
"domain": "",
"name": "sessionToken",
},
},
}
ampTokenString, e := sonic.Marshal(cookieMap)
utils.OkOrPanic(e, oops.WithContext(ctx).Wrap)
ampTokenEncrypted := cryptoutil.DesEncrypt(ampTokenString, cryptoutil.Normal)
ampToken := base64.StdEncoding.EncodeToString(ampTokenEncrypted)
// 设置cookie到jar: clientType=cpdaily_student; tenantId=ahnu; standAlone=0; sessionToken=5e55bfa7-d17e-4f5a-a06d-99a649855521;
targetSchoolUrl, e := url.Parse(detail.WecloudUrl)
utils.OkOrPanic(e, oops.WithContext(ctx).Wrap)
targetApiUrl, e := url.Parse("https://api.campushoy.com")
utils.OkOrPanic(e, oops.WithContext(ctx).Wrap)
cookieNameValues := map[string]string{
"clientType": "cpdaily_student",
"tenantId": detail.Id,
"standAlone": "0",
"sessionToken": loginData.SessionToken,
}
for name, value := range cookieNameValues {
cookie := &http.Cookie{
Name: name,
Value: value,
}
jar.SetCookies(targetSchoolUrl, []*http.Cookie{cookie})
jar.SetCookies(targetApiUrl, []*http.Cookie{cookie})
}
s.client.SetRedirectPolicy(resty.FlexibleRedirectPolicy(50))
req := s.client.R().
SetHeader("AmpCookies", ampToken).
SetContext(ctx)
ctx = utils.NewCpdailySmsContextBuilder().
WithDeviceID(s.deviceID).
WithTenantID(detail.Id).
WithSessionToken(loginData.SessionToken).
Build(ctx)
setSmsRequestHeaders(ctx, req)
resp, e := req.
Get(getCookieUrl)
utils.OkOrPanic(e, oops.WithContext(ctx).Wrap)
respJson := utils.ReadJson[cpdailySmsCommonResponse[any]](resp.Body)
respJson.EnsureSuccess()
}
func decodeData[T any](encodedData string) T {
decodedBytes, e := base64.StdEncoding.DecodeString(encodedData)
utils.OkOrPanic(e)
decryptedBytes := cryptoutil.AesDecrypt(decodedBytes)
result := utils.ReadJson[T](bytes.NewReader(decryptedBytes))
return result
}
func setSmsRequestHeaders(ctx context.Context, request *resty.Request) {
sessionToken, ok := ctx.Value(cst.CtxCpdailySessionToken).(string)
if ok {
encryptedSessionToken := cryptoutil.DesEncrypt([]byte(sessionToken), cryptoutil.Normal)
encodedSessionToken := base64.StdEncoding.EncodeToString(encryptedSessionToken)
request.SetHeader("SessionToken", encodedSessionToken)
request.SetHeader("sessionTokenKey", encodedSessionToken)
} else {
request.SetHeader("SessionToken", sessionToken)
request.SetHeader("sessionTokenKey", sessionToken)
}
deviceId, _ := ctx.Value(cst.CtxCpdailyDeviceID).(string)
cpdailyInfoDetail := map[string]any{
"deviceId": deviceId,
"systemName": "android",
"userId": "",
"appVersion": "9.8.8",
"model": "Unknown",
"lon": "0",
"lat": "0",
"cpdailyVersion": "9.8.8",
"systemVersion": "11",
}
cpdailyInfoStr, e := sonic.Marshal(cpdailyInfoDetail)
utils.OkOrPanic(e, oops.WithContext(ctx).Wrap)
encryptedCpdailyInfo := cryptoutil.DesEncrypt(cpdailyInfoStr, cryptoutil.Normal)
encodedCpdailyInfo := base64.StdEncoding.EncodeToString(encryptedCpdailyInfo)
request.SetHeaders(map[string]string{
"clientType": "cpdaily_student",
"deviceType": "1",
"CpdailyClientType": "CPDAILY",
"CpdailyStandAlone": "0",
"User-Agent": "Mozilla/5.0 (Linux; Android 11; Redmi Note 8 Pro Build/RP1A.230805.003; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/83.0.4103.106 Mobile Safari/537.36 cpdaily/9.8.8 wisedu/9.8.8",
"CpdailyInfo": encodedCpdailyInfo,
})
}
func (s *SmsLoginHandler) GetCookieString() string {
cookieJar := s.client.CookieJar()
serializableJar := cookieJar.(*cookiejar.PersistCookieJar)
return serializableJar.Serialize()
}