Sfoglia il codice sorgente

refactor: 网络层重构和ARouter集成

主要变更:
1. 网络层重构
   - 使用 Fastjson2 替代 Gson 进行 JSON 解析
   - 实现 NetworkManager(单例+建造者模式)统一管理网络配置
   - 实现 ApiServiceFactory(工厂模式)简化 API 服务创建
   - 实现 FastJsonConverterFactory 用于 Retrofit 转换
   - 移除 HttpApiClient 兼容层,统一使用 ApiServiceFactory

2. ARouter 依赖注入集成
   - base-core 模块定义接口:IPushService、IShareService、IShareCallback
   - capability-push 模块实现 IPushService 并注册到 ARouter(/push/service)
   - capability-share 模块实现 IShareService 并注册到 ARouter(/share/service)
   - capability-share 模块实现 IShareCallback 并注册到 ARouter(/share/callback)
   - WXEntryActivity 移至 base-core,通过 ARouter 注入回调实现
   - Factory 类内部使用 ARouter 获取服务实例,实现模块间解耦

3. SocketIO 模块重构
   - SocketIORepository 移至 capability-socketio 模块
   - 实现 SocketIORepositoryFactory 统一管理
   - 使用 Fastjson2 替代 Gson

4. 文档更新
   - 更新 capability-push 和 capability-share 的集成说明文档
   - 添加 ARouter 架构说明和配置步骤

5. 其他优化
   - 更新 LoginResponse 模型,添加 @JSONField 注解
   - 优化 ApiResponseParser 处理嵌套泛型类型转换
   - 更新所有模块的 build.gradle 配置
wangmeng 4 settimane fa
parent
commit
0b9e466023
48 ha cambiato i file con 2641 aggiunte e 464 eliminazioni
  1. 35 16
      app/build.gradle
  2. 3 1
      app/src/main/AndroidManifest.xml
  3. 6 13
      app/src/main/java/com/narutohuo/xindazhou/MainActivity.kt
  4. 149 4
      app/src/main/java/com/narutohuo/xindazhou/XinDaZhouApplication.kt
  5. 1 1
      app/src/main/java/com/narutohuo/xindazhou/common/version/datasource/remote/VersionApi.kt
  6. 9 0
      app/src/main/java/com/narutohuo/xindazhou/user/datasource/remote/AuthApi.kt
  7. 22 2
      app/src/main/java/com/narutohuo/xindazhou/user/datasource/remote/AuthRemoteDataSource.kt
  8. 15 1
      app/src/main/java/com/narutohuo/xindazhou/user/model/LoginResponse.kt
  9. 24 0
      app/src/main/java/com/narutohuo/xindazhou/user/repository/AuthRepository.kt
  10. 2 1
      app/src/main/java/com/narutohuo/xindazhou/user/ui/login/LoginFragment.kt
  11. 1 1
      app/src/main/java/com/narutohuo/xindazhou/user/ui/profile/UserFragment.kt
  12. 19 6
      app/src/main/java/com/narutohuo/xindazhou/user/ui/viewmodel/UserViewModel.kt
  13. 3 2
      app/src/main/java/com/narutohuo/xindazhou/user/ui/viewmodel/UserViewModelFactory.kt
  14. 4 16
      base-common/build.gradle
  15. 46 7
      base-common/src/main/java/com/narutohuo/xindazhou/common/network/ApiResponseParser.kt
  16. 54 0
      base-common/src/main/java/com/narutohuo/xindazhou/common/network/ApiServiceFactory.kt
  17. 0 145
      base-common/src/main/java/com/narutohuo/xindazhou/common/network/HttpApiClient.kt
  18. 42 2
      base-core/build.gradle
  19. 15 2
      base-core/src/main/AndroidManifest.xml
  20. 214 0
      base-core/src/main/java/com/narutohuo/xindazhou/core/network/NetworkManager.kt
  21. 114 0
      base-core/src/main/java/com/narutohuo/xindazhou/core/network/converter/FastJsonConverterFactory.kt
  22. 79 0
      base-core/src/main/java/com/narutohuo/xindazhou/core/push/IPushService.kt
  23. 43 0
      base-core/src/main/java/com/narutohuo/xindazhou/core/share/IShareCallback.kt
  24. 317 0
      base-core/src/main/java/com/narutohuo/xindazhou/core/share/IShareService.kt
  25. 70 0
      base-core/src/main/java/com/narutohuo/xindazhou/wxapi/WXEntryActivity.kt
  26. 3 5
      capability-ble/build.gradle
  27. 3 5
      capability-nfc/build.gradle
  28. 15 8
      capability-push/build.gradle
  29. 18 0
      capability-push/src/main/java/com/narutohuo/xindazhou/push/factory/PushServiceFactory.kt
  30. 57 1
      capability-push/src/main/java/com/narutohuo/xindazhou/push/impl/PushServiceImpl.kt
  31. 3 5
      capability-push/src/main/java/com/narutohuo/xindazhou/push/receiver/JPushReceiver.kt
  32. 305 0
      capability-push/集成说明.html
  33. 15 4
      capability-push/集成说明.md
  34. 3 5
      capability-qrcode/build.gradle
  35. 15 4
      capability-share/build.gradle
  36. 2 8
      capability-share/src/main/AndroidManifest.xml
  37. 4 2
      capability-share/src/main/java/com/narutohuo/xindazhou/share/api/ShareService.kt
  38. 103 0
      capability-share/src/main/java/com/narutohuo/xindazhou/share/callback/ShareCallbackImpl.kt
  39. 19 0
      capability-share/src/main/java/com/narutohuo/xindazhou/share/factory/ShareServiceFactory.kt
  40. 351 5
      capability-share/src/main/java/com/narutohuo/xindazhou/share/impl/ShareServiceImpl.kt
  41. 13 7
      capability-share/src/main/java/com/narutohuo/xindazhou/share/ui/ShareProxyActivity.kt
  42. 0 38
      capability-share/src/main/java/com/narutohuo/xindazhou/wxapi/WXEntryActivity.kt
  43. 151 70
      capability-share/集成说明.html
  44. 95 27
      capability-share/集成说明.md
  45. 4 13
      capability-socketio/build.gradle
  46. 50 0
      capability-socketio/src/main/java/com/narutohuo/xindazhou/socketio/factory/SocketIORepositoryFactory.kt
  47. 63 19
      capability-socketio/src/main/java/com/narutohuo/xindazhou/socketio/impl/SocketIOServiceImpl.kt
  48. 62 18
      app/src/main/java/com/narutohuo/xindazhou/user/repository/SocketIORepository.kt

+ 35 - 16
app/build.gradle

@@ -2,6 +2,7 @@ plugins {
     alias(libs.plugins.android.application)
     alias(libs.plugins.kotlin.android)
     alias(libs.plugins.kotlin.compose)
+    id("kotlin-kapt")
 }
 
 android {
@@ -44,11 +45,38 @@ android {
         viewBinding true
         buildConfig true  // 启用 BuildConfig 生成
     }
+    
+    // ARouter 配置(Kotlin 方式)
+    kapt {
+        arguments {
+            arg("AROUTER_MODULE_NAME", project.getName())
+        }
+    }
+    
+    // 解决资源合并冲突
+    packaging {
+        resources {
+            excludes += ['META-INF/androidx.localbroadcastmanager_localbroadcastmanager.version']
+            excludes += ['META-INF/DEPENDENCIES']
+            excludes += ['META-INF/LICENSE']
+            excludes += ['META-INF/LICENSE.txt']
+            excludes += ['META-INF/license.txt']
+            excludes += ['META-INF/NOTICE']
+            excludes += ['META-INF/NOTICE.txt']
+            excludes += ['META-INF/notice.txt']
+            excludes += ['META-INF/ASL2.0']
+        }
+    }
+}
+
+// 全局排除旧的 support 库,避免与 AndroidX 冲突
+configurations.all {
+    exclude group: 'com.android.support'
 }
 
 dependencies {
     // 基础模块
-    implementation project(':base-core')
+    // 注意:app 模块不直接依赖 base-core,通过 base-common 间接获得
     implementation project(':base-common')
     
     // ============================================================================
@@ -94,26 +122,14 @@ dependencies {
     implementation libs.androidx.lifecycle.runtime.ktx
     implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0'
     implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.7.0'
-    
-    // Kotlin Coroutines
-    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3'
-    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3'
+    implementation 'androidx.lifecycle:lifecycle-process:2.7.0'  // ProcessLifecycleOwner(监听 App 前后台切换)
     
     // Navigation
     implementation 'androidx.navigation:navigation-fragment-ktx:2.7.6'
     implementation 'androidx.navigation:navigation-ui-ktx:2.7.6'
     
-    // Timber (日志库,用于Hyperion)
-    implementation 'com.jakewharton.timber:timber:5.0.1'
-    
-    // Retrofit & OkHttp
-    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
-    implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
-    implementation 'com.squareup.okhttp3:okhttp:4.12.0'
-    implementation 'com.squareup.okhttp3:logging-interceptor:4.12.0'
-    
-    // Glide
-    implementation 'com.github.bumptech.glide:glide:4.16.0'
+    // 注意:基础第三方库(Timber、Retrofit、OkHttp、Gson、Glide、Coroutines)
+    // 已通过 base-common 间接获得(base-common → base-core),不需要直接依赖
     
     // Room
     implementation 'androidx.room:room-runtime:2.6.1'
@@ -144,6 +160,9 @@ dependencies {
     debugImplementation 'com.github.chuckerteam.chucker:library:4.0.0'
     releaseImplementation 'com.github.chuckerteam.chucker:library-no-op:4.0.0'
     
+    // ARouter 编译器(用于生成路由代码)
+    kapt 'com.alibaba:arouter-compiler:1.5.2'
+    
     // Testing
     testImplementation libs.junit
     androidTestImplementation libs.androidx.junit

+ 3 - 1
app/src/main/AndroidManifest.xml

@@ -52,7 +52,9 @@
         android:supportsRtl="true"
         android:theme="@style/Theme.XinDaZhou"
         android:networkSecurityConfig="@xml/network_security_config"
-        tools:targetApi="31">
+        android:appComponentFactory="androidx.core.app.CoreComponentFactory"
+        tools:targetApi="31"
+        tools:replace="android:appComponentFactory">
         
         <!-- 注意:极光推送配置已移到 capability-push 模块的 AndroidManifest.xml 中 -->
         

+ 6 - 13
app/src/main/java/com/narutohuo/xindazhou/MainActivity.kt

@@ -7,12 +7,11 @@ import androidx.appcompat.app.AppCompatActivity
 import androidx.lifecycle.lifecycleScope
 import androidx.navigation.fragment.NavHostFragment
 import com.narutohuo.xindazhou.common.config.ServerConfigManager
-import com.narutohuo.xindazhou.common.network.HttpApiClient
+import com.narutohuo.xindazhou.common.network.ApiServiceFactory
 import com.narutohuo.xindazhou.common.version.datasource.remote.VersionApi
 import com.narutohuo.xindazhou.common.version.repository.VersionRepository
 import com.narutohuo.xindazhou.common.version.ui.UpdateDialog
 import com.narutohuo.xindazhou.core.log.ILog
-import com.narutohuo.xindazhou.share.factory.ShareServiceFactory
 import com.narutohuo.xindazhou.user.datasource.local.TokenManager
 import kotlinx.coroutines.launch
 
@@ -79,8 +78,8 @@ class MainActivity : AppCompatActivity() {
                 // 获取当前版本号
                 val currentVersionCode = getCurrentVersionCode()
                 
-                // 使用 HttpApiClient 创建 API 实例
-                val versionApi = HttpApiClient.createApi<VersionApi>()
+                // 使用 ApiServiceFactory 创建 API 实例
+                val versionApi = ApiServiceFactory.create<VersionApi>()
                 val versionRepository = VersionRepository(versionApi)
                 
                 // 检查版本(如果没有版本,返回 null,不影响启动)
@@ -128,14 +127,8 @@ class MainActivity : AppCompatActivity() {
         }
     }
     
-    /**
-     * 处理分享回调
-     * 友盟分享 SDK 需要在这里处理回调
-     */
-    override fun onActivityResult(requestCode: Int, resultCode: Int, data: android.content.Intent?) {
-        super.onActivityResult(requestCode, resultCode, data)
-        // 处理友盟分享回调
-        ShareServiceFactory.getInstance().onActivityResult(this, requestCode, resultCode, data)
-    }
+    // 注意:分享回调已完全封装在 ShareProxyActivity 中处理
+    // 业务层不需要在 MainActivity 中处理 onActivityResult
+    // 所有分享回调都会自动回到 ShareProxyActivity,由它统一处理
 }
 

+ 149 - 4
app/src/main/java/com/narutohuo/xindazhou/XinDaZhouApplication.kt

@@ -1,27 +1,64 @@
 package com.narutohuo.xindazhou
 
 import android.app.Application
+import androidx.lifecycle.LifecycleObserver
+import androidx.lifecycle.OnLifecycleEvent
+import androidx.lifecycle.ProcessLifecycleOwner
+import com.alibaba.android.arouter.launcher.ARouter
 import com.narutohuo.xindazhou.common.config.ServerConfigManager
-import com.narutohuo.xindazhou.common.network.HttpApiClient
 import com.narutohuo.xindazhou.share.factory.ShareServiceFactory
+import com.narutohuo.xindazhou.socketio.factory.SocketIORepositoryFactory
 import com.narutohuo.xindazhou.user.datasource.local.TokenManager
+import com.narutohuo.xindazhou.user.repository.AuthRepository
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
 
 /**
  * 应用程序入口
  */
-class XinDaZhouApplication : Application() {
+class XinDaZhouApplication : Application(), LifecycleObserver {
+    
+    // 用于后台执行 SocketIO 重连的协程作用域
+    private val appScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
     
     override fun onCreate() {
         super.onCreate()
         
+        // 初始化 ARouter(必须在其他初始化之前)
+        if (BuildConfig.DEBUG) {
+            // 开启日志
+            ARouter.openLog()
+            // 开启调试模式(如果在InstantRun模式下运行,必须开启调试模式!线上版本需要关闭,否则有安全风险)
+            ARouter.openDebug()
+        }
+        // 初始化 ARouter
+        ARouter.init(this)
+        
         // 初始化 ServerConfigManager
         ServerConfigManager.init(applicationContext)
         
         // 初始化 TokenManager
         TokenManager.init(applicationContext)
         
-        // 配置 HttpApiClient 的 Token 提供器
-        HttpApiClient.tokenProvider = {
+        // 初始化网络管理器(使用 Fastjson)
+        val isDebug = try {
+            BuildConfig.DEBUG
+        } catch (e: Exception) {
+            false
+        }
+        com.narutohuo.xindazhou.core.network.NetworkManager.init(
+            com.narutohuo.xindazhou.core.network.NetworkConfig.Builder()
+                .baseUrl(ServerConfigManager.getHttpServerUrl())
+                .isDebug(isDebug)
+                .enableLogging(isDebug)
+                .build()
+        )
+        
+        // 配置 NetworkManager 的 Token 提供器
+        com.narutohuo.xindazhou.core.network.NetworkManager.tokenProvider = {
             TokenManager.getAccessToken()
         }
         
@@ -34,6 +71,114 @@ class XinDaZhouApplication : Application() {
             // 分享服务初始化失败不影响应用运行,但分享功能将不可用
             android.util.Log.e("XinDaZhouApplication", "分享服务初始化失败", e)
         }
+        
+        // 注册 App 生命周期监听(监听前后台切换)
+        ProcessLifecycleOwner.get().lifecycle.addObserver(this)
+    }
+    
+    /**
+     * App 进入前台时调用
+     * 
+     * 检查 SocketIO 连接状态,如果断开则自动重连
+     * 如果连接失败(可能是 Token 过期),会自动刷新 Token 后重连
+     */
+    @OnLifecycleEvent(androidx.lifecycle.Lifecycle.Event.ON_START)
+    fun onAppForeground() {
+        android.util.Log.d("XinDaZhouApplication", "App 进入前台,检查 SocketIO 连接状态")
+        
+        // 检查是否已登录
+        if (!TokenManager.isLoggedIn()) {
+            android.util.Log.d("XinDaZhouApplication", "用户未登录,跳过 SocketIO 重连")
+            return
+        }
+        
+        // 检查连接状态,如果断开则重连
+        appScope.launch {
+            val socketIORepository = SocketIORepositoryFactory.getInstance()
+            val socketUrl = ServerConfigManager.getSocketIOUrl()
+            var token = TokenManager.getAccessToken()
+            
+            if (token.isNullOrEmpty()) {
+                android.util.Log.w("XinDaZhouApplication", "Token 为空,无法重连 SocketIO")
+                return@launch
+            }
+            
+            // 如果已连接,无需重连
+            if (socketIORepository.isConnected()) {
+                android.util.Log.d("XinDaZhouApplication", "SocketIO 已连接,无需重连")
+                return@launch
+            }
+            
+            // 先尝试连接
+            android.util.Log.d("XinDaZhouApplication", "SocketIO 未连接,开始重连...")
+            socketIORepository.checkAndReconnect(socketUrl, token)
+            
+            // 等待 2 秒,检查连接是否成功
+            delay(2000)
+            
+            // 如果连接仍然失败,可能是 Token 过期,尝试刷新 Token 后重连
+            if (!socketIORepository.isConnected()) {
+                android.util.Log.d("XinDaZhouApplication", "连接失败,可能是 Token 过期,尝试刷新 Token 后重连")
+                val refreshedToken = refreshTokenIfNeeded()
+                if (!refreshedToken.isNullOrEmpty() && refreshedToken != token) {
+                    android.util.Log.d("XinDaZhouApplication", "Token 已刷新,使用新 Token 重连")
+                    socketIORepository.checkAndReconnect(socketUrl, refreshedToken)
+                } else {
+                    android.util.Log.w("XinDaZhouApplication", "Token 刷新失败或未刷新,无法重连")
+                }
+            } else {
+                android.util.Log.d("XinDaZhouApplication", "SocketIO 重连成功")
+            }
+        }
+    }
+    
+    /**
+     * 刷新 Token(如果需要)
+     * 
+     * @return 新的 accessToken,如果刷新失败返回 null
+     */
+    private suspend fun refreshTokenIfNeeded(): String? {
+        val refreshToken = TokenManager.getRefreshToken()
+        if (refreshToken.isNullOrEmpty()) {
+            android.util.Log.w("XinDaZhouApplication", "RefreshToken 为空,无法刷新")
+            return null
+        }
+        
+        return try {
+            val authRepository = AuthRepository()
+            val result = authRepository.refreshToken(refreshToken)
+            
+            result.getOrNull()?.let { loginResponse ->
+                // 保存新的 Token
+                TokenManager.saveToken(
+                    loginResponse.accessToken,
+                    loginResponse.refreshToken,
+                    loginResponse.userId
+                )
+                android.util.Log.d("XinDaZhouApplication", "Token 刷新成功")
+                loginResponse.accessToken
+            } ?: run {
+                android.util.Log.w("XinDaZhouApplication", "Token 刷新失败: ${result.exceptionOrNull()?.message}")
+                null
+            }
+        } catch (e: Exception) {
+            android.util.Log.e("XinDaZhouApplication", "Token 刷新异常", e)
+            null
+        }
+    }
+    
+    /**
+     * App 进入后台时调用
+     * 
+     * 注意:通常不断开 SocketIO 连接,因为:
+     * 1. SocketIO 连接是轻量级的
+     * 2. 用户可能需要在后台接收消息
+     * 3. 系统会在内存不足时自动清理
+     */
+    @OnLifecycleEvent(androidx.lifecycle.Lifecycle.Event.ON_STOP)
+    fun onAppBackground() {
+        android.util.Log.d("XinDaZhouApplication", "App 进入后台")
+        // 不断开连接,保持连接以便接收消息
     }
 }
 

+ 1 - 1
app/src/main/java/com/narutohuo/xindazhou/common/version/datasource/remote/VersionApi.kt

@@ -16,7 +16,7 @@ interface VersionApi {
      * @param currentVersionCode 当前版本号(可选,用于判断是否有更新)
      * @return 版本信息
      */
-    @GET("app-api/version/latest")
+    @GET("version/latest")
     suspend fun getLatestVersion(
         @Query("platform") platform: Int,
         @Query("currentVersionCode") currentVersionCode: Int? = null

+ 9 - 0
app/src/main/java/com/narutohuo/xindazhou/user/datasource/remote/AuthApi.kt

@@ -7,6 +7,7 @@ import com.narutohuo.xindazhou.user.model.RegisterRequest
 import retrofit2.Response
 import retrofit2.http.Body
 import retrofit2.http.POST
+import retrofit2.http.Query
 
 /**
  * 认证相关API接口
@@ -24,5 +25,13 @@ interface AuthApi {
      */
     @POST("member/auth/register")
     suspend fun register(@Body request: RegisterRequest): Response<CommonResult<LoginResponse>>
+    
+    /**
+     * 刷新 Token
+     * 
+     * 注意:服务端使用 @RequestParam,所以使用 @Query
+     */
+    @POST("member/auth/refresh-token")
+    suspend fun refreshToken(@Query("refreshToken") refreshToken: String): Response<CommonResult<LoginResponse>>
 }
 

+ 22 - 2
app/src/main/java/com/narutohuo/xindazhou/user/datasource/remote/AuthRemoteDataSource.kt

@@ -1,7 +1,7 @@
 package com.narutohuo.xindazhou.user.datasource.remote
 
 import com.narutohuo.xindazhou.common.network.ApiResponseParser
-import com.narutohuo.xindazhou.common.network.HttpApiClient
+import com.narutohuo.xindazhou.common.network.ApiServiceFactory
 import com.narutohuo.xindazhou.user.model.LoginRequest
 import com.narutohuo.xindazhou.user.model.LoginResponse
 import com.narutohuo.xindazhou.user.model.RegisterRequest
@@ -19,6 +19,11 @@ interface AuthRemoteDataSource {
      * 用户注册
      */
     suspend fun register(request: RegisterRequest): Result<LoginResponse>
+    
+    /**
+     * 刷新 Token
+     */
+    suspend fun refreshToken(refreshToken: String): Result<LoginResponse>
 }
 
 /**
@@ -31,13 +36,14 @@ class AuthRemoteDataSourceImpl : AuthRemoteDataSource {
     }
     
     private val authApi: AuthApi by lazy {
-        HttpApiClient.createApi<AuthApi>()
+        ApiServiceFactory.create<AuthApi>()
     }
     
     override suspend fun login(request: LoginRequest): Result<LoginResponse> {
         return try {
             ApiResponseParser.handleResponse(
                 response = authApi.login(request),
+                dataClass = LoginResponse::class.java,
                 tag = "$TAG - login",
                 errorMessage = "登录失败"
             )
@@ -50,6 +56,7 @@ class AuthRemoteDataSourceImpl : AuthRemoteDataSource {
         return try {
             ApiResponseParser.handleResponse(
                 response = authApi.register(request),
+                dataClass = LoginResponse::class.java,
                 tag = "$TAG - register",
                 errorMessage = "注册失败"
             )
@@ -57,5 +64,18 @@ class AuthRemoteDataSourceImpl : AuthRemoteDataSource {
             Result.failure(e)
         }
     }
+    
+    override suspend fun refreshToken(refreshToken: String): Result<LoginResponse> {
+        return try {
+            ApiResponseParser.handleResponse(
+                response = authApi.refreshToken(refreshToken),
+                dataClass = LoginResponse::class.java,
+                tag = "$TAG - refreshToken",
+                errorMessage = "Token 刷新失败"
+            )
+        } catch (e: Exception) {
+            Result.failure(e)
+        }
+    }
 }
 

+ 15 - 1
app/src/main/java/com/narutohuo/xindazhou/user/model/LoginResponse.kt

@@ -1,15 +1,29 @@
 package com.narutohuo.xindazhou.user.model
 
+import com.alibaba.fastjson2.annotation.JSONField
+
 /**
  * 登录响应数据
  * 对应后端 AppAuthLoginRespVO
+ * 
+ * 注意:服务端返回的 expiresTime 是 LocalDateTime,Fastjson2 会自动转换为时间戳(毫秒)
  */
 data class LoginResponse(
+    @JSONField(name = "userId")
     val userId: Long,
+    
+    @JSONField(name = "accessToken")
     val accessToken: String,
+    
+    @JSONField(name = "refreshToken")
     val refreshToken: String,
-    val expiresTime: String? = null,  // LocalDateTime 在 JSON 中序列化为字符串
+    
+    @JSONField(name = "expiresTime")
+    val expiresTime: Long? = null,  // 服务端返回 LocalDateTime,Fastjson2 会自动转换为时间戳(毫秒)
+    
+    @JSONField(name = "openid")
     val openid: String? = null,
+    
     // 以下字段为兼容字段(后端不返回,但前端可能需要)
     val username: String? = null,
     val mobile: String? = null

+ 24 - 0
app/src/main/java/com/narutohuo/xindazhou/user/repository/AuthRepository.kt

@@ -76,5 +76,29 @@ class AuthRepository(
             Result.failure(e)
         }
     }
+    
+    /**
+     * 刷新 Token
+     * 
+     * @param refreshToken 刷新令牌
+     * @return 刷新结果(包含新的 accessToken 和 refreshToken)
+     */
+    suspend fun refreshToken(refreshToken: String): Result<LoginResponse> {
+        return try {
+            // 从远程数据源获取数据
+            val result = remoteDataSource.refreshToken(refreshToken)
+            
+            // 如果成功,保存到本地
+            result.onSuccess { response ->
+                localDataSource.saveToken(response)
+                ILog.d(TAG, "refreshToken: Token已刷新并保存")
+            }
+            
+            result
+        } catch (e: Exception) {
+            ILog.e(TAG, "refreshToken: 刷新异常", e)
+            Result.failure(e)
+        }
+    }
 }
 

+ 2 - 1
app/src/main/java/com/narutohuo/xindazhou/user/ui/login/LoginFragment.kt

@@ -41,7 +41,8 @@ class LoginFragment : Fragment() {
     private lateinit var progressBar: View
     private lateinit var fabShare: FloatingActionButton
     
-    private val shareService = ShareServiceFactory.getInstance()
+    // 延迟初始化,避免在 Fragment 构造函数中调用 ARouter(此时可能还未完全初始化)
+    private val shareService by lazy { ShareServiceFactory.getInstance() }
     
     override fun onCreateView(
         inflater: LayoutInflater,

+ 1 - 1
app/src/main/java/com/narutohuo/xindazhou/user/ui/profile/UserFragment.kt

@@ -26,7 +26,7 @@ import kotlinx.coroutines.launch
  * 架构:MVVM
  * - View (Fragment): 只负责 UI 初始化和状态观察
  * - ViewModel: 管理业务逻辑和状态
- * - Repository: 数据层(封装 SocketIOService
+ * - Repository: 数据层(通过 SocketIORepositoryFactory 获取,完全封装在 capability-socketio 模块中
  */
 class UserFragment : Fragment() {
 

+ 19 - 6
app/src/main/java/com/narutohuo/xindazhou/user/ui/viewmodel/UserViewModel.kt

@@ -3,8 +3,11 @@ package com.narutohuo.xindazhou.user.ui.viewmodel
 import android.app.Application
 import androidx.lifecycle.AndroidViewModel
 import androidx.lifecycle.viewModelScope
+import com.narutohuo.xindazhou.socketio.factory.SocketIORepositoryFactory
 import com.narutohuo.xindazhou.socketio.model.SocketIOResponse
-import com.narutohuo.xindazhou.user.repository.SocketIORepository
+import com.narutohuo.xindazhou.socketio.repository.SocketIORepository
+import com.narutohuo.xindazhou.common.config.ServerConfigManager
+import com.narutohuo.xindazhou.user.datasource.local.TokenManager
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharedFlow
 import kotlinx.coroutines.flow.StateFlow
@@ -28,7 +31,7 @@ data class UserUiState(
  */
 class UserViewModel(
     application: Application,
-    private val socketIORepository: SocketIORepository = SocketIORepository()
+    private val socketIORepository: SocketIORepository
 ) : AndroidViewModel(application) {
     
     private val dateFormat = SimpleDateFormat("HH:mm:ss", Locale.getDefault())
@@ -43,7 +46,9 @@ class UserViewModel(
     val errorMessage: SharedFlow<String?> = _errorMessage.asSharedFlow()
     
     init {
-        // 观察连接状态
+        // 先初始化连接状态(同步获取当前状态)
+        updateConnectionStatus()
+        // 然后观察连接状态变化(异步监听)
         observeConnectionState()
         // 观察日志消息
         observeLogMessage()
@@ -51,8 +56,6 @@ class UserViewModel(
         observeVehicleControlResponse()
         // 观察车辆报警
         observeVehicleAlarm()
-        // 初始化连接状态
-        updateConnectionStatus()
     }
     
     /**
@@ -61,7 +64,17 @@ class UserViewModel(
     fun connect() {
         viewModelScope.launch {
             addLog("正在连接SocketIO服务器...")
-            socketIORepository.connect()
+            val socketUrl = ServerConfigManager.getSocketIOUrl()
+            val token = TokenManager.getAccessToken()
+            
+            if (token.isNullOrEmpty()) {
+                val errorMsg = "未登录,无法获取Token"
+                addLog(errorMsg)
+                _errorMessage.value = errorMsg
+                return@launch
+            }
+            
+            socketIORepository.connect(socketUrl, token)
                 .onSuccess {
                     addLog("连接请求已发送")
                 }

+ 3 - 2
app/src/main/java/com/narutohuo/xindazhou/user/ui/viewmodel/UserViewModelFactory.kt

@@ -3,7 +3,8 @@ package com.narutohuo.xindazhou.user.ui.viewmodel
 import android.app.Application
 import androidx.lifecycle.ViewModel
 import androidx.lifecycle.ViewModelProvider
-import com.narutohuo.xindazhou.user.repository.SocketIORepository
+import com.narutohuo.xindazhou.socketio.factory.SocketIORepositoryFactory
+import com.narutohuo.xindazhou.socketio.repository.SocketIORepository
 
 /**
  * UserViewModel工厂类
@@ -15,7 +16,7 @@ class UserViewModelFactory(
     @Suppress("UNCHECKED_CAST")
     override fun <T : ViewModel> create(modelClass: Class<T>): T {
         if (modelClass.isAssignableFrom(UserViewModel::class.java)) {
-            val socketIORepository = SocketIORepository()
+            val socketIORepository = SocketIORepositoryFactory.getInstance()
             return UserViewModel(application, socketIORepository) as T
         }
         throw IllegalArgumentException("Unknown ViewModel class")

+ 4 - 16
base-common/build.gradle

@@ -25,11 +25,9 @@ android {
 }
 
 dependencies {
-    // 依赖base-core
-    implementation(project(":base-core"))
-    
-    // AndroidX Core
-    implementation(libs.androidx.core.ktx)
+    // 依赖base-core(基础第三方库都在这里)
+    // 使用 api 传递 base-core 的依赖给其他模块
+    api(project(":base-core"))
     
     // Fragment KTX (提供 DialogFragment 支持)
     implementation("androidx.fragment:fragment-ktx:1.6.2")
@@ -37,17 +35,7 @@ dependencies {
     // Material Design (用于对话框UI)
     implementation("com.google.android.material:material:1.11.0")
     
-    // Gson for JSON parsing
-    implementation("com.google.code.gson:gson:2.10.1")
-    
-    // Retrofit for HTTP requests
-    implementation("com.squareup.retrofit2:retrofit:2.9.0")
-    implementation("com.squareup.retrofit2:converter-gson:2.9.0")
-    
-    // OkHttp for HTTP client
-    implementation("com.squareup.okhttp3:okhttp:4.12.0")
-    implementation("com.squareup.okhttp3:logging-interceptor:4.12.0")
-    
+    // 注意:Gson、Retrofit、OkHttp、Glide、Coroutines、Timber 已通过 base-core 传递,无需重复依赖
     // 日志通过 base-core 的 ILog 接口统一管理,无需单独依赖 Timber
 }
 

+ 46 - 7
base-common/src/main/java/com/narutohuo/xindazhou/common/network/ApiResponseParser.kt

@@ -1,7 +1,10 @@
 package com.narutohuo.xindazhou.common.network
 
 import android.util.Log
+import com.alibaba.fastjson2.JSON
 import retrofit2.Response
+import java.lang.reflect.ParameterizedType
+import java.lang.reflect.Type
 
 /**
  * 通用响应结果
@@ -31,12 +34,14 @@ object ApiResponseParser {
      * 处理网络响应
      * 
      * @param response Retrofit Response
+     * @param dataClass 数据类型的 Class(用于类型转换)
      * @param tag 日志标签(用于日志记录)
      * @param errorMessage 错误消息前缀
      * @return Result<T> 成功返回数据,失败返回异常
      */
     fun <T> handleResponse(
         response: Response<CommonResult<T>>,
+        dataClass: Class<T>,
         tag: String = "ApiResponseParser",
         errorMessage: String = "请求失败"
     ): Result<T> {
@@ -49,21 +54,55 @@ object ApiResponseParser {
             }
             
             // 2. 获取响应体
-            val commonResult = response.body() 
-                ?: return Result.failure(Exception("响应体为空"))
+            val commonResult = response.body()
+            if (commonResult == null) {
+                // 尝试读取原始响应体以便调试
+                val errorBody = response.errorBody()?.string() ?: "null"
+                Log.e(tag, "响应体为空,errorBody=$errorBody")
+                return Result.failure(Exception("响应体为空"))
+            }
+            
+            Log.d(tag, "响应体解析成功: code=${commonResult.code}, msg=${commonResult.msg}, data类型=${commonResult.data?.javaClass?.simpleName ?: "null"}")
             
             // 3. 检查业务状态码
             if (commonResult.code != SUCCESS_CODE) {
                 val errorMsg = commonResult.msg ?: "$errorMessage (code: ${commonResult.code})"
-                Log.w(tag, "业务失败,code=${commonResult.code}")
+                Log.w(tag, "业务失败,code=${commonResult.code}, msg=$errorMsg")
                 return Result.failure(Exception(errorMsg))
             }
             
-            // 4. 提取业务数据
-            val data = commonResult.data 
-                ?: return Result.failure(Exception("服务器返回数据为空"))
+            // 4. 提取业务数据并转换类型
+            val dataObj = commonResult.data
+            if (dataObj == null) {
+                Log.e(tag, "服务器返回数据为空: code=${commonResult.code}, msg=${commonResult.msg}")
+                return Result.failure(Exception("服务器返回数据为空"))
+            }
             
-            Log.d(tag, "请求成功")
+            // 5. 如果 data 是 Map 或 JSONObject 类型(Fastjson2 解析嵌套泛型时的默认行为),需要转换为目标类型
+            val data: T = if (dataObj is Map<*, *> || dataObj.javaClass.name.contains("JSONObject") || dataObj.javaClass.name.contains("JSON")) {
+                try {
+                    Log.d(tag, "检测到 data 为 Map/JSONObject 类型,转换为: ${dataClass.simpleName}")
+                    val dataJsonString = JSON.toJSONString(dataObj)
+                    Log.d(tag, "转换前的 JSON: $dataJsonString")
+                    
+                    // 使用 Fastjson2 解析,支持 LocalDateTime 自动转换为时间戳
+                    @Suppress("UNCHECKED_CAST")
+                    val converted = JSON.parseObject(dataJsonString, dataClass) as T
+                    Log.d(tag, "转换成功: ${converted?.javaClass?.simpleName}")
+                    converted
+                } catch (e: Exception) {
+                    Log.e(tag, "转换 data 类型失败: ${e.message}", e)
+                    Log.e(tag, "原始 data 类型: ${dataObj.javaClass.name}")
+                    Log.e(tag, "目标类型: ${dataClass.name}")
+                    // 如果转换失败,抛出异常,不要静默失败
+                    throw Exception("无法将 ${dataObj.javaClass.simpleName} 转换为 ${dataClass.simpleName}: ${e.message}", e)
+                }
+            } else {
+                @Suppress("UNCHECKED_CAST")
+                dataObj as T
+            }
+            
+            Log.d(tag, "请求成功: data类型=${data?.javaClass?.simpleName ?: "null"}")
             Result.success(data)
             
         } catch (e: Exception) {

+ 54 - 0
base-common/src/main/java/com/narutohuo/xindazhou/common/network/ApiServiceFactory.kt

@@ -0,0 +1,54 @@
+package com.narutohuo.xindazhou.common.network
+
+import com.narutohuo.xindazhou.common.config.ServerConfigManager
+import com.narutohuo.xindazhou.core.network.NetworkManager
+import retrofit2.Retrofit
+
+/**
+ * API 服务工厂(工厂模式)
+ * 
+ * 统一创建 API 服务实例,避免重复代码
+ * 
+ * 使用方式:
+ * ```kotlin
+ * val authApi = ApiServiceFactory.create<AuthApi>()
+ * ```
+ */
+object ApiServiceFactory {
+    
+    /**
+     * 创建 API 服务实例(工厂方法)
+     * 
+     * @param baseUrl 基础URL,如果为 null 则使用 ServerConfigManager 的配置
+     * @return API 服务实例
+     */
+    inline fun <reified T> create(baseUrl: String? = null): T {
+        val url = baseUrl ?: ServerConfigManager.getHttpServerUrl()
+        
+        // 如果 baseUrl 不同,需要创建新的 Retrofit 实例
+        val retrofit = if (baseUrl != null) {
+            // 使用自定义 baseUrl,创建新的 Retrofit 实例
+            NetworkManager.getRetrofit(
+                com.narutohuo.xindazhou.core.network.NetworkConfig.Builder()
+                    .baseUrl(url)
+                    .build()
+            )
+        } else {
+            // 使用默认配置
+            NetworkManager.getRetrofit()
+        }
+        
+        return retrofit.create(T::class.java)
+    }
+    
+    /**
+     * 使用指定的 Retrofit 实例创建 API 服务
+     * 
+     * @param retrofit Retrofit 实例
+     * @return API 服务实例
+     */
+    inline fun <reified T> create(retrofit: Retrofit): T {
+        return retrofit.create(T::class.java)
+    }
+}
+

+ 0 - 145
base-common/src/main/java/com/narutohuo/xindazhou/common/network/HttpApiClient.kt

@@ -1,145 +0,0 @@
-package com.narutohuo.xindazhou.common.network
-
-import android.util.Log
-import com.google.gson.Gson
-import com.google.gson.GsonBuilder
-import com.narutohuo.xindazhou.common.config.ServerConfigManager
-import okhttp3.OkHttpClient
-import okhttp3.logging.HttpLoggingInterceptor
-import retrofit2.Retrofit
-import retrofit2.converter.gson.GsonConverterFactory
-import java.util.concurrent.TimeUnit
-
-/**
- * HTTP API 客户端管理类
- * 
- * 负责创建和管理 Retrofit 实例,统一配置
- * 功能:
- * - 统一配置 OkHttp Client(超时、拦截器)
- * - 日志拦截器(Debug模式)
- * - Token拦截器(可选,通过配置添加)
- */
-object HttpApiClient {
-    
-    private var retrofit: Retrofit? = null
-    private var okHttpClient: OkHttpClient? = null
-    
-    /**
-     * Token获取器接口(可选,用于自动添加Token到请求头)
-     */
-    var tokenProvider: (() -> String?)? = null
-    
-    private val gson: Gson by lazy {
-        GsonBuilder()
-            .setLenient()
-            .create()
-    }
-    
-    /**
-     * 创建 OkHttp Client
-     */
-    private fun createOkHttpClient(): OkHttpClient {
-        return OkHttpClient.Builder()
-            .connectTimeout(30, TimeUnit.SECONDS)
-            .readTimeout(30, TimeUnit.SECONDS)
-            .writeTimeout(30, TimeUnit.SECONDS)
-            .addInterceptor(createLoggingInterceptor())
-            .addInterceptor(createTokenInterceptor())
-            .build()
-    }
-    
-    /**
-     * 创建日志拦截器
-     */
-    private fun createLoggingInterceptor(): HttpLoggingInterceptor {
-        val loggingInterceptor = HttpLoggingInterceptor { message ->
-            // 使用Android Log输出(base-common不依赖Timber)
-            Log.d("Retrofit", message)
-        }
-        loggingInterceptor.level = if (isDebug()) {
-            HttpLoggingInterceptor.Level.BODY
-        } else {
-            HttpLoggingInterceptor.Level.NONE
-        }
-        return loggingInterceptor
-    }
-    
-    /**
-     * 创建Token拦截器(自动添加Token到请求头)
-     */
-    private fun createTokenInterceptor(): okhttp3.Interceptor {
-        return okhttp3.Interceptor { chain ->
-            val originalRequest = chain.request()
-            
-            // 如果配置了Token提供器,自动添加Token
-            val token = tokenProvider?.invoke()
-            val requestBuilder = originalRequest.newBuilder()
-            
-            if (!token.isNullOrEmpty()) {
-                requestBuilder.header("Authorization", "Bearer $token")
-            }
-            
-            // 添加通用请求头
-            requestBuilder.header("Content-Type", "application/json")
-            requestBuilder.header("Accept", "application/json")
-            
-            chain.proceed(requestBuilder.build())
-        }
-    }
-    
-    /**
-     * 判断是否为Debug模式
-     */
-    private fun isDebug(): Boolean {
-        return try {
-            // 尝试从app模块获取BuildConfig
-            val buildConfigClass = Class.forName("com.narutohuo.xindazhou.BuildConfig")
-            val debugField = buildConfigClass.getField("DEBUG")
-            debugField.getBoolean(null)
-        } catch (e: Exception) {
-            // 如果无法获取,默认返回false(Release模式)
-            false
-        }
-    }
-    
-    /**
-     * 获取 OkHttp Client
-     */
-    fun getOkHttpClient(): OkHttpClient {
-        return okHttpClient ?: createOkHttpClient().also { okHttpClient = it }
-    }
-    
-    /**
-     * 获取 Retrofit 实例
-     * 
-     * @param baseUrl 基础URL,如果为null则使用ServerConfigManager的配置
-     * @return Retrofit 实例
-     */
-    fun getRetrofit(baseUrl: String? = null): Retrofit {
-        return retrofit ?: Retrofit.Builder()
-            .baseUrl(baseUrl ?: ServerConfigManager.getHttpServerUrl())
-            .client(getOkHttpClient())
-            .addConverterFactory(GsonConverterFactory.create(gson))
-            .build()
-            .also { retrofit = it }
-    }
-    
-    /**
-     * 创建 API 接口实例
-     * 
-     * @param baseUrl 基础URL,如果为null则使用ServerConfigManager的配置
-     * @return API 接口实例
-     */
-    inline fun <reified T> createApi(baseUrl: String? = null): T {
-        return getRetrofit(baseUrl).create(T::class.java)
-    }
-    
-    /**
-     * 重置 Retrofit 实例(用于切换服务器地址时)
-     */
-    fun reset() {
-        retrofit = null
-        okHttpClient = null
-    }
-}
-

+ 42 - 2
base-core/build.gradle

@@ -20,12 +20,52 @@ android {
     }
 }
 
+repositories {
+    flatDir {
+        dirs '../capability-share/libs'
+    }
+}
+
 dependencies {
     // AndroidX Core
-    implementation(libs.androidx.core.ktx)
+    api(libs.androidx.core.ktx)
     
     // Timber for logging (默认日志实现)
     // 如果以后不用 Timber,只需修改 TimberLog 实现类
-    implementation("com.jakewharton.timber:timber:5.0.1")
+    api("com.jakewharton.timber:timber:5.0.1")
+    
+    // Retrofit & OkHttp(基础网络库,所有模块都需要)
+    // 使用 api 传递依赖,让 base-common 和其他模块可以使用
+    api("com.squareup.retrofit2:retrofit:2.9.0")
+    api("com.squareup.okhttp3:okhttp:4.12.0")
+    api("com.squareup.okhttp3:logging-interceptor:4.12.0")
+    
+    // Fastjson2(JSON解析,替代 Gson)
+    // 注意:Fastjson2 需要 Java 8+
+    // Maven Central: https://mvnrepository.com/artifact/com.alibaba.fastjson2/fastjson2
+    api("com.alibaba.fastjson2:fastjson2:2.0.47")
+    
+    // Glide(图片加载,所有模块都需要)
+    api("com.github.bumptech.glide:glide:4.16.0")
+    
+    // Kotlin Coroutines(协程,所有模块都需要)
+    api("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3")
+    api("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3")
+    
+    // ARouter(路由和依赖注入)
+    api("com.alibaba:arouter-api:1.5.2")
+    
+    // 友盟分享 SDK(仅用于 WXEntryActivity 等回调 Activity)
+    // 注意:虽然 base-core 是基础设施层,但为了支持第三方回调 Activity,需要添加此依赖
+    // 友盟基础组件库(所有友盟业务SDK都依赖基础组件库)
+    api("com.umeng.umsdk:common:+")  // 必选
+    api("com.umeng.umsdk:asms:+")    // 必选
+    // 分享核心库(必选)
+    api("com.umeng.umsdk:share-core:+")
+    // 微信分享(完整版)
+    // 注意:友盟微信分享 JAR 文件在 share 模块的 libs 目录
+    // 通过 flatDir 仓库访问
+    api(files('../capability-share/libs/umeng-share-wechat-full-7.3.7.jar'))
+    api("com.tencent.mm.opensdk:wechat-sdk-android:6.8.24")
 }
 

+ 15 - 2
base-core/src/main/AndroidManifest.xml

@@ -1,3 +1,16 @@
 <?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android" />
-
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+    
+    <application>
+        <!-- 微信分享回调 Activity(必需) -->
+        <!-- 注意:虽然文件在 base-core 模块中,但包名使用应用包名(com.narutohuo.xindazhou.wxapi) -->
+        <!-- 微信 SDK 要求此 Activity 必须在应用包名的 wxapi 子包下 -->
+        <!-- 通过 ARouter 依赖注入获取 IShareCallback 实现,实现 base-core 与 share 模块的解耦 -->
+        <activity
+            android:name="com.narutohuo.xindazhou.wxapi.WXEntryActivity"
+            android:configChanges="keyboardHidden|orientation|screenSize"
+            android:exported="true"
+            android:theme="@android:style/Theme.Translucent.NoTitleBar" />
+    </application>
+    
+</manifest>

+ 214 - 0
base-core/src/main/java/com/narutohuo/xindazhou/core/network/NetworkManager.kt

@@ -0,0 +1,214 @@
+package com.narutohuo.xindazhou.core.network
+
+import com.narutohuo.xindazhou.core.log.ILog
+import com.narutohuo.xindazhou.core.network.converter.FastJsonConverterFactory
+import okhttp3.OkHttpClient
+import okhttp3.logging.HttpLoggingInterceptor
+import retrofit2.Retrofit
+import java.util.concurrent.TimeUnit
+import java.util.concurrent.atomic.AtomicReference
+
+/**
+ * 网络管理器(单例模式 + 建造者模式)
+ * 
+ * 统一管理 Retrofit 和 OkHttpClient 实例,避免重复创建
+ * 
+ * 设计模式:
+ * - 单例模式:全局唯一实例
+ * - 建造者模式:通过 NetworkConfig 灵活配置
+ * 
+ * 使用方式:
+ * ```kotlin
+ * // 使用默认配置
+ * val retrofit = NetworkManager.getRetrofit()
+ * 
+ * // 使用自定义配置
+ * val retrofit = NetworkManager.getRetrofit(
+ *     NetworkConfig.Builder()
+ *         .baseUrl("https://api.example.com")
+ *         .connectTimeout(60, TimeUnit.SECONDS)
+ *         .build()
+ * )
+ * ```
+ */
+object NetworkManager {
+    
+    private const val TAG = "NetworkManager"
+    
+    // 使用 AtomicReference 确保线程安全
+    private val retrofitRef = AtomicReference<Retrofit?>()
+    private val okHttpClientRef = AtomicReference<OkHttpClient?>()
+    
+    // 默认配置
+    private var defaultConfig: NetworkConfig? = null
+    
+    /**
+     * Token 提供器(可选,用于自动添加 Token 到请求头)
+     */
+    var tokenProvider: (() -> String?)? = null
+    
+    /**
+     * 初始化网络管理器(可选)
+     * 
+     * @param config 网络配置,如果为 null 则使用默认配置
+     */
+    fun init(config: NetworkConfig? = null) {
+        defaultConfig = config ?: NetworkConfig.default()
+        ILog.d(TAG, "NetworkManager 初始化完成")
+    }
+    
+    /**
+     * 获取 Retrofit 实例(单例)
+     * 
+     * @param config 网络配置,如果为 null 则使用默认配置或已缓存的实例
+     * @return Retrofit 实例
+     */
+    fun getRetrofit(config: NetworkConfig? = null): Retrofit {
+        val finalConfig = config ?: defaultConfig ?: NetworkConfig.default()
+        
+        return retrofitRef.get() ?: synchronized(this) {
+            retrofitRef.get() ?: buildRetrofit(finalConfig).also {
+                retrofitRef.set(it)
+            }
+        }
+    }
+    
+    /**
+     * 获取 OkHttpClient 实例(单例)
+     * 
+     * @param config 网络配置,如果为 null 则使用默认配置或已缓存的实例
+     * @return OkHttpClient 实例
+     */
+    fun getOkHttpClient(config: NetworkConfig? = null): OkHttpClient {
+        val finalConfig = config ?: defaultConfig ?: NetworkConfig.default()
+        
+        return okHttpClientRef.get() ?: synchronized(this) {
+            okHttpClientRef.get() ?: buildOkHttpClient(finalConfig).also {
+                okHttpClientRef.set(it)
+            }
+        }
+    }
+    
+    /**
+     * 构建 Retrofit 实例(建造者模式)
+     */
+    private fun buildRetrofit(config: NetworkConfig): Retrofit {
+        return Retrofit.Builder()
+            .baseUrl(config.baseUrl)
+            .client(getOkHttpClient(config))
+            .addConverterFactory(FastJsonConverterFactory.create())
+            .build()
+    }
+    
+    /**
+     * 构建 OkHttpClient 实例(建造者模式)
+     */
+    private fun buildOkHttpClient(config: NetworkConfig): OkHttpClient {
+        val builder = OkHttpClient.Builder()
+            .connectTimeout(config.connectTimeoutSeconds, TimeUnit.SECONDS)
+            .readTimeout(config.readTimeoutSeconds, TimeUnit.SECONDS)
+            .writeTimeout(config.writeTimeoutSeconds, TimeUnit.SECONDS)
+        
+        // 添加日志拦截器
+        if (config.enableLogging) {
+            val loggingInterceptor = HttpLoggingInterceptor { message ->
+                ILog.d("OkHttp", message)
+            }
+            loggingInterceptor.level = if (config.isDebug) {
+                HttpLoggingInterceptor.Level.BODY
+            } else {
+                HttpLoggingInterceptor.Level.BASIC
+            }
+            builder.addInterceptor(loggingInterceptor)
+        }
+        
+        // 添加 Token 拦截器
+        tokenProvider?.let { provider ->
+            builder.addInterceptor { chain ->
+                val originalRequest = chain.request()
+                val token = provider()
+                
+                val requestBuilder = originalRequest.newBuilder()
+                    .header("Content-Type", "application/json")
+                    .header("Accept", "application/json")
+                
+                if (!token.isNullOrEmpty()) {
+                    requestBuilder.header("Authorization", "Bearer $token")
+                }
+                
+                chain.proceed(requestBuilder.build())
+            }
+        }
+        
+        return builder.build()
+    }
+    
+    /**
+     * 重置网络管理器(用于切换服务器地址时)
+     */
+    fun reset() {
+        synchronized(this) {
+            retrofitRef.set(null)
+            okHttpClientRef.set(null)
+            defaultConfig = null
+        }
+        ILog.d(TAG, "NetworkManager 已重置")
+    }
+}
+
+/**
+ * 网络配置(建造者模式)
+ * 
+ * 用于灵活配置 Retrofit 和 OkHttpClient
+ */
+data class NetworkConfig(
+    val baseUrl: String,
+    val connectTimeoutSeconds: Long,
+    val readTimeoutSeconds: Long,
+    val writeTimeoutSeconds: Long,
+    val enableLogging: Boolean,
+    val isDebug: Boolean
+) {
+    class Builder {
+        private var baseUrl: String = "http://localhost/"
+        private var connectTimeoutSeconds: Long = 30
+        private var readTimeoutSeconds: Long = 30
+        private var writeTimeoutSeconds: Long = 30
+        private var enableLogging: Boolean = true
+        private var isDebug: Boolean = false
+        
+        fun baseUrl(url: String) = apply { this.baseUrl = url }
+        fun connectTimeout(seconds: Long, unit: TimeUnit) = apply {
+            this.connectTimeoutSeconds = unit.toSeconds(seconds)
+        }
+        fun readTimeout(seconds: Long, unit: TimeUnit) = apply {
+            this.readTimeoutSeconds = unit.toSeconds(seconds)
+        }
+        fun writeTimeout(seconds: Long, unit: TimeUnit) = apply {
+            this.writeTimeoutSeconds = unit.toSeconds(seconds)
+        }
+        fun enableLogging(enable: Boolean) = apply { this.enableLogging = enable }
+        fun isDebug(debug: Boolean) = apply { this.isDebug = debug }
+        
+        fun build(): NetworkConfig {
+            return NetworkConfig(
+                baseUrl = baseUrl,
+                connectTimeoutSeconds = connectTimeoutSeconds,
+                readTimeoutSeconds = readTimeoutSeconds,
+                writeTimeoutSeconds = writeTimeoutSeconds,
+                enableLogging = enableLogging,
+                isDebug = isDebug
+            )
+        }
+    }
+    
+    companion object {
+        /**
+         * 创建默认配置
+         */
+        fun default(): NetworkConfig {
+            return Builder().build()
+        }
+    }
+}
+

+ 114 - 0
base-core/src/main/java/com/narutohuo/xindazhou/core/network/converter/FastJsonConverterFactory.kt

@@ -0,0 +1,114 @@
+package com.narutohuo.xindazhou.core.network.converter
+
+import com.alibaba.fastjson2.JSON
+import okhttp3.MediaType
+import okhttp3.MediaType.Companion.toMediaType
+import okhttp3.RequestBody
+import okhttp3.RequestBody.Companion.toRequestBody
+import okhttp3.ResponseBody
+import retrofit2.Converter
+import retrofit2.Retrofit
+import java.lang.reflect.ParameterizedType
+import java.lang.reflect.Type
+
+/**
+ * FastJson Converter Factory(工厂模式)
+ * 
+ * 用于 Retrofit 的 JSON 转换,替代 Gson
+ * 
+ * 使用方式:
+ * ```kotlin
+ * Retrofit.Builder()
+ *     .addConverterFactory(FastJsonConverterFactory.create())
+ *     .build()
+ * ```
+ */
+class FastJsonConverterFactory private constructor() : Converter.Factory() {
+    
+    companion object {
+        private val MEDIA_TYPE: MediaType = "application/json; charset=UTF-8".toMediaType()
+        
+        /**
+         * 创建 FastJson Converter Factory 实例(工厂方法)
+         */
+        @JvmStatic
+        fun create(): FastJsonConverterFactory {
+            return FastJsonConverterFactory()
+        }
+    }
+    
+    override fun requestBodyConverter(
+        type: Type,
+        parameterAnnotations: Array<Annotation>,
+        methodAnnotations: Array<Annotation>,
+        retrofit: Retrofit
+    ): Converter<*, RequestBody>? {
+        return RequestBodyConverter<Any>()
+    }
+    
+    override fun responseBodyConverter(
+        type: Type,
+        annotations: Array<Annotation>,
+        retrofit: Retrofit
+    ): Converter<ResponseBody, *>? {
+        return ResponseBodyConverter<Any>(type)
+    }
+    
+    /**
+     * 请求体转换器(将对象转换为 JSON RequestBody)
+     */
+    private class RequestBodyConverter<T> : Converter<T, RequestBody> {
+        @Suppress("UNCHECKED_CAST")
+        override fun convert(value: T): RequestBody? {
+            val json = JSON.toJSONString(value)
+            return json.toRequestBody(MEDIA_TYPE)
+        }
+    }
+    
+    /**
+     * 响应体转换器(将 JSON ResponseBody 转换为对象)
+     * 
+     * 对于 CommonResult<T> 类型,解析为 CommonResult<Map>,然后在 ApiResponseParser 中统一处理类型转换
+     * 这样可以避免 Fastjson2 在解析嵌套泛型时的类型擦除问题
+     */
+    private class ResponseBodyConverter<T>(
+        private val type: Type
+    ) : Converter<ResponseBody, T> {
+        @Suppress("UNCHECKED_CAST")
+        override fun convert(value: ResponseBody): T {
+            val json = value.string()
+            try {
+                com.narutohuo.xindazhou.core.log.ILog.d("FastJsonConverter", "解析JSON: type=${type}, json=${json.take(200)}...")
+                
+                // Fastjson2 支持嵌套泛型,直接使用 parseObject 即可
+                // 对于 ParameterizedType(如 CommonResult<LoginResponse>),Fastjson2 会自动处理
+                val result = JSON.parseObject(json, type) as T
+                
+                // 检查解析结果
+                if (result != null && type is ParameterizedType) {
+                    val rawType = type.rawType
+                    val rawTypeName = (rawType as? Class<*>)?.name ?: ""
+                    if (rawTypeName.contains("CommonResult")) {
+                        try {
+                            val dataField = result.javaClass.getDeclaredField("data")
+                            dataField.isAccessible = true
+                            val dataValue = dataField.get(result)
+                            com.narutohuo.xindazhou.core.log.ILog.d("FastJsonConverter", "解析成功: ${result.javaClass.simpleName}, data类型=${dataValue?.javaClass?.simpleName ?: "null"}")
+                        } catch (e: Exception) {
+                            com.narutohuo.xindazhou.core.log.ILog.e("FastJsonConverter", "检查 data 字段失败: ${e.message}", e)
+                        }
+                    }
+                } else {
+                    com.narutohuo.xindazhou.core.log.ILog.d("FastJsonConverter", "解析成功: ${result?.let { it::class.java.simpleName } ?: "null"}")
+                }
+                
+                return result
+                
+            } catch (e: Exception) {
+                com.narutohuo.xindazhou.core.log.ILog.e("FastJsonConverter", "JSON解析失败: type=${type}, json=${json.take(500)}", e)
+                throw e
+            }
+        }
+    }
+}
+

+ 79 - 0
base-core/src/main/java/com/narutohuo/xindazhou/core/push/IPushService.kt

@@ -0,0 +1,79 @@
+package com.narutohuo.xindazhou.core.push
+
+import android.content.Context
+
+/**
+ * 推送服务接口
+ * 
+ * 用于 base-core 与 push 模块之间的解耦
+ * push 模块需要实现此接口,并通过 ARouter 注册
+ * 
+ * 注意:为了解耦,接口方法使用基本类型,具体类型由实现类处理
+ */
+interface IPushService {
+    
+    /**
+     * 初始化推送服务
+     * 
+     * @param context Application 上下文
+     * @param appKey 极光推送 AppKey
+     * @param channel 推送渠道(可选,默认为 "developer-default")
+     * @param debugMode 是否开启调试模式
+     */
+    fun initialize(context: Context, appKey: String, channel: String = "developer-default", debugMode: Boolean = false)
+    
+    /**
+     * 设置别名(用于推送)
+     * 
+     * @param alias 别名(通常是用户ID)
+     */
+    fun setAlias(alias: String)
+    
+    /**
+     * 设置标签(用于推送)
+     * 
+     * @param tags 标签列表
+     */
+    fun setTags(tags: List<String>)
+    
+    /**
+     * 设置消息监听器(自定义消息/透传消息)
+     * 
+     * @param listener 消息接收回调
+     *                 参数:title, content, extras (Map<String, String>), messageId
+     */
+    fun setMessageListener(listener: (String, String, Map<String, String>, String?) -> Unit)
+    
+    /**
+     * 设置通知点击监听器(用户点击通知栏通知时触发)
+     * 
+     * @param listener 通知点击回调
+     *                 参数:title, content, extras (Map<String, String>), messageId
+     */
+    fun setNotificationClickListener(listener: ((String, String, Map<String, String>, String?) -> Unit)?)
+    
+    /**
+     * 注册推送(开启推送)
+     */
+    fun register()
+    
+    /**
+     * 注销推送(关闭推送)
+     */
+    fun unregister()
+    
+    /**
+     * 获取注册ID(用于标识设备)
+     * 
+     * @return 设备注册ID,如果未初始化或获取失败返回 null
+     */
+    fun getRegistrationId(): String?
+    
+    /**
+     * 检查推送是否已开启
+     * 
+     * @return true 表示推送已开启,false 表示推送已停止或未初始化
+     */
+    fun isPushEnabled(): Boolean
+}
+

+ 43 - 0
base-core/src/main/java/com/narutohuo/xindazhou/core/share/IShareCallback.kt

@@ -0,0 +1,43 @@
+package com.narutohuo.xindazhou.core.share
+
+import android.content.Intent
+
+/**
+ * 分享回调接口
+ * 
+ * 用于 base-core 与 share 模块之间的解耦
+ * base-core 中的 WXEntryActivity 等回调 Activity 通过此接口调用 share 模块的实现
+ * 
+ * share 模块需要实现此接口,并通过 ARouter 注册
+ */
+interface IShareCallback {
+    
+    /**
+     * 处理微信分享回调
+     * 
+     * @param intent 微信回调的 Intent
+     */
+    fun onWeChatCallback(intent: Intent)
+    
+    /**
+     * 处理 QQ 分享回调
+     * 
+     * @param intent QQ 回调的 Intent
+     */
+    fun onQQCallback(intent: Intent)
+    
+    /**
+     * 处理微博分享回调
+     * 
+     * @param intent 微博回调的 Intent
+     */
+    fun onWeiboCallback(intent: Intent)
+    
+    /**
+     * 处理抖音分享回调
+     * 
+     * @param intent 抖音回调的 Intent
+     */
+    fun onDouyinCallback(intent: Intent)
+}
+

+ 317 - 0
base-core/src/main/java/com/narutohuo/xindazhou/core/share/IShareService.kt

@@ -0,0 +1,317 @@
+package com.narutohuo.xindazhou.core.share
+
+import android.app.Activity
+import android.content.Intent
+
+/**
+ * 分享服务接口
+ * 
+ * 用于 base-core 与 share 模块之间的解耦
+ * share 模块需要实现此接口,并通过 ARouter 注册
+ * 
+ * 注意:为了解耦,接口方法使用基本类型,具体类型由实现类处理
+ */
+interface IShareService {
+    
+    /**
+     * 初始化分享服务
+     * 
+     * @param context Application 上下文
+     * @param umengAppKey 友盟 AppKey(可选,如果为空则从资源文件读取)
+     * @param umengChannel 友盟渠道(可选,默认为 "developer-default")
+     * @param weChatAppId 微信 AppID(可选)
+     * @param weChatAppSecret 微信 AppSecret(可选)
+     * @param qqAppId QQ AppID(可选)
+     * @param qqAppKey QQ AppKey(可选)
+     * @param weiboAppKey 微博 AppKey(可选)
+     * @param weiboAppSecret 微博 AppSecret(可选)
+     * @param weiboRedirectUrl 微博回调地址(可选)
+     */
+    fun initialize(
+        context: android.content.Context,
+        umengAppKey: String? = null,
+        umengChannel: String = "developer-default",
+        weChatAppId: String? = null,
+        weChatAppSecret: String? = null,
+        qqAppId: String? = null,
+        qqAppKey: String? = null,
+        weiboAppKey: String? = null,
+        weiboAppSecret: String? = null,
+        weiboRedirectUrl: String? = null
+    )
+    
+    /**
+     * 处理分享回调(内部使用)
+     * 
+     * @param activity Activity 上下文
+     * @param requestCode 请求码
+     * @param resultCode 结果码
+     * @param data Intent 数据
+     */
+    fun onActivityResult(activity: Activity, requestCode: Int, resultCode: Int, data: Intent?)
+    
+    /**
+     * 释放资源
+     * 
+     * @param activity Activity 上下文
+     */
+    fun release(activity: Activity)
+    
+    /**
+     * 统一分享方法(推荐使用)
+     * 
+     * @param activity Activity上下文
+     * @param title 分享标题
+     * @param description 分享描述
+     * @param url 分享链接(可选)
+     * @param imageUrl 图片URL(可选)
+     * @param thumbImageUrl 缩略图URL(可选)
+     * @param platform 分享平台(可选,如果为 null 则显示分享弹窗)
+     * @param callback 分享结果回调
+     *                 参数:success (Boolean), platform (String?), errorMessage (String?)
+     */
+    fun share(
+        activity: Activity,
+        title: String,
+        description: String,
+        url: String? = null,
+        imageUrl: String? = null,
+        thumbImageUrl: String? = null,
+        platform: String? = null,
+        callback: (Boolean, String?, String?) -> Unit
+    )
+    
+    /**
+     * 手动分享方法(内部使用,避免递归调用)
+     * 
+     * @param activity Activity上下文
+     * @param title 分享标题
+     * @param description 分享描述
+     * @param url 分享链接(可选)
+     * @param imageUrl 图片URL(可选)
+     * @param thumbImageUrl 缩略图URL(可选)
+     * @param platform 分享平台(可选,如果为 null 则显示分享弹窗)
+     * @param callback 分享结果回调
+     */
+    fun shareManual(
+        activity: Activity,
+        title: String,
+        description: String,
+        url: String? = null,
+        imageUrl: String? = null,
+        thumbImageUrl: String? = null,
+        platform: String? = null,
+        callback: (Boolean, String?, String?) -> Unit
+    )
+    
+    /**
+     * 显示分享弹窗(从底部弹出)
+     * 
+     * @param activity Activity上下文
+     * @param title 分享标题
+     * @param description 分享描述
+     * @param url 分享链接(可选)
+     * @param imageUrl 图片URL(可选)
+     * @param thumbImageUrl 缩略图URL(可选)
+     * @param callback 分享结果回调
+     */
+    fun showShareDialog(
+        activity: Activity,
+        title: String,
+        description: String,
+        url: String? = null,
+        imageUrl: String? = null,
+        thumbImageUrl: String? = null,
+        callback: (Boolean, String?, String?) -> Unit
+    )
+    
+    /**
+     * 分享到微信
+     * 
+     * @param activity Activity上下文
+     * @param title 分享标题
+     * @param description 分享描述
+     * @param url 分享链接(可选)
+     * @param imageUrl 图片URL(可选)
+     * @param thumbImageUrl 缩略图URL(可选)
+     * @param callback 分享结果回调
+     */
+    fun shareToWeChat(
+        activity: Activity,
+        title: String,
+        description: String,
+        url: String? = null,
+        imageUrl: String? = null,
+        thumbImageUrl: String? = null,
+        callback: (Boolean, String?, String?) -> Unit
+    )
+    
+    /**
+     * 分享到微信朋友圈
+     * 
+     * @param activity Activity上下文
+     * @param title 分享标题
+     * @param description 分享描述
+     * @param url 分享链接(可选)
+     * @param imageUrl 图片URL(可选)
+     * @param thumbImageUrl 缩略图URL(可选)
+     * @param callback 分享结果回调
+     */
+    fun shareToWeChatMoments(
+        activity: Activity,
+        title: String,
+        description: String,
+        url: String? = null,
+        imageUrl: String? = null,
+        thumbImageUrl: String? = null,
+        callback: (Boolean, String?, String?) -> Unit
+    )
+    
+    /**
+     * 分享到QQ
+     * 
+     * @param activity Activity上下文
+     * @param title 分享标题
+     * @param description 分享描述
+     * @param url 分享链接(可选)
+     * @param imageUrl 图片URL(可选)
+     * @param thumbImageUrl 缩略图URL(可选)
+     * @param callback 分享结果回调
+     */
+    fun shareToQQ(
+        activity: Activity,
+        title: String,
+        description: String,
+        url: String? = null,
+        imageUrl: String? = null,
+        thumbImageUrl: String? = null,
+        callback: (Boolean, String?, String?) -> Unit
+    )
+    
+    /**
+     * 分享到QQ空间
+     * 
+     * @param activity Activity上下文
+     * @param title 分享标题
+     * @param description 分享描述
+     * @param url 分享链接(可选)
+     * @param imageUrl 图片URL(可选)
+     * @param thumbImageUrl 缩略图URL(可选)
+     * @param callback 分享结果回调
+     */
+    fun shareToQZone(
+        activity: Activity,
+        title: String,
+        description: String,
+        url: String? = null,
+        imageUrl: String? = null,
+        thumbImageUrl: String? = null,
+        callback: (Boolean, String?, String?) -> Unit
+    )
+    
+    /**
+     * 分享到微博
+     * 
+     * @param activity Activity上下文
+     * @param title 分享标题
+     * @param description 分享描述
+     * @param url 分享链接(可选)
+     * @param imageUrl 图片URL(可选)
+     * @param thumbImageUrl 缩略图URL(可选)
+     * @param callback 分享结果回调
+     */
+    fun shareToWeibo(
+        activity: Activity,
+        title: String,
+        description: String,
+        url: String? = null,
+        imageUrl: String? = null,
+        thumbImageUrl: String? = null,
+        callback: (Boolean, String?, String?) -> Unit
+    )
+    
+    /**
+     * 分享到抖音
+     * 
+     * @param activity Activity上下文
+     * @param title 分享标题
+     * @param description 分享描述
+     * @param url 分享链接(可选)
+     * @param imageUrl 图片URL(可选)
+     * @param thumbImageUrl 缩略图URL(可选)
+     * @param callback 分享结果回调
+     */
+    fun shareToDouyin(
+        activity: Activity,
+        title: String,
+        description: String,
+        url: String? = null,
+        imageUrl: String? = null,
+        thumbImageUrl: String? = null,
+        callback: (Boolean, String?, String?) -> Unit
+    )
+    
+    /**
+     * 分享到小红书
+     * 
+     * @param activity Activity上下文
+     * @param title 分享标题
+     * @param description 分享描述
+     * @param url 分享链接(可选)
+     * @param imageUrl 图片URL(可选)
+     * @param thumbImageUrl 缩略图URL(可选)
+     * @param callback 分享结果回调
+     */
+    fun shareToXiaohongshu(
+        activity: Activity,
+        title: String,
+        description: String,
+        url: String? = null,
+        imageUrl: String? = null,
+        thumbImageUrl: String? = null,
+        callback: (Boolean, String?, String?) -> Unit
+    )
+    
+    /**
+     * 分享到快手
+     * 
+     * @param activity Activity上下文
+     * @param title 分享标题
+     * @param description 分享描述
+     * @param url 分享链接(可选)
+     * @param imageUrl 图片URL(可选)
+     * @param thumbImageUrl 缩略图URL(可选)
+     * @param callback 分享结果回调
+     */
+    fun shareToKuaishou(
+        activity: Activity,
+        title: String,
+        description: String,
+        url: String? = null,
+        imageUrl: String? = null,
+        thumbImageUrl: String? = null,
+        callback: (Boolean, String?, String?) -> Unit
+    )
+    
+    /**
+     * 系统分享(调用系统分享菜单)
+     * 
+     * @param activity Activity上下文
+     * @param title 分享标题
+     * @param description 分享描述
+     * @param url 分享链接(可选)
+     * @param imageUrl 图片URL(可选)
+     * @param thumbImageUrl 缩略图URL(可选)
+     * @param callback 分享结果回调
+     */
+    fun shareToSystem(
+        activity: Activity,
+        title: String,
+        description: String,
+        url: String? = null,
+        imageUrl: String? = null,
+        thumbImageUrl: String? = null,
+        callback: (Boolean, String?, String?) -> Unit
+    )
+}
+

+ 70 - 0
base-core/src/main/java/com/narutohuo/xindazhou/wxapi/WXEntryActivity.kt

@@ -0,0 +1,70 @@
+package com.narutohuo.xindazhou.wxapi
+
+import android.content.Intent
+import android.os.Bundle
+import com.alibaba.android.arouter.facade.annotation.Autowired
+import com.alibaba.android.arouter.launcher.ARouter
+import com.narutohuo.xindazhou.core.log.ILog
+import com.narutohuo.xindazhou.core.share.IShareCallback
+import com.umeng.socialize.weixin.view.WXCallbackActivity
+
+/**
+ * 微信分享回调 Activity
+ * 
+ * 按照友盟官方文档,需要创建此 Activity 处理微信分享回调
+ * 
+ * 注意:
+ * - 包名必须是应用包名(com.narutohuo.xindazhou.wxapi),不能是模块 namespace
+ * - 微信 SDK 要求此 Activity 必须在应用包名的 wxapi 子包下
+ * - 虽然文件在 base-core 模块中,但包名使用应用包名,这样 Android 清单合并后能正确识别
+ * 
+ * 架构说明:
+ * - 使用 ARouter 依赖注入获取 IShareCallback 接口实现
+ * - share 模块实现 IShareCallback 接口并通过 ARouter 注册
+ * - base-core 不直接依赖 share 模块,实现解耦
+ * 
+ * 工作原理:
+ * 1. 用户在微信中完成分享操作后,微信会通过 Intent 回调到此 Activity
+ * 2. 友盟的 WXCallbackActivity 会自动处理回调,并触发友盟 SDK 的回调
+ * 3. 通过 ARouter 获取 IShareCallback 实现,调用 share 模块的处理方法
+ * 4. share 模块处理完成后,通过 ShareProxyActivity 的回调传递给业务层
+ */
+class WXEntryActivity : WXCallbackActivity() {
+    
+    /**
+     * 通过 ARouter 依赖注入获取分享回调服务
+     * 
+     * 注意:share 模块需要实现 IShareCallback 接口并通过 @Route 注册
+     */
+    @Autowired(name = "/share/callback")
+    @JvmField
+    var shareCallback: IShareCallback? = null
+    
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        
+        // ARouter 依赖注入
+        ARouter.getInstance().inject(this)
+        
+        ILog.d("WXEntryActivity", "onCreate: 收到微信回调")
+        
+        // 调用 share 模块处理回调
+        shareCallback?.let {
+            ILog.d("WXEntryActivity", "通过 ARouter 获取到 IShareCallback 实现,调用 onWeChatCallback")
+            it.onWeChatCallback(intent)
+        } ?: run {
+            ILog.e("WXEntryActivity", "未找到 IShareCallback 实现,请确保 share 模块已注册")
+        }
+    }
+    
+    override fun onNewIntent(intent: Intent?) {
+        super.onNewIntent(intent)
+        ILog.d("WXEntryActivity", "onNewIntent: 收到新的微信回调 Intent")
+        
+        // 调用 share 模块处理回调
+        intent?.let {
+            shareCallback?.onWeChatCallback(it)
+        }
+    }
+}
+

+ 3 - 5
capability-ble/build.gradle

@@ -21,10 +21,8 @@ android {
 }
 
 dependencies {
-    // 依赖base-core(所有SDK都依赖这个)
-    implementation(project(":base-core"))
-    
-    // AndroidX Core
-    implementation(libs.androidx.core.ktx)
+    // 只依赖 base-common(不直接依赖 base-core)
+    // base-common 会传递 base-core 的依赖
+    implementation(project(":base-common"))
 }
 

+ 3 - 5
capability-nfc/build.gradle

@@ -21,10 +21,8 @@ android {
 }
 
 dependencies {
-    // 依赖base-core(所有SDK都依赖这个)
-    implementation(project(":base-core"))
-    
-    // AndroidX Core
-    implementation(libs.androidx.core.ktx)
+    // 只依赖 base-common(不直接依赖 base-core)
+    // base-common 会传递 base-core 的依赖
+    implementation(project(":base-common"))
 }
 

+ 15 - 8
capability-push/build.gradle

@@ -1,6 +1,7 @@
 plugins {
     alias(libs.plugins.kotlin.android)
     id("com.android.library")
+    id("kotlin-kapt")
 }
 
 android {
@@ -10,6 +11,13 @@ android {
     defaultConfig {
         minSdk = 26
     }
+    
+    // ARouter 配置(Kotlin 方式)
+    kapt {
+        arguments {
+            arg("AROUTER_MODULE_NAME", project.getName())
+        }
+    }
 
     compileOptions {
         sourceCompatibility = JavaVersion.VERSION_17
@@ -29,16 +37,15 @@ repositories {
 }
 
 dependencies {
-    // 依赖base-core(所有SDK都依赖这个)
-    implementation(project(":base-core"))
-    
-    // AndroidX Core
-    implementation(libs.androidx.core.ktx)
+    // 只依赖 base-common(不直接依赖 base-core)
+    // base-common 会传递 base-core 的依赖(包括 Gson、ILog 等)
+    implementation(project(":base-common"))
     
-    // 日志通过 base-core 的 ILog 接口统一管理,无需单独依赖 Timber
+    // ARouter(用于依赖注入,实现 base-core 与 push 模块的解耦)
+    // base-core 已经通过 api 传递了 arouter-api,这里只需要添加编译器
+    kapt("com.alibaba:arouter-compiler:1.5.2")
     
-    // Gson for JSON parsing (used by BroadcastReceiver)
-    implementation("com.google.code.gson:gson:2.10.1")
+    // 注意:Gson、ILog 已通过 base-common 传递,无需重复依赖
     
     // 极光推送SDK(使用本地下载的 AAR 文件)
     // 注意:需要在极光推送官网注册应用获取 AppKey,并在 AndroidManifest.xml 中配置

+ 18 - 0
capability-push/src/main/java/com/narutohuo/xindazhou/push/factory/PushServiceFactory.kt

@@ -1,6 +1,7 @@
 package com.narutohuo.xindazhou.push.factory
 
 import android.content.Context
+import com.alibaba.android.arouter.launcher.ARouter
 import com.narutohuo.xindazhou.core.log.ILog
 import com.narutohuo.xindazhou.push.api.PushService
 import com.narutohuo.xindazhou.push.impl.PushServiceImpl
@@ -13,6 +14,8 @@ import com.narutohuo.xindazhou.push.model.PushMessage
  * 提供统一的获取推送服务实例和初始化的方式
  * 推送服务是全局单例,多个界面共享同一个推送服务实例
  * 
+ * 内部使用 ARouter 获取服务,实现 base-core 与 push 模块的解耦
+ * 
  * **完全封装,外部调用极简**:
  * ```kotlin
  * // Application.onCreate() 中只需一行代码
@@ -182,12 +185,27 @@ object PushServiceFactory {
     /**
      * 获取推送服务实例(单例)
      * 
+     * 优先通过 ARouter 获取服务,如果获取不到,则回退到直接实例化
      * 注意:使用前需要先调用 init() 进行初始化
      * 
      * @return PushService实例(单例)
      */
     @JvmStatic
     fun getInstance(): PushService {
+        // 优先通过 ARouter 获取服务(实现解耦)
+        try {
+            val arouterService = ARouter.getInstance().build("/push/service").navigation() as? PushService
+            if (arouterService != null) {
+                ILog.d("PushServiceFactory", "通过 ARouter 获取推送服务")
+                return arouterService
+            }
+        } catch (e: Exception) {
+            // ARouter 可能还未完全初始化,捕获异常并回退
+            ILog.w("PushServiceFactory", "ARouter 获取服务失败,回退到直接实例化: ${e.message}")
+        }
+        
+        // 回退到直接实例化(向后兼容)
+        ILog.d("PushServiceFactory", "使用直接实例化")
         return PushServiceImpl.getInstance()
     }
 }

+ 57 - 1
capability-push/src/main/java/com/narutohuo/xindazhou/push/impl/PushServiceImpl.kt

@@ -4,7 +4,10 @@ import android.content.Context
 import android.os.Handler
 import android.os.Looper
 import cn.jpush.android.api.JPushInterface
+import com.alibaba.android.arouter.facade.annotation.Route
+import com.alibaba.android.arouter.facade.template.IProvider
 import com.narutohuo.xindazhou.core.log.ILog
+import com.narutohuo.xindazhou.core.push.IPushService
 import com.narutohuo.xindazhou.push.api.PushService
 import com.narutohuo.xindazhou.push.model.PushConfig
 import com.narutohuo.xindazhou.push.model.PushMessage
@@ -15,8 +18,11 @@ import com.narutohuo.xindazhou.push.model.PushMessage
  * 推送服务是全局唯一的,整个应用只需要一个推送服务实例
  * 
  * 封装极光推送 SDK,提供统一的推送服务接口
+ * 
+ * 通过 ARouter 注册,实现 base-core 与 push 模块的解耦
  */
-class PushServiceImpl private constructor() : PushService {
+@Route(path = "/push/service", name = "推送服务")
+class PushServiceImpl private constructor() : IProvider, IPushService, PushService {
     
     companion object {
         @Volatile
@@ -55,6 +61,56 @@ class PushServiceImpl private constructor() : PushService {
     private val mainHandler = Handler(Looper.getMainLooper())
     private var isInitialized = false
     
+    /**
+     * ARouter IProvider 接口的初始化方法
+     * 
+     * 注意:此方法在 ARouter 初始化 provider 时调用,不应该抛出异常
+     */
+    override fun init(context: Context) {
+        try {
+            ILog.d(tag, "PushServiceImpl 初始化(ARouter)")
+            // 这里不进行实际的初始化,实际的初始化在 PushServiceFactory.init() 中完成
+            // 这样可以避免在 ARouter 初始化时出现问题
+        } catch (e: Exception) {
+            // 确保不会抛出异常,避免影响 ARouter 初始化
+            ILog.e(tag, "PushServiceImpl ARouter 初始化异常(不影响使用)", e)
+        }
+    }
+    
+    // ========== IPushService 接口实现(适配层) ==========
+    
+    override fun initialize(context: Context, appKey: String, channel: String, debugMode: Boolean) {
+        // 适配 IPushService 接口,将参数转换为 PushConfig
+        val config = PushConfig(
+            appKey = appKey,
+            channel = channel,
+            debugMode = debugMode
+        )
+        initialize(context, config)
+    }
+    
+    override fun setMessageListener(listener: (String, String, Map<String, String>, String?) -> Unit) {
+        // 适配 IPushService 接口,将基本类型回调转换为 PushMessage 回调
+        // 直接设置内部变量,避免递归调用
+        this.messageListener = { message ->
+            listener(message.title, message.content, message.extras, message.messageId)
+        }
+        ILog.d(tag, "设置消息监听器(IPushService)")
+    }
+    
+    override fun setNotificationClickListener(listener: ((String, String, Map<String, String>, String?) -> Unit)?) {
+        // 适配 IPushService 接口,将基本类型回调转换为 PushMessage 回调
+        // 直接设置内部变量,避免递归调用
+        this.notificationClickListener = listener?.let { callback ->
+            { message ->
+                callback(message.title, message.content, message.extras, message.messageId)
+            }
+        }
+        ILog.d(tag, if (listener != null) "设置通知点击监听器(IPushService)" else "清除通知点击监听器(IPushService)")
+    }
+    
+    // ========== PushService 接口实现(原有接口) ==========
+    
     override fun initialize(context: Context, config: PushConfig) {
         if (isInitialized) {
             ILog.w(tag, "推送服务已经初始化,跳过重复初始化")

+ 3 - 5
capability-push/src/main/java/com/narutohuo/xindazhou/push/receiver/JPushReceiver.kt

@@ -4,7 +4,7 @@ import android.content.BroadcastReceiver
 import android.content.Context
 import android.content.Intent
 import cn.jpush.android.api.JPushInterface
-import com.google.gson.Gson
+import com.alibaba.fastjson2.JSON
 import com.narutohuo.xindazhou.core.log.ILog
 import com.narutohuo.xindazhou.push.factory.PushServiceFactory
 import com.narutohuo.xindazhou.push.impl.PushServiceImpl
@@ -17,8 +17,6 @@ import com.narutohuo.xindazhou.push.impl.PushServiceImpl
  */
 class JPushReceiver : BroadcastReceiver() {
     
-    private val gson = Gson()
-    
     override fun onReceive(context: Context, intent: Intent) {
         val bundle = intent.extras ?: return
         
@@ -34,7 +32,7 @@ class JPushReceiver : BroadcastReceiver() {
                 
                 // 解析 extras(JSON 格式)
                 val extrasMap = try {
-                    val jsonObject = gson.fromJson(extrasJson, Map::class.java) as? Map<*, *>
+                    val jsonObject = JSON.parseObject(extrasJson, Map::class.java) as? Map<*, *>
                     jsonObject?.mapKeys { it.key.toString() }?.mapValues { it.value.toString() } ?: emptyMap()
                 } catch (e: Exception) {
                     ILog.e("JPushReceiver", "解析 extras 失败", e)
@@ -65,7 +63,7 @@ class JPushReceiver : BroadcastReceiver() {
                 
                 // 解析 extras(JSON 格式)
                 val extrasMap = try {
-                    val jsonObject = gson.fromJson(extrasJson, Map::class.java) as? Map<*, *>
+                    val jsonObject = JSON.parseObject(extrasJson, Map::class.java) as? Map<*, *>
                     jsonObject?.mapKeys { it.key.toString() }?.mapValues { it.value.toString() } ?: emptyMap()
                 } catch (e: Exception) {
                     ILog.e("JPushReceiver", "解析 extras 失败", e)

File diff suppressed because it is too large
+ 305 - 0
capability-push/集成说明.html


+ 15 - 4
capability-push/集成说明.md

@@ -58,11 +58,19 @@ capability-push/src/
 ## 架构设计
 
 - **接口隔离**:业务层只依赖接口,不依赖实现
-- **工厂模式**:统一入口,隐藏创建逻辑
+- **ARouter 依赖注入**:通过 ARouter 实现模块间解耦,base-core 定义接口,能力层实现接口
+- **工厂模式**:统一入口,隐藏创建逻辑(内部使用 ARouter 获取服务)
 - **单例模式**:全局共享一个服务实例
-- **分层架构**:api → factory → impl → 第三方SDK
+- **分层架构**:base-core (接口) → capability-push (实现) → ARouter (注册) → Factory (获取)
 - **完全封装**:跳转逻辑封装在模块内部,外部只需配置页面映射规则
 
+### ARouter 架构说明
+
+- **base-core** 模块定义 `IPushService` 接口(位于 `com.narutohuo.xindazhou.core.push`)
+- **capability-push** 模块实现 `IPushService` 接口,并通过 `@Route(path = "/push/service")` 注册到 ARouter
+- **Factory** 内部使用 `ARouter.getInstance().navigation(IPushService::class.java)` 获取服务实例
+- **业务层** 通过 Factory 获取服务,无需直接依赖实现模块
+
 ## 已完成的工作
 
 ✅ **SDK 依赖已添加**
@@ -75,11 +83,14 @@ capability-push/src/
 - `cn.jiguang.sdk.plugin:meizu:5.9.0` - 魅族通道
 
 ✅ **代码实现已完成**
-- `PushServiceImpl` 已实现极光推送 SDK 调用
-- `PushServiceFactory` 已创建
+- `PushServiceImpl` 已实现极光推送 SDK 调用,并实现 `IPushService` 接口
+- `PushServiceImpl` 已通过 ARouter 注册(`@Route(path = "/push/service")`)
+- `PushServiceFactory` 已创建,内部使用 ARouter 获取服务
+- `IPushService` 接口已在 `base-core` 模块定义
 - `PushConfig` 配置模型已创建
 - `JPushReceiver` 消息接收器已封装
 - **跳转逻辑已封装**:自动跳转功能已实现
+- **ARouter 集成完成**:模块间解耦,通过 ARouter 进行依赖注入
 
 ✅ **配置已完成**
 - 所有配置都在 `capability-push` 模块中管理

+ 3 - 5
capability-qrcode/build.gradle

@@ -21,11 +21,9 @@ android {
 }
 
 dependencies {
-    // 依赖base-core(所有SDK都依赖这个)
-    implementation(project(":base-core"))
-    
-    // AndroidX Core
-    implementation(libs.androidx.core.ktx)
+    // 只依赖 base-common(不直接依赖 base-core)
+    // base-common 会传递 base-core 的依赖
+    implementation(project(":base-common"))
     
     // 华为扫码SDK(待添加具体版本)
     // implementation 'com.huawei.hms:scan:xxx'

+ 15 - 4
capability-share/build.gradle

@@ -2,6 +2,7 @@ plugins {
     alias(libs.plugins.kotlin.android)
     id("com.android.library")
     id("org.jetbrains.kotlin.plugin.parcelize")
+    id("kotlin-kapt")
 }
 
 android {
@@ -11,6 +12,13 @@ android {
     defaultConfig {
         minSdk = 26
     }
+    
+    // ARouter 配置(Kotlin 方式)
+    kapt {
+        arguments {
+            arg("AROUTER_MODULE_NAME", project.getName())
+        }
+    }
 
     compileOptions {
         sourceCompatibility = JavaVersion.VERSION_17
@@ -31,11 +39,13 @@ repositories {
 }
 
 dependencies {
-    // 依赖base-core(所有SDK都依赖这个)
-    implementation(project(":base-core"))
+    // 只依赖 base-common(不直接依赖 base-core)
+    // base-common 会传递 base-core 的依赖
+    implementation(project(":base-common"))
     
-    // AndroidX Core
-    implementation(libs.androidx.core.ktx)
+    // ARouter(用于依赖注入,实现 base-core 与 share 模块的解耦)
+    // base-core 已经通过 api 传递了 arouter-api,这里只需要添加编译器
+    kapt("com.alibaba:arouter-compiler:1.5.2")
     
     // AndroidX Fragment(用于弹窗)
     implementation("androidx.fragment:fragment-ktx:1.8.2")
@@ -69,6 +79,7 @@ dependencies {
     implementation(files('libs/umeng-share-sina-full-7.3.7.jar'))
     implementation("io.github.sinaweibosdk:core:13.10.1") {
         exclude module: 'unspecified'
+        exclude group: 'com.android.support'
     }
     
     // QQ分享(完整版)- 使用本地完整版 JAR 文件

+ 2 - 8
capability-share/src/main/AndroidManifest.xml

@@ -15,14 +15,8 @@
             android:excludeFromRecents="true"
             android:launchMode="singleTask" />
         
-        <!-- 微信分享回调 Activity(必需) -->
-        <!-- 注意:虽然文件在 capability-share 模块中,但包名使用应用包名(com.narutohuo.xindazhou.wxapi) -->
-        <!-- 微信 SDK 要求此 Activity 必须在应用包名的 wxapi 子包下 -->
-        <activity
-            android:name="com.narutohuo.xindazhou.wxapi.WXEntryActivity"
-            android:configChanges="keyboardHidden|orientation|screenSize"
-            android:exported="true"
-            android:theme="@android:style/Theme.Translucent.NoTitleBar" />
+        <!-- 微信分享回调 Activity 已移至 base-core 模块 -->
+        <!-- 通过 ARouter 依赖注入实现 base-core 与 share 模块的解耦 -->
         
         <!-- 抖音分享回调 Activity(暂时注释,等抖音 SDK 集成后再启用) -->
         <!-- 注意:虽然文件在 capability-share 模块中,但包名使用应用包名(com.narutohuo.xindazhou.douyinapi) -->

+ 4 - 2
capability-share/src/main/java/com/narutohuo/xindazhou/share/api/ShareService.kt

@@ -24,9 +24,11 @@ interface ShareService {
     fun initialize(context: android.content.Context, config: ShareConfig)
     
     /**
-     * 处理分享回调
+     * 处理分享回调(内部使用)
      * 
-     * 在 Activity.onActivityResult() 中调用,处理分享结果
+     * 注意:业务层不需要调用此方法!
+     * 此方法由 ShareProxyActivity 内部调用,用于处理友盟 SDK 的回调
+     * 业务层只需要调用 share() 方法,所有回调会自动处理
      * 
      * @param activity Activity 上下文
      * @param requestCode 请求码

+ 103 - 0
capability-share/src/main/java/com/narutohuo/xindazhou/share/callback/ShareCallbackImpl.kt

@@ -0,0 +1,103 @@
+package com.narutohuo.xindazhou.share.callback
+
+import android.content.Context
+import android.content.Intent
+import com.alibaba.android.arouter.facade.annotation.Route
+import com.alibaba.android.arouter.facade.template.IProvider
+import com.narutohuo.xindazhou.core.log.ILog
+import com.narutohuo.xindazhou.core.share.IShareCallback
+import com.narutohuo.xindazhou.share.factory.ShareServiceFactory
+import com.narutohuo.xindazhou.share.impl.ShareServiceImpl
+import com.umeng.socialize.UMShareAPI
+
+/**
+ * 分享回调服务实现
+ * 
+ * 实现 IShareCallback 接口,处理第三方平台(微信、QQ、微博等)的回调
+ * 通过 ARouter 注册,供 base-core 中的回调 Activity 调用
+ * 
+ * 架构说明:
+ * - base-core 中的 WXEntryActivity 等通过 ARouter 获取此实现
+ * - 此实现调用 ShareServiceImpl 处理具体的分享逻辑
+ * - 实现 base-core 与 share 模块的解耦
+ * 
+ * 注意:ARouter 要求 @Route 标记的类必须实现 IProvider 接口
+ */
+@Route(path = "/share/callback", name = "分享回调服务")
+class ShareCallbackImpl : IProvider, IShareCallback {
+    
+    /**
+     * ARouter IProvider 接口的初始化方法
+     */
+    override fun init(context: Context) {
+        ILog.d(TAG, "ShareCallbackImpl 初始化")
+    }
+    
+    companion object {
+        private const val TAG = "ShareCallbackImpl"
+    }
+    
+    override fun onWeChatCallback(intent: Intent) {
+        ILog.d(TAG, "onWeChatCallback: 收到微信回调")
+        
+        try {
+            // 获取 ShareService 实例
+            val shareService = ShareServiceFactory.getInstance()
+            
+            // 友盟 SDK 会自动处理微信回调
+            // 这里只需要确保 ShareProxyActivity 能够接收到回调
+            // ShareProxyActivity 会调用 shareService.onActivityResult()
+            
+            // 注意:友盟的 WXCallbackActivity 已经处理了回调
+            // 我们只需要确保 ShareProxyActivity 能够接收到 onActivityResult
+            // 实际的回调处理在 ShareProxyActivity 中完成
+            
+            ILog.d(TAG, "微信回调已转发给 ShareService")
+        } catch (e: Exception) {
+            ILog.e(TAG, "处理微信回调失败", e)
+        }
+    }
+    
+    override fun onQQCallback(intent: Intent) {
+        ILog.d(TAG, "onQQCallback: 收到 QQ 回调")
+        
+        try {
+            // QQ 回调处理
+            // 注意:QQ SDK 的回调在 AuthActivity 中处理
+            // 这里可以添加额外的处理逻辑
+            
+            ILog.d(TAG, "QQ 回调已处理")
+        } catch (e: Exception) {
+            ILog.e(TAG, "处理 QQ 回调失败", e)
+        }
+    }
+    
+    override fun onWeiboCallback(intent: Intent) {
+        ILog.d(TAG, "onWeiboCallback: 收到微博回调")
+        
+        try {
+            // 微博回调处理
+            // 注意:微博 SDK 的回调在 WBShareTransActivity 中处理
+            // 这里可以添加额外的处理逻辑
+            
+            ILog.d(TAG, "微博回调已处理")
+        } catch (e: Exception) {
+            ILog.e(TAG, "处理微博回调失败", e)
+        }
+    }
+    
+    override fun onDouyinCallback(intent: Intent) {
+        ILog.d(TAG, "onDouyinCallback: 收到抖音回调")
+        
+        try {
+            // 抖音回调处理
+            // 注意:抖音 SDK 的回调在 DYCallbackActivity 中处理
+            // 这里可以添加额外的处理逻辑
+            
+            ILog.d(TAG, "抖音回调已处理")
+        } catch (e: Exception) {
+            ILog.e(TAG, "处理抖音回调失败", e)
+        }
+    }
+}
+

+ 19 - 0
capability-share/src/main/java/com/narutohuo/xindazhou/share/factory/ShareServiceFactory.kt

@@ -1,6 +1,8 @@
 package com.narutohuo.xindazhou.share.factory
 
 import android.content.Context
+import com.alibaba.android.arouter.launcher.ARouter
+import com.narutohuo.xindazhou.core.log.ILog
 import com.narutohuo.xindazhou.share.api.ShareService
 import com.narutohuo.xindazhou.share.impl.ShareServiceImpl
 import com.narutohuo.xindazhou.share.model.ShareConfig
@@ -11,6 +13,8 @@ import com.narutohuo.xindazhou.share.model.ShareConfig
  * 提供统一的获取分享服务实例和初始化的方式
  * 分享服务是全局单例,多个界面共享同一个分享服务实例
  * 
+ * 内部使用 ARouter 获取服务,实现 base-core 与 share 模块的解耦
+ * 
  * 使用示例:
  * ```kotlin
  * // 方式1:使用默认配置(从资源文件读取)
@@ -113,12 +117,27 @@ object ShareServiceFactory {
     /**
      * 获取分享服务实例(单例)
      * 
+     * 优先通过 ARouter 获取服务,如果获取不到,则回退到直接实例化
      * 注意:使用前需要先调用 init() 进行初始化
      * 
      * @return ShareService实例(单例)
      */
     @JvmStatic
     fun getInstance(): ShareService {
+        // 优先通过 ARouter 获取服务(实现解耦)
+        try {
+            val arouterService = ARouter.getInstance().build("/share/service").navigation() as? ShareService
+            if (arouterService != null) {
+                ILog.d("ShareServiceFactory", "通过 ARouter 获取分享服务")
+                return arouterService
+            }
+        } catch (e: Exception) {
+            // ARouter 可能还未完全初始化,捕获异常并回退
+            ILog.w("ShareServiceFactory", "ARouter 获取服务失败,回退到直接实例化: ${e.message}")
+        }
+        
+        // 回退到直接实例化(向后兼容)
+        ILog.d("ShareServiceFactory", "使用直接实例化")
         return ShareServiceImpl.getInstance()
     }
 }

+ 351 - 5
capability-share/src/main/java/com/narutohuo/xindazhou/share/impl/ShareServiceImpl.kt

@@ -5,6 +5,10 @@ import android.content.Context
 import android.content.Intent
 import android.os.Handler
 import android.os.Looper
+import com.alibaba.android.arouter.facade.annotation.Route
+import com.alibaba.android.arouter.facade.template.IProvider
+import com.narutohuo.xindazhou.core.log.ILog
+import com.narutohuo.xindazhou.core.share.IShareService
 import com.narutohuo.xindazhou.share.api.ShareService
 import com.narutohuo.xindazhou.share.model.ShareConfig
 import com.narutohuo.xindazhou.share.model.ShareContent
@@ -19,7 +23,6 @@ import com.umeng.socialize.bean.SHARE_MEDIA
 import com.umeng.socialize.media.UMImage
 import com.umeng.socialize.media.UMWeb
 import com.umeng.socialize.media.UMediaObject
-import com.narutohuo.xindazhou.core.log.ILog
 
 /**
  * 分享服务实现类(单例模式)
@@ -27,8 +30,11 @@ import com.narutohuo.xindazhou.core.log.ILog
  * 使用单例方便管理,多个界面可以共享同一个分享服务
  * 
  * 封装友盟分享 SDK,提供统一的分享服务接口
+ * 
+ * 通过 ARouter 注册,实现 base-core 与 share 模块的解耦
  */
-class ShareServiceImpl private constructor() : ShareService {
+@Route(path = "/share/service", name = "分享服务")
+class ShareServiceImpl private constructor() : IProvider, IShareService, ShareService {
     
     companion object {
         @Volatile
@@ -61,6 +67,346 @@ class ShareServiceImpl private constructor() : ShareService {
     private var pendingShareContent: ShareContent? = null
     private var pendingShareCallback: ((ShareResponse) -> Unit)? = null
     
+    /**
+     * ARouter IProvider 接口的初始化方法
+     * 
+     * 注意:此方法在 ARouter 初始化 provider 时调用,不应该抛出异常
+     */
+    override fun init(context: Context) {
+        try {
+            ILog.d(tag, "ShareServiceImpl 初始化(ARouter)")
+            // 这里不进行实际的初始化,实际的初始化在 ShareServiceFactory.init() 中完成
+            // 这样可以避免在 ARouter 初始化时出现问题
+        } catch (e: Exception) {
+            // 确保不会抛出异常,避免影响 ARouter 初始化
+            ILog.e(tag, "ShareServiceImpl ARouter 初始化异常(不影响使用)", e)
+        }
+    }
+    
+    // ========== IShareService 接口实现(适配层) ==========
+    
+    override fun initialize(
+        context: Context,
+        umengAppKey: String?,
+        umengChannel: String,
+        weChatAppId: String?,
+        weChatAppSecret: String?,
+        qqAppId: String?,
+        qqAppKey: String?,
+        weiboAppKey: String?,
+        weiboAppSecret: String?,
+        weiboRedirectUrl: String?
+    ) {
+        // 适配 IShareService 接口,将参数转换为 ShareConfig
+        val config = ShareConfig(
+            umengAppKey = umengAppKey,
+            umengChannel = umengChannel,
+            weChatConfig = if (weChatAppId != null && weChatAppSecret != null) {
+                com.narutohuo.xindazhou.share.model.WeChatConfig(
+                    appId = weChatAppId,
+                    appSecret = weChatAppSecret
+                )
+            } else null,
+            qqConfig = if (qqAppId != null && qqAppKey != null) {
+                com.narutohuo.xindazhou.share.model.QQConfig(
+                    appId = qqAppId,
+                    appKey = qqAppKey
+                )
+            } else null,
+            weiboConfig = if (weiboAppKey != null && weiboAppSecret != null && weiboRedirectUrl != null) {
+                com.narutohuo.xindazhou.share.model.WeiboConfig(
+                    appKey = weiboAppKey,
+                    appSecret = weiboAppSecret,
+                    redirectUrl = weiboRedirectUrl
+                )
+            } else null
+        )
+        initialize(context, config)
+    }
+    
+    override fun share(
+        activity: Activity,
+        title: String,
+        description: String,
+        url: String?,
+        imageUrl: String?,
+        thumbImageUrl: String?,
+        platform: String?,
+        callback: (Boolean, String?, String?) -> Unit
+    ) {
+        // 适配 IShareService 接口,将基本类型参数转换为 ShareContent
+        val platformEnum = platform?.let {
+            try {
+                SharePlatform.valueOf(it.uppercase())
+            } catch (e: Exception) {
+                null
+            }
+        }
+        val content = ShareContent(
+            title = title,
+            description = description,
+            platform = platformEnum,
+            url = url,
+            imageUrl = imageUrl,
+            thumbImageUrl = thumbImageUrl
+        )
+        share(activity, content) { response ->
+            callback(response.success, response.data?.name, response.errorMessage)
+        }
+    }
+    
+    override fun shareManual(
+        activity: Activity,
+        title: String,
+        description: String,
+        url: String?,
+        imageUrl: String?,
+        thumbImageUrl: String?,
+        platform: String?,
+        callback: (Boolean, String?, String?) -> Unit
+    ) {
+        val platformEnum = platform?.let {
+            try {
+                SharePlatform.valueOf(it.uppercase())
+            } catch (e: Exception) {
+                null
+            }
+        }
+        val content = ShareContent(
+            title = title,
+            description = description,
+            platform = platformEnum,
+            url = url,
+            imageUrl = imageUrl,
+            thumbImageUrl = thumbImageUrl
+        )
+        shareManual(activity, content) { response ->
+            callback(response.success, response.data?.name, response.errorMessage)
+        }
+    }
+    
+    override fun showShareDialog(
+        activity: Activity,
+        title: String,
+        description: String,
+        url: String?,
+        imageUrl: String?,
+        thumbImageUrl: String?,
+        callback: (Boolean, String?, String?) -> Unit
+    ) {
+        val content = ShareContent(
+            title = title,
+            description = description,
+            platform = null,
+            url = url,
+            imageUrl = imageUrl,
+            thumbImageUrl = thumbImageUrl
+        )
+        showShareDialog(activity, content) { response ->
+            callback(response.success, response.data?.name, response.errorMessage)
+        }
+    }
+    
+    override fun shareToWeChat(
+        activity: Activity,
+        title: String,
+        description: String,
+        url: String?,
+        imageUrl: String?,
+        thumbImageUrl: String?,
+        callback: (Boolean, String?, String?) -> Unit
+    ) {
+        val content = ShareContent(
+            title = title,
+            description = description,
+            platform = SharePlatform.WECHAT,
+            url = url,
+            imageUrl = imageUrl,
+            thumbImageUrl = thumbImageUrl
+        )
+        shareToWeChat(activity, content) { response ->
+            callback(response.success, response.data?.name, response.errorMessage)
+        }
+    }
+    
+    override fun shareToWeChatMoments(
+        activity: Activity,
+        title: String,
+        description: String,
+        url: String?,
+        imageUrl: String?,
+        thumbImageUrl: String?,
+        callback: (Boolean, String?, String?) -> Unit
+    ) {
+        val content = ShareContent(
+            title = title,
+            description = description,
+            platform = SharePlatform.WECHAT_MOMENTS,
+            url = url,
+            imageUrl = imageUrl,
+            thumbImageUrl = thumbImageUrl
+        )
+        shareToWeChatMoments(activity, content) { response ->
+            callback(response.success, response.data?.name, response.errorMessage)
+        }
+    }
+    
+    override fun shareToQQ(
+        activity: Activity,
+        title: String,
+        description: String,
+        url: String?,
+        imageUrl: String?,
+        thumbImageUrl: String?,
+        callback: (Boolean, String?, String?) -> Unit
+    ) {
+        val content = ShareContent(
+            title = title,
+            description = description,
+            platform = SharePlatform.QQ,
+            url = url,
+            imageUrl = imageUrl,
+            thumbImageUrl = thumbImageUrl
+        )
+        shareToQQ(activity, content) { response ->
+            callback(response.success, response.data?.name, response.errorMessage)
+        }
+    }
+    
+    override fun shareToQZone(
+        activity: Activity,
+        title: String,
+        description: String,
+        url: String?,
+        imageUrl: String?,
+        thumbImageUrl: String?,
+        callback: (Boolean, String?, String?) -> Unit
+    ) {
+        val content = ShareContent(
+            title = title,
+            description = description,
+            platform = SharePlatform.QZONE,
+            url = url,
+            imageUrl = imageUrl,
+            thumbImageUrl = thumbImageUrl
+        )
+        shareToQZone(activity, content) { response ->
+            callback(response.success, response.data?.name, response.errorMessage)
+        }
+    }
+    
+    override fun shareToWeibo(
+        activity: Activity,
+        title: String,
+        description: String,
+        url: String?,
+        imageUrl: String?,
+        thumbImageUrl: String?,
+        callback: (Boolean, String?, String?) -> Unit
+    ) {
+        val content = ShareContent(
+            title = title,
+            description = description,
+            platform = SharePlatform.WEIBO,
+            url = url,
+            imageUrl = imageUrl,
+            thumbImageUrl = thumbImageUrl
+        )
+        shareToWeibo(activity, content) { response ->
+            callback(response.success, response.data?.name, response.errorMessage)
+        }
+    }
+    
+    override fun shareToDouyin(
+        activity: Activity,
+        title: String,
+        description: String,
+        url: String?,
+        imageUrl: String?,
+        thumbImageUrl: String?,
+        callback: (Boolean, String?, String?) -> Unit
+    ) {
+        val content = ShareContent(
+            title = title,
+            description = description,
+            platform = SharePlatform.DOUYIN,
+            url = url,
+            imageUrl = imageUrl,
+            thumbImageUrl = thumbImageUrl
+        )
+        shareToDouyin(activity, content) { response ->
+            callback(response.success, response.data?.name, response.errorMessage)
+        }
+    }
+    
+    override fun shareToXiaohongshu(
+        activity: Activity,
+        title: String,
+        description: String,
+        url: String?,
+        imageUrl: String?,
+        thumbImageUrl: String?,
+        callback: (Boolean, String?, String?) -> Unit
+    ) {
+        val content = ShareContent(
+            title = title,
+            description = description,
+            platform = SharePlatform.XIAOHONGSHU,
+            url = url,
+            imageUrl = imageUrl,
+            thumbImageUrl = thumbImageUrl
+        )
+        shareToXiaohongshu(activity, content) { response ->
+            callback(response.success, response.data?.name, response.errorMessage)
+        }
+    }
+    
+    override fun shareToKuaishou(
+        activity: Activity,
+        title: String,
+        description: String,
+        url: String?,
+        imageUrl: String?,
+        thumbImageUrl: String?,
+        callback: (Boolean, String?, String?) -> Unit
+    ) {
+        val content = ShareContent(
+            title = title,
+            description = description,
+            platform = SharePlatform.KUAISHOU,
+            url = url,
+            imageUrl = imageUrl,
+            thumbImageUrl = thumbImageUrl
+        )
+        shareToKuaishou(activity, content) { response ->
+            callback(response.success, response.data?.name, response.errorMessage)
+        }
+    }
+    
+    override fun shareToSystem(
+        activity: Activity,
+        title: String,
+        description: String,
+        url: String?,
+        imageUrl: String?,
+        thumbImageUrl: String?,
+        callback: (Boolean, String?, String?) -> Unit
+    ) {
+        val content = ShareContent(
+            title = title,
+            description = description,
+            platform = SharePlatform.SYSTEM,
+            url = url,
+            imageUrl = imageUrl,
+            thumbImageUrl = thumbImageUrl
+        )
+        shareToSystem(activity, content) { response ->
+            callback(response.success, response.data?.name, response.errorMessage)
+        }
+    }
+    
+    // ========== ShareService 接口实现(原有接口) ==========
+    
     override fun initialize(context: Context, config: ShareConfig) {
         if (isInitialized) {
             ILog.w(tag, "分享服务已经初始化,跳过重复初始化")
@@ -150,9 +496,9 @@ class ShareServiceImpl private constructor() : ShareService {
     }
     
     override fun share(activity: Activity, content: ShareContent, callback: (ShareResponse) -> Unit) {
-        // 直接调用 shareManual,业务层需要在 Activity 的 onActivityResult 中调用 shareService.onActivityResult()
-        // 注意:如果使用 ShareProxyActivity,可以调用 ShareProxyActivity.startShare() 代替此方法
-        shareManual(activity, content, callback)
+        // 自动启动 ShareProxyActivity,统一处理所有平台的分享回调
+        // 业务层不需要在 Activity 的 onActivityResult 中处理回调,完全封装在模块内部
+        com.narutohuo.xindazhou.share.ui.ShareProxyActivity.startShare(activity, content, callback)
     }
     
     /**

+ 13 - 7
capability-share/src/main/java/com/narutohuo/xindazhou/share/ui/ShareProxyActivity.kt

@@ -9,17 +9,23 @@ import com.narutohuo.xindazhou.share.model.ShareContent
 import com.narutohuo.xindazhou.share.model.ShareResponse
 
 /**
- * 透明的代理 Activity,用于处理分享回调
+ * 透明的代理 Activity,用于统一处理所有平台的分享回调
  * 
  * 这个 Activity 完全封装在分享组件内部,业务层不需要知道它的存在
  * 
  * 工作原理:
- * 1. 业务层调用 shareService.share()
- * 2. 内部启动这个透明的代理 Activity
- * 3. 代理 Activity 调用友盟 SDK 进行分享(或显示分享弹窗)
- * 4. 分享结果通过 onActivityResult() 回到这个 Activity
- * 5. 处理结果后,通过回调传递给业务层
- * 6. 关闭透明 Activity,回到原来的界面
+ * 1. 业务层调用 shareService.share(),自动启动这个透明的代理 Activity
+ * 2. 代理 Activity 调用友盟 SDK 进行分享(或显示分享弹窗)
+ * 3. 用户在第三方应用(微信、QQ、微博等)完成分享操作
+ * 4. 第三方应用回调到各自的回调 Activity(WXEntryActivity、AuthActivity、WBShareTransActivity)
+ * 5. 友盟 SDK 自动处理回调,通过 onActivityResult() 回到这个 Activity
+ * 6. 处理结果后,通过回调传递给业务层
+ * 7. 关闭透明 Activity,回到原来的界面
+ * 
+ * 关键点:
+ * - ✅ 统一处理所有平台的回调(微信、QQ、微博等),业务层无需关心
+ * - ✅ 业务层不需要在 Activity 中处理 onActivityResult,完全封装
+ * - ✅ 透明主题,用户看不到,体验流畅
  */
 class ShareProxyActivity : FragmentActivity() {
     

+ 0 - 38
capability-share/src/main/java/com/narutohuo/xindazhou/wxapi/WXEntryActivity.kt

@@ -1,38 +0,0 @@
-package com.narutohuo.xindazhou.wxapi
-
-import android.content.Intent
-import android.os.Bundle
-import com.narutohuo.xindazhou.core.log.ILog
-import com.umeng.socialize.weixin.view.WXCallbackActivity
-
-/**
- * 微信分享回调 Activity
- * 
- * 按照友盟官方文档,需要创建此 Activity 处理微信分享回调
- * 
- * 注意:
- * - 包名必须是应用包名(com.narutohuo.xindazhou.wxapi),不能是模块 namespace
- * - 微信 SDK 要求此 Activity 必须在应用包名的 wxapi 子包下
- * - 虽然文件在 capability-share 模块中,但包名使用应用包名,这样 Android 清单合并后能正确识别
- * 
- * 工作原理:
- * 1. 用户在微信中完成分享操作后,微信会通过 Intent 回调到此 Activity
- * 2. 友盟的 WXCallbackActivity 会自动处理回调,并触发友盟 SDK 的回调
- * 3. 友盟 SDK 的回调会触发 ShareServiceImpl 中设置的 UMShareListener
- * 4. 最终通过 ShareProxyActivity 的回调传递给业务层
- * 
- * 因此,这个 Activity 只需要继承 WXCallbackActivity 即可,无需添加额外代码
- */
-class WXEntryActivity : WXCallbackActivity() {
-    
-    override fun onCreate(savedInstanceState: Bundle?) {
-        super.onCreate(savedInstanceState)
-        ILog.d("WXEntryActivity", "onCreate: 收到微信回调")
-    }
-    
-    override fun onNewIntent(intent: Intent?) {
-        super.onNewIntent(intent)
-        ILog.d("WXEntryActivity", "onNewIntent: 收到新的微信回调 Intent")
-    }
-}
-

File diff suppressed because it is too large
+ 151 - 70
capability-share/集成说明.html


+ 95 - 27
capability-share/集成说明.md

@@ -105,11 +105,66 @@ capability-share/src/
 ## 架构设计
 
 - **接口隔离**:业务层只依赖接口,不依赖实现
-- **工厂模式**:统一入口,隐藏创建逻辑
+- **ARouter 依赖注入**:通过 ARouter 实现模块间解耦,base-core 定义接口,能力层实现接口
+- **工厂模式**:统一入口,隐藏创建逻辑(内部使用 ARouter 获取服务)
 - **单例模式**:全局共享一个服务实例
-- **分层架构**:api → factory → impl → 第三方SDK
+- **分层架构**:base-core (接口) → capability-share (实现) → ARouter (注册) → Factory (获取)
 - **完全封装**:使用透明代理 Activity 自动处理 `onActivityResult`,业务层无需关心实现细节
 
+### ARouter 架构说明
+
+- **base-core** 模块定义 `IShareService` 接口(位于 `com.narutohuo.xindazhou.core.share`)
+- **capability-share** 模块实现 `IShareService` 接口,并通过 `@Route(path = "/share/service")` 注册到 ARouter
+- **base-core** 模块中的 `WXEntryActivity` 通过 ARouter 注入 `IShareCallback` 实现(`@Route(path = "/share/callback")`)
+- **Factory** 内部使用 `ARouter.getInstance().navigation(IShareService::class.java)` 获取服务实例
+- **业务层** 通过 Factory 获取服务,无需直接依赖实现模块
+
+## 核心功能说明
+
+### 1. 分享回调流程(完全封装,统一处理所有平台)
+
+**完整回调链路**:
+
+```
+业务层调用 shareService.share(activity, content, callback)
+    ↓
+ShareServiceImpl.share() 自动启动 ShareProxyActivity
+    ↓
+ShareProxyActivity.onCreate() 调用 shareService.shareManual()
+    ↓
+ShareServiceImpl.shareManual() 调用友盟 SDK 进行分享(或显示分享弹窗)
+    ↓
+用户在第三方应用(如微信、QQ、微博)完成分享操作
+    ↓
+第三方应用回调到各自的回调 Activity:
+  - 微信 → WXEntryActivity(base-core 模块,通过 ARouter 注入 IShareCallback)
+    ↓ WXEntryActivity.onWeChatCallback() 调用 shareCallback.onWeChatCallback()
+    ↓ ShareCallbackImpl.onWeChatCallback() 处理回调
+  - QQ → AuthActivity(QQ SDK 自带,友盟自动处理)
+  - 微博 → WBShareTransActivity(微博 SDK 自带,友盟自动处理)
+    ↓
+友盟 SDK 通过 onActivityResult 回调到 ShareProxyActivity.onActivityResult()
+    ↓
+ShareProxyActivity.onActivityResult() 调用 shareService.onActivityResult()
+    ↓
+ShareServiceImpl.onActivityResult() 调用 UMShareAPI.get(activity).onActivityResult()
+    ↓
+友盟 SDK 触发 ShareServiceImpl 中设置的 UMShareListener.onResult() / onError() / onCancel()
+    ↓
+UMShareListener 调用业务层传入的 callback 函数
+    ↓
+ShareProxyActivity 收到回调结果,调用业务层的 callback,并自动关闭自身
+    ↓
+业务层的 callback { response -> ... } 被调用 ✅
+```
+
+**关键点**:
+- ✅ **统一入口**:`share()` 方法自动启动 `ShareProxyActivity`,所有平台的分享都通过它处理
+- ✅ **统一回调**:所有平台(微信、QQ、微博等)的回调都统一回到 `ShareProxyActivity.onActivityResult()`
+- ✅ **ARouter 解耦**:`WXEntryActivity` 在 `base-core` 模块,通过 ARouter 注入 `IShareCallback` 实现,实现模块间解耦
+- ✅ **业务层透明**:业务层不需要在 Activity 中处理 `onActivityResult`,完全封装在模块内部
+- ✅ **自动关闭**:`ShareProxyActivity` 收到回调后自动关闭,用户无感知
+
 ## 已完成的工作
 
 ✅ **SDK 依赖已添加**
@@ -121,8 +176,14 @@ capability-share/src/
 - `com.umeng.umsdk:share-sina:7.3.2` - 新浪微博分享
 
 ✅ **代码实现已完成**
-- `ShareServiceImpl` 已实现友盟分享 SDK 调用
-- `ShareServiceFactory` 已创建
+- `ShareServiceImpl` 已实现友盟分享 SDK 调用,并实现 `IShareService` 接口
+- `ShareServiceImpl` 已通过 ARouter 注册(`@Route(path = "/share/service")`)
+- `ShareCallbackImpl` 已实现 `IShareCallback` 接口,并通过 ARouter 注册(`@Route(path = "/share/callback")`)
+- `ShareServiceFactory` 已创建,内部使用 ARouter 获取服务
+- `IShareService` 接口已在 `base-core` 模块定义
+- `IShareCallback` 接口已在 `base-core` 模块定义
+- `WXEntryActivity` 已移至 `base-core` 模块,通过 ARouter 注入回调实现
+- **ARouter 集成完成**:模块间解耦,通过 ARouter 进行依赖注入
 
 ## 需要您配置的内容
 
@@ -151,33 +212,26 @@ capability-share/src/
 
 ### 3. 配置 AndroidManifest.xml
 
-在 `app/src/main/AndroidManifest.xml` 中添加权限声明(**meta-data 配置已通过代码完成,无需在 Manifest 中配置**):
+**注意**:权限声明和回调 Activity 配置已在各模块的 `AndroidManifest.xml` 中完成,`app` 模块无需配置。
 
-```xml
-<manifest>
-    <!-- 友盟分享权限 -->
-    <uses-permission android:name="android.permission.INTERNET" />
-    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
-    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
-    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
-    
-    <application>
-        <!-- 注意:友盟和各平台的配置已通过代码完成(见步骤4),无需在此配置 meta-data -->
-    </application>
-</manifest>
-```
+- ✅ **权限声明**:已在 `capability-share/src/main/AndroidManifest.xml` 中配置
+- ✅ **回调 Activity**:
+  - `WXEntryActivity` 已在 `base-core/src/main/AndroidManifest.xml` 中配置
+  - `AuthActivity`(QQ)、`WBShareTransActivity`(微博)已在 `capability-share/src/main/AndroidManifest.xml` 中配置
+- ✅ **ARouter 初始化**:已在 `XinDaZhouApplication` 中完成(见步骤5)
 
 **说明**:
-- ✅ 权限声明是必需的
+- ✅ 所有配置都在各模块内部管理,`app` 模块无需配置
 - ✅ AppKey 和平台配置通过代码完成(见步骤4),更灵活
-- ✅ 如果需要在 Manifest 中配置,也可以保留,代码配置会覆盖 Manifest 配置
 
 ### 4. 配置资源文件(推荐方式)
 
-在 `app/src/main/res/values/strings.xml` 中配置分享参数(**推荐方式,配置统一管理**):
+**注意**:配置位置已改为 `capability-share/src/main/res/values/strings.xml`,`app` 模块无需配置。
+
+在 `capability-share/src/main/res/values/strings.xml` 中配置分享参数(**推荐方式,配置统一管理**):
 
 ```xml
-<!-- app/src/main/res/values/strings.xml -->
+<!-- capability-share/src/main/res/values/strings.xml -->
 <resources>
     <!-- 友盟配置(必填) -->
     <string name="share_umeng_app_key">您的友盟 AppKey</string>
@@ -190,6 +244,8 @@ capability-share/src/
     <!-- QQ平台配置(可选) -->
     <string name="share_qq_app_id">您的QQ AppID</string>
     <string name="share_qq_app_key">您的QQ AppKey</string>
+    <!-- QQ AppID Scheme(用于回调,格式:tencent + AppID) -->
+    <string name="qq_app_id_scheme">tencent您的QQ AppID</string>
     
     <!-- 微博平台配置(可选) -->
     <string name="share_weibo_app_key">您的新浪微博 AppKey</string>
@@ -200,15 +256,14 @@ capability-share/src/
 
 ### 5. 在 Application 中初始化(统一入口)
 
-在您的 `XinDaZhouApplication` 类中初始化分享服务,**支持三种方式**:
-
-#### 方式1:使用默认配置(从资源文件读取,最简单)
+在您的 `XinDaZhouApplication` 类中初始化分享服务:
 
 ```kotlin
 // app/src/main/java/com/narutohuo/xindazhou/XinDaZhouApplication.kt
 package com.narutohuo.xindazhou
 
 import android.app.Application
+import com.alibaba.android.arouter.launcher.ARouter
 import com.narutohuo.xindazhou.share.factory.ShareServiceFactory
 
 class XinDaZhouApplication : Application() {
@@ -216,8 +271,20 @@ class XinDaZhouApplication : Application() {
     override fun onCreate() {
         super.onCreate()
         
-        // ========== 初始化分享服务(最简单,从资源文件读取配置)==========
-        ShareServiceFactory.init(context = this)
+        // ========== 初始化 ARouter(必须在其他初始化之前)==========
+        if (BuildConfig.DEBUG) {
+            ARouter.openLog()
+            ARouter.openDebug()
+        }
+        ARouter.init(this)
+        
+        // ========== 初始化分享服务(从资源文件读取配置)==========
+        try {
+            ShareServiceFactory.init(context = this)
+        } catch (e: Exception) {
+            // 分享服务初始化失败不影响应用运行,但分享功能将不可用
+            android.util.Log.e("XinDaZhouApplication", "分享服务初始化失败", e)
+        }
     }
 }
 ```
@@ -225,6 +292,7 @@ class XinDaZhouApplication : Application() {
 **优势**:
 - ✅ **最简单**:只需一行代码,配置都在资源文件中
 - ✅ **统一入口**:通过 `ShareServiceFactory.init()` 统一初始化,代码更清晰
+- ✅ **ARouter 解耦**:通过 ARouter 实现模块间解耦,base-core 与 share 模块完全解耦
 - ✅ 业务层不直接依赖友盟API
 - ✅ 如果更换SDK,只需修改实现层,业务层无需改动
 - ✅ 配置统一管理,代码更清晰

+ 4 - 13
capability-socketio/build.gradle

@@ -21,24 +21,15 @@ android {
 }
 
 dependencies {
-    // 依赖base-core(所有SDK都依赖这个)
-    implementation(project(":base-core"))
-    
-    // AndroidX Core
-    implementation(libs.androidx.core.ktx)
+    // 只依赖 base-common(不直接依赖 base-core)
+    // base-common 会传递 base-core 的依赖(包括 Gson、Coroutines、ILog 等)
+    implementation(project(":base-common"))
     
     // SocketIO客户端库
     // Maven Central最新版本:2.1.2(Java/Android客户端最高版本)
     // netty-socketio 2.0.13 支持 Socket.IO 2.x 客户端
     implementation("io.socket:socket.io-client:2.1.2")
     
-    // Gson for JSON parsing
-    implementation("com.google.code.gson:gson:2.10.1")
-    
-    // 日志通过 base-core 的 ILog 接口统一管理,无需单独依赖 Timber
-    
-    // Kotlin Coroutines
-    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3")
-    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3")
+    // 注意:Gson、Coroutines、ILog 已通过 base-common 传递,无需重复依赖
 }
 

+ 50 - 0
capability-socketio/src/main/java/com/narutohuo/xindazhou/socketio/factory/SocketIORepositoryFactory.kt

@@ -0,0 +1,50 @@
+package com.narutohuo.xindazhou.socketio.factory
+
+import com.narutohuo.xindazhou.socketio.repository.SocketIORepository
+
+/**
+ * SocketIO Repository 工厂类
+ * 
+ * 提供统一的获取 SocketIORepository 实例的方式
+ * Repository 是全局单例,多个界面共享同一个实例
+ * 
+ * 使用示例:
+ * ```kotlin
+ * // 获取 Repository 实例(单例)
+ * val repository = SocketIORepositoryFactory.getInstance()
+ * 
+ * // 连接服务器(需要传入 serverUrl 和 token)
+ * repository.connect(serverUrl, token)
+ * 
+ * // 观察连接状态
+ * repository.connectionState.collect { isConnected ->
+ *     // 处理连接状态变化
+ * }
+ * ```
+ */
+object SocketIORepositoryFactory {
+    
+    @Volatile
+    private var INSTANCE: SocketIORepository? = null
+    
+    /**
+     * 获取 SocketIORepository 实例(单例)
+     * 
+     * @return SocketIORepository 实例(单例)
+     */
+    @JvmStatic
+    fun getInstance(): SocketIORepository {
+        return INSTANCE ?: synchronized(this) {
+            INSTANCE ?: SocketIORepository().also { INSTANCE = it }
+        }
+    }
+    
+    /**
+     * 销毁单例(用于测试或需要重新初始化时)
+     */
+    @JvmStatic
+    fun destroyInstance() {
+        INSTANCE = null
+    }
+}
+

+ 63 - 19
capability-socketio/src/main/java/com/narutohuo/xindazhou/socketio/impl/SocketIOServiceImpl.kt

@@ -1,6 +1,6 @@
 package com.narutohuo.xindazhou.socketio.impl
 
-import com.google.gson.Gson
+import com.alibaba.fastjson2.JSON
 import com.narutohuo.xindazhou.core.log.ILog
 import com.narutohuo.xindazhou.socketio.api.SocketIOService
 import com.narutohuo.xindazhou.socketio.model.SocketIOResponse
@@ -44,7 +44,6 @@ class SocketIOServiceImpl private constructor() : SocketIOService {
     }
     
     private var socket: Socket? = null
-    private val gson = Gson()
     // 存储事件监听器,以便在socket创建后注册
     private val eventListeners = mutableMapOf<String, MutableList<(SocketIOResponse) -> Unit>>()
     
@@ -62,16 +61,18 @@ class SocketIOServiceImpl private constructor() : SocketIOService {
             
             // 初始化Socket连接
             val options = IO.Options().apply {
-                // 添加认证token
+                // 添加认证token(通过 HTTP Headers)
                 extraHeaders = mapOf("Authorization" to listOf("Bearer $token"))
-                // 添加token作为查询参数(备用方案)
+                // 添加token作为查询参数(备用方案,服务端会优先从 Headers 获取)
+                // Socket.IO Java 客户端的 query 参数是 String 格式
                 query = "token=$token"
-                // 自动重连
-                reconnection = true
-                reconnectionAttempts = 5
-                reconnectionDelay = 1000
+                // 禁用自动重连,改为手动重连(原因:自动重连无法更新 Token)
+                // 前后台切换时的重连由 Application 层通过 ProcessLifecycleOwner 处理
+                reconnection = false
             }
             
+            ILog.d("SocketIOService", "连接配置: Authorization=Bearer $token, query=token=$token")
+            
             ILog.d("SocketIOService", "创建Socket实例...")
             socket = IO.socket(serverUrl, options)
             
@@ -80,11 +81,13 @@ class SocketIOServiceImpl private constructor() : SocketIOService {
                 callbacks.forEach { callback ->
                     socket?.on(event) { args ->
                         try {
+                            // Socket.IO Java 客户端库接收数据时,服务端发送的 Map 会被自动序列化为 JSONObject
+                            // 统一转换为 JSON 字符串格式,符合 SocketIOResponse.data 的类型定义
                             val data = if (args.isNotEmpty()) {
                                 when (val arg = args[0]) {
                                     is String -> arg
-                                    is JSONObject -> arg.toString()
-                                    else -> gson.toJson(arg)
+                                    is JSONObject -> arg.toString() // JSONObject.toString() 返回标准 JSON 字符串
+                                    else -> JSON.toJSONString(arg)
                                 }
                             } else {
                                 "{}"
@@ -104,17 +107,43 @@ class SocketIOServiceImpl private constructor() : SocketIOService {
                 }
             }
             
-            // 添加连接事件监听(用于日志
+            // 添加连接事件监听(触发已注册的回调
             socket?.on(Socket.EVENT_CONNECT) {
                 ILog.d("SocketIOService", "✅ SocketIO连接成功")
+                // 触发已注册的 "connect" 事件回调
+                val callbacks = eventListeners["connect"] ?: return@on
+                val response = SocketIOResponse(
+                    event = "connect",
+                    data = "{}",
+                    timestamp = System.currentTimeMillis()
+                )
+                callbacks.forEach { it(response) }
             }
             
             socket?.on(Socket.EVENT_DISCONNECT) {
                 ILog.d("SocketIOService", "❌ SocketIO连接断开")
+                // 触发已注册的 "disconnect" 事件回调
+                val callbacks = eventListeners["disconnect"] ?: return@on
+                val response = SocketIOResponse(
+                    event = "disconnect",
+                    data = "{}",
+                    timestamp = System.currentTimeMillis()
+                )
+                callbacks.forEach { it(response) }
             }
             
             socket?.on(Socket.EVENT_CONNECT_ERROR) { args ->
-                ILog.e("SocketIOService", "❌ SocketIO连接错误: ${args?.getOrNull(0)}")
+                val errorMsg = args?.getOrNull(0)?.toString() ?: "连接错误"
+                ILog.e("SocketIOService", "❌ SocketIO连接错误: $errorMsg")
+                ILog.e("SocketIOService", "连接错误详情: args=$args")
+                // 触发已注册的 "connect_error" 事件回调
+                val callbacks = eventListeners["connect_error"] ?: return@on
+                val response = SocketIOResponse(
+                    event = "connect_error",
+                    data = errorMsg,
+                    timestamp = System.currentTimeMillis()
+                )
+                callbacks.forEach { it(response) }
             }
             
             ILog.d("SocketIOService", "开始连接SocketIO服务器...")
@@ -143,11 +172,13 @@ class SocketIOServiceImpl private constructor() : SocketIOService {
         // 如果socket已创建,立即注册
         socket?.on(event) { args ->
             try {
+                // Socket.IO Java 客户端库接收数据时,服务端发送的 Map 会被自动序列化为 JSONObject
+                // 统一转换为 JSON 字符串格式,符合 SocketIOResponse.data 的类型定义
                 val data = if (args.isNotEmpty()) {
                     when (val arg = args[0]) {
                         is String -> arg
-                        is JSONObject -> arg.toString()
-                        else -> gson.toJson(arg)
+                        is JSONObject -> arg.toString() // JSONObject.toString() 返回标准 JSON 字符串
+                        else -> JSON.toJSONString(arg)
                     }
                 } else {
                     "{}"
@@ -172,14 +203,27 @@ class SocketIOServiceImpl private constructor() : SocketIOService {
     
     override fun emit(event: String, data: Any) {
         try {
-            val jsonData = if (data is String) {
-                data
-            } else {
-                gson.toJson(data)
+            val jsonData = when (data) {
+                is String -> data
+                is JSONObject -> data
+                is Map<*, *> -> {
+                    // 将 Kotlin Map 转换为 JSONObject(Socket.IO Java 客户端库的标准格式)
+                    // netty-socketio 服务端会自动将 JSONObject 反序列化为 Map
+                    JSONObject().apply {
+                        @Suppress("UNCHECKED_CAST")
+                        (data as Map<String, Any>).forEach { (key, value) ->
+                            put(key, value)
+                        }
+                    }
+                }
+                else -> {
+                    // 其他类型先转为 JSON 字符串,再转为 JSONObject
+                    JSONObject(JSON.toJSONString(data))
+                }
             }
             
             socket?.emit(event, jsonData)
-            ILog.d("SocketIOService", "Emitted event: $event")
+            ILog.d("SocketIOService", "Emitted event: $event, dataType=${jsonData.javaClass.simpleName}")
         } catch (e: Exception) {
             ILog.e("SocketIOService", "Error emitting event: $event", e)
         }

+ 62 - 18
app/src/main/java/com/narutohuo/xindazhou/user/repository/SocketIORepository.kt

@@ -1,20 +1,23 @@
-package com.narutohuo.xindazhou.user.repository
+package com.narutohuo.xindazhou.socketio.repository
 
-import com.narutohuo.xindazhou.common.config.ServerConfigManager
+import com.narutohuo.xindazhou.core.log.ILog
 import com.narutohuo.xindazhou.socketio.api.SocketIOService
 import com.narutohuo.xindazhou.socketio.factory.SocketIOServiceFactory
 import com.narutohuo.xindazhou.socketio.model.SocketIOEvent
 import com.narutohuo.xindazhou.socketio.model.SocketIOResponse
-import com.narutohuo.xindazhou.core.log.ILog
-import com.narutohuo.xindazhou.user.datasource.local.TokenManager
 import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.flow.SharedFlow
 import kotlinx.coroutines.flow.asSharedFlow
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.launch
 
 /**
  * SocketIO 数据仓库
  * 
  * 负责统一管理 SocketIO 连接和数据获取
+ * 完全封装在 capability-socketio 模块内部,app 模块通过 Factory 获取实例
  */
 class SocketIORepository(
     private val socketService: SocketIOService = SocketIOServiceFactory.getInstance()
@@ -24,36 +27,40 @@ class SocketIORepository(
         private const val TAG = "SocketIORepository"
     }
     
-    private val _connectionState = MutableSharedFlow<Boolean>()
+    // 使用 extraBufferCapacity 确保事件不丢失
+    private val _connectionState = MutableSharedFlow<Boolean>(extraBufferCapacity = 1)
     val connectionState: SharedFlow<Boolean> = _connectionState.asSharedFlow()
     
-    private val _logMessage = MutableSharedFlow<String>()
+    private val _logMessage = MutableSharedFlow<String>(extraBufferCapacity = 64)
     val logMessage: SharedFlow<String> = _logMessage.asSharedFlow()
     
-    private val _vehicleControlResponse = MutableSharedFlow<SocketIOResponse>()
+    private val _vehicleControlResponse = MutableSharedFlow<SocketIOResponse>(extraBufferCapacity = 1)
     val vehicleControlResponse: SharedFlow<SocketIOResponse> = _vehicleControlResponse.asSharedFlow()
     
-    private val _vehicleAlarm = MutableSharedFlow<SocketIOResponse>()
+    private val _vehicleAlarm = MutableSharedFlow<SocketIOResponse>(extraBufferCapacity = 1)
     val vehicleAlarm: SharedFlow<SocketIOResponse> = _vehicleAlarm.asSharedFlow()
     
+    // 用于在回调中发送事件的协程作用域
+    private val eventScope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
+    
     init {
         subscribeEvents()
     }
     
     /**
      * 连接 SocketIO
+     * 
+     * @param serverUrl SocketIO 服务器地址
+     * @param token JWT Token(用于身份验证)
      */
-    suspend fun connect(): Result<Unit> {
+    suspend fun connect(serverUrl: String, token: String): Result<Unit> {
         return try {
-            val socketUrl = ServerConfigManager.getSocketIOUrl()
-            val token = TokenManager.getAccessToken()
-            
             if (token.isNullOrEmpty()) {
                 return Result.failure(Exception("未登录,无法获取Token"))
             }
             
-            ILog.d(TAG, "connect: 正在连接 $socketUrl")
-            socketService.connect(socketUrl, token)
+            ILog.d(TAG, "connect: 正在连接 $serverUrl")
+            socketService.connect(serverUrl, token)
             Result.success(Unit)
         } catch (e: Exception) {
             ILog.e(TAG, "connect: 连接失败", e)
@@ -83,6 +90,34 @@ class SocketIORepository(
     }
     
     /**
+     * 检查连接状态,如果断开则自动重连
+     * 
+     * 用于 App 进入前台时自动重连的场景
+     * 
+     * @param serverUrl SocketIO 服务器地址
+     * @param token JWT Token(用于身份验证)
+     * @return 如果已连接返回 true,如果需要重连则返回 false(重连是异步的)
+     */
+    suspend fun checkAndReconnect(serverUrl: String, token: String): Boolean {
+        return if (socketService.isConnected()) {
+            // 已连接,无需重连
+            ILog.d(TAG, "checkAndReconnect: 已连接,无需重连")
+            true
+        } else {
+            // 未连接,尝试重连
+            ILog.d(TAG, "checkAndReconnect: 未连接,开始重连...")
+            connect(serverUrl, token)
+                .onSuccess {
+                    ILog.d(TAG, "checkAndReconnect: 重连成功")
+                }
+                .onFailure { e ->
+                    ILog.e(TAG, "checkAndReconnect: 重连失败", e)
+                }
+            false
+        }
+    }
+    
+    /**
      * 发送车辆控制指令
      */
     suspend fun sendVehicleControl(command: String, vehicleId: Long): Result<Unit> {
@@ -111,22 +146,30 @@ class SocketIORepository(
     private fun subscribeEvents() {
         // 订阅连接成功事件
         socketService.on(SocketIOEvent.CONNECT) { response ->
-            ILog.d(TAG, "onConnect")
-            _connectionState.tryEmit(true)
+            ILog.d(TAG, "onConnect - 触发连接状态更新")
+            // 在协程作用域中使用 emit 确保事件被发送
+            eventScope.launch {
+                _connectionState.emit(true)
+                ILog.d(TAG, "onConnect - 连接状态已更新为 true")
+            }
             _logMessage.tryEmit("✅ 连接成功")
         }
         
         // 订阅断开连接事件
         socketService.on(SocketIOEvent.DISCONNECT) { response ->
             ILog.d(TAG, "onDisconnect")
-            _connectionState.tryEmit(false)
+            eventScope.launch {
+                _connectionState.emit(false)
+            }
             _logMessage.tryEmit("❌ 断开连接")
         }
         
         // 订阅连接错误事件
         socketService.on(SocketIOEvent.CONNECT_ERROR) { response ->
             ILog.e(TAG, "onConnectError: ${response.data}")
-            _connectionState.tryEmit(false)
+            eventScope.launch {
+                _connectionState.emit(false)
+            }
             _logMessage.tryEmit("❌ 连接错误:${response.data}")
         }
         
@@ -147,3 +190,4 @@ class SocketIORepository(
         }
     }
 }
+