Browse Source

学习考试一体机提交

sunqiang 1 year ago
commit
d8097bed4b
100 changed files with 4732 additions and 0 deletions
  1. 10 0
      .gitignore
  2. 1 0
      HttpCoreLibrary/.gitignore
  3. 51 0
      HttpCoreLibrary/build.gradle
  4. 0 0
      HttpCoreLibrary/consumer-rules.pro
  5. 21 0
      HttpCoreLibrary/proguard-rules.pro
  6. 5 0
      HttpCoreLibrary/src/main/AndroidManifest.xml
  7. 81 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/HttpClient.kt
  8. 21 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/HttpConfig.kt
  9. 9 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/apk/AppInfo.kt
  10. 12 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/apk/Data.kt
  11. 9 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/apk/LabInfo.kt
  12. 7 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/apk/aaa.kt
  13. 425 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/client/ApiRepository.kt
  14. 213 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/client/ExamClient.kt
  15. 9 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/client/factory/ClientFactory.kt
  16. 9 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/client/factory/RetrofitFactory.kt
  17. 319 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/client/retrofit/ApiService.java
  18. 615 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/client/retrofit/ExamRetrofit.kt
  19. 16 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/config/ConfigCore.kt
  20. 29 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/config/ConfigFactory.kt
  21. 68 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/config/ConfigParam.kt
  22. 30 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/config/OkHttpDNS.kt
  23. 22 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/converter/NullOnEmptyConverterFactory.kt
  24. 9 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/exception/NetException.kt
  25. 51 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/interceptor/TokenHeaderInterceptor.kt
  26. 26 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/interceptor/formatter/GsonFormatter.kt
  27. 37 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/interceptor/formatter/JSONFormatter.kt
  28. 26 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/interceptor/formatter/OrgJsonFormatter.kt
  29. 14 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/net/DownloadListener.kt
  30. 156 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/net/DownloadTask.kt
  31. 6 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/pojo/AccessRecordBeanList.kt
  32. 9 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/pojo/BannerImageBeanList.kt
  33. 8 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/pojo/CategoryTreeList.kt
  34. 6 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/pojo/CertVoList.kt
  35. 30 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/pojo/ExamCourseVoList.java
  36. 7 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/pojo/ExamTopicList.kt
  37. 8 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/pojo/LearnRecordVoList.kt
  38. 7 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/pojo/MockLists.kt
  39. 11 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/pojo/MockTestVoList.kt
  40. 7 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/pojo/TopicClassifyVoList.kt
  41. 5 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/CommonDataResponse.kt
  42. 7 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/CommonListResponse.kt
  43. 18 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/CommonResponse.kt
  44. 46 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/LogInterceptor.java
  45. 94 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/LogUtil.java
  46. 16 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/request/AccessTokenReq.java
  47. 14 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/request/AssessRecordReq.java
  48. 16 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/request/CourseListReq.java
  49. 20 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/request/ExamAnswerReq.java
  50. 13 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/request/ExamLearnReq.java
  51. 15 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/request/ExamScoreReq.java
  52. 13 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/request/FaceCompareReq.java
  53. 12 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/request/HandPaperReq.java
  54. 12 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/request/HandPractiseReq.java
  55. 17 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/request/LearnLoginReq.java
  56. 20 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/request/LearnRecordReq.java
  57. 20 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/request/MockAnswerReq.java
  58. 14 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/request/MockTestReq.java
  59. 14 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/request/PaperQuReq.java
  60. 16 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/request/ViolationReq.java
  61. 22 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/response/AccessRecordBean.java
  62. 14 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/response/AccessTokenResp.java
  63. 20 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/response/ApkInfoResp.java
  64. 12 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/response/BannerImageBean.java
  65. 23 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/response/CategoryTree.java
  66. 28 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/response/CertVo.java
  67. 171 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/response/ExamCourseVo.java
  68. 115 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/response/ExamTopic.java
  69. 16 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/response/ExamVerify.java
  70. 22 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/response/HandPaperBean.java
  71. 20 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/response/HandPractiseBean.java
  72. 14 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/response/LearnBonusBean.java
  73. 29 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/response/LearnLoginVo.java
  74. 63 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/response/LearnRecordVo.java
  75. 50 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/response/MockTestVo.java
  76. 14 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/response/TopicClassifyVo.java
  77. 25 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/response/UserInfo.java
  78. 22 0
      HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/response/ViolationBean.java
  79. 8 0
      LICENSE
  80. 3 0
      README.md
  81. 1 0
      RcCore/.gitignore
  82. 53 0
      RcCore/build.gradle
  83. 0 0
      RcCore/consumer-rules.pro
  84. BIN
      RcCore/libs/tbs_sdk_v4.3.0.165_20210628_103707.jar
  85. 21 0
      RcCore/proguard-rules.pro
  86. 5 0
      RcCore/src/main/AndroidManifest.xml
  87. 8 0
      RcCore/src/main/java/com/rc/core/event/RefreshEvent.kt
  88. 27 0
      RcCore/src/main/java/com/rc/core/log/RcLog.kt
  89. 71 0
      RcCore/src/main/java/com/rc/core/ui/ActivityCollector.kt
  90. 106 0
      RcCore/src/main/java/com/rc/core/ui/activity/RcBaseActivity.kt
  91. 235 0
      RcCore/src/main/java/com/rc/core/ui/activity/RcRefreshActivity.kt
  92. 36 0
      RcCore/src/main/java/com/rc/core/ui/common/AbsUIDelegate.kt
  93. 25 0
      RcCore/src/main/java/com/rc/core/ui/common/IUIListener.kt
  94. 93 0
      RcCore/src/main/java/com/rc/core/ui/common/UIDelegateImpl.kt
  95. 101 0
      RcCore/src/main/java/com/rc/core/ui/dialog/LoadingDialog.kt
  96. 112 0
      RcCore/src/main/java/com/rc/core/ui/dialog/RcBaseDialog.kt
  97. 83 0
      RcCore/src/main/java/com/rc/core/ui/fragment/RcBaseFragment.kt
  98. 37 0
      RcCore/src/main/java/com/rc/core/ui/fragment/RcLazyFragment.kt
  99. 225 0
      RcCore/src/main/java/com/rc/core/ui/fragment/RcRefreshFragment.kt
  100. 0 0
      RcCore/src/main/java/com/rc/core/ui/widget/MultipleStatusView.kt

+ 10 - 0
.gitignore

@@ -0,0 +1,10 @@
+*.iml
+/.gradle
+/.idea
+.DS_Store
+/build
+/captures
+.externalNativeBuild
+.cxx
+local.properties
+/gradle-build

+ 1 - 0
HttpCoreLibrary/.gitignore

@@ -0,0 +1 @@
+/build

+ 51 - 0
HttpCoreLibrary/build.gradle

@@ -0,0 +1,51 @@
+plugins {
+    id 'com.android.library'
+    id 'kotlin-android'
+}
+
+android {
+    compileSdkVersion env.compileSdkVersion
+    buildToolsVersion env.buildToolsVersion
+
+    defaultConfig {
+        minSdkVersion env.minSdkVersion
+        targetSdkVersion env.targetSdkVersion
+        versionCode 1
+        versionName "1.0"
+
+        consumerProguardFiles "consumer-rules.pro"
+    }
+
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+        }
+    }
+    compileOptions {
+        sourceCompatibility env.jdk_version
+        targetCompatibility env.jdk_version
+    }
+    kotlinOptions {
+        jvmTarget = '1.8'
+    }
+}
+
+dependencies {
+
+    implementation fileTree(dir: "libs", include: ["*.jar"])
+    implementation dep.kotlinStdlib
+    implementation dep.androidxCoreKtx
+    implementation dep.androidxLocalbroadcastmanager
+
+    api dep.retrofit
+    implementation dep.converterGson
+    implementation dep.converterScalars
+    implementation dep.rxJavaAdapter
+    implementation dep.okhttp3Logs
+    api dep.rxJava
+    api dep.rxAndroid
+    api dep.gson
+
+//    implementation dep.luban
+}

+ 0 - 0
HttpCoreLibrary/consumer-rules.pro


+ 21 - 0
HttpCoreLibrary/proguard-rules.pro

@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile

+ 5 - 0
HttpCoreLibrary/src/main/AndroidManifest.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.rc.httpcore">
+
+</manifest>

+ 81 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/HttpClient.kt

@@ -0,0 +1,81 @@
+package com.rc.httpcore
+
+import android.content.Context
+import android.util.Log
+import com.rc.httpcore.client.factory.ClientFactory
+import com.rc.httpcore.client.factory.RetrofitFactory
+import com.rc.httpcore.config.OkHttpDNS
+import com.rc.httpcore.converter.NullOnEmptyConverterFactory
+import com.rc.httpcore.interceptor.TokenHeaderInterceptor
+import com.rc.httpcore.vo.LogInterceptor
+import okhttp3.OkHttpClient
+import okhttp3.logging.HttpLoggingInterceptor
+import retrofit2.Retrofit
+import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
+import retrofit2.converter.gson.GsonConverterFactory
+import java.util.concurrent.TimeUnit
+import java.util.logging.Level
+import java.util.logging.Logger
+
+object HttpClient {
+
+//    private const val TIMEOUT_DEFAULT = 3 * 60L
+    private const val TIMEOUT_DEFAULT = 10L
+
+    private var mAppContext: Context? = null
+    private val LOGGER: Logger = Logger.getLogger(HttpClient::class.java.name)
+    var token: String?=null
+    var vName: String = "1.0.0"
+
+
+    fun init(appContext: Context) {
+        this.mAppContext = appContext
+    }
+
+    fun getAppContext() = mAppContext
+
+    fun createClientFactory(): ClientFactory {
+        return when (HttpConfig.HTTP_STRATEGY_Retrofit) {
+            HttpConfig.HTTP_STRATEGY -> RetrofitFactory()
+            else -> RetrofitFactory()
+        }
+    }
+
+    fun <T> createRetrofitApi(
+        apiClass: Class<T>,
+        baseUrl: String = HttpConfig.API_BASE_URL,
+    ): T {
+        return buildRetrofit(baseUrl).create(apiClass)
+    }
+
+    private fun buildRetrofit(
+        baseUrl: String = HttpConfig.API_BASE_URL,
+        okHttpClient: OkHttpClient = buildHttpClient()
+    ): Retrofit {
+        return Retrofit.Builder()
+            .client(okHttpClient)
+            .baseUrl(baseUrl)
+            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
+            .addConverterFactory(NullOnEmptyConverterFactory())
+            .addConverterFactory(GsonConverterFactory.create())
+            .build()
+    }
+
+    private fun buildHttpClient(): OkHttpClient {
+//        //设置日志打印级别
+//        val interceptor = HttpLoggingInterceptor(HttpLoggingInterceptor.Logger { message ->
+//            LOGGER.log(Level.INFO, message)
+//        })
+//        interceptor.level = HttpLoggingInterceptor.Level.BODY
+        return OkHttpClient.Builder()
+            .readTimeout(TIMEOUT_DEFAULT, TimeUnit.SECONDS) // 设置读取超时时间
+            .connectTimeout(TIMEOUT_DEFAULT, TimeUnit.SECONDS) // 设置请求超时时间
+            .writeTimeout(TIMEOUT_DEFAULT, TimeUnit.SECONDS) // 设置写入超时时间
+            .addNetworkInterceptor(TokenHeaderInterceptor())
+            .addInterceptor(LogInterceptor())//添加请求日志
+            .retryOnConnectionFailure(true) // 设置出现错误进行重新连接
+//            .dns(OkHttpDNS())
+            .build()
+    }
+
+}

+ 21 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/HttpConfig.kt

@@ -0,0 +1,21 @@
+package com.rc.httpcore
+
+class HttpConfig {
+
+    companion object {
+
+        //var API_BASE_URL = "http://lab.zjznai.com/labSystem/"
+        //var API_BASE_URL = "http://pc44sory.xiaomy.net:31738/"
+//        var API_BASE_URL = "http://192.168.251.2/labSystem/"
+//        var API_BASE_URL = "http://192.168.1.8:8080/"
+        var API_BASE_URL = "http://172.16.0.65/api/"
+        var API_BASE_IMG_URL = "http://172.16.0.65/api/"
+
+
+        var FILE_BROWSER_BASE_URL = "http://192.168.251.2:8012/"
+
+        const val HTTP_STRATEGY_Retrofit = 1
+
+        var HTTP_STRATEGY = HTTP_STRATEGY_Retrofit
+    }
+}

+ 9 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/apk/AppInfo.kt

@@ -0,0 +1,9 @@
+package com.rc.httpcore.apk
+
+import com.rc.httpcore.vo.response.ApkInfoResp
+
+data class AppInfo(
+    val labInfo: LabInfo,
+    val needUpgrade: Boolean,
+    val appInfo: ApkInfoResp?  //版本升级得内容
+)

+ 12 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/apk/Data.kt

@@ -0,0 +1,12 @@
+package com.rc.httpcore.apk
+
+data class Data(
+    val account: String,
+    val avatar: String,
+    val cardNum: String,
+    val deptId: Long,
+    val mobile: String,
+    val userId: Long,
+    val userName: String,
+    val userType: Int
+)

+ 9 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/apk/LabInfo.kt

@@ -0,0 +1,9 @@
+package com.rc.httpcore.apk
+
+data class LabInfo(
+    val floorId: String,
+    val floorName: String,
+    val room: String,
+    val subjectId: String,
+    val subjectName: String
+)

+ 7 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/apk/aaa.kt

@@ -0,0 +1,7 @@
+package com.rc.httpcore.apk
+
+data class aaa(
+    val code: Int,
+    val `data`: Data,
+    val message: String
+)

+ 425 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/client/ApiRepository.kt

@@ -0,0 +1,425 @@
+package com.rc.httpcore.client
+
+import com.rc.httpcore.HttpClient
+import com.rc.httpcore.apk.AppInfo
+import com.rc.httpcore.vo.CommonListResponse
+import com.rc.httpcore.vo.request.*
+import com.rc.httpcore.vo.response.*
+import io.reactivex.Observable
+import io.reactivex.android.schedulers.AndroidSchedulers
+import io.reactivex.schedulers.Schedulers
+
+object ApiRepository {
+
+    private val mClientFactory by lazy { HttpClient.createClientFactory() }
+
+//    /**
+//     * 登录获取token
+//     */
+//    fun authOneLogin(): Observable<Boolean> {
+//        return mClientFactory.createLabClient()
+//            .authOneLogin()
+//            .subscribeOn(Schedulers.io())
+//            .observeOn(AndroidSchedulers.mainThread())
+//    }
+
+    fun getCheck(deviceNum: String, type: String, version: Double): Observable<AppInfo> {
+        return mClientFactory.createLabClient()
+            .getCheck(deviceNum, type, version)
+            .subscribeOn(Schedulers.io())
+            .observeOn(AndroidSchedulers.mainThread())
+    }
+    /**
+     * 查询APK版本
+     *
+     * @param id 设备唯一编码
+     */
+    fun apkVersion(id: String): Observable<ApkInfoResp> {
+        return mClientFactory.createLabClient()
+            .apkVersion(id)
+            .subscribeOn(Schedulers.io())
+            .observeOn(AndroidSchedulers.mainThread())
+    }
+
+    /**
+     * 监听每分钟进行调用
+     */
+    fun monitor(deviceNo:String): Observable<Boolean> {
+        return mClientFactory.createLabClient()
+            .monitor(deviceNo)
+            .subscribeOn(Schedulers.io())
+            .observeOn(AndroidSchedulers.mainThread())
+    }
+
+    /**
+     * 上传APK更新状态
+     *
+     * @param state 0:升级失败; 1:升级成功; 2:升级中
+     */
+    fun onepcApkUpdate(id: String, state: String): Observable<Boolean> {
+        return mClientFactory.createLabClient()
+            .onepcApkUpdate(id, state)
+            .subscribeOn(Schedulers.io())
+            .observeOn(AndroidSchedulers.mainThread())
+    }
+
+    /**
+     * 查询轮播图
+     *
+     * @param id 设备唯一编码
+     */
+    fun bannerImages(id: String): Observable<List<BannerImageBean>> {
+        return mClientFactory.createLabClient()
+            .bannerImages(id)
+            .subscribeOn(Schedulers.io())
+            .observeOn(AndroidSchedulers.mainThread())
+    }
+
+    /**
+     * 人脸比对
+     */
+    fun faceCompare(param: FaceCompareReq): Observable<Boolean> {
+        return mClientFactory.createLabClient()
+            .faceCompare(param)
+            .subscribeOn(Schedulers.io())
+            .observeOn(AndroidSchedulers.mainThread())
+    }
+
+    /**
+     * 人脸比对
+     */
+    fun compare(param: FaceCompareReq): Observable<Boolean> {
+        return mClientFactory.createLabClient()
+            .compare(param)
+            .subscribeOn(Schedulers.io())
+            .observeOn(AndroidSchedulers.mainThread())
+    }
+
+    /**
+     * 学习一体机 用户端登录
+     *
+     * @param username 刷学生卡数据
+     * @param androidId 设备唯一码
+     * @param realLogin false:第一次登录,获取token;ture:第二次登录,真正的登录
+     */
+    fun learnLogin(username: String, androidId: String, realLogin: Boolean = false,isLogins:String): Observable<LearnLoginVo> {
+        val param = LearnLoginReq().apply {
+            userName = username
+            type = if (realLogin) "2" else "1"
+            machineCode = androidId
+            isLogin=isLogins
+        }
+        return mClientFactory.createLabClient()
+            .learnLogin(param)
+            .subscribeOn(Schedulers.io())
+            .observeOn(AndroidSchedulers.mainThread())
+    }
+
+
+    fun cardNum(cardNum: String): Observable<LearnLoginVo> {
+        return mClientFactory.createLabClient()
+            .cardNum(cardNum)
+            .subscribeOn(Schedulers.io())
+            .observeOn(AndroidSchedulers.mainThread())
+    }
+
+    fun loginToken(username: String, androidId: String): Observable<LearnLoginVo> {
+        val param = LearnLoginReq().apply {
+            userName = username
+            machineCode = androidId
+
+        }
+        return mClientFactory.createLabClient()
+            .loginToken(param)
+            .subscribeOn(Schedulers.io())
+            .observeOn(AndroidSchedulers.mainThread())
+    }
+
+    /**
+     * 安全学习-学习分类
+     */
+    fun categoryTreeList(): Observable<List<CategoryTree>> {
+        return mClientFactory.createLabClient()
+            .categoryTreeList()
+            .subscribeOn(Schedulers.io())
+            .observeOn(AndroidSchedulers.mainThread())
+    }
+
+    /**
+     * 安全学习-查询学习资源列表
+     */
+    fun examCourseList(param: CourseListReq): Observable<CommonListResponse<ExamCourseVo>> {
+        return mClientFactory.createLabClient()
+            .examCourseList(param)
+            .subscribeOn(Schedulers.io())
+            .observeOn(AndroidSchedulers.mainThread())
+    }
+
+    /**
+     * 安全学习-获取学习资源详细信息
+     */
+    fun examCourseDetail(id: String): Observable<ExamCourseVo> {
+        return mClientFactory.createLabClient()
+            .examCourseDetail(id)
+            .subscribeOn(Schedulers.io())
+            .observeOn(AndroidSchedulers.mainThread())
+    }
+
+    /**
+     * 安全学习-开始学习
+     */
+    fun examLearnStart(param: ExamLearnReq): Observable<Boolean> {
+        return mClientFactory.createLabClient()
+            .examLearnStart(param)
+            .subscribeOn(Schedulers.io())
+            .observeOn(AndroidSchedulers.mainThread())
+    }
+
+    /**
+     * 安全学习-开始学习
+     */
+    fun examLearnFinish(param: ExamLearnReq): Observable<LearnBonusBean> {
+        return mClientFactory.createLabClient()
+            .examLearnFinish(param)
+            .subscribeOn(Schedulers.io())
+            .observeOn(AndroidSchedulers.mainThread())
+    }
+
+    /**
+     * 安全学习-个人学习记录
+     */
+    fun examLearnRecord(param: LearnRecordReq): Observable<CommonListResponse<LearnRecordVo>> {
+        return mClientFactory.createLabClient()
+            .examLearnRecord(param)
+            .subscribeOn(Schedulers.io())
+            .observeOn(AndroidSchedulers.mainThread())
+    }
+
+    /**
+     * 查询题目类型下拉框
+     */
+    fun classifyQueryOption(): Observable<List<TopicClassifyVo>> {
+        return mClientFactory.createLabClient()
+            .classifyQueryOption()
+            .subscribeOn(Schedulers.io())
+            .observeOn(AndroidSchedulers.mainThread())
+    }
+
+    /**
+     * 查询模拟列表
+     */
+    fun onlineList(param: MockTestReq): Observable<CommonListResponse<MockTestVo>> {
+        return mClientFactory.createLabClient()
+            .onlineList(param)
+            .subscribeOn(Schedulers.io())
+            .observeOn(AndroidSchedulers.mainThread())
+    }
+
+    /**
+     * 开始-查询考试前后否需要人脸验证
+     *
+     * @param examId 非模拟考试模式下都传-1
+     */
+    fun examStartVerify(type: String, examId: String = "-1"): Observable<ExamVerify> {
+        return mClientFactory.createLabClient()
+            .examStartVerify(type, examId)
+            .subscribeOn(Schedulers.io())
+            .observeOn(AndroidSchedulers.mainThread())
+    }
+
+    /**
+     * 开始考试-创建考卷
+     *
+     * @param examId 非模拟考试模式下都传-1
+     */
+    fun examStart(type: String, examId: String = "-1"): Observable<ExamTopic> {
+        return mClientFactory.createLabClient()
+            .examStart(type, examId)
+            .subscribeOn(Schedulers.io())
+            .observeOn(AndroidSchedulers.mainThread())
+    }
+
+    /**
+     * 考试 保存用户答案 一题一交
+     */
+    fun examFillAnswer(param: ExamAnswerReq): Observable<Boolean> {
+        return mClientFactory.createLabClient()
+            .examFillAnswer(param)
+            .subscribeOn(Schedulers.io())
+            .observeOn(AndroidSchedulers.mainThread())
+    }
+
+    /**
+     * 交卷- 完成考试
+     */
+    fun examHandPaper(param: HandPaperReq): Observable<HandPaperBean> {
+        return mClientFactory.createLabClient()
+            .examHandPaper(param)
+            .subscribeOn(Schedulers.io())
+            .observeOn(AndroidSchedulers.mainThread())
+    }
+
+    /**
+     * 个人考试成绩列表查询
+     */
+    fun paperMyList(param: ExamScoreReq): Observable<CommonListResponse<ExamTopic>> {
+        return mClientFactory.createLabClient()
+            .paperMyList(param)
+            .subscribeOn(Schedulers.io())
+            .observeOn(AndroidSchedulers.mainThread())
+    }
+
+    /**
+     * 个人考试成绩详情查询
+     */
+    fun paperDetail(id: String): Observable<ExamTopic> {
+        return mClientFactory.createLabClient()
+            .paperDetail(id)
+            .subscribeOn(Schedulers.io())
+            .observeOn(AndroidSchedulers.mainThread())
+    }
+
+    /**
+     * 考试 查询问题和选项 - 一题一查
+     */
+    fun paperExamQuDetail(param: PaperQuReq): Observable<ExamTopic.ElPaperQu> {
+        return mClientFactory.createLabClient()
+            .paperExamQuDetail(param)
+            .subscribeOn(Schedulers.io())
+            .observeOn(AndroidSchedulers.mainThread())
+    }
+
+    /**
+     * 查询我的证书列表
+     */
+    fun queryMyCert(pageNum: Int, pageSize: Int): Observable<CommonListResponse<CertVo>> {
+        return mClientFactory.createLabClient()
+            .queryMyCert(pageNum, pageSize)
+            .subscribeOn(Schedulers.io())
+            .observeOn(AndroidSchedulers.mainThread())
+    }
+
+    /**
+     * 模拟练习-开始练习
+     */
+    fun mockTestStart(ids: String): Observable<ExamTopic> {
+        return mClientFactory.createLabClient()
+            .mockTestStart(ids)
+            .subscribeOn(Schedulers.io())
+            .observeOn(AndroidSchedulers.mainThread())
+    }
+
+    /**
+     * 练习 查询问题和选项 - 一题一查
+     */
+    fun paperMockQuDetail(param: PaperQuReq): Observable<ExamTopic.ElPaperQu> {
+        return mClientFactory.createLabClient()
+            .paperMockQuDetail(param)
+            .subscribeOn(Schedulers.io())
+            .observeOn(AndroidSchedulers.mainThread())
+    }
+
+    /**
+     * 练习 保存用户答案 - 一题一交
+     */
+    fun mockAnswer(param: MockAnswerReq): Observable<Boolean> {
+        return mClientFactory.createLabClient()
+            .mockAnswer(param)
+            .subscribeOn(Schedulers.io())
+            .observeOn(AndroidSchedulers.mainThread())
+    }
+
+    /**
+     * 结束练习
+     */
+    fun handPractise(param: HandPractiseReq): Observable<HandPractiseBean> {
+        return mClientFactory.createLabClient()
+            .handPractise(param)
+            .subscribeOn(Schedulers.io())
+            .observeOn(AndroidSchedulers.mainThread())
+    }
+
+    /**
+     * 个人中心-用户信息
+     */
+    fun queryUserInfo(): Observable<UserInfo> {
+        return mClientFactory.createLabClient()
+            .queryUserInfo()
+            .subscribeOn(Schedulers.io())
+            .observeOn(AndroidSchedulers.mainThread())
+    }
+
+    /**
+     * 个人中心-违规信息
+     */
+    fun queryViolationList(param: ViolationReq): Observable<List<ViolationBean>> {
+        return mClientFactory.createLabClient()
+            .queryViolationList(param)
+            .subscribeOn(Schedulers.io())
+            .observeOn(AndroidSchedulers.mainThread())
+    }
+
+    /**
+     * 是否有待处理的违规
+     */
+    fun existTodoViolation(): Observable<Boolean> {
+        return mClientFactory.createLabClient()
+            .existTodoViolation()
+            .subscribeOn(Schedulers.io())
+            .observeOn(AndroidSchedulers.mainThread())
+    }
+
+    /**
+     * 开始考核-生成考核考卷
+     * @param id 章节ID
+     */
+    fun classTestStart(id: String): Observable<ExamTopic> {
+        return mClientFactory.createLabClient()
+            .classTestStart(id)
+            .subscribeOn(Schedulers.io())
+            .observeOn(AndroidSchedulers.mainThread())
+    }
+
+    /**
+     * 结束考核
+     * @param id 考核ID(试卷ID)
+     */
+    fun handAssess(id: String): Observable<HandPractiseBean> {
+        return mClientFactory.createLabClient()
+            .handAssess(id)
+            .subscribeOn(Schedulers.io())
+            .observeOn(AndroidSchedulers.mainThread())
+    }
+
+    /**
+     * 考核记录列表
+     */
+    fun classTestRecord(param: AssessRecordReq): Observable<CommonListResponse<AccessRecordBean>> {
+        return mClientFactory.createLabClient()
+            .classTestRecord(param)
+            .subscribeOn(Schedulers.io())
+            .observeOn(AndroidSchedulers.mainThread())
+    }
+
+    /**
+     * 考核详情
+     * @param id 考核ID(试卷ID)
+     */
+    fun classTestDetail(id: String): Observable<ExamTopic> {
+        return mClientFactory.createLabClient()
+            .classTestDetail(id)
+            .subscribeOn(Schedulers.io())
+            .observeOn(AndroidSchedulers.mainThread())
+    }
+
+    /**
+     * 退出登录
+     */
+    fun loginOut(): Observable<Boolean> {
+        return mClientFactory.createLabClient()
+            .loginOut()
+            .subscribeOn(Schedulers.io())
+            .observeOn(AndroidSchedulers.mainThread())
+    }
+
+}

+ 213 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/client/ExamClient.kt

@@ -0,0 +1,213 @@
+package com.rc.httpcore.client
+
+import com.rc.httpcore.apk.AppInfo
+import com.rc.httpcore.vo.CommonListResponse
+import com.rc.httpcore.vo.CommonResponse
+import com.rc.httpcore.vo.request.*
+import com.rc.httpcore.vo.response.*
+import io.reactivex.Observable
+
+interface ExamClient {
+//
+//    /**
+//     * 登录获取token
+//     */
+//    fun authOneLogin(): Observable<Boolean>
+
+
+    fun getCheck(deviceNum: String,type:String,version:Double): Observable<AppInfo>
+    /**
+     * 查询APK版本
+     *
+     * @param id 设备唯一编码
+     */
+    fun apkVersion(id: String): Observable<ApkInfoResp>
+
+    fun monitor(deviceNo: String): Observable<Boolean>
+
+    /**
+     * 上传APK更新状态
+     *
+     * @param state 0:升级失败; 1:升级成功; 2:升级中
+     */
+    fun onepcApkUpdate(id: String, state: String): Observable<Boolean>
+
+    /**
+     * 查询轮播图
+     *
+     * @param id 设备唯一编码
+     */
+    fun bannerImages(id: String): Observable<List<BannerImageBean>>
+
+    /**
+     * 人脸比对
+     */
+    fun faceCompare(param: FaceCompareReq): Observable<Boolean>
+
+    fun compare(param: FaceCompareReq): Observable<Boolean>
+
+    /**
+     * 学习一体机 用户端登录
+     *
+     * @param param 刷学生卡数据 userName
+     */
+    fun learnLogin(param: LearnLoginReq): Observable<LearnLoginVo>
+
+    fun cardNum(cardNum: String): Observable<LearnLoginVo>
+
+    /**
+     * 获取token
+     *
+     * @param param 刷学生卡数据 userName
+     */
+    fun loginToken(param: LearnLoginReq): Observable<LearnLoginVo>
+
+    /**
+     * 安全学习-学习分类
+     */
+    fun categoryTreeList(): Observable<List<CategoryTree>>
+
+    /**
+     * 安全学习-查询学习资源列表
+     */
+    fun examCourseList(param: CourseListReq): Observable<CommonListResponse<ExamCourseVo>>
+
+
+    /**
+     * 安全学习-获取学习资源详细信息
+     */
+    fun examCourseDetail(id: String): Observable<ExamCourseVo>
+
+    /**
+     * 安全学习-开始学习
+     */
+    fun examLearnStart(param: ExamLearnReq): Observable<Boolean>
+
+    /**
+     * 安全学习-开始学习
+     */
+    fun examLearnFinish(param: ExamLearnReq): Observable<LearnBonusBean>
+
+    /**
+     * 安全学习-个人学习记录
+     */
+    fun examLearnRecord(param: LearnRecordReq): Observable<CommonListResponse<LearnRecordVo>>
+
+    /**
+     * 查询题目类型下拉框
+     */
+    fun classifyQueryOption(): Observable<List<TopicClassifyVo>>
+
+    /**
+     * 查询模拟列表
+     */
+    fun onlineList(param: MockTestReq): Observable<CommonListResponse<MockTestVo>>
+
+    /**
+     * 开始-查询考试前后否需要人脸验证
+     *
+     * @param examId 非模拟考试模式下都传-1
+     */
+    fun examStartVerify(type: String, examId: String = "-1"): Observable<ExamVerify>
+
+    /**
+     * 开始考试-创建考卷
+     *
+     * @param examId 非模拟考试模式下都传-1
+     */
+    fun examStart(type: String, examId: String = "-1"): Observable<ExamTopic>
+
+    /**
+     * 考试 保存用户答案 一题一交
+     */
+    fun examFillAnswer(param: ExamAnswerReq): Observable<Boolean>
+
+    /**
+     * 交卷- 完成考试
+     */
+    fun examHandPaper(param: HandPaperReq): Observable<HandPaperBean>
+
+    /**
+     * 个人考试成绩列表查询
+     */
+    fun paperMyList(param: ExamScoreReq): Observable<CommonListResponse<ExamTopic>>
+
+    /**
+     * 个人考试成绩详情查询
+     */
+    fun paperDetail(id: String): Observable<ExamTopic>
+
+    /**
+     * 考试 查询问题和选项 - 一题一查
+     */
+    fun paperExamQuDetail(param: PaperQuReq): Observable<ExamTopic.ElPaperQu>
+
+    /**
+     * 查询我的证书列表
+     */
+    fun queryMyCert(pageNum: Int, pageSize: Int): Observable<CommonListResponse<CertVo>>
+
+    /**
+     * 模拟练习-开始练习
+     */
+    fun mockTestStart(ids: String): Observable<ExamTopic>
+
+    /**
+     * 练习 查询问题和选项 - 一题一查
+     */
+    fun paperMockQuDetail(param: PaperQuReq): Observable<ExamTopic.ElPaperQu>
+
+    /**
+     * 练习 保存用户答案 - 一题一交
+     */
+    fun mockAnswer(param: MockAnswerReq): Observable<Boolean>
+
+    /**
+     * 结束练习
+     */
+    fun handPractise(param: HandPractiseReq): Observable<HandPractiseBean>
+
+    /**
+     * 个人中心-用户信息
+     */
+    fun queryUserInfo(): Observable<UserInfo>
+
+    /**
+     * 个人中心-违规信息
+     */
+    fun queryViolationList(param: ViolationReq): Observable<List<ViolationBean>>
+
+    /**
+     * 是否有待处理的违规
+     */
+    fun existTodoViolation(): Observable<Boolean>
+
+    /**
+     * 开始考核-生成考核考卷
+     * @param id 章节ID
+     */
+    fun classTestStart(id: String): Observable<ExamTopic>
+
+    /**
+     * 结束考核
+     * @param id 考核ID(试卷ID)
+     */
+    fun handAssess(id: String): Observable<HandPractiseBean>
+
+    /**
+     * 考核记录列表
+     */
+    fun classTestRecord(param: AssessRecordReq): Observable<CommonListResponse<AccessRecordBean>>
+
+    /**
+     * 考核详情
+     * @param id 考核ID(试卷ID)
+     */
+    fun classTestDetail(id: String): Observable<ExamTopic>
+
+    /**
+     * 退出登录
+     */
+    fun loginOut(): Observable<Boolean>
+
+}

+ 9 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/client/factory/ClientFactory.kt

@@ -0,0 +1,9 @@
+package com.rc.httpcore.client.factory
+
+import com.rc.httpcore.client.ExamClient
+
+interface ClientFactory {
+
+    fun createLabClient(): ExamClient
+
+}

+ 9 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/client/factory/RetrofitFactory.kt

@@ -0,0 +1,9 @@
+package com.rc.httpcore.client.factory
+
+import com.rc.httpcore.client.retrofit.ExamRetrofit
+
+class RetrofitFactory : ClientFactory {
+
+    override fun createLabClient() = ExamRetrofit()
+
+}

+ 319 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/client/retrofit/ApiService.java

@@ -0,0 +1,319 @@
+package com.rc.httpcore.client.retrofit;
+
+import com.rc.httpcore.apk.AppInfo;
+import com.rc.httpcore.pojo.AccessRecordBeanList;
+import com.rc.httpcore.pojo.BannerImageBeanList;
+import com.rc.httpcore.pojo.CategoryTreeList;
+import com.rc.httpcore.pojo.CertVoList;
+import com.rc.httpcore.pojo.ExamCourseVoList;
+import com.rc.httpcore.pojo.ExamTopicList;
+import com.rc.httpcore.pojo.LearnRecordVoList;
+import com.rc.httpcore.pojo.MockLists;
+import com.rc.httpcore.pojo.MockTestVoList;
+import com.rc.httpcore.pojo.TopicClassifyVoList;
+import com.rc.httpcore.vo.CommonDataResponse;
+import com.rc.httpcore.vo.CommonListResponse;
+import com.rc.httpcore.vo.CommonResponse;
+import com.rc.httpcore.vo.request.AccessTokenReq;
+import com.rc.httpcore.vo.request.AssessRecordReq;
+import com.rc.httpcore.vo.request.ExamAnswerReq;
+import com.rc.httpcore.vo.request.ExamLearnReq;
+import com.rc.httpcore.vo.request.ExamScoreReq;
+import com.rc.httpcore.vo.request.FaceCompareReq;
+import com.rc.httpcore.vo.request.HandPaperReq;
+import com.rc.httpcore.vo.request.HandPractiseReq;
+import com.rc.httpcore.vo.request.LearnLoginReq;
+import com.rc.httpcore.vo.request.LearnRecordReq;
+import com.rc.httpcore.vo.request.MockAnswerReq;
+import com.rc.httpcore.vo.request.PaperQuReq;
+import com.rc.httpcore.vo.response.AccessRecordBean;
+import com.rc.httpcore.vo.response.AccessTokenResp;
+import com.rc.httpcore.vo.response.ApkInfoResp;
+import com.rc.httpcore.vo.response.BannerImageBean;
+import com.rc.httpcore.vo.response.CategoryTree;
+import com.rc.httpcore.vo.response.CertVo;
+import com.rc.httpcore.vo.response.ExamCourseVo;
+import com.rc.httpcore.vo.response.ExamTopic;
+import com.rc.httpcore.vo.response.ExamVerify;
+import com.rc.httpcore.vo.response.HandPaperBean;
+import com.rc.httpcore.vo.response.HandPractiseBean;
+import com.rc.httpcore.vo.response.LearnBonusBean;
+import com.rc.httpcore.vo.response.LearnLoginVo;
+import com.rc.httpcore.vo.response.LearnRecordVo;
+import com.rc.httpcore.vo.response.MockTestVo;
+import com.rc.httpcore.vo.response.TopicClassifyVo;
+import com.rc.httpcore.vo.response.UserInfo;
+import com.rc.httpcore.vo.response.ViolationBean;
+
+import java.util.List;
+import java.util.Map;
+
+import io.reactivex.Observable;
+import retrofit2.http.Body;
+import retrofit2.http.GET;
+import retrofit2.http.POST;
+import retrofit2.http.PUT;
+import retrofit2.http.Path;
+import retrofit2.http.Query;
+import retrofit2.http.QueryMap;
+
+public interface ApiService {
+
+    /**
+     * 登录获取token
+     */
+    @POST("auth/one/login")
+    Observable<CommonDataResponse<AccessTokenResp>> authOneLogin(@Body AccessTokenReq param);
+
+    //上报信息 获取当前设备基础信息  不返回token  只用于绑定终端  必须在调用登录api
+    @GET("iot/aio/report")
+    Observable<CommonDataResponse<AppInfo>> getCheck(@Query("deviceNo") String deviceNum, @Query("code") String type, @Query("version") double version);
+
+    //上报在线 离线
+    @GET("iot/aio/monitor")
+    Observable<CommonResponse> monitor(@Query("deviceNo") String deviceNo);
+    /**
+     * 查询APK版本
+     */
+    @GET("laboratory/apkfile/onepcApkDetail/{id}")
+    Observable<CommonDataResponse<ApkInfoResp>> apkVersion(@Path("id") String id);
+
+    /**
+     * 上传APK更新状态
+     *
+     * @param state 0:升级失败; 1:升级成功; 2:升级中
+     */
+    @PUT("laboratory/apkfile/onepcApkUpdate/{id}/{state}")
+    Observable<CommonResponse> onepcApkUpdate(@Path("id") String id, @Path("state") String state);
+
+    /**
+     * 查询轮播图
+     */
+//    @GET("laboratory/onemachine/rotation/chart")
+    @GET("system/rotationChart/selectList")
+//    Observable<CommonDataResponse<BannerImageBeanList>> bannerImages(@Query("hardwareNum") String hardwareNum);
+    Observable<CommonListResponse<BannerImageBean>> bannerImages(@Query("hardwareNum") String hardwareNum,@Query("pcType") int pcType);
+
+    /**
+     * 人脸比对
+     */
+    @POST("algorithm/faceApi/compare")
+    Observable<CommonDataResponse<String>> faceCompare(@Body FaceCompareReq param);
+
+    /**
+     * 人脸比对
+     */
+    @POST("system/user/face/compare")
+    Observable<CommonDataResponse<String>> compare(@Body FaceCompareReq param);
+
+
+    /**
+     * 学习一体机 用户端登录
+     */
+    @POST("auth/learn/login")
+    Observable<CommonDataResponse<LearnLoginVo>> learnLogin(@Body LearnLoginReq param);
+
+    //根据卡号登录
+    @GET("auth/cardNum")
+    Observable<CommonDataResponse<LearnLoginVo>> cardNum(@Query("cardNum") String cardNum);
+
+    /**
+     * 学习一体机 用户端登录
+     */
+    @POST("auth/learn/getToken")
+    Observable<CommonDataResponse<LearnLoginVo>> loginToken(@Body LearnLoginReq param);
+
+    /**
+     * 安全学习-学习分类
+     */
+    @GET("exam/api/category/treeList/{id}")
+    Observable<CommonDataResponse<CategoryTreeList>> categoryTreeList(@Path("id") String id);
+
+    /**
+     * 安全学习-查询学习资源列表
+     */
+    @GET("exam/api/course/list")
+//    Observable<CommonListResponse<ExamCourseVo>> examCourseList(@QueryMap Map<String, Object> filters);
+    Observable<CommonDataResponse<ExamCourseVoList>> examCourseList(@QueryMap Map<String, Object> filters);
+
+    /**
+     * 安全学习-获取学习资源详细信息
+     */
+    @GET("exam/api/course/{id}")
+    Observable<CommonDataResponse<ExamCourseVo>> examCourseDetail(@Path("id") String id);
+
+    /**
+     * 安全学习-开始学习
+     */
+    @POST("exam/api/course/learn/start")
+    Observable<CommonResponse> examLearnStart(@Body ExamLearnReq param);
+
+    /**
+     * 安全学习-完成学习
+     */
+    @POST("exam/api/course/learn/finish")
+    Observable<CommonDataResponse<LearnBonusBean>> examLearnFinish(@Body ExamLearnReq param);
+
+    /**
+     * 安全学习-个人学习记录
+     */
+    @POST("exam/api/course/learn/list")
+//    Observable<CommonDataResponse<LearnRecordVo>> examLearnRecord(@Query("pageNum") int pageNum, @Query("pageSize") int pageSize, @Body LearnRecordReq param);
+    Observable<CommonDataResponse<LearnRecordVoList>> examLearnRecord(@Body LearnRecordReq param);
+
+    /**
+     * 查询题目类型下拉框
+     */
+    @POST("exam/api/classify/queryOption")
+    Observable<CommonDataResponse<TopicClassifyVoList>> classifyQueryOption(@Body Object obj);
+
+    /**
+     * 查询模拟列表
+     */
+    @GET("exam/api/exam/onlineList")
+//    Observable<CommonListResponse<MockTestVo>> onlineList(@Query("pageNum") int pageNum, @Query("pageSize") int pageSize, @Query("classifyIds") String classifyIds);
+//    Observable<CommonListResponse<MockTestVoList>> onlineList(@Query("pageNum") int pageNum, @Query("pageSize") int pageSize, @Query("classifyIds") String classifyIds);
+    Observable<CommonDataResponse<MockLists>> onlineList(@Query("page") int pageNum, @Query("pageSize") int pageSize, @Query("classifyIds") String classifyIds);
+
+    /**
+     * 开始-查询考试前后否需要人脸验证
+     *
+     * @param examId 非模拟考试模式下都传-1
+     */
+    @GET("exam/api/exam/start/verify/{type}")
+    Observable<CommonDataResponse<ExamVerify>> examStartVerify(@Path("type") String type, @Query("examId") String examId);
+
+    /**
+     * 开始考试-创建考卷
+     *
+     * @param examId 非模拟考试模式下都传-1
+     */
+    @GET("exam/api/exam/start/{type}")
+    Observable<CommonDataResponse<ExamTopic>> examStart(@Path("type") String type, @Query("examId") String examId);
+
+    /**
+     * 考试 保存用户答案 一题一交
+     */
+    @POST("exam/api/exam/fillAnswer")
+    Observable<CommonResponse> examFillAnswer(@Body ExamAnswerReq param);
+
+    /**
+     * 交卷- 完成考试
+     */
+    @POST("exam/api/exam/handPaper")
+    Observable<CommonDataResponse<HandPaperBean>> examHandPaper(@Body HandPaperReq param);
+
+    /**
+     * 个人考试成绩列表查询
+     */
+    @POST("exam/api/paper/myList")
+//    Observable<CommonDataResponse<ExamTopicList>> paperMyList(@Query("page") int pageNum, @Query("pageSize") int pageSize, @Body ExamScoreReq param);
+    Observable<CommonDataResponse<ExamTopicList>> paperMyList( @Body ExamScoreReq param);
+
+    /**
+     * 个人考试成绩详情查询
+     */
+    @GET("exam/api/paper/{id}")
+    Observable<CommonDataResponse<ExamTopic>> paperDetail(@Path("id") String id);
+
+    /**
+     * 考试 查询问题和选项 - 一题一查
+     */
+    @POST("exam/api/exam/paperDetail")
+    Observable<CommonDataResponse<ExamTopic.ElPaperQu>> paperExamQuDetail(@Body PaperQuReq param);
+
+    /**
+     * 查询我的证书列表
+     */
+    @POST("exam/api/queryMyCert")
+    Observable<CommonDataResponse<CertVoList>> queryMyCert(@Query("page") int pageNum, @Query("pageSize") int pageSize, @Body Object obj);
+
+    /**
+     * 模拟练习-开始练习
+     */
+    @GET("exam/api/practise/start/{ids}")
+    Observable<CommonDataResponse<ExamTopic>> mockTestStart(@Path("ids") String ids);
+
+    /**
+     * 练习 查询问题和选项 - 一题一查
+     */
+    @POST("exam/api/practise/paperDetail")
+    Observable<CommonDataResponse<ExamTopic.ElPaperQu>> paperMockQuDetail(@Body PaperQuReq param);
+
+    /**
+     * 练习 保存用户答案 - 一题一交
+     */
+    @POST("exam/api/practise/fillAnswer")
+    Observable<CommonResponse> mockAnswer(@Body MockAnswerReq param);
+
+    /**
+     * 结束练习
+     */
+    @POST("exam/api/practise/handPractise")
+    Observable<CommonDataResponse<HandPractiseBean>> handPractise(@Body HandPractiseReq param);
+
+    /**
+     * 个人中心-用户信息
+     */
+//    @GET("laboratory/violation/userInfo")
+    @GET("exam/api/myViolation")
+    Observable<CommonDataResponse<UserInfo>> queryUserInfo();
+
+    /**
+     * 个人中心-违规信息
+     */
+//    @GET("laboratory/violation/historyList")
+    @GET("exam/api/violation/historyList")
+//    Observable<CommonListResponse<ViolationBean>> queryViolationList(
+    Observable<CommonDataResponse<ViolationBean>> queryViolationList(
+            @Query("page") int pageNum, @Query("pageSize") int pageSize,
+            @Query("statTime") String statTime, @Query("endTime") String endTime,
+            @Query("overStatus") String overStatus);
+
+    /**
+     * 是否有待处理的违规
+     */
+//    @GET("laboratory/violation/exist")
+    @GET("exam/api/myViolation")
+    Observable<CommonDataResponse<UserInfo>> existTodoViolation();
+
+    /**
+     * 开始考核-生成考核考卷
+     *
+     * @param id 章节ID
+     */
+    @POST("exam/api/assess/start/{id}")
+    Observable<CommonDataResponse<ExamTopic>> classTestStart(@Path("id") String id);
+
+    /**
+     * 结束考核
+     *
+     * @param id 考核ID(试卷ID)
+     */
+    @POST("exam/api/assess/finish/{id}")
+    Observable<CommonDataResponse<HandPractiseBean>> handAssess(@Path("id") String id);
+
+    /**
+     * 考核记录列表
+     *
+     */
+    @POST("exam/api/assess/list/{id}")
+//    Observable<CommonDataResponse<AccessRecordBeanList>> classTestRecord(@Path("id") String id, @Query("page") int pageNum, @Query("pageSize") int pageSize);
+    Observable<CommonDataResponse<AccessRecordBeanList>> classTestRecord(@Body AssessRecordReq param);
+
+    /**
+     * 考核详情
+     *
+     * @param id 考核ID(试卷ID)
+     */
+    @POST("exam/api/assess/detail/{id}")
+    Observable<CommonDataResponse<ExamTopic>> classTestDetail(@Path("id") String id);
+
+    /**
+     * 退出登录
+     */
+//    @POST("auth/learn/loginOut")
+    @GET("auth/logout")
+    Observable<CommonResponse> loginOut();
+
+}

+ 615 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/client/retrofit/ExamRetrofit.kt

@@ -0,0 +1,615 @@
+package com.rc.httpcore.client.retrofit
+
+import com.rc.httpcore.HttpClient
+import com.rc.httpcore.apk.AppInfo
+import com.rc.httpcore.client.ExamClient
+import com.rc.httpcore.exception.NetException
+import com.rc.httpcore.vo.CommonListResponse
+import com.rc.httpcore.vo.request.*
+import com.rc.httpcore.vo.response.*
+import io.reactivex.Observable
+
+class ExamRetrofit : ExamClient {
+
+    private val apiService by lazy {
+        HttpClient.createRetrofitApi(ApiService::class.java)
+    }
+
+    /**
+     * 登录获取token
+     */
+//    override fun authOneLogin(): Observable<Boolean> {
+//        val param = AccessTokenReq().apply {
+//            username = "onecUser"
+//            password = "admin123"
+//        }
+//        return apiService.authOneLogin(param)
+//            .map { response ->
+//                if (!response.isSuccess()) {
+//                    throw NetException(response.code, response.message)
+//                }
+//
+//                HttpClient.token = response.data?.access_token
+//                return@map !HttpClient.token.isNullOrEmpty()
+//            }
+//    }
+
+    override fun getCheck(deviceNum: String, type: String, version: Double): Observable<AppInfo> {
+        return apiService.getCheck(deviceNum, type, version)
+            .map { response ->
+                if (!response.isSuccess()) {
+                    throw NetException(response.code, response.message)
+                }
+                return@map response.data
+            }
+    }
+
+    /**
+     * 查询APK版本
+     *
+     * @param id 设备唯一编码
+     */
+    override fun apkVersion(id: String): Observable<ApkInfoResp> {
+        return apiService.apkVersion(id)
+            .map { response ->
+                if (!response.isSuccess()) {
+                    throw NetException(response.code, response.message)
+                }
+                return@map response.data
+            }
+    }
+
+    override fun monitor(deviceNo: String): Observable<Boolean> {
+        return apiService.monitor(deviceNo)
+            .map { response ->
+                if (!response.isSuccess()) {
+                    throw NetException(response.code, response.message)
+                }
+
+                return@map true
+            }
+    }
+
+    /**
+     * 上传APK更新状态
+     *
+     * @param state 0:升级失败; 1:升级成功; 2:升级中
+     */
+    override fun onepcApkUpdate(id: String, state: String): Observable<Boolean> {
+        return apiService.onepcApkUpdate(id, state)
+            .map { response ->
+                if (!response.isSuccess()) {
+                    throw NetException(response.code, response.message)
+                }
+                return@map true
+            }
+    }
+
+    /**
+     * 查询轮播图
+     *
+     * @param id 设备唯一编码
+     */
+    override fun bannerImages(id: String): Observable<List<BannerImageBean>> {
+        return apiService.bannerImages(id,1)
+            .map { response ->
+                if (!response.isSuccess()) {
+                    throw NetException(response.code, response.message)
+                }
+//                return@map response.data!!.records
+                return@map response.data
+            }
+    }
+
+    /**
+     * 人脸比对
+     */
+    override fun faceCompare(param: FaceCompareReq): Observable<Boolean> {
+        return apiService.faceCompare(param)
+            .map { response ->
+                if (!response.isSuccess()) {
+                    throw NetException(response.code, response.message)
+                }
+
+                return@map true
+            }
+    }
+
+    override fun compare(param: FaceCompareReq): Observable<Boolean> {
+        return apiService.compare(param)
+            .map { response ->
+                if (!response.isSuccess()) {
+                    throw NetException(response.code, response.message)
+                }
+
+                return@map true
+            }
+    }
+
+    /**
+     * 学习一体机 用户端登录
+     *
+     * @param param 刷学生卡数据 userName
+     */
+    override fun learnLogin(param: LearnLoginReq): Observable<LearnLoginVo> {
+        return apiService.learnLogin(param)
+            .map { response ->
+                if (!response.isSuccess()) {
+                    throw NetException(response.code, response.message)
+                }
+//                if (response.code == "4466") {
+//                    response.data?.isLogin = "1"
+//                    response.data?.expires_in = response.message
+////                    HttpClient.token = response.data?.access_token
+//                    return@map response.data
+//                } else {
+                HttpClient.token = response.data?.token
+                return@map response.data
+//                }
+
+            }
+    }
+
+    override fun cardNum(cardNum: String): Observable<LearnLoginVo> {
+        return apiService.cardNum(cardNum)
+            .map { response ->
+                if (!response.isSuccess()) {
+                    throw NetException(response.code, response.message)
+                }
+//                HttpClient.token = response.data?.access_token
+                HttpClient.token = response.data?.token
+                return@map response.data
+            }
+    }
+
+    override fun loginToken(param: LearnLoginReq): Observable<LearnLoginVo> {
+        return apiService.loginToken(param)
+            .map { response ->
+                if (!response.isSuccess()) {
+                    throw NetException(response.code, response.message)
+                }
+//                HttpClient.token = response.data?.access_token
+                return@map response.data
+            }
+    }
+
+    /**
+     * 安全学习-学习分类
+     */
+    override fun categoryTreeList(): Observable<List<CategoryTree>> {
+        return apiService.categoryTreeList("0")
+            .map { response ->
+                if (!response.isSuccess()) {
+                    throw NetException(response.code, response.message)
+                }
+
+                return@map response.data!!.records
+            }
+    }
+
+    /**
+     * 安全学习-查询学习资源列表
+     */
+    override fun examCourseList(param: CourseListReq): Observable<CommonListResponse<ExamCourseVo>> {
+        val filters = mutableMapOf<String, Any>().apply {
+            put("cateId", param.cateId ?: "")
+            put("title", param.title ?: "")
+            put("page", param.pageNum)
+            put("pageSize", param.pageSize)
+            if (!param.scopeType.isNullOrEmpty()) {
+                put("scopeType", param.scopeType)
+            }
+//            put("status", "1")
+        }
+        return apiService.examCourseList(filters)
+            .map { response ->
+                if (!response.isSuccess()) {
+                    throw NetException(response.code, response.message)
+                }
+                val commonListResponse = CommonListResponse<ExamCourseVo>()
+                commonListResponse.rows = response.data!!.records
+                commonListResponse.total = response.data!!.total
+                return@map commonListResponse
+            }
+    }
+
+    /**
+     * 安全学习-获取学习资源详细信息
+     */
+    override fun examCourseDetail(id: String): Observable<ExamCourseVo> {
+        return apiService.examCourseDetail(id)
+            .map { response ->
+                if (!response.isSuccess()) {
+                    throw NetException(response.code, response.message)
+                }
+
+                return@map response.data
+            }
+    }
+
+    /**
+     * 安全学习-开始学习
+     */
+    override fun examLearnStart(param: ExamLearnReq): Observable<Boolean> {
+        return apiService.examLearnStart(param)
+            .map { response ->
+                if (!response.isSuccess()) {
+                    throw NetException(response.code, response.message)
+                }
+
+                return@map true
+            }
+    }
+
+    /**
+     * 安全学习-开始学习
+     */
+    override fun examLearnFinish(param: ExamLearnReq): Observable<LearnBonusBean> {
+        return apiService.examLearnFinish(param)
+            .map { response ->
+                if (!response.isSuccess()) {
+                    throw NetException(response.code, response.message)
+                }
+
+                return@map response.data
+            }
+    }
+
+    /**
+     * 安全学习-个人学习记录
+     */
+    override fun examLearnRecord(param: LearnRecordReq): Observable<CommonListResponse<LearnRecordVo>> {
+//        return apiService.examLearnRecord(param.pageNum, param.pageSize, param)
+        return apiService.examLearnRecord(param)
+            .map { response ->
+                if (!response.isSuccess()) {
+                    throw NetException(response.code, response.message)
+                }
+                val commonListResponse = CommonListResponse<LearnRecordVo>()
+                commonListResponse.rows = response.data!!.records
+                commonListResponse.total = response.data!!.total
+                return@map commonListResponse
+            }
+    }
+
+    /**
+     * 查询题目类型下拉框
+     */
+    override fun classifyQueryOption(): Observable<List<TopicClassifyVo>> {
+        return apiService.classifyQueryOption(Object())
+            .map { response ->
+                if (!response.isSuccess()) {
+                    throw NetException(response.code, response.message)
+                }
+
+                return@map response.data!!.records
+            }
+    }
+
+    /**
+     * 查询模拟列表
+     */
+    override fun onlineList(param: MockTestReq): Observable<CommonListResponse<MockTestVo>> {
+        return apiService.onlineList(param.pageNum, param.pageSize, param.classifyIds)
+            .map { response ->
+                if (!response.isSuccess()) {
+                    throw NetException(response.code, response.message)
+                }
+                val commonListResponse = CommonListResponse<MockTestVo>()
+                commonListResponse.rows = response.data?.records
+                commonListResponse.total = response.data!!.total
+                return@map commonListResponse
+            }
+    }
+
+    /**
+     * 开始-查询考试前后否需要人脸验证
+     *
+     * @param examId 非模拟考试模式下都传-1
+     */
+    override fun examStartVerify(type: String, examId: String): Observable<ExamVerify> {
+        return apiService.examStartVerify(type, examId)
+            .map { response ->
+                if (!response.isSuccess()) {
+                    throw NetException(response.code, response.message)
+                }
+
+                return@map response.data
+            }
+    }
+
+    /**
+     * 开始考试-创建考卷
+     *
+     * @param examId 非模拟考试模式下都传-1
+     */
+    override fun examStart(type: String, examId: String): Observable<ExamTopic> {
+        return apiService.examStart(type, examId)
+            .map { response ->
+                if (!response.isSuccess()) {
+                    throw NetException(response.code, response.message)
+                }
+
+                return@map response.data
+            }
+    }
+
+    /**
+     * 考试 保存用户答案 一题一交
+     */
+    override fun examFillAnswer(param: ExamAnswerReq): Observable<Boolean> {
+        return apiService.examFillAnswer(param)
+            .map { response ->
+                if (!response.isSuccess()) {
+                    throw NetException(response.code, response.message)
+                }
+
+                return@map true
+            }
+    }
+
+    /**
+     * 交卷- 完成考试
+     */
+    override fun examHandPaper(param: HandPaperReq): Observable<HandPaperBean> {
+        return apiService.examHandPaper(param)
+            .map { response ->
+                if (!response.isSuccess()) {
+                    throw NetException(response.code, response.message)
+                }
+
+                return@map response.data
+            }
+    }
+
+    /**
+     * 个人考试成绩列表查询
+     */
+    override fun paperMyList(param: ExamScoreReq): Observable<CommonListResponse<ExamTopic>> {
+//        return apiService.paperMyList(param.pageNum, param.pageSize, param)
+        return apiService.paperMyList(param)
+            .map { response ->
+                if (!response.isSuccess()) {
+                    throw NetException(response.code, response.message)
+                }
+                val commonListResponse = CommonListResponse<ExamTopic>()
+                commonListResponse.rows = response.data!!.records
+                commonListResponse.total = response.data!!.total
+                return@map commonListResponse
+            }
+    }
+
+    /**
+     * 个人考试成绩详情查询
+     */
+    override fun paperDetail(id: String): Observable<ExamTopic> {
+        return apiService.paperDetail(id)
+            .map { response ->
+                if (!response.isSuccess()) {
+                    throw NetException(response.code, response.message)
+                }
+
+                return@map response.data
+            }
+    }
+
+    /**
+     * 考试 查询问题和选项 - 一题一查
+     */
+    override fun paperExamQuDetail(param: PaperQuReq): Observable<ExamTopic.ElPaperQu> {
+        return apiService.paperExamQuDetail(param)
+            .map { response ->
+                if (!response.isSuccess()) {
+                    throw NetException(response.code, response.message)
+                }
+
+                return@map response.data
+            }
+    }
+
+    /**
+     * 查询我的证书列表
+     */
+    override fun queryMyCert(pageNum: Int, pageSize: Int): Observable<CommonListResponse<CertVo>> {
+        return apiService.queryMyCert(pageNum, pageSize, Object())
+            .map { response ->
+                if (!response.isSuccess()) {
+                    throw NetException(response.code, response.message)
+                }
+                val commonListResponse = CommonListResponse<CertVo>()
+                commonListResponse.rows = response.data!!.records
+                commonListResponse.total = response.data!!.total
+                return@map commonListResponse
+            }
+    }
+
+    /**
+     * 模拟练习-开始练习
+     */
+    override fun mockTestStart(ids: String): Observable<ExamTopic> {
+        return apiService.mockTestStart(ids)
+            .map { response ->
+                if (!response.isSuccess()) {
+                    throw NetException(response.code, response.message)
+                }
+
+                return@map response.data
+            }
+    }
+
+    /**
+     * 练习 查询问题和选项 - 一题一查
+     */
+    override fun paperMockQuDetail(param: PaperQuReq): Observable<ExamTopic.ElPaperQu> {
+        return apiService.paperMockQuDetail(param)
+            .map { response ->
+                if (!response.isSuccess()) {
+                    throw NetException(response.code, response.message)
+                }
+
+                return@map response.data
+            }
+    }
+
+    /**
+     * 练习 保存用户答案 - 一题一交
+     */
+    override fun mockAnswer(param: MockAnswerReq): Observable<Boolean> {
+        return apiService.mockAnswer(param)
+            .map { response ->
+                if (!response.isSuccess()) {
+                    throw NetException(response.code, response.message)
+                }
+
+                return@map true
+            }
+    }
+
+    /**
+     * 结束练习
+     */
+    override fun handPractise(param: HandPractiseReq): Observable<HandPractiseBean> {
+        return apiService.handPractise(param)
+            .map { response ->
+                if (!response.isSuccess()) {
+                    throw NetException(response.code, response.message)
+                }
+
+                return@map response.data
+            }
+    }
+
+    /**
+     * 个人中心-用户信息
+     */
+    override fun queryUserInfo(): Observable<UserInfo> {
+        return apiService.queryUserInfo()
+            .map { response ->
+                if (!response.isSuccess()) {
+                    throw NetException(response.code, response.message)
+                }
+
+                return@map response.data
+            }
+    }
+
+    /**
+     * 个人中心-违规信息
+     */
+    override fun queryViolationList(param: ViolationReq): Observable<List<ViolationBean>> {
+        return apiService.queryViolationList(
+            param.pageNum,
+            param.pageSize,
+            param.statTime,
+            param.endTime,
+            param.overStatus
+        )
+            .map { response ->
+                if (!response.isSuccess()) {
+                    throw NetException(response.code, response.message)
+                }
+                var listData = mutableListOf<ViolationBean>()
+                var vioHad = ViolationBean()
+                vioHad.createTime = response.data!!.createTime
+                vioHad.remake = response.data!!.remake
+                vioHad.violationList = response.data!!.violationList
+                listData.add(vioHad)
+                return@map listData
+            }
+    }
+
+    /**
+     * 是否有待处理的违规
+     */
+    override fun existTodoViolation(): Observable<Boolean> {
+        return apiService.existTodoViolation()
+            .map { response ->
+                if (!response.isSuccess()) {
+                    throw NetException(response.code, response.message)
+                }
+                if (response.data!!.status.equals("1")){
+                    return@map false
+                }else{
+                    return@map true
+                }
+//                return@map response.data!!.status.equals("1")
+            }
+    }
+
+    /**
+     * 开始考核-生成考核考卷
+     * @param id 章节ID
+     */
+    override fun classTestStart(id: String): Observable<ExamTopic> {
+        return apiService.classTestStart(id)
+            .map { response ->
+                if (!response.isSuccess()) {
+                    throw NetException(response.code, response.message)
+                }
+
+                return@map response.data
+            }
+    }
+
+    /**
+     * 结束考核
+     * @param id 考核ID(试卷ID)
+     */
+    override fun handAssess(id: String): Observable<HandPractiseBean> {
+        return apiService.handAssess(id)
+            .map { response ->
+                if (!response.isSuccess()) {
+                    throw NetException(response.code, response.message)
+                }
+
+                return@map response.data
+            }
+    }
+
+    /**
+     * 考核记录列表
+     */
+    override fun classTestRecord(param: AssessRecordReq): Observable<CommonListResponse<AccessRecordBean>> {
+//        return apiService.classTestRecord(param.chapterId, param.page, param.pageSize)
+        return apiService.classTestRecord(param)
+            .map { response ->
+                if (!response.isSuccess()) {
+                    throw NetException(response.code, response.message)
+                }
+                val commonListResponse = CommonListResponse<AccessRecordBean>()
+                commonListResponse.rows = response.data!!.records
+                commonListResponse.total = response.data!!.total
+                return@map commonListResponse
+            }
+    }
+
+    /**
+     * 考核详情
+     * @param id 考核ID(试卷ID)
+     */
+    override fun classTestDetail(id: String): Observable<ExamTopic> {
+        return apiService.classTestDetail(id)
+            .map { response ->
+                if (!response.isSuccess()) {
+                    throw NetException(response.code, response.message)
+                }
+
+                return@map response.data
+            }
+    }
+
+    /**
+     * 退出登录
+     */
+    override fun loginOut(): Observable<Boolean> {
+        return apiService.loginOut()
+            .map { response ->
+                if (!response.isSuccess()) {
+                    throw NetException(response.code, response.message)
+                }
+
+                return@map true
+            }
+    }
+
+}

+ 16 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/config/ConfigCore.kt

@@ -0,0 +1,16 @@
+package com.rc.httpcore.config
+
+import com.rc.httpcore.HttpConfig
+
+object ConfigCore {
+
+    fun initConfig(config: ConfigParam?, baseUrlSp: String?, fileBrowserBaseUrlSp: String?) {
+        if (null != config) {
+            HttpConfig.API_BASE_URL = if (baseUrlSp.isNullOrEmpty()) config.baseUrl else baseUrlSp
+            HttpConfig.FILE_BROWSER_BASE_URL =
+                if (fileBrowserBaseUrlSp.isNullOrEmpty()) config.fileBrowserBaseUrl else fileBrowserBaseUrlSp
+            HttpConfig.HTTP_STRATEGY = config.httpStrategy
+        }
+    }
+
+}

+ 29 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/config/ConfigFactory.kt

@@ -0,0 +1,29 @@
+package com.rc.httpcore.config
+
+import com.rc.httpcore.HttpConfig
+
+class ConfigFactory {
+
+    companion object {
+        val buildDebugConfig = ConfigParam.Builder()
+            .baseUrl("http://180.76.134.43:30002/")
+            .fileBrowserBaseUrl("http://180.76.134.43:31007/")
+            .httpStrategy(HttpConfig.HTTP_STRATEGY_Retrofit)
+            .mqttServerUri("tcp://180.76.134.43:18830")
+            .mqttUName("dlc")
+            .mqttUPwd("123456")
+            .build()
+
+        val buildReleaseConfig = ConfigParam.Builder()
+//            .baseUrl("https://lab.sxitdlc.com/labSystem/")
+            .baseUrl("http://172.16.0.65/api/")
+//            .fileBrowserBaseUrl("http://180.76.134.43:31007/")
+            .fileBrowserBaseUrl("http://172.16.0.65/api/")
+            .httpStrategy(HttpConfig.HTTP_STRATEGY_Retrofit)
+            .mqttServerUri("tcp://180.76.134.43:1883")
+            .mqttUName("mqtt")
+            .mqttUPwd("mqtt@zd1883")
+            .build()
+    }
+
+}

+ 68 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/config/ConfigParam.kt

@@ -0,0 +1,68 @@
+package com.rc.httpcore.config
+
+import com.rc.httpcore.HttpConfig
+
+class ConfigParam(builder: Builder) {
+
+    val baseUrl: String = builder.baseUrl
+    val fileBrowserBaseUrl: String = builder.fileBrowserBaseUrl
+
+    val httpStrategy: Int = builder.httpStrategy
+
+    val mqttServerUri: String = builder.mqttServerUri
+    val mqttUName: String = builder.mqttUName
+    val mqttUPwd: String = builder.mqttUPwd
+
+    class Builder {
+        internal var baseUrl: String
+        internal var fileBrowserBaseUrl: String
+        internal var httpStrategy: Int
+        internal var mqttServerUri: String
+        internal var mqttUName: String
+        internal var mqttUPwd: String
+
+        init {
+            baseUrl = "http://pc44sory.xiaomy.net:31738/"
+            fileBrowserBaseUrl = "http://180.76.134.43:31007/"
+            httpStrategy = HttpConfig.HTTP_STRATEGY_Retrofit
+            mqttServerUri = "tcp://42.193.12.99:1883"
+            mqttUName = "dlc"
+            mqttUPwd = "123456"
+        }
+
+        fun baseUrl(baseUrl: String): Builder {
+            this.baseUrl = baseUrl
+            return this
+        }
+
+        fun fileBrowserBaseUrl(fileBrowserBaseUrl: String): Builder {
+            this.fileBrowserBaseUrl = fileBrowserBaseUrl
+            return this
+        }
+
+        fun httpStrategy(httpStrategy: Int): Builder {
+            this.httpStrategy = httpStrategy
+            return this
+        }
+
+        fun mqttServerUri(mqttServerUri: String): Builder {
+            this.mqttServerUri = mqttServerUri
+            return this
+        }
+
+        fun mqttUName(mqttUName: String): Builder {
+            this.mqttUName = mqttUName
+            return this
+        }
+
+        fun mqttUPwd(mqttUPwd: String): Builder {
+            this.mqttUPwd = mqttUPwd
+            return this
+        }
+
+        fun build(): ConfigParam {
+            return ConfigParam(this)
+        }
+
+    }
+}

+ 30 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/config/OkHttpDNS.kt

@@ -0,0 +1,30 @@
+package com.rc.httpcore.config
+
+import android.text.TextUtils
+import android.util.Log
+import okhttp3.Dns
+import java.net.InetAddress
+import java.net.UnknownHostException
+
+
+class OkHttpDNS : Dns {
+    private var mIpString: String? = null
+
+    @Throws(UnknownHostException::class)
+    override fun lookup(hostname: String): List<InetAddress> {
+        Log.d("OkHttpDNS", "hostname----:$hostname")
+        if (TextUtils.isEmpty(hostname) || null == hostname) return Dns.SYSTEM.lookup(hostname)
+
+        //根据域名获取IP
+        val ips = InetAddress.getAllByName(hostname)
+        for (inetAddress in ips) {
+            mIpString = inetAddress.hostAddress
+            Log.d("OkHttpDNS", "mIpString----:$mIpString")
+        }
+        return if (null != mIpString && !TextUtils.isEmpty(mIpString)) {
+            listOf(*InetAddress.getAllByName(mIpString))
+        } else {
+            Dns.SYSTEM.lookup(hostname)
+        }
+    }
+}

+ 22 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/converter/NullOnEmptyConverterFactory.kt

@@ -0,0 +1,22 @@
+package com.rc.httpcore.converter
+
+import okhttp3.ResponseBody
+import retrofit2.Converter
+import retrofit2.Retrofit
+import java.lang.reflect.Type
+
+class NullOnEmptyConverterFactory : Converter.Factory() {
+
+    override fun responseBodyConverter(
+        type: Type?,
+        annotations: Array<out Annotation>?,
+        retrofit: Retrofit?
+    ): Converter<ResponseBody, *> {
+        val delegate: Converter<ResponseBody, Any>? =
+            retrofit?.nextResponseBodyConverter(this, type, annotations)
+
+        return Converter<ResponseBody, Any>() {
+            if (it.contentLength() == 0L) null else delegate?.convert(it)
+        }
+    }
+}

+ 9 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/exception/NetException.kt

@@ -0,0 +1,9 @@
+package com.rc.httpcore.exception
+
+class NetException(val code: String, message: String?): Exception(message) {
+
+    override fun toString(): String {
+        val message = super.getLocalizedMessage() ?: ""
+        return "$message (${code})"
+    }
+}

+ 51 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/interceptor/TokenHeaderInterceptor.kt

@@ -0,0 +1,51 @@
+package com.rc.httpcore.interceptor
+
+import android.content.Intent
+import android.util.Log
+import androidx.localbroadcastmanager.content.LocalBroadcastManager
+import com.google.gson.Gson
+import com.rc.httpcore.HttpClient
+import com.rc.httpcore.vo.CommonResponse
+import okhttp3.Interceptor
+import okhttp3.Request
+import okhttp3.Response
+import java.lang.Exception
+import java.nio.charset.StandardCharsets
+
+class TokenHeaderInterceptor : Interceptor {
+
+    override fun intercept(chain: Interceptor.Chain): Response {
+        val originalRequest: Request = chain.request()
+        val requestBuilder: Request.Builder = originalRequest.newBuilder()
+
+        if (!HttpClient.token.isNullOrEmpty()) {
+            Log.d("=====authorization","${HttpClient.token!!}")
+            requestBuilder.addHeader("authorization", HttpClient.token!!)
+        }
+        requestBuilder
+            .header("vName", HttpClient.vName)
+            .header("Accept", "application/json, text/plain, */*")
+            .build()
+        val newlyRequest = requestBuilder.build()
+        val response = chain.proceed(newlyRequest)
+        try {
+            response.body()?.let {
+                val source = it.source()
+                source.request(Long.MAX_VALUE)
+                val data = source.buffer.clone().readString(StandardCharsets.UTF_8)
+
+                val commonResponse = Gson().fromJson<CommonResponse>(data, CommonResponse::class.java)
+                if (commonResponse.isTokenExpired()) {
+                    HttpClient.getAppContext()?.apply {
+                        val intent = Intent("com.rc.core.token_expired")
+                        LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
+                    }
+                }
+            }
+        } catch (e: Exception) {
+            e.printStackTrace()
+        }
+
+        return response
+    }
+}

+ 26 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/interceptor/formatter/GsonFormatter.kt

@@ -0,0 +1,26 @@
+package com.rc.httpcore.interceptor.formatter
+
+import com.google.gson.GsonBuilder
+import com.google.gson.JsonParser
+
+class GsonFormatter : JSONFormatter() {
+
+    companion object {
+        fun buildIfSupported(): JSONFormatter? {
+            return try {
+                Class.forName("com.google.gson.Gson")
+                GsonFormatter()
+            } catch (ignore: ClassNotFoundException) {
+                null
+            }
+        }
+    }
+
+    private val GSON = GsonBuilder().setPrettyPrinting().create()
+    private val PARSER = JsonParser()
+
+    override fun format(source: String?): String? {
+        return GSON.toJson(PARSER.parse(source))
+    }
+
+}

+ 37 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/interceptor/formatter/JSONFormatter.kt

@@ -0,0 +1,37 @@
+package com.rc.httpcore.interceptor.formatter
+
+import org.json.JSONException
+
+/**
+ * json格式化
+ */
+open class JSONFormatter {
+
+    companion object {
+
+        private val FORMATTER = findJSONFormatter()
+
+        fun formatJSON(source: String?): String? {
+            return try {
+                FORMATTER.format(source)
+            } catch (e: java.lang.Exception) {
+                ""
+            }
+        }
+
+        private fun findJSONFormatter(): JSONFormatter {
+            val jsonFormatter = OrgJsonFormatter.buildIfSupported()
+            if (jsonFormatter != null) {
+                return jsonFormatter
+            }
+            val gsonFormatter = GsonFormatter.buildIfSupported()
+            return gsonFormatter ?: JSONFormatter()
+        }
+    }
+
+    @Throws(JSONException::class)
+    open fun format(source: String?): String? {
+        return ""
+    }
+
+}

+ 26 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/interceptor/formatter/OrgJsonFormatter.kt

@@ -0,0 +1,26 @@
+package com.rc.httpcore.interceptor.formatter
+
+import org.json.JSONObject
+
+class OrgJsonFormatter : JSONFormatter() {
+
+    companion object {
+
+        private const val INDENT_SPACES = 4
+
+        fun buildIfSupported(): OrgJsonFormatter? {
+            return try {
+                Class.forName("org.json.JSONObject")
+                OrgJsonFormatter()
+            } catch (ignore: ClassNotFoundException) {
+                null
+            }
+        }
+    }
+
+    override fun format(source: String?): String? {
+        return if (source.isNullOrEmpty())
+            "" else JSONObject(source).toString(INDENT_SPACES)
+    }
+
+}

+ 14 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/net/DownloadListener.kt

@@ -0,0 +1,14 @@
+package com.rc.httpcore.net
+
+interface DownloadListener {
+
+    fun onProgress(progress: Int)
+
+    fun onSuccess()
+
+    fun onFailed(errMsg: String?)
+
+    fun onPaused()
+
+    fun onCanceled()
+}

+ 156 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/net/DownloadTask.kt

@@ -0,0 +1,156 @@
+package com.rc.httpcore.net
+
+import android.os.AsyncTask
+import android.os.Environment
+import android.text.TextUtils
+import okhttp3.OkHttpClient
+import okhttp3.Request
+import java.io.File
+import java.io.InputStream
+import java.io.RandomAccessFile
+
+/**
+ * eg: http://www.download.com/projectname/dl/test.apk
+ * 将文件下载到sd卡/Download目录。
+ * 下载到本地的文件件名,如果没指定则为test.apk。
+ *
+ */
+class DownloadTask(
+    private val listener: DownloadListener,
+    private val dlPath: String = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).path,
+    private var fileName: String?,
+    private val headers: Map<String, String>? = null
+) :
+    AsyncTask<String, Int, Int>() {
+
+    companion object {
+        const val TYPE_SUCCESS: Int = 1
+        const val TYPE_FAILED: Int = 2
+        const val TYPE_PAUSED: Int = 3
+        const val TYPE_CANCELED: Int = 4
+    }
+
+    private var isCanceled: Boolean = false
+    private var isPaused: Boolean = false
+
+    private var lastProgress: Int = 0
+
+    override fun doInBackground(vararg params: String?): Int {
+        var file: File? = null
+        var inputStream: InputStream? = null
+        var saveFile: RandomAccessFile? = null
+        try {
+            var downloadedLength = 0L
+            val downloadUrl = params[0]
+            downloadUrl ?: return TYPE_FAILED
+
+            if (TextUtils.isEmpty(fileName)) {
+                fileName = downloadUrl.substring(downloadUrl.lastIndexOf("/"))
+            }
+
+            file = File(dlPath, fileName!!)
+            if (file.exists()) {
+                downloadedLength = file.length()
+            }
+            val contentLength: Long = getContentLength(downloadUrl)
+            if (contentLength == 0L) {
+                return TYPE_FAILED
+            } else if (contentLength == downloadedLength) {
+                // 已经下载的字节数和文件的总字节数相等,说明已经下载完成。
+                return TYPE_SUCCESS
+            }
+            val builder = Request.Builder()
+            headers?.let {
+                for ((key, value) in it)
+                    builder.addHeader(key, value)
+            }
+            val request = builder
+                // 断点下载,指定从那个字节开始下
+                .addHeader("RANGE", "bytes=" + downloadedLength + "-")
+                .url(downloadUrl)
+                .build()
+            val response = OkHttpClient()
+                .newCall(request)
+                .execute()
+
+            inputStream = response.body()!!.byteStream()
+            saveFile = RandomAccessFile(file, "rw")
+            saveFile.seek(downloadedLength) // 跳过已经下载的字节
+            val buff = ByteArray(1024)
+            var total = 0
+            var len = 0;
+            while ({ len = inputStream.read(buff);len }() != -1) {
+                if (isCanceled) {
+                    return TYPE_CANCELED
+                } else if (isPaused) {
+                    return TYPE_PAUSED
+                } else {
+                    total += len
+                    saveFile.write(buff, 0, len)
+                    // 计算已经下载的百分比
+                    val progress: Int = ((total + downloadedLength) * 100 / contentLength).toInt()
+                    publishProgress(progress)
+                }
+            }
+            response.body()!!.close()
+            return TYPE_SUCCESS
+        } catch (e: Exception) {
+            e.printStackTrace()
+        } finally {
+            try {
+                inputStream?.close()
+                saveFile?.close()
+                if (isCanceled) file?.delete()
+            } catch (e: Exception) {
+                e.printStackTrace()
+            }
+        }
+        return TYPE_FAILED
+    }
+
+    override fun onProgressUpdate(vararg values: Int?) {
+        val progress = values[0]
+        progress?.let {
+            if (it > lastProgress) {
+                listener.onProgress(it)
+                lastProgress = it
+            }
+        }
+    }
+
+    override fun onPostExecute(status: Int?) {
+        when (status) {
+            TYPE_SUCCESS -> listener.onSuccess()
+            TYPE_FAILED -> listener.onFailed(null)
+            TYPE_PAUSED -> listener.onPaused()
+            TYPE_CANCELED -> listener.onCanceled()
+            else -> Unit
+        }
+    }
+
+    fun pauseDownload() {
+        this.isPaused = true
+    }
+
+    fun cancelDownload() {
+        this.isCanceled = true
+    }
+
+    private fun getContentLength(downloadUrl: String): Long {
+        val builder = Request.Builder()
+        headers?.let {
+            for ((key, value) in it)
+                builder.addHeader(key, value)
+        }
+        val request = builder.url(downloadUrl).build()
+        val response = OkHttpClient()
+            .newCall(request)
+            .execute()
+        if (response.isSuccessful) {
+            val contentLength = response.body()!!.contentLength()
+            response.close()
+            return contentLength
+        }
+        return 0
+    }
+}

+ 6 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/pojo/AccessRecordBeanList.kt

@@ -0,0 +1,6 @@
+package com.rc.httpcore.pojo
+
+import com.rc.httpcore.vo.response.AccessRecordBean
+
+data class AccessRecordBeanList(val records: List<AccessRecordBean>?,
+                                val total: Int)

+ 9 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/pojo/BannerImageBeanList.kt

@@ -0,0 +1,9 @@
+package com.rc.httpcore.pojo
+
+import com.rc.httpcore.vo.response.BannerImageBean
+import com.rc.httpcore.vo.response.TopicClassifyVo
+
+data class BannerImageBeanList(
+    val records: List<BannerImageBean>?,
+    val total: Int
+)

+ 8 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/pojo/CategoryTreeList.kt

@@ -0,0 +1,8 @@
+package com.rc.httpcore.pojo
+
+import com.rc.httpcore.vo.response.CategoryTree
+
+data class CategoryTreeList(
+    val records: List<CategoryTree>?,
+    val total: Int
+)

+ 6 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/pojo/CertVoList.kt

@@ -0,0 +1,6 @@
+package com.rc.httpcore.pojo
+
+import com.rc.httpcore.vo.response.CertVo
+
+data class CertVoList(val records: List<CertVo>?,
+                      val total: Int)

+ 30 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/pojo/ExamCourseVoList.java

@@ -0,0 +1,30 @@
+package com.rc.httpcore.pojo;
+
+
+import com.rc.httpcore.vo.response.ExamCourseVo;
+
+import java.util.List;
+
+
+public class ExamCourseVoList {
+
+    private List<ExamCourseVo> records;
+    //  var total: Int = 0
+    private int total = 0;
+
+    public List<ExamCourseVo> getRecords() {
+        return records;
+    }
+
+    public void setRecords(List<ExamCourseVo> records) {
+        this.records = records;
+    }
+
+    public int getTotal() {
+        return total;
+    }
+
+    public void setTotal(int total) {
+        this.total = total;
+    }
+}

+ 7 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/pojo/ExamTopicList.kt

@@ -0,0 +1,7 @@
+package com.rc.httpcore.pojo
+
+import com.rc.httpcore.vo.response.ExamTopic
+
+
+data class ExamTopicList(val records: List<ExamTopic>?,
+                         val total: Int)

+ 8 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/pojo/LearnRecordVoList.kt

@@ -0,0 +1,8 @@
+package com.rc.httpcore.pojo
+
+import com.rc.httpcore.vo.response.LearnRecordVo
+
+data class LearnRecordVoList (
+    val records: List<LearnRecordVo>?,
+    val total: Int
+)

+ 7 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/pojo/MockLists.kt

@@ -0,0 +1,7 @@
+package com.rc.httpcore.pojo
+
+
+import com.rc.httpcore.vo.response.MockTestVo
+
+data class MockLists (val records: List<MockTestVo>?,
+                      val total: Int)

+ 11 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/pojo/MockTestVoList.kt

@@ -0,0 +1,11 @@
+package com.rc.httpcore.pojo
+
+
+
+import com.rc.httpcore.vo.response.MockTestVo
+
+data class MockTestVoList(
+    val records: List<MockTestVo>?,
+    val data: List<MockTestVo>?,
+    val total: Int
+)

+ 7 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/pojo/TopicClassifyVoList.kt

@@ -0,0 +1,7 @@
+package com.rc.httpcore.pojo
+
+
+import com.rc.httpcore.vo.response.TopicClassifyVo
+
+data class TopicClassifyVoList(val records: List<TopicClassifyVo>?,
+                               val total: Int)

+ 5 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/CommonDataResponse.kt

@@ -0,0 +1,5 @@
+package com.rc.httpcore.vo
+
+class CommonDataResponse<T> : CommonResponse() {
+    var data: T? = null
+}

+ 7 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/CommonListResponse.kt

@@ -0,0 +1,7 @@
+package com.rc.httpcore.vo
+
+class CommonListResponse<T> : CommonResponse() {
+    var rows: List<T>? = null
+    var data: List<T>? = null
+    var total: Int = 0
+}

+ 18 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/CommonResponse.kt

@@ -0,0 +1,18 @@
+package com.rc.httpcore.vo
+
+open class CommonResponse {
+
+    var code: String = ""
+    var msg: String = ""
+    var message: String = ""
+
+    companion object {
+        const val CODE_SUCCESS = "200"
+        const val CODE_TOKEN_EXPIRED = "401"
+    }
+
+    fun isSuccess() = CODE_SUCCESS == code
+
+    fun isTokenExpired() = CODE_TOKEN_EXPIRED == code
+
+}

+ 46 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/LogInterceptor.java

@@ -0,0 +1,46 @@
+package com.rc.httpcore.vo;
+
+import android.util.Log;
+
+import java.io.IOException;
+
+import okhttp3.FormBody;
+import okhttp3.Interceptor;
+import okhttp3.Request;
+
+public class LogInterceptor implements Interceptor {
+
+    public static String TAG = "HttpClient";
+
+    @Override
+    public okhttp3.Response intercept(Interceptor.Chain chain) throws IOException {
+        Request request = chain.request();
+        long startTime = System.currentTimeMillis();
+        okhttp3.Response response = chain.proceed(chain.request());
+        long endTime = System.currentTimeMillis();
+        long duration=endTime-startTime;
+        okhttp3.MediaType mediaType = response.body().contentType();
+        String content = response.body().string();
+        Log.d(TAG,"\n");
+        Log.d(TAG,"----------Start----------------");
+        Log.d(TAG, "| "+request.toString());
+        String method=request.method();
+        if("POST".equals(method)){
+            StringBuilder sb = new StringBuilder();
+            if (request.body() instanceof FormBody) {
+                FormBody body = (FormBody) request.body();
+                for (int i = 0; i < body.size(); i++) {
+                    sb.append(body.encodedName(i) + "=" + body.encodedValue(i) + ",");
+                }
+                sb.delete(sb.length() - 1, sb.length());
+                Log.d(TAG, "| RequestParams:{"+sb.toString()+"}");
+            }
+        }
+        LogUtil.eLength(TAG,"| Response:" + content);
+//        Log.d(TAG, "| Response:" + content);
+        Log.d(TAG,"----------End:"+duration+"毫秒----------");
+        return response.newBuilder()
+                .body(okhttp3.ResponseBody.create(mediaType, content))
+                .build();
+    }
+}

+ 94 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/LogUtil.java

@@ -0,0 +1,94 @@
+package com.rc.httpcore.vo;
+
+import android.util.Log;
+
+public class LogUtil {
+
+    private LogUtil() {
+        /* cannot be instantiated */
+        throw new UnsupportedOperationException("cannot be instantiated");
+    }
+
+
+    public static boolean isDebug;// 是否需要打印bug,在application的onCreate函数里面初始化
+    private static final String TAG = "HttpClient";
+
+
+    // 下面四个是默认tag的函数
+    public static void i(String msg) {
+        if (isDebug)
+            Log.i(TAG, msg);
+    }
+
+
+    public static void d(String msg) {
+        if (isDebug)
+            Log.d(TAG, msg);
+    }
+
+
+    public static void e(String msg) {
+        if (isDebug)
+            Log.e(TAG, msg);
+    }
+
+
+    public static void v(String msg) {
+        if (isDebug)
+            Log.v(TAG, msg);
+    }
+
+
+    // 下面是传入自定义tag的函数
+    public static void i(String tag, String msg) {
+        if (isDebug)
+            Log.i(tag, msg);
+    }
+
+
+    public static void d(String tag, String msg) {
+        if (isDebug)
+            Log.i(tag, msg);
+    }
+
+
+    public static void e(String tag, String msg) {
+        if (isDebug)
+            Log.i(tag, msg);
+    }
+
+
+    public static void v(String tag, String msg) {
+        if (isDebug)
+            Log.i(tag, msg);
+    }
+
+
+    /**
+     * 截断输出日志
+     *
+     * @param msg
+     */
+    public static void eLength(String tag, String msg) {
+
+        if (tag == null || tag.length() == 0
+                || msg == null || msg.length() == 0)
+            return;
+
+
+        int segmentSize = 3 * 1024;
+        long length = msg.length();
+        if (length <= segmentSize) {// 长度小于等于限制直接打印
+            Log.e(tag, msg);
+        } else {
+            while (msg.length() > segmentSize) {// 循环分段打印日志
+                String logContent = msg.substring(0, segmentSize);
+                msg = msg.replace(logContent, "");
+                Log.e(tag, logContent);
+            }
+            Log.e(tag, msg);// 打印剩余日志
+        }
+
+    }
+
+}

+ 16 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/request/AccessTokenReq.java

@@ -0,0 +1,16 @@
+package com.rc.httpcore.vo.request;
+
+/**
+ * info
+ *
+ * @author ReiChin_
+ */
+public class AccessTokenReq {
+
+    public String username;
+    public String password;
+
+//    Pair("username", "onecUser"),
+//    Pair("password", "admin123"))
+
+}

+ 14 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/request/AssessRecordReq.java

@@ -0,0 +1,14 @@
+package com.rc.httpcore.vo.request;
+
+/**
+ * info
+ *
+ * @author ReiChin_
+ */
+public class AssessRecordReq {
+
+    public String chapterId; // 章节ID
+    public int page;
+    public int pageSize;
+
+}

+ 16 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/request/CourseListReq.java

@@ -0,0 +1,16 @@
+package com.rc.httpcore.vo.request;
+
+/**
+ * info
+ *
+ * @author ReiChin_
+ */
+public class CourseListReq {
+
+    public String cateId; // 分类ID
+    public String title; // 标题
+    public int pageNum;
+    public int pageSize;
+    public String scopeType; // 适用范围(1. 安全准入考试,2. 负面清单考试,3. 黑名单考试)
+
+}

+ 20 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/request/ExamAnswerReq.java

@@ -0,0 +1,20 @@
+package com.rc.httpcore.vo.request;
+
+import java.util.List;
+
+/**
+ * info
+ *
+ * @author ReiChin_
+ */
+public class ExamAnswerReq {
+
+    public String paperId; // 试卷ID
+    public String quId; // 题目ID
+    public List<Answer> elPaperQuAnswerList; // 用户答案
+
+    public static class Answer {
+        public String id; // 选项id
+    }
+
+}

+ 13 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/request/ExamLearnReq.java

@@ -0,0 +1,13 @@
+package com.rc.httpcore.vo.request;
+
+/**
+ * info
+ *
+ * @author ReiChin_
+ */
+public class ExamLearnReq {
+
+    public String chapterId; // 章节id
+    public String courseId; // 课程id
+
+}

+ 15 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/request/ExamScoreReq.java

@@ -0,0 +1,15 @@
+package com.rc.httpcore.vo.request;
+
+/**
+ * info
+ *
+ * @author ReiChin_
+ */
+public class ExamScoreReq {
+
+    public String scopeType;
+
+    public int page;
+    public int pageSize;
+
+}

+ 13 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/request/FaceCompareReq.java

@@ -0,0 +1,13 @@
+package com.rc.httpcore.vo.request;
+
+/**
+ * info
+ *
+ * @author ReiChin_
+ */
+public class FaceCompareReq {
+
+    public byte[] data; // 特征码
+    public String userId;
+
+}

+ 12 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/request/HandPaperReq.java

@@ -0,0 +1,12 @@
+package com.rc.httpcore.vo.request;
+
+/**
+ * info
+ *
+ * @author ReiChin_
+ */
+public class HandPaperReq {
+
+    public String id; // 试卷id
+
+}

+ 12 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/request/HandPractiseReq.java

@@ -0,0 +1,12 @@
+package com.rc.httpcore.vo.request;
+
+/**
+ * info
+ *
+ * @author ReiChin_
+ */
+public class HandPractiseReq {
+
+    public String id;
+
+}

+ 17 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/request/LearnLoginReq.java

@@ -0,0 +1,17 @@
+package com.rc.httpcore.vo.request;
+
+/**
+ * info
+ *
+ * @author ReiChin_
+ */
+public class LearnLoginReq {
+
+    public String id;
+    public String userName;
+    public String type; // 登录类型,1 表示第一次调用获取信息,2 人脸验证之后调用 实现真正的登录
+    public String machineCode; // 设备码
+    public int aioType = 3; // 4-化学品终端 3-学习一体机
+    public String isLogin = "0"; //0 未登录   二次登陆 1
+
+}

+ 20 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/request/LearnRecordReq.java

@@ -0,0 +1,20 @@
+package com.rc.httpcore.vo.request;
+
+/**
+ * info
+ *
+ * @author ReiChin_
+ */
+public class LearnRecordReq {
+
+    public String cateId; // 分类ID
+    public String cateTitle; // 分类标题
+    public String courseId; // 课程ID
+    public String courseTitle; // 课程标题
+    public String scopeType; // 适用范围(1. 安全准入考试,2. 负面清单考试,3. 黑名单考试)
+
+//    public int pageNum;
+    public int page;
+    public int pageSize;
+
+}

+ 20 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/request/MockAnswerReq.java

@@ -0,0 +1,20 @@
+package com.rc.httpcore.vo.request;
+
+import java.util.List;
+
+/**
+ * info
+ *
+ * @author ReiChin_
+ */
+public class MockAnswerReq {
+
+    public String practiseId; // 练习ID
+    public String quId; // 题目ID
+    public List<Answer> answerList; // 用户答案
+
+    public static class Answer {
+        public String id; // 选项id
+    }
+
+}

+ 14 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/request/MockTestReq.java

@@ -0,0 +1,14 @@
+package com.rc.httpcore.vo.request;
+
+/**
+ * info
+ *
+ * @author ReiChin_
+ */
+public class MockTestReq {
+
+    public int pageNum;
+    public int pageSize;
+    public String classifyIds;
+
+}

+ 14 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/request/PaperQuReq.java

@@ -0,0 +1,14 @@
+package com.rc.httpcore.vo.request;
+
+/**
+ * info
+ *
+ * @author ReiChin_
+ */
+public class PaperQuReq {
+
+    public String paperId; // 试卷ID
+    public String practiseId; // 练习ID
+    public String quId; // 题目ID
+
+}

+ 16 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/request/ViolationReq.java

@@ -0,0 +1,16 @@
+package com.rc.httpcore.vo.request;
+
+/**
+ * info
+ *
+ * @author ReiChin_
+ */
+public class ViolationReq {
+
+    public int pageNum;
+    public int pageSize;
+    public String statTime; // yyyy-MM-dd
+    public String endTime; // yyyy-MM-dd
+    public String overStatus;
+
+}

+ 22 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/response/AccessRecordBean.java

@@ -0,0 +1,22 @@
+package com.rc.httpcore.vo.response;
+
+/**
+ * info
+ *
+ * @author ReiChin_
+ */
+public class AccessRecordBean {
+
+    public String paperId; // 试卷ID
+    public String assessName; // 考核名称
+    public String assessTime; // 考核时间 yyyy-MM-dd HH:mm:ss
+    public String allQu; // 全部题数
+    public String radioQu; // 单选题数
+    public String multiQu; // 多选题数
+    public String judgeQu; // 判断题数
+    public String accuracy; // 考核正确率,100 表示通过,其余都不通过
+    public String points; // 获得奖励分
+    public String errors; // 错误数
+    public String correctly; // 正确数
+
+}

+ 14 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/response/AccessTokenResp.java

@@ -0,0 +1,14 @@
+package com.rc.httpcore.vo.response;
+
+/**
+ * info
+ *
+ * @author ReiChin_
+ */
+public class AccessTokenResp {
+
+    public String access_token;
+    public String user_id;
+    public String expires_in;
+    public String username;
+}

+ 20 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/response/ApkInfoResp.java

@@ -0,0 +1,20 @@
+package com.rc.httpcore.vo.response;
+
+/**
+ * info
+ *
+ * @author ReiChin_
+ */
+public class ApkInfoResp {
+
+
+    //备份路径
+    public String remark;
+    //升级的url
+    public String url;
+    //升级版本号
+    public String version;
+    //当前设备类型  3 化学品终端
+    public String type;
+
+}

+ 12 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/response/BannerImageBean.java

@@ -0,0 +1,12 @@
+package com.rc.httpcore.vo.response;
+
+/**
+ * info
+ *
+ * @author ReiChin_
+ */
+public class BannerImageBean {
+
+    public String imgUrl;
+
+}

+ 23 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/response/CategoryTree.java

@@ -0,0 +1,23 @@
+package com.rc.httpcore.vo.response;
+
+import java.util.List;
+
+/**
+ * info
+ *
+ * @author ReiChin_
+ */
+public class CategoryTree {
+
+    public String userId;
+    public String id;
+    public String title;
+    public String parentId;
+    public String level;
+    public List<CategoryTree> children;
+
+    @Override
+    public String toString() {
+        return title;
+    }
+}

+ 28 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/response/CertVo.java

@@ -0,0 +1,28 @@
+package com.rc.httpcore.vo.response;
+
+/**
+ * info
+ *
+ * @author ReiChin_
+ */
+public class CertVo {
+
+    public String createBy;
+    public String createTime;
+    public String updateBy;
+    public String updateTime;
+    public String deptId;
+    public String deptName;
+    public String userId;
+    public String id;
+    public String joinUserId; // 用户id
+    public String userNickName;
+    public String userName;
+    public String examId; //关联考试id
+    public String certId; // 关联证书id
+    public String expirationTime; // 过期时间
+    public String certTitle; // 证书名称
+    public String certUrl; // 证书地址
+    public String code;
+
+}

+ 171 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/response/ExamCourseVo.java

@@ -0,0 +1,171 @@
+package com.rc.httpcore.vo.response;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * info
+ *
+ * @author ReiChin_
+ */
+public class ExamCourseVo implements Serializable {
+
+    public String allCateTitle; // 课程分类(级联)
+    public String title; // 课程名称
+    public String cateTitle; // 分类标题
+    public String chapters; // 章节数
+    public String accessNum; // 访问人数
+    public String createTime; // 创建时间
+    public String duration; // 课程时长
+    public String durationStr;
+    public String durations; // 课程总时长
+//    public String durationsStr;
+
+
+    public long learnDurations; //  用户学习总时长
+    public String learnDurationStr;
+
+    public String learns; // 学习人数
+    public String type; // 课程类型
+    public String id;
+
+    public String updateTime; // 更新时间
+    public String remark;
+    public String deptId; // 题目ID
+    public String deptName; // 部门名称
+    public String userId; // 用户ID(数据权限)
+    public String cateId; // 分类ID
+    public String pri; // 是否公开
+    public String level; // 等级
+    public String orderRest; // 是否限制顺序
+    public String label; // 课程标签
+    public String effectiveTime; // PDF翻页有效时间
+    public String ceType; // 课程有效时间类型
+    public String ceTime; // 课程有效时间
+    public String keywords; // 课程关键字
+    public String img; // 封面图片
+    public String description; // 课程简介
+    public String status; // 课程状态 0 下架,1 上架
+    public String videoDraggable; // 是否显示课程拖拽进度 0 不可拖拽
+    public String points; // 积分值
+    public String machinePoints; // 学习机积分值
+    public String settings; // 设置选项
+    public List<ChapterVo> chapterList;
+    public String scopeType; // 适用范围(1. 安全准入考试,2. 负面清单考试,3. 黑名单考试)
+    public String learnStatus; //  0 学习中,2 未学习,3 待考核,1 已完成
+
+    public String decodeScopeType() {
+        if (null == scopeType || scopeType.isEmpty()) {
+            return "";
+        }
+        String[] array = scopeType.split(",");
+        StringBuilder builder = new StringBuilder();
+        for (String type : array) {
+            builder.append(decodeScopeType(type)).append("、");
+        }
+        if (builder.length() > 0) {
+            builder.deleteCharAt(builder.length() - 1);
+        }
+        return builder.toString();
+    }
+
+    private String decodeScopeType(String type) {
+        if ("1".equals(type)) {
+            return "安全准入考试";
+        } else if ("2".equals(type)) {
+            return "负面清单考试";
+        } else if ("3".equals(type)) {
+            return "黑名单考试";
+        }
+        return "";
+    }
+
+    public String decodeLearnStatus() {
+        if ("0".equals(learnStatus)) {
+            return "学习中";
+        } else if ("1".equals(learnStatus)) {
+            return "已完成";
+        } else if ("2".equals(learnStatus)) {
+            return "未学习";
+        } else if ("3".equals(learnStatus)) {
+            return "待考核";
+        }
+        return "";
+    }
+
+    public static class ChapterVo implements Serializable {
+        public String createBy;
+        public String createTime; // 创建时间
+        public String updateBy;
+        public String updateTime; // 更新时间
+        public String remark;
+        public String deptId; // 题目ID
+        public String deptName; // 部门名称
+        public String userId; // 用户ID(数据权限)
+        public String id; // 章节ID
+        public String courseId; // 课程ID
+        public String title; // 标题
+        public String parentId; // 父级ID
+        public String position;
+        public String resourcesId; // 关联资源ID
+        public String chapterData; // 资源地址或上传的地址
+        public String description; // 简介
+        public String free; // 是否免费试读章节,0为收费,1为免费
+        public String type; // 0 为目录 其他类型同课件类型 文件类型 1 文档,2 视频,3 图片,4 音频,5 富文本
+        public String postfix; // 文件后缀
+        public String allowdownload; // 允许下载资源
+        public long duration; // 课件时长
+        public String durationStr; // 课件时长
+        public String learnStatus; // 学习状态 0 学习中,2 未学习,1 已完成
+        public String hasChildren;
+        public List<ChapterVo> children;
+
+        public String isAssess; // 是否考核(0 否,1 是)
+        public String assessName; // 考核名称
+        public String assessStatus; // 考核状态 0 未完成,1 已完成
+        public int hasAssess; // 考核次数
+
+        /**
+         * 该章节是否学习和考核都完成
+         */
+        public boolean chapterCompleted() {
+            // 学习未完成
+            if (!"1".equals(learnStatus)) return false;
+            // 配置了考核,但是考核未通过
+            if ("1".equals(isAssess) && !"1".equals(assessStatus)) return false;
+            return true;
+        }
+
+        /**
+         * 是否显示课后考核,①配置了课后考核;②并且课后考后未通过
+         */
+        public boolean showClassTest() {
+            // 没有配置 >> 不显示
+            if (!"1".equals(isAssess)) return false;
+            // 有配置,考核通过 >> 不显示
+            if ("1".equals(assessStatus)) return false;
+            return true;
+        }
+
+        /**
+         * 是否显示考试记录,①配置了考试;②考试次数大于0
+         */
+        public boolean showClassTestRecord() {
+            return "1".equals(isAssess) && hasAssess > 0;
+        }
+
+        /**
+         * 0 学习中,2 未学习,3 待考核,1 已完成
+         */
+        public int formatLearnStatus() {
+            if ("0".equals(learnStatus)) return 0; // 学习中
+            if ("1".equals(learnStatus)) {
+                // 待考核(考核未完成):配置了考核,但是考核未通过
+                boolean isAssessing = "1".equals(isAssess) && !"1".equals(assessStatus);
+                return isAssessing ? 3 : 1;
+            } else return 2; // 未学习
+        }
+
+    }
+
+}

+ 115 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/response/ExamTopic.java

@@ -0,0 +1,115 @@
+package com.rc.httpcore.vo.response;
+
+import java.util.List;
+
+/**
+ * info
+ *
+ * @author ReiChin_
+ */
+public class ExamTopic {
+
+    public String id;
+    public String createTime; // 创建时间
+    public String updateTime; // 更新时间
+    public String deptId; // 题目ID
+    public String deptName; // 部门名称
+    public String examId; // 考试ID
+    public String joinUserId; // 用户ID
+    public String limitTime; // 截止时间
+    public String objScore; // 客观得分
+    public String qualifyScore; // 及格分
+    public String state; // 试卷状态
+    public String subjScore; // 主观分
+    public String title; // 考试标题
+    public String totalScore; // 试卷总分
+    public int totalTime; // 考试时长
+    public String userId; // 用户ID(数据权限)
+    public String userTime; // 用户时长
+    public String userTimeStr;
+    public String userScore; // 用户得分
+    public String violationId; // 违规id
+    public String scopeType;
+    public String hasSaq;
+    public String classifyNames; // 分类名称 多个以逗号分隔
+
+    public List<ElPaperQu> elPaperQuList; // 试卷考题信息
+
+    public ExamConfig examConfig;
+    public OtherData otherData;
+
+    public List<ElPaperQu> radioList; // 单选题列表
+    public List<ElPaperQu> multiList; // 多选题列表
+    public List<ElPaperQu> judgeList; // 判断题
+
+    public static class ElPaperQu {
+        public int currentNum; // 当前题目编号 for android local
+        public int topicTotal; // 总题目数 for android local
+
+        public String actualScore; // 实际得分
+        public String analysis; // 整题题解
+        public String answer;
+        public boolean answered; // 是否已答 0 未答,1 已答
+        public String content; // 题目问题
+        public String createBy;
+        public String createTime; // 创建时间
+        public String updateBy;
+        public String updateTime; // 更新时间
+        public String userId; // 用户ID(数据权限)
+        public String deptId; // 题目ID
+        public String deptName; // 部门名称
+        public boolean isRight; // 是否答对
+        public String id;
+        public String paperId; // 试卷ID
+        public String practiseId; // 练习ID
+        public String quId; // 题目ID
+        public String quType; // 题目类型
+        public String sort; // 问题排序
+        public String score; // 单题分分值
+        public List<ElPaperQuAnswer> elPaperQuAnswerList; // 试卷考题答案【考试用】
+        public List<ElPaperQuAnswer> answerList; // 试卷考题答案【练习用】
+    }
+
+    public static class ElPaperQuAnswer {
+        public String abc; // 选项标签
+        public String answerId; // 回答项ID
+//        public String checked; // 是否选中
+        public boolean checked; // 是否选中
+        public String content; // 选项描述
+        public String createBy;
+        public String createTime; // 创建时间
+        public String deptId; // 题目ID
+        public String deptName; // 部门名称
+        public String id;
+//        public String isRight; // 是否正确项
+        public boolean isRight; // 是否正确项
+        public String paperId; // 试卷ID
+        public String practiseId; // 练习ID
+        public String quId; // 题目ID
+        public String sort; // 排序
+        public String updateBy;
+        public String updateTime; // 更新时间
+        public String userId; // 用户ID(数据权限)
+        public String analysis; // 整题题解
+    }
+
+    public static class ExamConfig {
+        public int radioSumCount; // 单选题数量
+        public int radioSumScore; // 单选题总分
+        public int multiSumCount; // 多选题数量
+        public int multiSumScore; // 多选题总分
+        public int judgeSumCount; // 判断题数量
+        public int judgeSumScore; // 判断题总分
+    }
+
+    public static class OtherData {
+        public String allQu; // 题目总数
+        public String radioQu; // 单选题数
+        public String multiQu; // 多选题数
+        public String judgeQu; // 判断题数
+        public String accuracy; // 正确率,100表示通过,其余都不通过
+        public String title; // 章节名称
+        public String assessName; // 考核名称
+    }
+
+}

+ 16 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/response/ExamVerify.java

@@ -0,0 +1,16 @@
+package com.rc.httpcore.vo.response;
+
+/**
+ * info
+ *
+ * @author ReiChin_
+ */
+public class ExamVerify {
+
+//    public int preFaceVerify; // 考前是否人脸验证 0 否,1 是
+    public boolean preFaceVerify; // 考前是否人脸验证 0 否,1 是
+//    public int endFaceVerify; // 考后是否人脸验证 0 否,1 是
+    public boolean endFaceVerify; // 考后是否人脸验证 0 否,1 是
+    public String examId;
+
+}

+ 22 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/response/HandPaperBean.java

@@ -0,0 +1,22 @@
+package com.rc.httpcore.vo.response;
+
+/**
+ * info
+ *
+ * @author ReiChin_
+ */
+public class HandPaperBean {
+
+    public String userCertUrl; // 证书地址
+    public String correctly; // 正确数
+    public String answered="0"; // 已答数
+    public String userTime; // 考试用时
+    public String unAnswered="0"; // 未答数
+    public String userScore; // 考试得分
+    public String errors="0"; // 错误数
+    public boolean passed; // 合格 / 不合格
+
+    public String examType; // 考试类型
+    public String paperId; // 试卷ID
+
+}

+ 20 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/response/HandPractiseBean.java

@@ -0,0 +1,20 @@
+package com.rc.httpcore.vo.response;
+
+/**
+ * info
+ *
+ * @author ReiChin_
+ */
+public class HandPractiseBean {
+
+    public String unAnswered; // 未答数
+    public String answered; // 已答数
+    public String errors; // 错误数
+    public String correctly; // 正确数
+    public String points; // 获得积分
+    public String accuracy; // 正确率
+
+    public boolean passed; // 合格 / 不合格
+    public String paperId; // 试卷ID
+
+}

+ 14 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/response/LearnBonusBean.java

@@ -0,0 +1,14 @@
+package com.rc.httpcore.vo.response;
+
+/**
+ * info
+ *
+ * @author ReiChin_
+ */
+public class LearnBonusBean {
+
+    public String points; // 获得积分
+    public String durations; // 学习时长
+    public String durationStr;
+
+}

+ 29 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/response/LearnLoginVo.java

@@ -0,0 +1,29 @@
+package com.rc.httpcore.vo.response;
+
+/**
+ * info
+ *
+ * @author ReiChin_
+ */
+public class LearnLoginVo {
+
+    public String access_token;
+    public String token;
+    public String user_id;
+    public String userId;
+    public String type;
+    public String expires_in;  // msg
+    public String username;
+    public String userName;
+    public String nickName; // 用户姓名
+
+    public String isLogin = "0"; //0 未登录   二次登陆 1
+
+
+    public String  cardNum;
+    public String  deptId;
+    public String  userType;
+    public String  mobile;
+    public String  account;
+
+}

+ 63 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/response/LearnRecordVo.java

@@ -0,0 +1,63 @@
+package com.rc.httpcore.vo.response;
+
+/**
+ * info
+ *
+ * @author ReiChin_
+ */
+public class LearnRecordVo {
+
+    public String cateId; // 分类ID
+    public String cateTitle; // 分类标题
+    public String courseId; // 课程ID
+    public String courseTitle; // 课程标题
+    public String title; // 课程标题
+    public String learnTime; // 学习时间
+    public String learnDuration; // 学习时长
+    public String learnDurationStr;
+    public String bonusPoints; // 获得奖励分
+    public String joinUserId;// ": 22
+
+    public String scopeType; // 适用范围(1. 安全准入考试,2. 负面清单考试,3. 黑名单考试)
+    public String learnStatus; //  0 学习中,2 未学习,3 待考核,1 已完成
+
+    public String decodeScopeType() {
+        if (null == scopeType || scopeType.isEmpty()) {
+            return "";
+        }
+        String[] array = scopeType.split(",");
+        StringBuilder builder = new StringBuilder();
+        for (String type : array) {
+            builder.append(decodeScopeType(type)).append("、");
+        }
+        if (builder.length() > 0) {
+            builder.deleteCharAt(builder.length() -1);
+        }
+        return builder.toString();
+    }
+
+    private String decodeScopeType(String type) {
+        if ("1".equals(type)) {
+            return "安全准入考试";
+        } else if ("2".equals(type)) {
+            return "负面清单考试";
+        } else if ("3".equals(type)) {
+            return "黑名单考试";
+        }
+        return "";
+    }
+
+    public String decodeLearnStatus() {
+        if ("0".equals(learnStatus)) {
+            return "学习中";
+        } else if ("1".equals(learnStatus)) {
+            return "已完成";
+        } else if ("2".equals(learnStatus)) {
+            return "未学习";
+        } else if ("3".equals(learnStatus)) {
+            return "待考核";
+        }
+        return "";
+    }
+
+}

+ 50 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/response/MockTestVo.java

@@ -0,0 +1,50 @@
+package com.rc.httpcore.vo.response;
+
+/**
+ * info
+ *
+ * @author ReiChin_
+ */
+public class MockTestVo {
+
+    public String id;
+    public String title; // 考试名称
+
+    public String classifyNames; // 分类名称 多个以逗号分隔
+
+    public String updateTime; // 更新时间
+    public String totalScore; // 总分数
+    public String userId; // 用户ID(数据权限)
+    public String timeLimit; // 是否限时
+    public String state; // 考试状态 1 正常 0 下架
+    public String startTime; // 开始时间
+    public String scopeType; // 适用范围(1. 安全准入考试,2. 负面清单考试,3. 黑名单考试,4.模拟考试)
+
+    public String openType; // 1公开2部门3定员
+    public String joinType; // 组题方式1题库,2指定
+    public String level; // 难度:1中等,2较难,3难
+    public String preFaceVerify; // 考前是否人脸验证 0 否,1 是
+    public String qualifyScore; // 及格分数
+
+    public String endFaceVerify; // 考后是否人脸验证 0 否,1 是
+    public String endTime; // 结束时间
+
+    public String deptId; // 题目ID
+    public String deptName; // 部门名称
+//    public List<String> deptIds; // 考试部门列表
+
+    public String createBy;
+    public String createTime; // 创建时间
+    public String updateBy;
+    public String remark;
+
+    public String certId; // 关联证书id
+    public String code; // 学科类别
+    public String content; // 考试描述
+    public String conversion; // 积分换算值(1 积分=多少试卷分数)
+    public String totalTime; // 总时长(分钟)
+    public String classifyIds; // 分类ID多个以逗号分隔
+    public String courseIds; // 资源ID多个以逗号分隔
+//            public String repoList;
+//            public String examCerts;
+}

+ 14 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/response/TopicClassifyVo.java

@@ -0,0 +1,14 @@
+package com.rc.httpcore.vo.response;
+
+/**
+ * info
+ *
+ * @author ReiChin_
+ */
+public class TopicClassifyVo {
+
+    public String userId;
+    public String id;
+    public String title;
+
+}

+ 25 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/response/UserInfo.java

@@ -0,0 +1,25 @@
+package com.rc.httpcore.vo.response;
+
+/**
+ * info
+ *
+ * @author ReiChin_
+ */
+public class UserInfo {
+
+    public String userId;
+    public String avatar; // 头像
+    public String userName; // 姓名
+    public String joinUserName; // 姓名
+    public String deptName;
+    public String status;
+    public String violatioNum; // 违规次数
+//    public String negativeListCount; // 负面清单次数
+    public String negativeListNum; // 负面清单次数
+    public String blackListNum; // 黑名单次数
+//    public String totalPoints; // 积分
+    public String creditScore; // 积分
+    public String bonusPoints; // 奖励分
+    public String studyLen; // 学习时长统计
+
+}

+ 22 - 0
HttpCoreLibrary/src/main/java/com/rc/httpcore/vo/response/ViolationBean.java

@@ -0,0 +1,22 @@
+package com.rc.httpcore.vo.response;
+
+import java.util.List;
+
+/**
+ * info
+ *
+ * @author ReiChin_
+ */
+public class ViolationBean {
+
+    public String createTime;
+    public String remake; // 违规描述
+    public List<Item> violationList;
+
+    public static class Item {
+        public String type; // 类型,1:警告;2:约谈;3:学习;4:练习;5:考试
+        public String content; // 违规行为描述
+        public String status; // 处理状态,0:未完成;1已完成
+    }
+
+}

+ 8 - 0
LICENSE

@@ -0,0 +1,8 @@
+MIT License
+Copyright (c) <year> <copyright holders>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 3 - 0
README.md

@@ -0,0 +1,3 @@
+# lab-android
+
+学习一体机  V3 西北农林

+ 1 - 0
RcCore/.gitignore

@@ -0,0 +1 @@
+/build

+ 53 - 0
RcCore/build.gradle

@@ -0,0 +1,53 @@
+plugins {
+    id 'com.android.library'
+    id 'kotlin-android'
+}
+
+android {
+    compileSdkVersion env.compileSdkVersion
+    buildToolsVersion env.buildToolsVersion
+
+    defaultConfig {
+        minSdkVersion env.minSdkVersion
+        targetSdkVersion env.targetSdkVersion
+        versionCode 1
+        versionName "1.0"
+
+        consumerProguardFiles "consumer-rules.pro"
+    }
+
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+        }
+    }
+    compileOptions {
+        sourceCompatibility env.jdk_version
+        targetCompatibility env.jdk_version
+    }
+    kotlinOptions {
+        jvmTarget = '1.8'
+    }
+    buildFeatures{
+        viewBinding = true
+    }
+
+}
+
+dependencies {
+
+    implementation project(':HttpCoreLibrary')
+
+    api fileTree(dir: "libs", include: ["*.jar"])
+    implementation dep.kotlinStdlib
+    implementation dep.androidxCoreKtx
+    implementation dep.androidxSwipeRefreshLayout
+
+    implementation dep.androidxAppCompat
+    implementation dep.androidMaterial
+    implementation dep.RecyclerViewAdapterHelper
+
+    implementation dep.eventbus
+
+}

+ 0 - 0
RcCore/consumer-rules.pro


BIN
RcCore/libs/tbs_sdk_v4.3.0.165_20210628_103707.jar


+ 21 - 0
RcCore/proguard-rules.pro

@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile

+ 5 - 0
RcCore/src/main/AndroidManifest.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.rc.core">
+
+</manifest>

+ 8 - 0
RcCore/src/main/java/com/rc/core/event/RefreshEvent.kt

@@ -0,0 +1,8 @@
+package com.rc.core.event
+
+/**
+ * info
+ *
+ * @author ReiChin_
+ */
+data class RefreshEvent(var refresh: Boolean = true, var flag: String)

+ 27 - 0
RcCore/src/main/java/com/rc/core/log/RcLog.kt

@@ -0,0 +1,27 @@
+package com.rc.core.log
+
+import java.util.logging.Level
+import java.util.logging.Logger
+
+/**
+ * info
+ *
+ * @author ReiChin_
+ */
+object RcLog {
+
+    private val logger = Logger.getLogger(RcLog::class.java.name)
+
+    fun warn(message: String, throwable: Throwable? = null) {
+        printLog(Level.WARNING, message, throwable)
+    }
+
+    fun info(message: String, throwable: Throwable? = null) {
+        printLog(Level.INFO, message, throwable)
+    }
+
+    private fun printLog(level: Level, message: String, throwable: Throwable? = null) {
+        logger.log(level, message, throwable)
+    }
+
+}

+ 71 - 0
RcCore/src/main/java/com/rc/core/ui/ActivityCollector.kt

@@ -0,0 +1,71 @@
+package com.rc.core.ui
+
+import android.app.Activity
+import java.util.ArrayList
+
+/**
+ * info
+ *
+ * @author ReiChin_
+ */
+object ActivityCollector {
+
+    /**
+     * 存放应用已开启的所有Activity.
+     */
+    private val mActivities by lazy { ArrayList<Activity>() }
+
+    /**
+     * 添加一个Activity到Activity列表中.
+     *
+     * @param activity
+     * : Activity实例.
+     */
+    fun addActivity(activity: Activity) {
+        mActivities.add(activity)
+    }
+
+    /**
+     * 在Activity列表中,从顶部删除一定数量的Activity.
+     * -1表示删除所有的Activity, 如果count大于Activity列表的size, 将删除所有的Activity.
+     *
+     * @param count
+     * : 要删的Activity数量.
+     */
+    fun removeActivity(count: Int) {
+        val listSize = mActivities.size
+        val delCount = if (-1 == count || count > listSize) listSize else count
+        for (i in 0 until delCount) {
+            val activity = mActivities[listSize - i - 1]
+            if (!activity.isFinishing) {
+                activity.finish()
+            }
+            mActivities.remove(activity)
+        }
+    }
+
+    /**
+     * 从Activity列表中删除指定的Activity.
+     *
+     * @param activity
+     * : 要删的Activity.
+     */
+    fun removeActivity(activity: Activity) {
+        if (mActivities.contains(activity)) {
+            mActivities.remove(activity)
+        }
+    }
+
+    /**
+     * 关闭所有的Activity, 退出程序.
+     */
+    fun finishAll() {
+        for (activity in mActivities) {
+            if (!activity.isFinishing) {
+                activity.finish()
+            }
+        }
+        mActivities.clear()
+    }
+
+}

+ 106 - 0
RcCore/src/main/java/com/rc/core/ui/activity/RcBaseActivity.kt

@@ -0,0 +1,106 @@
+package com.rc.core.ui.activity
+
+import android.graphics.Color
+import android.os.Build
+import android.os.Bundle
+import android.view.View
+import androidx.annotation.ColorInt
+import androidx.appcompat.app.AppCompatActivity
+import androidx.recyclerview.widget.RecyclerView
+import androidx.viewbinding.ViewBinding
+import com.rc.core.ui.ActivityCollector
+import com.rc.core.ui.common.AbsUIDelegate
+import com.rc.core.ui.common.IUIListener
+import com.rc.core.util.ScreenAdapter
+import io.reactivex.disposables.Disposable
+
+/**
+ * info
+ *
+ * @author ReiChin_
+ */
+abstract class RcBaseActivity<VB : ViewBinding> : AppCompatActivity(), IUIListener {
+
+    private lateinit var _viewBinding: VB
+
+    protected val viewBinding get() = _viewBinding
+
+    private lateinit var mUIDelegate: AbsUIDelegate
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        ActivityCollector.addActivity(this)
+        ScreenAdapter.setCustomDensity(this)
+        mUIDelegate = AbsUIDelegate.create()
+        configImmersiveMode()
+        beforeSetContentView()
+        _viewBinding = createViewBinding()
+        setContentView(_viewBinding.root)
+        initViews(savedInstanceState)
+        initListener()
+        initData()
+    }
+
+    override fun onResume() {
+        super.onResume()
+        ScreenAdapter.setCustomDensity(this)
+    }
+
+    protected abstract fun createViewBinding(): VB
+
+    protected open fun configImmersiveMode() {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+                window.decorView.systemUiVisibility =
+                    View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
+            }
+            window.statusBarColor = statusBarColor()
+        }
+    }
+
+    @ColorInt
+    protected open fun statusBarColor(): Int = Color.WHITE
+
+    protected open fun beforeSetContentView() {
+    }
+
+    protected open fun initViews(savedInstanceState: Bundle?) {
+    }
+
+    protected open fun initListener() {
+    }
+
+    protected open fun initData() {
+    }
+
+    override fun onDestroy() {
+        super.onDestroy()
+        mUIDelegate.clearDisposable()
+        ActivityCollector.removeActivity(this)
+    }
+
+    override fun showLoading(message: String?, cancelable: Boolean) {
+        mUIDelegate.showLoading(this, message, cancelable)
+    }
+
+    override fun dismissLoading() {
+        mUIDelegate.dismissLoading()
+    }
+
+    override fun showToast(message: String) {
+        mUIDelegate.showToast(this, message)
+    }
+
+    override fun showNetError(throwable: Throwable) {
+        mUIDelegate.showNetError(this, throwable)
+    }
+
+    override fun addDisposable(disposable: Disposable) {
+        mUIDelegate.addDisposable(disposable)
+    }
+
+    override fun createItemDecoration(): RecyclerView.ItemDecoration? {
+        return mUIDelegate.createItemDecoration(this)
+    }
+
+}

+ 235 - 0
RcCore/src/main/java/com/rc/core/ui/activity/RcRefreshActivity.kt

@@ -0,0 +1,235 @@
+package com.rc.core.ui.activity
+
+import android.os.Bundle
+import android.view.View
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
+import androidx.viewbinding.ViewBinding
+import com.chad.library.adapter.base.BaseQuickAdapter
+import com.chad.library.adapter.base.listener.OnItemChildClickListener
+import com.chad.library.adapter.base.listener.OnItemClickListener
+import com.chad.library.adapter.base.listener.OnLoadMoreListener
+import com.chad.library.adapter.base.viewholder.BaseViewHolder
+import com.rc.core.R
+import com.rc.core.event.RefreshEvent
+import com.rc.core.ui.widget.MultipleStatusView
+import com.rc.core.util.net.NetConnectedListener
+import com.rc.core.util.net.NetWatchdog
+import io.reactivex.disposables.Disposable
+import org.greenrobot.eventbus.EventBus
+import org.greenrobot.eventbus.Subscribe
+import org.greenrobot.eventbus.ThreadMode
+
+/**
+ * info
+ *
+ * @author ReiChin_
+ */
+abstract class RcRefreshActivity<T, VB : ViewBinding>
+constructor(val mAdapter: BaseQuickAdapter<T, BaseViewHolder>) : RcBaseActivity<VB>(),
+    OnLoadMoreListener,
+    OnItemClickListener,
+    OnItemChildClickListener {
+
+    companion object {
+        const val FIRST_PAGE = 1
+        const val PAGE_SIZE = 15
+    }
+
+    protected abstract val mMultipleStatusView: MultipleStatusView?
+    protected abstract val mSrlRefresh: SwipeRefreshLayout
+    protected abstract val mRvContent: RecyclerView
+
+    protected var mCurrentPage = FIRST_PAGE
+    protected var mRefresh = false
+
+    private val mNetWatchdog: NetWatchdog by lazy { NetWatchdog(this) }
+    private var mLoadingData = false
+
+    override fun initListener() {
+        mMultipleStatusView?.setOnRetryClickListener { loadData(true) }
+        mSrlRefresh.setOnRefreshListener { loadData(true) }
+
+        initNetWatchdog()
+    }
+
+    override fun initViews(savedInstanceState: Bundle?) {
+        initRecyclerView()
+    }
+
+    private fun initRecyclerView() {
+        mAdapter.setOnItemClickListener(this)
+        mAdapter.setOnItemChildClickListener(this)
+
+        mRvContent.layoutManager = createLayoutManager()
+//        mAdapter.loadMoreModule.loadMoreView = ItLoadMoreView()
+        mAdapter.loadMoreModule.setOnLoadMoreListener(this)
+        mAdapter.loadMoreModule.isAutoLoadMore = true
+        // 当自动加载开启,同时数据不满一屏时,是否继续执行自动加载更多(默认为true)
+        mAdapter.loadMoreModule.isEnableLoadMoreIfNotFullPage = true
+        mAdapter.animationEnable = true
+        mAdapter.setAnimationWithDefault(BaseQuickAdapter.AnimationType.SlideInBottom)
+
+        createItemDecoration()?.let {
+            mRvContent.addItemDecoration(it)
+        }
+
+        mRvContent.adapter = mAdapter
+    }
+
+    protected open fun createLayoutManager(): RecyclerView.LayoutManager {
+        return LinearLayoutManager(this)
+    }
+
+    /**
+     * 初始化网络监听
+     */
+    private fun initNetWatchdog() {
+        mNetWatchdog.startWatch()
+        mNetWatchdog.setNetConnectedListener(object : NetConnectedListener {
+            override fun onReNetConnected(isReconnect: Boolean) {
+                mMultipleStatusView?.let {
+                    if (isReconnect) {
+                        it.showContent()
+                    }
+                }
+                if (mAdapter.data.isEmpty() && !mLoadingData && isReconnect) {
+                    loadData(true)
+                }
+            }
+
+            override fun onNetUnConnected() {
+                if (mAdapter.data.isEmpty()) {
+                    mMultipleStatusView?.showNoNetwork()
+                }
+            }
+        })
+    }
+
+    override fun onDestroy() {
+        super.onDestroy()
+        EventBus.getDefault().unregister(this)
+        mNetWatchdog.stopWatch()
+    }
+
+    /**
+     * 数据加载成功的处理逻辑
+     *
+     * @param data 从服务端查询的数据
+     */
+    protected open fun dispatchLoadDataSuccess(data: List<T>?) {
+        mLoadingData = false
+        if (isDestroyed) return
+
+        mMultipleStatusView?.showContent()
+        if (mRefresh) {
+            if (null == data || data.isEmpty()) {
+                if (null == mMultipleStatusView) {
+                    mAdapter.setEmptyView(R.layout.view_list_empty)
+                } else {
+                    mMultipleStatusView!!.showEmpty()
+                }
+            }
+            mAdapter.setNewInstance(data?.toMutableList())
+            mAdapter.loadMoreModule.loadMoreComplete()
+            if (null == data || data.size < PAGE_SIZE) {
+                mAdapter.loadMoreModule.loadMoreEnd(goneLoadMoreView())
+            }
+            mSrlRefresh.isRefreshing = false
+        } else {
+            if (null == data || data.size < PAGE_SIZE) {
+                if (null != data) {
+                    mAdapter.addData(data)
+                }
+                mAdapter.loadMoreModule.loadMoreEnd(goneLoadMoreView())
+            } else {
+                mAdapter.addData(data)
+                mAdapter.loadMoreModule.loadMoreComplete()
+            }
+        }
+    }
+
+    /**
+     * 数据加载失败的处理逻辑
+     */
+    protected open fun dispatchLoadDataFailure(throwable: Throwable) {
+        mLoadingData = false
+        if (isDestroyed) return
+
+        if (mRefresh) {
+            if (null == mMultipleStatusView) {
+                mAdapter.setEmptyView(R.layout.view_list_empty)
+            } else {
+                mMultipleStatusView!!.showError()
+            }
+            mSrlRefresh.isRefreshing = false
+        } else {
+            mCurrentPage = if (mCurrentPage-- < FIRST_PAGE) FIRST_PAGE else mCurrentPage
+            mAdapter.loadMoreModule.loadMoreEnd(goneLoadMoreView())
+        }
+        throwable.printStackTrace()
+        showNetError(throwable)
+    }
+
+    protected open fun goneLoadMoreView() = false
+
+    private fun showLoadingView() {
+        mMultipleStatusView?.let {
+            if (mRefresh) {
+                it.showLoading()
+            }
+        }
+    }
+
+    protected fun loadData(refresh: Boolean) {
+        mLoadingData = true
+        if (refresh) {
+            mCurrentPage = FIRST_PAGE
+        }
+        mRefresh = refresh
+        showLoadingView()
+        queryData()?.let { addDisposable(it) }
+    }
+
+    /**
+     * 查询数据
+     */
+    abstract fun queryData(): Disposable?
+
+    override fun onLoadMore() {
+        val dataSize = mAdapter.data.size
+        if (dataSize < PAGE_SIZE) {
+            mAdapter.loadMoreModule.loadMoreEnd(false)
+        } else {
+            if (dataSize % PAGE_SIZE != 0) {
+                mAdapter.loadMoreModule.loadMoreEnd(false)
+            } else {
+                mCurrentPage++
+                loadData(false)
+            }
+        }
+    }
+
+    override fun onItemClick(adapter: BaseQuickAdapter<*, *>, view: View, position: Int) {
+    }
+
+    override fun onItemChildClick(adapter: BaseQuickAdapter<*, *>, view: View, position: Int) {
+    }
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        EventBus.getDefault().register(this)
+    }
+
+    @Subscribe(threadMode = ThreadMode.MAIN)
+    fun onMessageEvent(event: RefreshEvent) {
+        if (event.refresh) {
+            dispatchRefreshEvent(event)
+        }
+    }
+
+    protected open fun dispatchRefreshEvent(event: RefreshEvent) {
+    }
+
+}

+ 36 - 0
RcCore/src/main/java/com/rc/core/ui/common/AbsUIDelegate.kt

@@ -0,0 +1,36 @@
+package com.rc.core.ui.common
+
+import android.content.Context
+import androidx.recyclerview.widget.RecyclerView
+import io.reactivex.disposables.Disposable
+
+/**
+ * info
+ *
+ * @author ReiChin_
+ */
+abstract class AbsUIDelegate {
+
+    companion object {
+        fun create() = UIDelegateImpl()
+    }
+
+    abstract fun showLoading(
+        context: Context,
+        message: String? = null,
+        cancelable: Boolean = false
+    )
+
+    abstract fun dismissLoading()
+
+    abstract fun showToast(context: Context?, message: String)
+
+    abstract fun showNetError(context: Context?, throwable: Throwable)
+
+    abstract fun addDisposable(disposable: Disposable)
+
+    abstract fun clearDisposable()
+
+    abstract fun createItemDecoration(context: Context?): RecyclerView.ItemDecoration?
+
+}

+ 25 - 0
RcCore/src/main/java/com/rc/core/ui/common/IUIListener.kt

@@ -0,0 +1,25 @@
+package com.rc.core.ui.common
+
+import androidx.recyclerview.widget.RecyclerView
+import io.reactivex.disposables.Disposable
+
+/**
+ * info
+ *
+ * @author ReiChin_
+ */
+interface IUIListener {
+
+    fun showLoading(message: String? = null, cancelable: Boolean = false)
+
+    fun dismissLoading()
+
+    fun showToast(message: String)
+
+    fun showNetError(throwable: Throwable)
+
+    fun addDisposable(disposable: Disposable)
+
+    fun createItemDecoration(): RecyclerView.ItemDecoration?
+
+}

+ 93 - 0
RcCore/src/main/java/com/rc/core/ui/common/UIDelegateImpl.kt

@@ -0,0 +1,93 @@
+package com.rc.core.ui.common
+
+import android.content.Context
+import android.widget.Toast
+import androidx.core.content.ContextCompat
+import androidx.recyclerview.widget.DividerItemDecoration
+import androidx.recyclerview.widget.RecyclerView
+import com.rc.core.R
+import com.rc.core.ui.dialog.LoadingDialog
+import com.rc.core.ui.widget.decoration.NoLastLineItemDecoration
+import com.rc.httpcore.exception.NetException
+import io.reactivex.disposables.CompositeDisposable
+import io.reactivex.disposables.Disposable
+import retrofit2.HttpException
+import java.net.ConnectException
+import java.net.SocketTimeoutException
+
+/**
+ * info
+ *
+ * @author ReiChin_
+ */
+class UIDelegateImpl : AbsUIDelegate() {
+
+    private var mPdLoading: LoadingDialog? = null
+
+    override fun showLoading(
+        context: Context,
+        message: String?,
+        cancelable: Boolean
+    ) {
+        if (null == mPdLoading) {
+            mPdLoading = LoadingDialog(context, message)
+        }
+        mPdLoading?.let {
+            it.setMessage(message)
+            it.setCancelable(cancelable)
+            it.show()
+        }
+    }
+
+    override fun dismissLoading() {
+        mPdLoading?.let {
+            it.dismiss()
+            mPdLoading = null
+        }
+    }
+
+    override fun showToast(context: Context?, message: String) {
+        context?.let {
+            Toast.makeText(it, message, Toast.LENGTH_SHORT).show()
+        }
+    }
+
+    override fun showNetError(context: Context?, throwable: Throwable) {
+        when (throwable) {
+            is NetException -> {
+                if (throwable.message.isNullOrEmpty()) {
+                    "接口请求失败(${throwable.code})"
+                } else {
+                    throwable.message!!
+                }
+            }
+            is SocketTimeoutException -> "请求超时,请稍后重试"
+            is ConnectException -> "无法连接服务器,请检查网络"
+            is HttpException -> "服务器繁忙,请稍后重试"
+            else -> null
+        }?.let { showToast(context, it) }
+    }
+
+    private var mCompositeDisposable: CompositeDisposable? = null
+
+    override fun addDisposable(disposable: Disposable) {
+        mCompositeDisposable = (mCompositeDisposable ?: CompositeDisposable()).apply {
+            if (!isDisposed) add(disposable)
+        }
+    }
+
+    override fun clearDisposable() {
+        mCompositeDisposable = mCompositeDisposable?.let {
+            it.clear()
+            null
+        }
+    }
+
+    override fun createItemDecoration(context: Context?): RecyclerView.ItemDecoration? {
+        return context?.let {
+            NoLastLineItemDecoration(it, DividerItemDecoration.VERTICAL).apply {
+                setDrawable(ContextCompat.getDrawable(it, R.drawable.shape_item_divider)!!)
+            }
+        }
+    }
+}

+ 101 - 0
RcCore/src/main/java/com/rc/core/ui/dialog/LoadingDialog.kt

@@ -0,0 +1,101 @@
+package com.rc.core.ui.dialog
+
+import android.app.Dialog
+import android.content.Context
+import android.os.Bundle
+import android.view.*
+import com.rc.core.R
+import com.rc.core.databinding.DialogLoadingBinding
+
+/**
+ * info
+ *
+ * @author ReiChin_
+ */
+class LoadingDialog(context: Context, private val message: String? = null) :
+    Dialog(context, R.style.LoadingDialog) {
+
+    fun setMessage(message: String?) {
+        viewBinding.message.visibility = if (message.isNullOrEmpty()) View.GONE else View.VISIBLE
+        viewBinding.message.text = message
+    }
+
+    private val viewBinding: DialogLoadingBinding by lazy {
+        DialogLoadingBinding.inflate(LayoutInflater.from(context))
+    }
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+
+        setContentView(viewBinding.root)
+
+        setMessage(message)
+    }
+
+}
+
+//class LoadingDialog : DialogFragment() {
+//
+//    override fun onCreate(savedInstanceState: Bundle?) {
+//        super.onCreate(savedInstanceState)
+//        setStyle(STYLE_NO_TITLE, R.style.LoadingDialog)
+//    }
+//
+//    private lateinit var mViewBinding: DialogLoadingBinding
+//
+//    override fun onCreateView(
+//        inflater: LayoutInflater,
+//        container: ViewGroup?,
+//        savedInstanceState: Bundle?
+//    ): View {
+//        mViewBinding = DialogLoadingBinding.inflate(inflater, container, false)
+//        return mViewBinding.root
+//    }
+//
+//    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+//        super.onViewCreated(view, savedInstanceState)
+//
+//        mViewBinding.message.visibility = if (mMessage.isNullOrEmpty()) View.GONE else View.VISIBLE
+//        mViewBinding.message.text = mMessage
+//    }
+//
+//    override fun onStart() {
+//        super.onStart()
+//        initWindowConfig()
+//    }
+//
+//    private fun initWindowConfig() {
+//        dialog?.window?.apply {
+//            setWindowAnimations(R.style.PopWindowAnimStyle)
+//            setGravity(Gravity.CENTER)
+//            attributes.width = WindowManager.LayoutParams.MATCH_PARENT
+//            attributes.height = WindowManager.LayoutParams.WRAP_CONTENT
+//        }
+//    }
+//
+//    private var mMessage: CharSequence? = null
+//
+//    fun setMessage(message: CharSequence?): LoadingDialog {
+//        this.mMessage = message
+//        return this
+//    }
+//
+//    private var addTag = false
+//
+//    fun show(manager: FragmentManager): LoadingDialog {
+//        if (addTag) return this
+//        addTag = true
+//
+//        if (this.isAdded || null != manager.findFragmentByTag(LoadingDialog::class.simpleName)) {
+//            manager.beginTransaction().remove(this).commit()
+//        }
+//        show(manager, LoadingDialog::class.simpleName)
+//
+//        Handler().post {
+//            addTag = false
+//        }
+//
+//        return this
+//    }
+//
+//}

+ 112 - 0
RcCore/src/main/java/com/rc/core/ui/dialog/RcBaseDialog.kt

@@ -0,0 +1,112 @@
+package com.rc.core.ui.dialog
+
+import android.os.Bundle
+import android.view.*
+import androidx.fragment.app.DialogFragment
+import androidx.recyclerview.widget.RecyclerView
+import androidx.viewbinding.ViewBinding
+import com.rc.core.R
+import com.rc.core.ui.common.AbsUIDelegate
+import com.rc.core.ui.common.IUIListener
+import io.reactivex.disposables.Disposable
+
+/**
+ * info
+ *
+ * @author ReiChin_
+ */
+abstract class RcBaseDialog<VB : ViewBinding> :
+    DialogFragment(), IUIListener {
+
+    private lateinit var _viewBinding: VB
+
+    protected val viewBinding get() = _viewBinding
+
+    private lateinit var mUIDelegate: AbsUIDelegate
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        mUIDelegate = AbsUIDelegate.create()
+
+        setStyle(STYLE_NO_TITLE, R.style.LoadingDialog)
+    }
+
+    override fun onCreateView(
+        inflater: LayoutInflater,
+        container: ViewGroup?,
+        savedInstanceState: Bundle?
+    ): View? {
+        initWindowConfig()
+        _viewBinding = createViewBinding(inflater, container)
+        return _viewBinding.root
+    }
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+        initViews(savedInstanceState)
+    }
+
+    override fun onResume() {
+        super.onResume()
+        initDialogSize()
+    }
+
+    protected open fun initDialogSize() {
+        dialog?.window?.apply {
+            attributes.width = WindowManager.LayoutParams.MATCH_PARENT
+            attributes.height = WindowManager.LayoutParams.WRAP_CONTENT
+        }
+    }
+
+    protected open fun initWindowConfig() {
+//        dialog?.window?.apply {
+//            setWindowAnimations(R.style.BottomDialog_Animation)
+//            setGravity(Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL)
+//        }
+//
+//        dialog?.window?.apply {
+//            attributes.gravity = Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL
+//            attributes.windowAnimations = R.style.BottomDialog_Animation
+//            requestFeature(Window.FEATURE_NO_TITLE)
+//            setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
+//        }
+//        dialog?.setCanceledOnTouchOutside(true)
+    }
+
+    protected abstract fun createViewBinding(
+        inflater: LayoutInflater,
+        container: ViewGroup?
+    ): VB
+
+    protected abstract fun initViews(savedInstanceState: Bundle?)
+
+    override fun showLoading(message: String?, cancelable: Boolean) {
+        mUIDelegate.showLoading(requireContext(), message, cancelable)
+    }
+
+    override fun dismissLoading() {
+        mUIDelegate.dismissLoading()
+    }
+
+    override fun showToast(message: String) {
+        mUIDelegate.showToast(context, message)
+    }
+
+    override fun showNetError(throwable: Throwable) {
+        mUIDelegate.showNetError(context, throwable)
+    }
+
+    override fun addDisposable(disposable: Disposable) {
+        mUIDelegate.addDisposable(disposable)
+    }
+
+    override fun onDestroyView() {
+        mUIDelegate.clearDisposable()
+        super.onDestroyView()
+    }
+
+    override fun createItemDecoration(): RecyclerView.ItemDecoration? {
+        return mUIDelegate.createItemDecoration(context)
+    }
+
+}

+ 83 - 0
RcCore/src/main/java/com/rc/core/ui/fragment/RcBaseFragment.kt

@@ -0,0 +1,83 @@
+package com.rc.core.ui.fragment
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import androidx.recyclerview.widget.RecyclerView
+import androidx.viewbinding.ViewBinding
+import com.rc.core.ui.common.AbsUIDelegate
+import com.rc.core.ui.common.IUIListener
+import io.reactivex.disposables.Disposable
+
+/**
+ * info
+ *
+ * @author ReiChin_
+ */
+abstract class RcBaseFragment<VB : ViewBinding> :
+    Fragment(), IUIListener {
+
+    private lateinit var _viewBinding: VB
+
+    protected val viewBinding get() = _viewBinding
+
+    private lateinit var mUIDelegate: AbsUIDelegate
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        mUIDelegate = AbsUIDelegate.create()
+    }
+
+    override fun onCreateView(
+        inflater: LayoutInflater,
+        container: ViewGroup?,
+        savedInstanceState: Bundle?
+    ): View? {
+        _viewBinding = createViewBinding(inflater, container)
+        return _viewBinding.root
+    }
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+        initViews(savedInstanceState)
+    }
+
+    protected abstract fun createViewBinding(
+        inflater: LayoutInflater,
+        container: ViewGroup?
+    ): VB
+
+    protected abstract fun initViews(savedInstanceState: Bundle?)
+
+    override fun showLoading(message: String?, cancelable: Boolean) {
+        context?.let { mUIDelegate.showLoading(it, message, cancelable) }
+    }
+
+    override fun dismissLoading() {
+        mUIDelegate.dismissLoading()
+    }
+
+    override fun showToast(message: String) {
+        mUIDelegate.showToast(context, message)
+    }
+
+    override fun showNetError(throwable: Throwable) {
+        mUIDelegate.showNetError(context, throwable)
+    }
+
+    override fun addDisposable(disposable: Disposable) {
+        mUIDelegate.addDisposable(disposable)
+    }
+
+    override fun onDestroyView() {
+        mUIDelegate.clearDisposable()
+        super.onDestroyView()
+    }
+
+    override fun createItemDecoration(): RecyclerView.ItemDecoration? {
+        return mUIDelegate.createItemDecoration(context)
+    }
+
+}

+ 37 - 0
RcCore/src/main/java/com/rc/core/ui/fragment/RcLazyFragment.kt

@@ -0,0 +1,37 @@
+package com.rc.core.ui.fragment
+
+import androidx.viewbinding.ViewBinding
+
+/**
+ * 懒加载
+ *
+ * @author ReiChin_
+ */
+abstract class RcLazyFragment<VB : ViewBinding> : RcBaseFragment<VB>() {
+
+    override fun onResume() {
+        super.onResume()
+        lazyLoad()
+    }
+
+    private fun lazyLoad() {
+        if (!loadDateCompleted()) {
+            onLoadData()
+        }
+    }
+
+    /**
+     * 加载数据
+     */
+    protected abstract fun onLoadData()
+
+    /**
+     * 数据是否加载完毕,子类可覆写此方法,根据需要返回。
+     * 1) false,每次画面可见时,都会回调[.onLoadData]。
+     * 2) true,每次画面可见时,不会回调[.onLoadData]。
+     *
+     * @return 数据是否加载完成。
+     */
+    protected open fun loadDateCompleted() = false
+
+}

+ 225 - 0
RcCore/src/main/java/com/rc/core/ui/fragment/RcRefreshFragment.kt

@@ -0,0 +1,225 @@
+package com.rc.core.ui.fragment
+
+import android.os.Bundle
+import android.view.View
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
+import androidx.viewbinding.ViewBinding
+import com.chad.library.adapter.base.BaseQuickAdapter
+import com.chad.library.adapter.base.listener.OnItemChildClickListener
+import com.chad.library.adapter.base.listener.OnItemClickListener
+import com.chad.library.adapter.base.listener.OnLoadMoreListener
+import com.chad.library.adapter.base.viewholder.BaseViewHolder
+import com.rc.core.event.RefreshEvent
+import com.rc.core.ui.widget.MultipleStatusView
+import com.rc.core.util.net.NetConnectedListener
+import com.rc.core.util.net.NetWatchdog
+import io.reactivex.disposables.Disposable
+import org.greenrobot.eventbus.EventBus
+import org.greenrobot.eventbus.Subscribe
+import org.greenrobot.eventbus.ThreadMode
+
+/**
+ * info
+ *
+ * @author ReiChin_
+ */
+abstract class RcRefreshFragment<T, VB : ViewBinding>
+constructor(val mAdapter: BaseQuickAdapter<T, BaseViewHolder>) : RcLazyFragment<VB>(),
+    OnLoadMoreListener,
+    OnItemClickListener,
+    OnItemChildClickListener {
+
+    companion object {
+        protected const val FIRST_PAGE = 1
+        protected const val PAGE_SIZE = 15
+    }
+
+    protected abstract val mMultipleStatusView: MultipleStatusView?
+    protected abstract val mSrlRefresh: SwipeRefreshLayout
+    protected abstract val mRvContent: RecyclerView
+
+    protected var mCurrentPage = FIRST_PAGE
+    protected var mRefresh = false
+
+    private val mNetWatchdog: NetWatchdog by lazy { NetWatchdog(context!!) }
+    private var mLoadingData = false
+
+    override fun initViews(savedInstanceState: Bundle?) {
+        initNetWatchdog()
+        initSwipeRecyclerView()
+    }
+
+    private fun initSwipeRecyclerView() {
+        mMultipleStatusView?.setOnRetryClickListener { loadData(true) }
+        mSrlRefresh.setOnRefreshListener { loadData(true) }
+
+        mAdapter.setOnItemClickListener(this)
+        mAdapter.setOnItemChildClickListener(this)
+
+        mRvContent.layoutManager = LinearLayoutManager(context)
+//        mAdapter.loadMoreModule.loadMoreView = ItLoadMoreView()
+        mAdapter.loadMoreModule.setOnLoadMoreListener(this)
+        mAdapter.loadMoreModule.isAutoLoadMore = true
+        // 当自动加载开启,同时数据不满一屏时,是否继续执行自动加载更多(默认为true)
+        mAdapter.loadMoreModule.isEnableLoadMoreIfNotFullPage = true
+        mAdapter.animationEnable = true
+        mAdapter.setAnimationWithDefault(BaseQuickAdapter.AnimationType.SlideInBottom)
+
+        createItemDecoration()?.let {
+            mRvContent.addItemDecoration(it)
+        }
+
+        mRvContent.adapter = mAdapter
+    }
+
+    /**
+     * 初始化网络监听
+     */
+    private fun initNetWatchdog() {
+        mNetWatchdog.startWatch()
+        mNetWatchdog.setNetConnectedListener(object : NetConnectedListener {
+            override fun onReNetConnected(isReconnect: Boolean) {
+                mMultipleStatusView?.let {
+                    if (isReconnect) {
+                        it.showContent()
+                    }
+                }
+                if (mAdapter.data.isEmpty() && !mLoadingData && isReconnect) {
+                    loadData(true)
+                }
+            }
+
+            override fun onNetUnConnected() {
+                if (mAdapter.data.isEmpty()) {
+                    mMultipleStatusView?.showNoNetwork()
+                }
+            }
+        })
+    }
+
+    override fun onDestroyView() {
+        mNetWatchdog.stopWatch()
+        super.onDestroyView()
+    }
+
+    protected fun loadData(refresh: Boolean) {
+        mLoadingData = true
+        if (refresh) {
+            mCurrentPage = FIRST_PAGE
+        }
+        mRefresh = refresh
+        showLoadingView()
+        val disposable = queryData()
+        addDisposable(disposable)
+    }
+
+    abstract fun queryData(): Disposable
+
+    override fun onLoadData() {
+        loadData(true)
+    }
+
+    override fun onLoadMore() {
+        val dataSize = mAdapter.data.size
+        if (dataSize < PAGE_SIZE) {
+            mAdapter.loadMoreModule.loadMoreEnd(false)
+        } else {
+            if (dataSize % PAGE_SIZE != 0) {
+                mAdapter.loadMoreModule.loadMoreEnd(false)
+            } else {
+                mCurrentPage++
+                loadData(false)
+            }
+        }
+    }
+
+    /**
+     * 数据加载成功的处理逻辑
+     *
+     * @param data 从服务端查询的数据
+     */
+    protected open fun dispatchLoadDataSuccess(data: List<T>?) {
+        mLoadingData = false
+        if (isDetached) return
+
+        mMultipleStatusView?.showContent()
+        if (mRefresh) {
+            if (null == data || data.isEmpty()) {
+                mMultipleStatusView?.showEmpty()
+            }
+            mAdapter.setNewInstance(data?.toMutableList())
+            mAdapter.loadMoreModule.loadMoreComplete()
+            if (null == data || data.size < PAGE_SIZE) {
+                mAdapter.loadMoreModule.loadMoreEnd(goneLoadMoreView())
+            }
+            mSrlRefresh.isRefreshing = false
+        } else {
+            if (null == data || data.size < PAGE_SIZE) {
+                if (null != data) {
+                    mAdapter.addData(data)
+                }
+                mAdapter.loadMoreModule.loadMoreEnd(goneLoadMoreView())
+            } else {
+                mAdapter.addData(data)
+                mAdapter.loadMoreModule.loadMoreComplete()
+            }
+        }
+    }
+
+    /**
+     * 数据加载失败的处理逻辑
+     */
+    protected open fun dispatchLoadDataFailure() {
+        mLoadingData = false
+        if (isDetached) return
+
+        if (mRefresh) {
+            mMultipleStatusView?.showError()
+            mSrlRefresh.isRefreshing = false
+        } else {
+            mCurrentPage = if (mCurrentPage-- < FIRST_PAGE) FIRST_PAGE else mCurrentPage
+            mAdapter.loadMoreModule.loadMoreEnd(goneLoadMoreView())
+        }
+    }
+
+    protected open fun goneLoadMoreView() = false
+
+    private fun showLoadingView() {
+        mMultipleStatusView?.let {
+            if (mRefresh) {
+                it.showLoading()
+            }
+        }
+    }
+
+    override fun loadDateCompleted() = mAdapter.data.isNotEmpty()
+
+    override fun onItemClick(adapter: BaseQuickAdapter<*, *>, view: View, position: Int) {
+    }
+
+    override fun onItemChildClick(adapter: BaseQuickAdapter<*, *>, view: View, position: Int) {
+    }
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        EventBus.getDefault().register(this)
+    }
+
+    override fun onDestroy() {
+        super.onDestroy()
+        EventBus.getDefault().unregister(this)
+    }
+
+    @Subscribe(threadMode = ThreadMode.MAIN)
+    fun onMessageEvent(event: RefreshEvent) {
+        if (event.refresh) {
+            dispatchRefreshEvent(event)
+        }
+    }
+
+    protected open fun dispatchRefreshEvent(event: RefreshEvent) {
+    }
+
+}

+ 0 - 0
RcCore/src/main/java/com/rc/core/ui/widget/MultipleStatusView.kt


Some files were not shown because too many files changed in this diff