فهرست منبع

feat(chat): 实现聊天功能并集成AI助手接口

- 添加 ChatData 和 ChatModel 数据类用于处理聊天请求和响应
- 修改 HeaderInterceptor 配置请求头,添加认证和环境相关参数
- 更新 Service 接口,将 SSE 流改为 POST 请求到 AI 助手端点
- 重构 ChatActivity,移除分页列表结构,实现单次问答界面
- 在 ChatVM 中实现 SSE 流式响应处理逻辑,支持不同消息类型解析
- 移除 WelcomeActivity 中的 SSE 按钮,调整基地址配置
- 修改布局文件,从 RecyclerView 改为 TextView 显示问答内容
徐勤民 21 ساعت پیش
والد
کامیت
6801b998e2

+ 1 - 1
app/src/main/java/com/nova/brain/glass/MyApplication.java

@@ -13,7 +13,7 @@ import com.xuqm.base.di.manager.HttpManager;
  */
 public class MyApplication extends App {
 
-    public static String baseUrl = "http://192.168.27.248:8080";
+    public static String baseUrl = "http://22fs132201.imwork.net";
 
     @Override
     public void onCreate() {

+ 44 - 0
app/src/main/java/com/nova/brain/glass/model/ChatModel.kt

@@ -0,0 +1,44 @@
+package com.nova.brain.glass.model
+
+//{
+//    "id": 74216,
+//    "role": "assistant",
+//    "createTime": "2026-04-16T06:13:32.678Z",
+//    "type": "reason",
+//    "data": {
+//        "content": "数据。",
+//        "duration": 0.0
+//    },
+//    "metadata": {}
+//}
+//{
+//    "id": 74216,
+//    "role": "assistant",
+//    "createTime": "2026-04-16T06:13:32.678Z",
+//    "type": "string",
+//    "data": "最",
+//    "metadata": {}
+//}
+//{"date":"2026-04-16T06:49:19.790Z","msg":"当前话题存在进行中的请求,请稍后重试","code":409,"success":false,"uri":"/docqa/chat/qa03","status":409}
+data class ChatModel(
+    val type: String,
+    val msg: String,
+)
+
+data class ChatModel1(
+    val id: Int,
+    val role: String,
+    val createTime: String,
+    val type: String,
+    val data: String,
+)
+data class ChatModel2Data(
+    val content: String,
+)
+data class ChatModel2(
+    val id: Int,
+    val role: String,
+    val createTime: String,
+    val type: String,
+    val data: ChatModel2Data,
+)

+ 9 - 0
app/src/main/java/com/nova/brain/glass/model/data/ChatData.kt

@@ -0,0 +1,9 @@
+package com.nova.brain.glass.model.data
+
+//{"question":"我的任务哪个更紧急","topicId":14478,"knowledgeBaseId":"AI_ASSISTANT_MODE_FOR_GLASSES","model":"AI助手模式"}
+data class ChatData(
+    val question: String,
+    val topicId: Int = 14478,
+    val knowledgeBaseId: String = "AI_ASSISTANT_MODE_FOR_GLASSES",
+    val model: String = "AI助手模式"
+)

+ 8 - 1
app/src/main/java/com/nova/brain/glass/repository/HeaderInterceptor.kt

@@ -25,8 +25,15 @@ class HeaderInterceptor(val context: Context) : Interceptor {
 
         //请求定制:添加请求头
         val requestBuilder = original.newBuilder()
-            .header("Authentication", context.getStringForPreferences(SHARE_UESR_TOKEN))
+            .header("Authorization", "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJsb2dpblR5cGUiOiJsb2dpbiIsImxvZ2luSWQiOiJBSTAwMjIiLCJyblN0ciI6Im1OWUxobGZqWDJ2bEJMc21RdnFxSUE4dU9ZN0NOY3AzIiwidXNlckluZm8iOnsic3RhZmZObyI6IkFJMDAyMiJ9fQ.4BHcVpdkznqaQwPsyTn1my3_Zo0AliOILj_PjbCIK3k")
             .addHeader("Content-Type", "application/json;charset=UTF-8")
+            .addHeader("Cookie", "__itrace_wid=87125211-8742-4f12-b5ca-32b9b6c860e4; locale=zh-Hans; _webtracing_device_id=t_13501877-b9b303fc-d3f52eb530e026b0")
+            .addHeader("Environment", "1")
+            .addHeader("currentUserId", "AI0022")
+            .addHeader("terminal", "1")
+            .addHeader("modulename", "web")
+            .addHeader("currentUserName", "")
+
 
 //        context.getStringForPreferences(SHARE_UESR_TOKEN).loge()
 

+ 4 - 2
app/src/main/java/com/nova/brain/glass/repository/Service.kt

@@ -1,5 +1,7 @@
 package com.nova.brain.glass.repository
 
+import com.nova.brain.glass.model.ChatModel
+import com.nova.brain.glass.model.data.ChatData
 import io.reactivex.Observable
 import okhttp3.RequestBody
 import okhttp3.ResponseBody
@@ -18,7 +20,7 @@ interface Service {
     fun demoPost(@Body body: RequestBody): Observable<ResponseBody>
 
     @Streaming
-    @GET("stream/{n}")
-    fun demoStream(@Path("n") n: Int): Observable<ResponseBody>
+    @POST("/cbrain-gateway/cbraindep/docqa/chat/qa03")
+    fun chat(@Body body: ChatData): Observable<ResponseBody>
 
 }

+ 23 - 15
app/src/main/java/com/nova/brain/glass/ui/ChatActivity.kt

@@ -1,20 +1,20 @@
 package com.nova.brain.glass.ui
 
 import android.widget.TextView
+import androidx.lifecycle.ViewModelProvider
 import com.nova.brain.glass.R
 import com.nova.brain.glass.databinding.ActivityChatBinding
 import com.nova.brain.glass.helper.OfflineCmdListener
 import com.nova.brain.glass.helper.OfflineCmdServiceHelper
 import com.nova.brain.glass.model.ChatItem
 import com.nova.brain.glass.viewmodel.ChatVM
-import com.xuqm.base.adapter.BasePagedAdapter
+import com.nova.brain.glass.viewmodel.WelcomeVM
 import com.xuqm.base.adapter.CommonPagedAdapter
 import com.xuqm.base.adapter.ViewHolder
-import com.xuqm.base.ui.BaseListActivity
-import com.xuqm.base.ui.BaseListFormLayoutNormalActivity
+import com.xuqm.base.ui.BaseActivity
 import io.noties.markwon.Markwon
 
-class ChatActivity : BaseListFormLayoutNormalActivity<ChatItem, ChatVM, ActivityChatBinding>() {
+class ChatActivity : BaseActivity<ActivityChatBinding>() {
     override fun getLayoutId(): Int = R.layout.activity_chat
     override fun fullscreen(): Boolean = true
     private val listener = object : OfflineCmdListener {
@@ -28,11 +28,20 @@ class ChatActivity : BaseListFormLayoutNormalActivity<ChatItem, ChatVM, Activity
             }
         }
     }
-private lateinit var markwon: Markwon
+    private lateinit var markwon: Markwon
+    private lateinit var vm: ChatVM
     override fun initData() {
         super.initData()
 
-         markwon = Markwon.create(this);
+        vm = ViewModelProvider(this)[ChatVM::class.java]
+        markwon = Markwon.create(this)
+
+        binding.title.text = "我的代办任务有哪些?"
+        vm.result.observe( this){
+            binding.content.text = it
+        }
+
+        vm.demoPostSse()
     }
 
     override fun onResume() {
@@ -45,14 +54,13 @@ private lateinit var markwon: Markwon
         OfflineCmdServiceHelper.removeOnLineListener(listener)
     }
 
-    private val adapter = object : CommonPagedAdapter<ChatItem>(R.layout.item_chat) {
-        override fun convert(holder: ViewHolder, item: ChatItem, position: Int) {
-            holder.setText(R.id.title, item.title)
-
-            val tv = holder.getView<TextView>(R.id.content)
-            markwon.setMarkdown(tv, item.content);
-        }
-    }
+//    private val adapter = object : CommonPagedAdapter<ChatItem>(R.layout.item_chat) {
+//        override fun convert(holder: ViewHolder, item: ChatItem, position: Int) {
+//            holder.setText(R.id.title, item.title)
+//
+//            val tv = holder.getView<TextView>(R.id.content)
+//            markwon.setMarkdown(tv, item.content);
+//        }
+//    }
 
-    override fun adapter(): BasePagedAdapter<ChatItem> = adapter
 }

+ 0 - 1
app/src/main/java/com/nova/brain/glass/ui/WelcomeActivity.kt

@@ -27,7 +27,6 @@ class WelcomeActivity : BaseActivity<ActivityWelcomeBinding>() {
         }
         binding.btnGet.setOnClickListener { vm.demoGet() }
         binding.btnPost.setOnClickListener { vm.demoPost() }
-        binding.btnSse.setOnClickListener { vm.demoPostSse() }
         binding.md.setOnClickListener { startActivity(Intent(this@WelcomeActivity, ChatActivity::class.java)) }
     }
 

+ 51 - 42
app/src/main/java/com/nova/brain/glass/viewmodel/ChatVM.kt

@@ -1,46 +1,55 @@
 package com.nova.brain.glass.viewmodel
 
-import com.nova.brain.glass.model.ChatItem
-import com.xuqm.base.viewmodel.BaseListViewModel
-import com.xuqm.base.viewmodel.callback.Response
-
-
-class ChatVM : BaseListViewModel<ChatItem>() {
-    override fun loadData(
-        page: Int,
-        onResponse: Response<ChatItem>
-    ) {
-        onResponse.onResponse(arrayListOf<ChatItem>().apply {
-            add(
-                ChatItem(
-                    "本周周报", """
-## 统计数据截止到:2026年3月19日 1:36 
-
-### 纪检Agent3期产品设计】明日到达计划完成时间。 
-
-#### *重点关注*:张三【测试运维部】负责的“C大脑-脑实例- V2.24-测试方案设计
-
- * 当前状态:已延期13天,严重程度为【严重延期】。
- * 参谋建议:建议高优处理。
-
-""".trimIndent()
-                )
-            )
-            add(
-                ChatItem(
-                    "我最紧急的任务是哪个", """
-                
-# 标题
-
-这是 **加粗**、*斜体*、~~删除线~~
-
-- 列表1
-- 列表2
-
-[点击跳转](https://openai.com)
-            """.trimIndent()
-                )
-            )
-        })
+import androidx.lifecycle.MutableLiveData
+import com.nova.brain.glass.model.ChatModel
+import com.nova.brain.glass.model.ChatModel1
+import com.nova.brain.glass.model.ChatModel2
+import com.nova.brain.glass.model.data.ChatData
+import com.nova.brain.glass.repository.Service
+import com.xuqm.base.common.GsonImplHelp
+import com.xuqm.base.di.manager.HttpManager
+import com.xuqm.sdhbwfu.core.viewModel.BaseViewModel
+import io.reactivex.schedulers.Schedulers
+
+
+class ChatVM : BaseViewModel() {
+    val result = MutableLiveData<String>()
+    private var t = "string"
+    fun demoPostSse() {
+        t = ""
+        HttpManager.getApi(Service::class.java).chat(ChatData("我的代办任务有哪些?"))
+            .subscribeOn(Schedulers.io())
+            .subscribe({ body ->
+                val sb = StringBuilder()
+                body.charStream().buffered().use { reader ->
+                    try {
+                        var line: String?
+                        while (reader.readLine().also { line = it } != null) {
+                            val l = line!!
+                            if (l.isNotEmpty()) {
+                                val model = GsonImplHelp.get().toObject(l, ChatModel::class.java)
+                                if (t != model.type) {
+                                    sb.clear()
+                                }
+                                t = model.type
+                                if (model.type == "string") {
+                                    val model1 =
+                                        GsonImplHelp.get().toObject(l, ChatModel1::class.java)
+                                    sb.appendLine(model1.data)
+                                } else {
+                                    val model2 =
+                                        GsonImplHelp.get().toObject(l, ChatModel2::class.java)
+                                    sb.appendLine(model2.data.content)
+                                }
+                                result.postValue(sb.toString())
+                            }
+                        }
+                    } catch (e: Exception) {
+                        result.postValue("AI反馈异常: ${e.message}")
+                    }
+                }
+            }, { e ->
+                result.postValue("AI反馈异常: ${e.message}")
+            }).adds()
     }
 }

+ 0 - 24
app/src/main/java/com/nova/brain/glass/viewmodel/WelcomeVM.kt

@@ -36,28 +36,4 @@ class WelcomeVM : BaseViewModel() {
             }).adds()
     }
 
-    fun demoPostSse() {
-        result.postValue("SSE 连接中...")
-        HttpManager.getApi(Service::class.java).demoStream(5)
-            .subscribeOn(Schedulers.io())
-            .subscribe({ body ->
-                val sb = StringBuilder("SSE 流式响应:\n")
-                body.charStream().buffered().use { reader ->
-                    try {
-                        var line: String?
-                        while (reader.readLine().also { line = it } != null) {
-                            val l = line!!
-                            if (l.isNotEmpty()) {
-                                sb.appendLine(l)
-                                result.postValue(sb.toString())
-                            }
-                        }
-                    } catch (e: Exception) {
-                        result.postValue("SSE 读取异常: ${e.message}")
-                    }
-                }
-            }, { e ->
-                result.postValue("SSE 失败: ${e.message}")
-            }).adds()
-    }
 }

+ 18 - 10
app/src/main/res/layout/activity_chat.xml

@@ -1,18 +1,26 @@
 <?xml version="1.0" encoding="utf-8"?>
 <layout>
-
-    <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
-        android:id="@+id/main"
+    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:background="@color/app_color_black">
+        android:paddingHorizontal="29dp"
+        android:background="@color/app_color_black"
+        android:paddingVertical="10dp"
+        android:orientation="vertical">
 
-        <androidx.recyclerview.widget.RecyclerView
-            android:id="@+id/baseRecyclerView"
+        <TextView
+            android:id="@+id/title"
             android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:overScrollMode="never" />
-
+            android:layout_height="wrap_content"
+            android:textColor="#2EB242"
+            android:textSize="10sp" />
 
-    </androidx.constraintlayout.widget.ConstraintLayout>
+        <TextView
+            android:id="@+id/content"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="10dp"
+            android:textColor="#ff40FF5E"
+            android:textSize="14sp" />
+    </LinearLayout>
 </layout>