mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4
535 字
2 分钟
[破碎数据研究组] ██服务器登录API解析

保管时间 026/05/12 保管单位 Kuinone 等级 main#


API 概述#

端点: /api/v2/platform/login
方法: POST
功能: 用户登录认证,支持多种登录方式(账号密码、手机短信、微信、天翼等) 1

请求头#

参数名类型必填说明
UserIdInteger用户ID
SchoolIdInteger学校ID
TenantString租户代码 2
TenantCodeString租户代码

请求体参数#

参数名类型必填说明
userNameString用户名/手机号
passwordString条件必填密码(MD5加密后转大写) 3
loginTypeint登录类型(见下方登录类型表) 4
deviceIdString设备ID
osDisplayString系统显示信息(Build.DISPLAY) 5
imeiString设备IMEI
apString网络类型
macString条件必填MAC地址(设备支持时必填) 6
isNewboolean是否新用户
activationCodeString激活码
installTimelong应用安装时间
mdmVersionCodeintMDM版本号
mdmVersionNameStringMDM版本名称
forceActivateBenefitBoolean强制激活权益
stateString条件必填状态(天翼登录需要) 7
smsCodeString条件必填短信验证码(短信登录需要) 8
codeString条件必填二维码code(二维码登录需要) 9
operatorIdLong条件必填操作员ID
boundTenantAppCodeString条件必填绑定租户应用代码
boundTenantCodeString条件必填绑定租户代码

登录类型 (loginType)#

常量名说明
0UN_KNOW未知
1ACCOUNT账号登录
3PHONE手机号登录
4PHONE_SMS手机短信登录
5ACCOUNT_OR_PHONE账号或手机号登录
6WE_CHAT微信登录
9ZSCM浙数登录
10QR_XY二维码登录
13YUE_QING越清登录
15TIAN_YI天翼登录
99EDU_CLOUD教育云登录

响应#

响应体为JSON格式,解析为 Account 对象 10 。主要字段包括:

  • 用户基本信息(userId, userName, userType等)
  • 认证信息(accessToken, expiresIn等)
  • 学校信息(schoolId, schoolName等)
  • 设备绑定信息
  • 签名相关字段
  • 时间戳信息(timestampDTO)

使用示例#

账号密码登录#

LoginRequest request = LoginRequest.Companion.passwordLogin("username", "password", "deviceId");
``` [11](#0-10)
### 手机短信登录
```java
LoginRequest request = LoginRequest.Companion.phoneSMSLogin("phone", "smsCode", "deviceId");
``` [12](#0-11)
### 微信登录
```java
LoginRequest request = LoginRequest.Companion.weChatLogin("sceneId", "deviceId");
``` [13](#0-12)
## 调用流程
1. 创建 `LoginRequest` 对象(使用Companion工厂方法或直接构造)
2. 调用 `AccountRepository.userLogin(loginRequest)` [14](#0-13)
3. Repository内部调用 `AccountRemoteDataSource.userLogin(loginRequest)` [1](#0-0)
4. 返回 `Observable<Account>` 对象
## Notes
- 密码在发送前会进行MD5加密并转为大写 [3](#0-2)
- MAC地址仅在设备支持时才发送 [6](#0-5)
- 登录成功后会处理密钥信息(uv和randomStr)并保存到本地文件 [15](#0-14)
- 响应处理包括解析Account对象、保存最近登录信息、更新系统时间等 [10](#0-9)
### Citations
**File:** app/src/main/java/com/xuehai/launcher/user/data/AccountRemoteDataSource.java (L206-253)
```java
public Observable<LResponse> userLogin(LoginRequest loginRequest) throws JSONException {
MyLog.i("[Lc]::Http", "登录");
JSONObject jSONObject = new JSONObject();
try {
jSONObject.put("userName", loginRequest.getAccountName());
jSONObject.put("password", EncryptionUtil.getMD5(loginRequest.getPassword()).toUpperCase());
jSONObject.put("loginType", loginRequest.getLoginType());
jSONObject.put("deviceId", loginRequest.getDeviceId());
jSONObject.put("osDisplay", loginRequest.getOsDisplay());
jSONObject.put("imei", loginRequest.getImei());
jSONObject.put("ap", loginRequest.getAp());
if (DeviceSupportCompat.INSTANCE.isDeviceSupport()) {
jSONObject.put("mac", loginRequest.getMac());
} else {
jSONObject.put("mac", "");
}
jSONObject.put("isNew", loginRequest.getIsNew());
jSONObject.put("activationCode", loginRequest.getActivationCode());
jSONObject.put("installTime", loginRequest.getInstallTime());
jSONObject.put("mdmVersionCode", loginRequest.getMdmVersionCode());
jSONObject.put("mdmVersionName", loginRequest.getMdmVersionName());
jSONObject.put("forceActivateBenefit", loginRequest.getForceActivateBenefit());
jSONObject.put("state", loginRequest.getState());
if (loginRequest.getSmsCode() != null) {
jSONObject.put("smsCode", loginRequest.getSmsCode());
}
if (loginRequest.getCode() != null) {
jSONObject.put("code", loginRequest.getCode());
}
if (loginRequest.getOperatorId() != null) {
jSONObject.put("operatorId", loginRequest.getOperatorId());
}
if (loginRequest.getBoundTenantAppCode() != null) {
jSONObject.put("boundTenantAppCode", loginRequest.getBoundTenantAppCode());
}
if (loginRequest.getBoundTenantCode() != null) {
jSONObject.put("boundTenantCode", loginRequest.getBoundTenantCode());
}
} catch (JSONException e) {
e.printStackTrace();
}
LRequest lRequestBuild = new LRequest.Builder().url(url() + "/api/v2/platform/login").post(LRequestBody.create(jSONObject)).build();
lRequestBuild.getHeader().headers.put("UserId", Integer.valueOf(loginRequest.getUserId()));
lRequestBuild.getHeader().headers.put("SchoolId", Integer.valueOf(loginRequest.getSchoolId()));
lRequestBuild.getHeader().headers.put("Tenant", loginRequest.getTenantCode());
lRequestBuild.getHeader().headers.put("TenantCode", loginRequest.getTenantCode());
return requestWithRestfulByRx(lRequestBuild);
}

File: app/src/main/java/com/xuehai/launcher/data/entity/LoginRequest.java (L54-62)

public final LoginRequest passwordLogin(String account, String passwordIn, String deviceId) {
Intrinsics.checkNotNullParameter(account, "account");
Intrinsics.checkNotNullParameter(passwordIn, "passwordIn");
Intrinsics.checkNotNullParameter(deviceId, "deviceId");
LoginRequest loginRequest = new LoginRequest(5, deviceId, null, 4, null);
loginRequest.setAccountName(account);
loginRequest.setPassword(passwordIn);
return loginRequest;
}

File: app/src/main/java/com/xuehai/launcher/data/entity/LoginRequest.java (L64-72)

public final LoginRequest phoneSMSLogin(String phone, String mobileCode, String deviceId) {
Intrinsics.checkNotNullParameter(phone, "phone");
Intrinsics.checkNotNullParameter(mobileCode, "mobileCode");
Intrinsics.checkNotNullParameter(deviceId, "deviceId");
LoginRequest loginRequest = new LoginRequest(4, deviceId, null, 4, null);
loginRequest.setAccountName(phone);
loginRequest.setSmsCode(mobileCode);
return loginRequest;
}

File: app/src/main/java/com/xuehai/launcher/data/entity/LoginRequest.java (L88-94)

public final LoginRequest weChatLogin(String sceneId, String deviceId) {
Intrinsics.checkNotNullParameter(sceneId, "sceneId");
Intrinsics.checkNotNullParameter(deviceId, "deviceId");
LoginRequest loginRequest = new LoginRequest(6, deviceId, null, 4, null);
loginRequest.setAccountName(sceneId);
return loginRequest;
}

File: app/src/main/java/com/xuehai/launcher/data/entity/LoginRequest.java (L118-134)

public static final class LoginType {
public static final int ACCOUNT = 1;
public static final int ACCOUNT_OR_PHONE = 5;
public static final int EDU_CLOUD = 99;
public static final LoginType INSTANCE = new LoginType();
public static final int PHONE = 3;
public static final int PHONE_SMS = 4;
public static final int QR_XY = 10;
public static final int TIAN_YI = 15;
public static final int UN_KNOW = 0;
public static final int WE_CHAT = 6;
public static final int YUE_QING = 13;
public static final int ZSCM = 9;
private LoginType() {
}
}

File: app/src/main/java/com/xuehai/launcher/user/data/AccountRepository.java (L856-926)

public final Account unifiedDealLoginResult(LoginResult loginResult) throws Throwable {
Intrinsics.checkNotNullParameter(loginResult, "loginResult");
TraceHelper.beginSection("unifiedDealLoginResult");
CacheManager.clearUserCache$default(false, 1, null);
LResponse response = loginResult.getResponse();
String responseBody = response.getResponseBody();
Account account = (Account) JsonUtil.parseJson(responseBody, Account.class);
if (account == null) {
BuryDetail.loginOnlineFailReason(responseBody);
response.setError(ErrorCode.ERROR_PARSER_JSON);
throw new ResponseException(response);
}
if (account.isContinueToBind()) {
throw new WeChatLoginException(response);
}
int loginType = loginResult.getLoginType();
String accountName = loginResult.getAccountName();
String password = loginResult.getPassword();
if (account.getUv() != null && account.getRandomStr() != null) {
MyLog.e("AccountRepository", "登录返回了密钥,uv=" + account.getUv() + ",randomStr=" + account.getRandomStr());
XhSecretKeyUtils.writeSecretToFile(account.getUv(), account.getRandomStr());
}
pwdChangeSwitch = new PwdChangeSwitch(account.getForcePwdChangeSwitch());
saveLatelyAccountName(loginType, accountName);
saveLatelyPassword(loginType, password);
saveLatelyAccount(account);
LoginUtil.afterLogin$default(account, false, 2, null);
UrlCache.checkLauncherUrlChange();
PollInterfaceManager.INSTANCE.resetErrorInfos();
PollInterfaceManager.setTokenValidStatus(true);
UserConfiguration.INSTANCE.markCodeLogin(account.isCodeLoginEnabled());
UserConfiguration.INSTANCE.markUsed();
Account.TimestampDTO timestampDTO = account.getTimestampDTO();
if (timestampDTO != null) {
SettingsHelper.INSTANCE.updateSystemTime(timestampDTO.getDateTime());
}
SceneInfoControler.refreshSceneInfosCache();
MessageModel messageModel = ModelFactory.getMessageModel();
messageModel.registerClient(messageModel.getClientId());
SignatureRepository signatureRepository = new SignatureRepository();
if (account.isFirstAuthorized()) {
signatureRepository.saveSignatureStatus(SignatureInfo.SignatureStatus.NO_STATUS);
}
signatureRepository.updateSignIgnoreCount(account.getSignIgnoreRemainingTimes());
try {
JSONObject jSONObject = new JSONObject(responseBody);
if (jSONObject.has("joinedClass")) {
ArrayList<ClassEntity> arrayList = new ArrayList<>();
JSONArray jSONArray = new JSONArray(jSONObject.optString("joinedClass"));
int length = jSONArray.length();
for (int i = 0; i < length; i++) {
JSONObject jSONObject2 = jSONArray.getJSONObject(i);
ClassEntity classEntity = new ClassEntity();
classEntity.setClassId(jSONObject2.optLong("classId", 0L));
classEntity.setClassName(jSONObject2.optString("className"));
classEntity.setGradeName(jSONObject2.optString("grade"));
classEntity.setClassType(0);
classEntity.setSchoolId(account.getSchoolId());
arrayList.add(classEntity);
}
new ClassRepository().saveClassList(arrayList);
}
if (jSONObject.has("tenantCodes")) {
new UserRepository().updateTenants(UtilExtKt.parseJsonToList(jSONObject.optString("tenantCodes"), String.class));
}
} catch (Exception e) {
MyLog.w("[Lc]::", "accountJson parse error", e);
}
TraceHelper.endSection("unifiedDealLoginResult");
return account;
}

File: app/src/main/java/com/xuehai/launcher/user/data/AccountRepository.java (L933-943)

public final Observable<Account> userLogin(final LoginRequest loginRequest) {
Intrinsics.checkNotNullParameter(loginRequest, "loginRequest");
Observable map = login(loginRequest).observeOn(ThreadPlugins.ioScheduler()).map(new Function() {
@Override
public final Object apply(Object obj) {
return AccountRepository.m1278userLogin$lambda0(this.f$0, loginRequest, (LResponse) obj);
}
});
Intrinsics.checkNotNullExpressionValue(map, "login(loginRequest).obse…uest, response)\n }");
return map;
}
分享

如果这篇文章对你有帮助,欢迎分享给更多人!

[破碎数据研究组] ██服务器登录API解析
https://k.asxz.one/posts/xuehai-login-api/
作者
Kuinone
发布于
2026-07-21
许可协议
CC-0

部分信息可能已经过时

目录