feat(chat): 添加群组成员管理功能

- 新增 AddMemberActivity 页面实现选择联系人界面
- 实现双标签页切换(邀请专家/邀请员工)功能
- 添加搜索框支持实时过滤联系人列表
- 集成 RecyclerView 列表展示与多选功能
- 实现底部确认按钮回传选中用户ID列表
- 添加 GroupMemberNavAdapter 群成员导航适配器
- 扩展 GroupMemberInfo 类添加业务字段支持
- 更新聊天导航头部布局优化界面结构
- 新增电话拨打对话框组件支持一键拨号
- 完善群组信息成员详情泛型定义增强类型安全
This commit is contained in:
2026-07-17 17:59:55 +08:00
parent 14f7542037
commit c044b8cbdc
4 changed files with 65 additions and 13 deletions
@@ -13,6 +13,7 @@ import com.xjjk.healthyclients.bean.emergency.OrderThroughBean
import com.xjjk.healthyclients.bean.emergency.initUserOrderPageBean import com.xjjk.healthyclients.bean.emergency.initUserOrderPageBean
import com.xjjk.healthyclients.bean.home.ProposalBean import com.xjjk.healthyclients.bean.home.ProposalBean
import com.xjjk.healthyclients.data.bean.ApiResponse import com.xjjk.healthyclients.data.bean.ApiResponse
import com.xjjk.healthyclients.data.bean.PageBean
import okhttp3.RequestBody import okhttp3.RequestBody
import retrofit2.http.Body import retrofit2.http.Body
import retrofit2.http.GET import retrofit2.http.GET
@@ -95,12 +96,12 @@ interface EmergencyApi {
* 搜索专家(专业人员) * 搜索专家(专业人员)
*/ */
@GET("/health-emergency/api/emergency/order/searchExpert") @GET("/health-emergency/api/emergency/order/searchExpert")
suspend fun searchExpert(@QueryMap params: MutableMap<String, Any?>): ApiResponse<List<ExpertBean>> suspend fun searchExpert(@QueryMap params: MutableMap<String, Any?>): ApiResponse<PageBean<ExpertBean>>
/** /**
* 搜索员工 * 搜索员工
*/ */
@GET("/health-emergency/api/emergency/order/searchEmployee") @GET("/health-emergency/api/emergency/order/searchEmployee")
suspend fun searchEmployee(@QueryMap params: MutableMap<String, Any?>): ApiResponse<List<EmployeeBean>> suspend fun searchEmployee(@QueryMap params: MutableMap<String, Any?>): ApiResponse<PageBean<EmployeeBean>>
/** /**
* 实际群成员列表 * 实际群成员列表
*/ */
@@ -122,4 +123,9 @@ interface EmergencyApi {
@GET("/health-emergency/emergency/LargeScreenNew/onePersonCase") @GET("/health-emergency/emergency/LargeScreenNew/onePersonCase")
suspend fun getProposalInfo(): ApiResponse<ProposalBean> suspend fun getProposalInfo(): ApiResponse<ProposalBean>
/**
* 关爱联络员身份判断
*/
@GET("/health-emergency/emergency/LargeScreenNew/isLiaisonUser")
suspend fun isLiaisonUser(): ApiResponse<Boolean>
} }
@@ -17,6 +17,7 @@ import com.xjjk.healthyclients.bean.emergency.initUserOrderPageBean
import com.xjjk.healthyclients.bean.home.ProposalBean import com.xjjk.healthyclients.bean.home.ProposalBean
import com.xjjk.healthyclients.data.api.EmergencyApi import com.xjjk.healthyclients.data.api.EmergencyApi
import com.xjjk.healthyclients.data.bean.ApiResponse import com.xjjk.healthyclients.data.bean.ApiResponse
import com.xjjk.healthyclients.data.bean.PageBean
import com.xjjk.healthyclients.retrofit.RetrofitManager import com.xjjk.healthyclients.retrofit.RetrofitManager
import com.xjjk.healthyclients.retrofit.RetrofitManager.toRequestBody import com.xjjk.healthyclients.retrofit.RetrofitManager.toRequestBody
import com.xjjk.healthyclients.superfuntion.toJson import com.xjjk.healthyclients.superfuntion.toJson
@@ -129,7 +130,7 @@ object EmergencyRepository : BaseRepository() {
return apiCall { service.getParamedicGroupInfo(map) } return apiCall { service.getParamedicGroupInfo(map) }
} }
suspend fun searchExpert(realname: String, centerId: String, excludeSessionId: String, pageNo: Int, pageSize: Int): ApiResponse<List<ExpertBean>> { suspend fun searchExpert(realname: String, centerId: String, excludeSessionId: String, pageNo: Int, pageSize: Int): ApiResponse<PageBean<ExpertBean>> {
val map = mutableMapOf<String, Any?>() val map = mutableMapOf<String, Any?>()
map["realname"] = realname map["realname"] = realname
map["centerId"] = centerId map["centerId"] = centerId
@@ -139,7 +140,7 @@ object EmergencyRepository : BaseRepository() {
return apiCall { service.searchExpert(map) } return apiCall { service.searchExpert(map) }
} }
suspend fun searchEmployee(realname: String, orgCode: String, workNo: String, phone: String, excludeSessionId: String, pageNo: Int, pageSize: Int): ApiResponse<List<EmployeeBean>> { suspend fun searchEmployee(realname: String, orgCode: String, workNo: String, phone: String, excludeSessionId: String, pageNo: Int, pageSize: Int): ApiResponse<PageBean<EmployeeBean>> {
val map = mutableMapOf<String, Any?>() val map = mutableMapOf<String, Any?>()
map["realname"] = realname map["realname"] = realname
map["orgCode"] = orgCode map["orgCode"] = orgCode
@@ -176,4 +177,8 @@ object EmergencyRepository : BaseRepository() {
suspend fun getProposalInfo(): ApiResponse<ProposalBean> { suspend fun getProposalInfo(): ApiResponse<ProposalBean> {
return apiCall { service.getProposalInfo() } return apiCall { service.getProposalInfo() }
} }
suspend fun isLiaisonUser(): ApiResponse<Boolean> {
return apiCall { service.isLiaisonUser() }
}
} }
@@ -11,6 +11,7 @@ import android.text.TextUtils
import android.util.Log import android.util.Log
import android.view.View import android.view.View
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.core.util.Consumer
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle import androidx.lifecycle.repeatOnLifecycle
@@ -88,8 +89,16 @@ class EmergencyFragment :
private val paramedicOperateListener = object : OnParamedicOperateListener { private val paramedicOperateListener = object : OnParamedicOperateListener {
override fun addGroupUser(groupId: String, memberList: List<String>, memberType: Int) { override fun addGroupUser(
mViewModel.addGroupUser(groupId, memberList, memberType) groupId: String,
memberList: List<String>,
memberType: Int,
callback: Consumer<Boolean>
) {
// 通过 successCall 把 ViewModel 的操作结果回传给 tuichat 模块
mViewModel.addGroupUser(groupId, memberList, memberType) { success ->
callback.accept(success)
}
} }
override fun removeGroupUser(groupId: String, memberList: List<String>) { override fun removeGroupUser(groupId: String, memberList: List<String>) {
@@ -121,7 +130,7 @@ class EmergencyFragment :
val response = withTimeoutOrNull(10000L) { deferred.await() } val response = withTimeoutOrNull(10000L) { deferred.await() }
?: return@runBlocking emptyList<EmployeeBean>() ?: return@runBlocking emptyList<EmployeeBean>()
response.takeIf { it.success }?.result ?: emptyList() response.takeIf { it.success }?.result?.records ?: emptyList()
} catch (e: Exception) { } catch (e: Exception) {
emptyList() emptyList()
} }
@@ -149,7 +158,7 @@ class EmergencyFragment :
val response = withTimeoutOrNull(10000L) { deferred.await() } val response = withTimeoutOrNull(10000L) { deferred.await() }
?: return@runBlocking emptyList<ExpertBean>() ?: return@runBlocking emptyList<ExpertBean>()
response.takeIf { it.success }?.result ?: emptyList() response.takeIf { it.success }?.result?.records ?: emptyList()
} catch (e: Exception) { } catch (e: Exception) {
emptyList() emptyList()
} }
@@ -187,6 +196,12 @@ class EmergencyFragment :
.request { allGranted, grantedList, deniedList -> setUpMap() } .request { allGranted, grantedList, deniedList -> setUpMap() }
ParamedicOperateHelper.getInstance().setListener(paramedicOperateListener) ParamedicOperateHelper.getInstance().setListener(paramedicOperateListener)
mViewModel.isLiaisonUser { result ->
/*if (!result) {
mBinding.fragmentEmergencyParamedic.visibility = View.GONE
}*/
}
} }
override fun initData() { override fun initData() {
@@ -45,6 +45,20 @@ class EmergencyViewModel : BaseViewModel() {
) )
} }
fun isLiaisonUser(successCall: (Boolean) -> Unit = {}){
launch(
{
handleRequest(
EmergencyRepository.isLiaisonUser(),
successBlock = {
it.result?.let { it1 ->
successCall.invoke(it1)
}
})
}
)
}
fun getEmergencyCall( fun getEmergencyCall(
callType: String, callType: String,
resourceId: String, resourceId: String,
@@ -125,7 +139,7 @@ class EmergencyViewModel : BaseViewModel() {
handleRequest( handleRequest(
EmergencyRepository.searchExpert(realname, centerId, excludeSessionId, pageNo, pageSize), EmergencyRepository.searchExpert(realname, centerId, excludeSessionId, pageNo, pageSize),
successBlock = { successBlock = {
it.result?.let { it1 -> it.result?.records?.let { it1 ->
successCall.invoke(it1) successCall.invoke(it1)
} }
}) })
@@ -139,7 +153,7 @@ class EmergencyViewModel : BaseViewModel() {
handleRequest( handleRequest(
EmergencyRepository.searchEmployee(realname, orgCode, workNo, phone, excludeSessionId, pageNo, pageSize), EmergencyRepository.searchEmployee(realname, orgCode, workNo, phone, excludeSessionId, pageNo, pageSize),
successBlock = { successBlock = {
it.result?.let { it1 -> it.result?.records?.let { it1 ->
successCall.invoke(it1) successCall.invoke(it1)
} }
}) })
@@ -159,6 +173,15 @@ class EmergencyViewModel : BaseViewModel() {
}) })
} }
/**
* 邀请成员加入群组。
*
* @param groupId 群组 ID
* @param memberList 成员 userId 列表
* @param memberType 成员类型
* @param successCall 结果回调:true 表示服务器写入成功,false 表示失败(网络异常、业务错误、result 为空或 false
* 回调在协程上下文触发,调用方需自行切回主线程
*/
fun addGroupUser(groupId: String, memberList: List<String>, fun addGroupUser(groupId: String, memberList: List<String>,
memberType: Int, successCall: (Boolean) -> Unit = {} memberType: Int, successCall: (Boolean) -> Unit = {}
) { ) {
@@ -166,9 +189,12 @@ class EmergencyViewModel : BaseViewModel() {
handleRequest( handleRequest(
EmergencyRepository.addGroupUser(groupId, memberList, memberType), EmergencyRepository.addGroupUser(groupId, memberList, memberType),
successBlock = { successBlock = {
it.result?.let { it1 -> // result 为空或 false 都视为失败,便于调用方串行后续动作(如刷新列表)
successCall.invoke(it1) successCall.invoke(it.result == true)
} },
errorBlock = {
successCall.invoke(false)
false
}) })
}) })
} }