diff --git a/app/build.gradle b/app/build.gradle index 9ff56b9..5452259 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -16,8 +16,8 @@ android { minSdkVersion 24 targetSdkVersion 29 - versionCode 13 - versionName "1.1.2" + versionCode 14 + versionName "1.1.3" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" diff --git a/app/src/main/java/com/xwad/os/activity/activation/ActivationActivity.java b/app/src/main/java/com/xwad/os/activity/activation/ActivationActivity.java index 1b7465e..8f1ba55 100644 --- a/app/src/main/java/com/xwad/os/activity/activation/ActivationActivity.java +++ b/app/src/main/java/com/xwad/os/activity/activation/ActivationActivity.java @@ -5,7 +5,6 @@ import android.text.Editable; import android.text.InputFilter; import android.text.Spanned; import android.text.TextUtils; -import android.text.method.DigitsKeyListener; import android.view.View; import androidx.lifecycle.Observer; @@ -17,6 +16,7 @@ import com.xwad.os.bean.OrderInfo; import com.xwad.os.bean.PayInfo; import com.xwad.os.bean.VipInfo; import com.xwad.os.databinding.ActivityActivationBinding; +import com.xwad.os.utils.ActivationUtil; import com.xwad.os.utils.BitmapUtils; import java.util.List; @@ -83,7 +83,9 @@ public class ActivationActivity extends BaseMvvmActivity optional = vipInfos.stream().findFirst(); if (optional.isPresent()) { VipInfo vipInfo = optional.get(); - mViewDataBinding.tvPrice.setText(vipInfo.getPrice()); + mViewDataBinding.tvPrice.setText("¥" + vipInfo.getPrice()); + mViewDataBinding.tvVipName.setText(vipInfo.getName()); + mViewDataBinding.tvVipPrice.setText("¥" + vipInfo.getPrice()); mViewModel.getPayInfo(vipInfo.getId()); } } @@ -112,6 +114,25 @@ public class ActivationActivity extends BaseMvvmActivity() { + @Override + public void onChanged(Boolean aBoolean) { + if (aBoolean) { + mViewModel.getActivation(); + } + } + }); + + mViewModel.mGetCodeData.observe(this, new Observer() { + @Override + public void onChanged(Boolean aBoolean) { + if (aBoolean) { + ActivationUtil.getInstance().startJxwLauncher(); + finish(); + } + } + }); + } public class BtnClick { diff --git a/app/src/main/java/com/xwad/os/activity/activation/ActivationViewModel.java b/app/src/main/java/com/xwad/os/activity/activation/ActivationViewModel.java index 786700f..e8bb9bb 100644 --- a/app/src/main/java/com/xwad/os/activity/activation/ActivationViewModel.java +++ b/app/src/main/java/com/xwad/os/activity/activation/ActivationViewModel.java @@ -5,15 +5,19 @@ import android.util.Log; import androidx.lifecycle.MutableLiveData; import com.hjq.toast.Toaster; +import com.tencent.mmkv.MMKV; import com.trello.rxlifecycle4.RxLifecycle; import com.trello.rxlifecycle4.android.ActivityEvent; import com.xwad.os.base.mvvm.BaseViewModel; import com.xwad.os.bean.BaseResponse; +import com.xwad.os.bean.CodeBean; import com.xwad.os.bean.OrderInfo; import com.xwad.os.bean.PayInfo; import com.xwad.os.bean.VipInfo; +import com.xwad.os.config.CommonConfig; import com.xwad.os.databinding.ActivityActivationBinding; import com.xwad.os.network.NetInterfaceManager; +import com.xwad.os.utils.ActivationUtil; import java.util.List; @@ -23,6 +27,8 @@ import io.reactivex.rxjava3.disposables.Disposable; public class ActivationViewModel extends BaseViewModel { + private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE); + @Override public ActivityActivationBinding getVDBinding() { return binding; @@ -135,6 +141,8 @@ public class ActivationViewModel extends BaseViewModel mCodeActivationData = new MutableLiveData<>(); + public void codeActivation(String code) { NetInterfaceManager.getInstance().getCodeActivationControl(code) .compose(RxLifecycle.bindUntilEvent(getLifecycle(), ActivityEvent.DESTROY)) @@ -147,6 +155,11 @@ public class ActivationViewModel extends BaseViewModel mGetCodeData = new MutableLiveData<>(); + + public void getActivation() { + NetInterfaceManager.getInstance().getActivationCodeControl() + .compose(RxLifecycle.bindUntilEvent(getLifecycle(), ActivityEvent.DESTROY)) + .subscribe(new Observer>() { + @Override + public void onSubscribe(@NonNull Disposable d) { + Log.e("getActivation", "onSubscribe: "); + } + + @Override + public void onNext(@NonNull BaseResponse baseResponse) { + if (baseResponse.code == 200) { + CodeBean codeBean = baseResponse.data; + mMMKV.encode(CommonConfig.ACTIVATIONBEAN_CODE_KEY, codeBean.getCode()); + mGetCodeData.setValue(true); + ActivationUtil.getInstance().setActivation(1); + } else { + ActivationUtil.getInstance().setActivation(0); + } + } + + @Override + public void onError(@NonNull Throwable e) { + Log.e("getActivation", "onError: " + e.getMessage()); + } + + @Override + public void onComplete() { + Log.e("getActivation", "onComplete: "); + } + }); + } } diff --git a/app/src/main/java/com/xwad/os/activity/home/HomeActivity.java b/app/src/main/java/com/xwad/os/activity/home/HomeActivity.java index 5f23ba3..4b4aa6c 100644 --- a/app/src/main/java/com/xwad/os/activity/home/HomeActivity.java +++ b/app/src/main/java/com/xwad/os/activity/home/HomeActivity.java @@ -40,6 +40,7 @@ import com.xwad.os.activity.permission.PermissionActivity; import com.xwad.os.activity.update.UpdateActivity; import com.xwad.os.base.mvvm.BaseMvvmActivity; import com.xwad.os.bean.AppUpdateInfo; +import com.xwad.os.bean.UserInfo; import com.xwad.os.bean.jxw.TabBean; import com.xwad.os.config.CommonConfig; import com.xwad.os.databinding.ActivityHomeBinding; @@ -64,6 +65,7 @@ import com.xwad.os.jxw.event.UpdateGradeEvent; import com.xwad.os.jxw.fragment.SztzFragment; import com.xwad.os.manager.DeviceSNManager; import com.xwad.os.manager.RemoteManager; +import com.xwad.os.utils.ActivationUtil; import com.xwad.os.utils.ApkUtils; import com.xwad.os.utils.DataUtil; import com.xwad.os.utils.OpenApkUtils; @@ -217,7 +219,22 @@ public class HomeActivity extends BaseMvvmActivity() { + @Override + public void onChanged(Boolean aBoolean) { + if (aBoolean) { + ActivationUtil.getInstance().startJxwLauncher(); + } + } + }); + mViewModel.getActivation(); + mViewModel.mUserInfoData.observe(this, new Observer() { + @Override + public void onChanged(UserInfo userInfo) { + } + }); + mViewModel.getUserInfo(); initDatas(); } diff --git a/app/src/main/java/com/xwad/os/activity/home/HomeViewModel.java b/app/src/main/java/com/xwad/os/activity/home/HomeViewModel.java index d60bc4c..96cd6bc 100644 --- a/app/src/main/java/com/xwad/os/activity/home/HomeViewModel.java +++ b/app/src/main/java/com/xwad/os/activity/home/HomeViewModel.java @@ -10,13 +10,16 @@ import com.hjq.toast.Toaster; import com.tencent.mmkv.MMKV; import com.trello.rxlifecycle4.RxLifecycle; import com.trello.rxlifecycle4.android.ActivityEvent; +import com.trello.rxlifecycle4.android.FragmentEvent; import com.xwad.os.BuildConfig; import com.xwad.os.R; import com.xwad.os.base.mvvm.BaseViewModel; import com.xwad.os.bean.AppUpdateInfo; import com.xwad.os.bean.BaseResponse; +import com.xwad.os.bean.CodeBean; import com.xwad.os.bean.SnInfo; import com.xwad.os.bean.SystemSettings; +import com.xwad.os.bean.UserInfo; import com.xwad.os.config.CommonConfig; import com.xwad.os.databinding.ActivityHomeBinding; import com.xwad.os.jxw.SPUtils; @@ -232,4 +235,73 @@ public class HomeViewModel extends BaseViewModel mGetCodeData = new MutableLiveData<>(); + + public void getActivation() { + NetInterfaceManager.getInstance().getActivationCodeControl() + .compose(RxLifecycle.bindUntilEvent(getLifecycle(), ActivityEvent.DESTROY)) + .subscribe(new Observer>() { + @Override + public void onSubscribe(@NonNull Disposable d) { + Log.e("getActivation", "onSubscribe: "); + } + + @Override + public void onNext(@NonNull BaseResponse baseResponse) { + if (baseResponse.code == 200) { + CodeBean codeBean = baseResponse.data; + mMMKV.encode(CommonConfig.ACTIVATIONBEAN_CODE_KEY, codeBean.getCode()); + mGetCodeData.setValue(true); + ActivationUtil.getInstance().setActivation(1); + } else { + ActivationUtil.getInstance().setActivation(0); + } + } + + @Override + public void onError(@NonNull Throwable e) { + Log.e("getActivation", "onError: " + e.getMessage()); + } + + @Override + public void onComplete() { + Log.e("getActivation", "onComplete: "); + } + }); + } + + public MutableLiveData mUserInfoData = new MutableLiveData<>(); + + public void getUserInfo() { + NetInterfaceManager.getInstance().getUserInfoControl() + .compose(RxLifecycle.bindUntilEvent(getLifecycle(), ActivityEvent.DESTROY)) + .subscribe(new Observer>() { + @Override + public void onSubscribe(@NonNull Disposable d) { + Log.e("getUserInfo", "onSubscribe: "); + } + + @Override + public void onNext(@NonNull BaseResponse userInfoBaseResponse) { + Log.e("getUserInfo", "onNext: " + userInfoBaseResponse); + if (userInfoBaseResponse.code == 200) { + UserInfo userInfo = userInfoBaseResponse.data; + + mUserInfoData.setValue(userInfo); + } + } + + @Override + public void onError(@NonNull Throwable e) { + Log.e("getUserInfo", "onError: " + e.getMessage()); + } + + @Override + public void onComplete() { + Log.e("getUserInfo", "onComplete: "); + } + }); + } + } diff --git a/app/src/main/java/com/xwad/os/activity/login/LoginViewModel.java b/app/src/main/java/com/xwad/os/activity/login/LoginViewModel.java index 2b967ac..e83362e 100644 --- a/app/src/main/java/com/xwad/os/activity/login/LoginViewModel.java +++ b/app/src/main/java/com/xwad/os/activity/login/LoginViewModel.java @@ -14,6 +14,7 @@ import com.xwad.os.bean.LoginInfo; import com.xwad.os.bean.LoginInfoDevice; import com.xwad.os.config.CommonConfig; import com.xwad.os.databinding.ActivityLoginBinding; +import com.xwad.os.manager.DeviceSNManager; import com.xwad.os.network.NetInterfaceManager; import io.reactivex.rxjava3.annotations.NonNull; @@ -67,7 +68,7 @@ public class LoginViewModel extends BaseViewModel mLoginInfoData = new MutableLiveData<>(); public void codeLogin(String mobile, String code) { - NetInterfaceManager.getInstance().getCodeLoginObservable(mobile, code) + NetInterfaceManager.getInstance().getNewCodeLoginObservable(mobile, code) .compose(RxLifecycle.bindUntilEvent(getLifecycle(), ActivityEvent.DESTROY)) .subscribe(new Observer>() { @Override @@ -85,6 +86,7 @@ public class LoginViewModel extends BaseViewModel> getNewCodeLoginObservable(String mobile, String code) { + return mRetrofit.create(LoginApi.class) + .newCodeLogin(mobile, code) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()); + } + public Observable getUserLoginObservable(String mobile, String password) { return mRetrofit.create(LoginApi.class) .userLogin(mobile, password, DeviceSNManager.getDeviceSN()) @@ -416,6 +424,13 @@ public class NetInterfaceManager { .observeOn(AndroidSchedulers.mainThread()); } + public Observable> getActivationCodeControl() { + return mRetrofit.create(UserApi.class) + .getActivationCode(getToken(), DeviceSNManager.getDeviceSN()) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()); + } + public Observable> getPayQrcodeControl(String orderSn) { return mRetrofit.create(UserApi.class) .payQrcode(getToken(), DeviceSNManager.getDeviceSN(), orderSn) diff --git a/app/src/main/java/com/xwad/os/network/UrlAddress.java b/app/src/main/java/com/xwad/os/network/UrlAddress.java index e2ae9b5..6be1cd2 100644 --- a/app/src/main/java/com/xwad/os/network/UrlAddress.java +++ b/app/src/main/java/com/xwad/os/network/UrlAddress.java @@ -7,10 +7,14 @@ public class UrlAddress { /*发送登录验证码*/ public final static String LOGIN_GET_CODE = "login/get-code"; /*验证码登录*/ + @Deprecated public final static String CODE_LOGIN = "login/code-login"; /*账号密码登录*/ public final static String USER_LOGIN = "login/user-login"; + public final static String NEW_CODE_LOGIN = "login/new-login"; + + /*用户会员信息(用于判断是否有VIP)*/ public static final String USER_INFO = "user/info"; /*套餐可选列表*/ @@ -19,6 +23,8 @@ public class UrlAddress { public static final String BUY = "user/buy"; /*激活码激活*/ public static final String activation_code = "activation/code"; + /*获取激活码(激活成功后才有)*/ + public static final String get_activation_code = "activation/get-code"; /*发起支付*/ public static final String PAY_QRCODE = "pay/qrcode"; /*续费购买(已经是VIP过期或没过期)*/ diff --git a/app/src/main/java/com/xwad/os/network/api/LoginApi.java b/app/src/main/java/com/xwad/os/network/api/LoginApi.java index 28d341a..ce758c3 100644 --- a/app/src/main/java/com/xwad/os/network/api/LoginApi.java +++ b/app/src/main/java/com/xwad/os/network/api/LoginApi.java @@ -32,4 +32,11 @@ public interface LoginApi { @Query("password") String password, @Field("sn") String sn ); + + @FormUrlEncoded + @POST(UrlAddress.NEW_CODE_LOGIN) + Observable> newCodeLogin( + @Field("mobile") String mobile, + @Field("code") String code + ); } diff --git a/app/src/main/java/com/xwad/os/network/api/UserApi.java b/app/src/main/java/com/xwad/os/network/api/UserApi.java index ed9fbf3..8f9ec49 100644 --- a/app/src/main/java/com/xwad/os/network/api/UserApi.java +++ b/app/src/main/java/com/xwad/os/network/api/UserApi.java @@ -1,6 +1,7 @@ package com.xwad.os.network.api; import com.xwad.os.bean.BaseResponse; +import com.xwad.os.bean.CodeBean; import com.xwad.os.bean.OrderInfo; import com.xwad.os.bean.PayInfo; import com.xwad.os.bean.UserInfo; @@ -45,6 +46,12 @@ public interface UserApi { @Field("code") String code ); + @GET(UrlAddress.get_activation_code) + Observable> getActivationCode( + @Header("token") String token, + @Query("sn") String sn + ); + @FormUrlEncoded @POST(UrlAddress.PAY_QRCODE) Observable> payQrcode( diff --git a/app/src/main/java/com/xwad/os/service/main/MainService.java b/app/src/main/java/com/xwad/os/service/main/MainService.java index 5704bd4..e212ee0 100644 --- a/app/src/main/java/com/xwad/os/service/main/MainService.java +++ b/app/src/main/java/com/xwad/os/service/main/MainService.java @@ -9,10 +9,12 @@ import android.text.TextUtils; import android.util.Log; import com.blankj.utilcode.util.NetworkUtils; +import com.tencent.mmkv.MMKV; import com.xwad.os.activity.NoticeActivity; import com.xwad.os.alarm.AlarmUtils; import com.xwad.os.base.rx.BaseRxService; import com.xwad.os.bean.AlarmClockData; +import com.xwad.os.config.CommonConfig; import java.util.Calendar; import java.util.HashMap; @@ -20,6 +22,8 @@ import java.util.HashMap; public class MainService extends BaseRxService implements MainSContact.MainSView, NetworkUtils.OnNetworkStatusChangedListener { private static final String TAG = "MainService"; + private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE); + public MainSPresenter mPresenter; public MainService() { @@ -53,7 +57,7 @@ public class MainService extends BaseRxService implements MainSContact.MainSView mPresenter.attachView(this); mPresenter.setLifecycle(getLifecycleSubject()); mPresenter.getCloudLessonSettings(); -// registerAlarmReceiver(); + registerReceivers(); NetworkUtils.registerNetworkStatusChangedListener(this); startJxwLauncher(); } @@ -83,10 +87,18 @@ public class MainService extends BaseRxService implements MainSContact.MainSView // } } + private void registerReceivers() { + // registerAlarmReceiver(); + registerJxwRegisterRefreshReceiver(); + } + private void unregisterReceiver() { // if (alarmReceiver != null) { // unregisterReceiver(alarmReceiver); // } + if (null != mJxwRegisterReceiver) { + unregisterReceiver(mJxwRegisterReceiver); + } } public static final String ALARMWAKEUP = "ALARM_WAKEUP"; @@ -155,4 +167,28 @@ public class MainService extends BaseRxService implements MainSContact.MainSView } + public static final String JXW_REGISTER_SUCCESS = "com.zzj.regist_success"; + + private JxwRegisterReceiver mJxwRegisterReceiver; + + private void registerJxwRegisterRefreshReceiver() { + if (mJxwRegisterReceiver == null) { + mJxwRegisterReceiver = new JxwRegisterReceiver(); + } + IntentFilter filter = new IntentFilter(); + filter.addAction(JXW_REGISTER_SUCCESS); + registerReceiver(mJxwRegisterReceiver, filter); + } + + public class JxwRegisterReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + Log.e("JxwRegisterReceiver", "onReceive: " + action); + if (JXW_REGISTER_SUCCESS.equals(action)) { + mMMKV.encode(JXW_REGISTER_SUCCESS, true); + } + } + } + } diff --git a/app/src/main/java/com/xwad/os/utils/ActivationUtil.java b/app/src/main/java/com/xwad/os/utils/ActivationUtil.java index b1d3dd6..9f90fa3 100644 --- a/app/src/main/java/com/xwad/os/utils/ActivationUtil.java +++ b/app/src/main/java/com/xwad/os/utils/ActivationUtil.java @@ -1,10 +1,14 @@ package com.xwad.os.utils; import android.annotation.SuppressLint; +import android.content.ComponentName; import android.content.Context; +import android.content.Intent; +import android.util.Log; import com.tencent.mmkv.MMKV; import com.xwad.os.config.CommonConfig; +import com.xwad.os.service.main.MainService; public class ActivationUtil { /*写入1为激活*/ @@ -15,6 +19,8 @@ public class ActivationUtil { /*默认过期时间*/ public static final int DEFAULT_EXPIRE_TIME = -1; + private static final String TAG = "ActivationUtil"; + private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE); @SuppressLint("StaticFieldLeak") @@ -110,4 +116,24 @@ public class ActivationUtil { return mMMKV.decodeInt(CommonConfig.ACCOUNT_LOGIN_STATU, 0) == 1; } + public void startJxwLauncher() { + String code = mMMKV.decodeString(CommonConfig.ACTIVATIONBEAN_CODE_KEY, ""); + boolean regist_success = mMMKV.decodeBool(MainService.JXW_REGISTER_SUCCESS, false); + if (!regist_success) { + Intent intent = new Intent("UIUI_ACTIVATION"); + ComponentName componentName = new ComponentName("com.jxw.launcher", "com.jxw.engine.platsign.MainActivity"); + intent.setComponent(componentName); + intent.putExtra("uiui_activation_code", code); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + try { + mContext.startActivity(intent); + } catch (Exception e) { + Log.e("getEBagCode", "startActivity: " + e.getMessage()); + } + } else { + Log.e("getEBagCode", "onNext: com.zzj.regist_success is true"); + } + + } + } \ No newline at end of file diff --git a/app/src/main/java/com/xwad/os/view/jxw/view/dialog/QhbzDialog.java b/app/src/main/java/com/xwad/os/view/jxw/view/dialog/QhbzDialog.java index c9cb100..c4b8979 100644 --- a/app/src/main/java/com/xwad/os/view/jxw/view/dialog/QhbzDialog.java +++ b/app/src/main/java/com/xwad/os/view/jxw/view/dialog/QhbzDialog.java @@ -55,7 +55,7 @@ public class QhbzDialog extends Dialog { super.onCreate(bundle); setContentView(R.layout.dialog_qhbz); WindowManager windowManager = getWindow().getWindowManager(); - getWindow().setBackgroundDrawableResource(R.drawable.bg_shape_ffffff_40); +// getWindow().setBackgroundDrawableResource(R.drawable.bg_shape_ffffff_40); Display defaultDisplay = windowManager.getDefaultDisplay(); WindowManager.LayoutParams attributes = getWindow().getAttributes(); defaultDisplay.getSize(new Point()); diff --git a/app/src/main/res/layout/activity_activation.xml b/app/src/main/res/layout/activity_activation.xml index 485042a..4defda8 100644 --- a/app/src/main/res/layout/activity_activation.xml +++ b/app/src/main/res/layout/activity_activation.xml @@ -258,6 +258,7 @@ android:textSize="9sp" /> @@ -561,7 +563,7 @@ android:layout_marginStart="16dp" android:layout_marginEnd="16dp" android:gravity="center" - android:text="@string/license_notice" + android:text="@string/license_hint" android:textColor="#9bb2cc" android:textSize="7sp" app:layout_constraintBottom_toBottomOf="parent" @@ -585,6 +587,7 @@ android:gravity="center" android:maxLines="1" android:singleLine="true" + android:inputType="text" android:textColor="@color/black" android:textSize="12sp" app:layout_constraintBottom_toBottomOf="parent" @@ -603,10 +606,9 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" - android:maxLines="1" - android:singleLine="true" - android:text="注意:涂层刮开视为使用,无法退换货" - android:textColor="@color/red" + android:maxLines="2" + android:text="@string/license_notice" + android:textColor="#D01D27" android:textSize="7sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" @@ -624,8 +626,8 @@ android:layout_width="110dp" android:layout_height="25dp" android:adjustViewBounds="true" - android:scaleType="centerCrop" android:onClick="@{click::keyConfirm}" + android:scaleType="centerCrop" android:src="@drawable/icon_activation_confirm" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 12a7a9e..4479411 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -19,7 +19,8 @@ 桌面运行需授予权限,点击设置 本服务需获取网络、账号、设备、应用、交易信息以及您主动上传的数据,用于提供应用内支付服务。同时为了提升您的使用体验,基于您的“同意"为您启个性化服务。\n使用本服务,即表示您已满 14 周岁,并同意上述内容及学王365软终端内支付用户协议,关于学王365软终端内支付与隐私的声明。 - 如果您已经订购实体包装版,您可以在包装盒内找到“许可证”贴纸,\n请刮开涂层,将密钥输入在下面的对话框内 + 如果您的许可证从经销商手中获得,您可以在本页下方的对话框中输入许可证密钥,验证成功后,即可实现许可证的开通绑定,无需重复支付 + 注意:请妥善保管您的许可证密钥,切勿遗失;\n许可证属于数字化虚拟商品,暂不支持退换,感谢您的理解与支持 许可证验证成功,本账户已可长期使用,\n点击开启学习之旅! 有效期:%d年