feat(chat): 添加群组管理功能和微信分享支持

- 新增 EmployeeBean、ExpertBean 和 GroupMemberBean 数据模型
- 实现群组成员管理接口和操作辅助类
- 添加微信 SDK 依赖并集成位置信息分享功能
- 在聊天界面中实现群成员列表显示和动态更新
- 增加急救员群组创建和管理的相关 API 接口
- 实现群组成员搜索、添加和移除功能
- 添加位置消息长按分享到微信的功能
This commit is contained in:
2026-07-16 18:47:03 +08:00
parent 7cca7e0b79
commit 14f7542037
6 changed files with 318 additions and 3 deletions
+1 -1
View File
@@ -137,5 +137,5 @@ dependencies {
//JSoup //JSoup
api 'org.jsoup:jsoup:1.12.1' api 'org.jsoup:jsoup:1.12.1'
implementation 'com.tencent.mm.opensdk:wechat-sdk-android:+'
} }
@@ -6,6 +6,7 @@ import android.annotation.SuppressLint
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.os.Message import android.os.Message
import android.util.Log
import android.view.Gravity import android.view.Gravity
import android.view.KeyEvent import android.view.KeyEvent
import android.view.View import android.view.View
@@ -19,9 +20,16 @@ import com.permissionx.guolindev.PermissionX
import com.sw.healthyclients.data.local.DataStoreManager import com.sw.healthyclients.data.local.DataStoreManager
import com.sw.healthyclients.utils.CustomActivityManager import com.sw.healthyclients.utils.CustomActivityManager
import com.sw.healthyclients.view.CustomToast import com.sw.healthyclients.view.CustomToast
import com.tencent.mm.opensdk.modelmsg.SendMessageToWX
import com.tencent.mm.opensdk.modelmsg.WXMediaMessage
import com.tencent.mm.opensdk.modelmsg.WXWebpageObject
import com.tencent.mm.opensdk.openapi.IWXAPI
import com.tencent.mm.opensdk.openapi.WXAPIFactory
import com.tencent.qcloud.tuicore.TUICore import com.tencent.qcloud.tuicore.TUICore
import com.tencent.qcloud.tuikit.tuichat.TUIChatService import com.tencent.qcloud.tuikit.tuichat.TUIChatService
import com.tencent.qcloud.tuikit.tuichat.classicui.event.IMChatEvent import com.tencent.qcloud.tuikit.tuichat.classicui.event.IMChatEvent
import com.tencent.qcloud.tuikit.tuichat.interfaces.ShareToWeChatListener
import com.tencent.qcloud.tuikit.tuichat.presenter.WeChatShareHelper
import com.xjjk.healthyclients.base.BaseVMBActivity import com.xjjk.healthyclients.base.BaseVMBActivity
import com.xjjk.healthyclients.bean.CardInfoBean import com.xjjk.healthyclients.bean.CardInfoBean
import com.xjjk.healthyclients.bottomtab.HomeBottomTabLayout import com.xjjk.healthyclients.bottomtab.HomeBottomTabLayout
@@ -68,6 +76,39 @@ class MainActivity : BaseVMBActivity<MainViewModel, ActivityMainBinding>(R.layou
Manifest.permission.POST_NOTIFICATIONS Manifest.permission.POST_NOTIFICATIONS
) )
val APP_ID: String = "wxb33cd60beda0a212"
private var api: IWXAPI? = null
private val shareToWeChatListener = ShareToWeChatListener { lat, lon, desc ->
val url = ("https://ditu.amap.com/regeo?lng=" + lon
+ "&lat=" + lat + "&name=" + desc + "&src=uriapi&innersrc=uriapi")
Log.i("TAGG", url)
val webpage = WXWebpageObject()
webpage.webpageUrl = url
val msg = WXMediaMessage(webpage)
msg.title = desc
msg.description = desc
val req = SendMessageToWX.Req()
req.transaction = System.currentTimeMillis().toString()
req.message = msg
req.scene = SendMessageToWX.Req.WXSceneSession
api?.sendReq(req)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
regToWx()
WeChatShareHelper.getInstance().setListener(shareToWeChatListener)
}
private fun regToWx() {
// 通过 WXAPIFactory 工厂,获取 IWXAPI 的实例
api = WXAPIFactory.createWXAPI(this, APP_ID, false);
// 将应用的 appId 注册到微信
api?.registerApp(APP_ID);
}
override fun initView(savedInstanceState: Bundle?) { override fun initView(savedInstanceState: Bundle?) {
UpdateBuilder.create().check()// 启动更新任务 UpdateBuilder.create().check()// 启动更新任务
@@ -81,9 +122,9 @@ class MainActivity : BaseVMBActivity<MainViewModel, ActivityMainBinding>(R.layou
// main_tab_layout.setUnreadTip(TAG_INDEX, "99+") // main_tab_layout.setUnreadTip(TAG_INDEX, "99+")
// main_tab_layout.setUnreadTip(TAG_COLLECT, null, false) // main_tab_layout.setUnreadTip(TAG_COLLECT, null, false)
} }
} }
override fun initData() { override fun initData() {
// if(DataStoreManager.isLogin()){ // if(DataStoreManager.isLogin()){
// getAppViewModel().getIMSig(successCall = { // getAppViewModel().getIMSig(successCall = {
@@ -1,5 +1,8 @@
package com.xjjk.healthyclients.data.api package com.xjjk.healthyclients.data.api
import com.tencent.qcloud.tuikit.tuichat.bean.EmployeeBean
import com.tencent.qcloud.tuikit.tuichat.bean.ExpertBean
import com.tencent.qcloud.tuikit.tuichat.bean.GroupMemberBean
import com.xjjk.healthyclients.bean.emergency.BigDiseaseBean import com.xjjk.healthyclients.bean.emergency.BigDiseaseBean
import com.xjjk.healthyclients.bean.emergency.BigDiseaseDetailsBean import com.xjjk.healthyclients.bean.emergency.BigDiseaseDetailsBean
import com.xjjk.healthyclients.bean.emergency.EmergencyGroupInfo import com.xjjk.healthyclients.bean.emergency.EmergencyGroupInfo
@@ -83,6 +86,36 @@ interface EmergencyApi {
*/ */
@GET("/health-emergency/api/emergency/createGroupMag") @GET("/health-emergency/api/emergency/createGroupMag")
suspend fun getIMGroupInfo(@QueryMap params: MutableMap<String, Any?>): ApiResponse<EmergencyGroupInfo> suspend fun getIMGroupInfo(@QueryMap params: MutableMap<String, Any?>): ApiResponse<EmergencyGroupInfo>
/**
* 获取急救员群组信息
*/
@GET("/health-emergency/api/emergency/createGroupMagFirstAider")
suspend fun getParamedicGroupInfo(@QueryMap params: MutableMap<String, Any?>): ApiResponse<EmergencyGroupInfo>
/**
* 搜索专家(专业人员)
*/
@GET("/health-emergency/api/emergency/order/searchExpert")
suspend fun searchExpert(@QueryMap params: MutableMap<String, Any?>): ApiResponse<List<ExpertBean>>
/**
* 搜索员工
*/
@GET("/health-emergency/api/emergency/order/searchEmployee")
suspend fun searchEmployee(@QueryMap params: MutableMap<String, Any?>): ApiResponse<List<EmployeeBean>>
/**
* 实际群成员列表
*/
@GET("/health-emergency/api/emergency/order/getActualGroupMemberList")
suspend fun getActualGroupMemberList(@QueryMap params: MutableMap<String, Any?>): ApiResponse<List<GroupMemberBean>>
/**
* 邀请进群
*/
@POST("/health-emergency/api/emergency/addGroupUser")
suspend fun addGroupUser(@Body requestBody: RequestBody): ApiResponse<Boolean>
/**
* 移除群成员
*/
@POST("/health-emergency/api/emergency/removeGroupUser")
suspend fun removeGroupUser(@Body requestBody: RequestBody): ApiResponse<Boolean>
/** /**
* 获取一人一案 * 获取一人一案
*/ */
@@ -1,5 +1,8 @@
package com.xjjk.healthyclients.data.repository package com.xjjk.healthyclients.data.repository
import com.tencent.qcloud.tuikit.tuichat.bean.EmployeeBean
import com.tencent.qcloud.tuikit.tuichat.bean.ExpertBean
import com.tencent.qcloud.tuikit.tuichat.bean.GroupMemberBean
import com.xjjk.healthyclients.base.repository.BaseRepository import com.xjjk.healthyclients.base.repository.BaseRepository
import com.xjjk.healthyclients.bean.CommonSettingMenuBean import com.xjjk.healthyclients.bean.CommonSettingMenuBean
import com.xjjk.healthyclients.bean.emergency.AddBigDiseaseSubmitBean import com.xjjk.healthyclients.bean.emergency.AddBigDiseaseSubmitBean
@@ -117,6 +120,59 @@ object EmergencyRepository : BaseRepository() {
return apiCall { service.getIMGroupInfo(map) } return apiCall { service.getIMGroupInfo(map) }
} }
suspend fun getParamedicGroupInfo(groupName: String, longitude: Double,
latitude: Double): ApiResponse<EmergencyGroupInfo> {
var map = mutableMapOf<String, Any?>()
map["longitude"] = longitude
map["latitude"] = latitude
map["name"] = groupName //群聊名称改为后台生成
return apiCall { service.getParamedicGroupInfo(map) }
}
suspend fun searchExpert(realname: String, centerId: String, excludeSessionId: String, pageNo: Int, pageSize: Int): ApiResponse<List<ExpertBean>> {
val map = mutableMapOf<String, Any?>()
map["realname"] = realname
map["centerId"] = centerId
map["excludeSessionId"] = excludeSessionId
map["pageNo"] = pageNo
map["pageSize"] = pageSize
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>> {
val map = mutableMapOf<String, Any?>()
map["realname"] = realname
map["orgCode"] = orgCode
map["workNo"] = workNo
map["phone"] = phone
map["excludeSessionId"] = excludeSessionId
map["pageNo"] = pageNo
map["pageSize"] = pageSize
return apiCall { service.searchEmployee(map) }
}
suspend fun getActualGroupMemberList(sessionId: String): ApiResponse<List<GroupMemberBean>> {
val map = mutableMapOf<String, Any?>()
map["sessionId"] = sessionId
return apiCall { service.getActualGroupMemberList(map) }
}
suspend fun addGroupUser(groupId: String, memberList: List<String>,
memberType: Int): ApiResponse<Boolean> {
val map = mutableMapOf<String, Any?>()
map["groupId"] = groupId
map["memberList"] = memberList
map["memberType"] = memberType
return apiCall { service.addGroupUser(map.toJson().toRequestBody()) }
}
suspend fun removeGroupUser(groupId: String, memberList: List<String>): ApiResponse<Boolean> {
val map = mutableMapOf<String, Any?>()
map["groupId"] = groupId
map["memberList"] = memberList
return apiCall { service.removeGroupUser(map.toJson().toRequestBody()) }
}
suspend fun getProposalInfo(): ApiResponse<ProposalBean> { suspend fun getProposalInfo(): ApiResponse<ProposalBean> {
return apiCall { service.getProposalInfo() } return apiCall { service.getProposalInfo() }
} }
@@ -30,11 +30,17 @@ import com.amap.api.maps.model.Marker
import com.amap.api.maps.model.MarkerOptions import com.amap.api.maps.model.MarkerOptions
import com.amap.api.maps.model.MyLocationStyle import com.amap.api.maps.model.MyLocationStyle
import com.permissionx.guolindev.PermissionX import com.permissionx.guolindev.PermissionX
import com.tencent.qcloud.tuikit.tuichat.bean.EmployeeBean
import com.tencent.qcloud.tuikit.tuichat.bean.ExpertBean
import com.tencent.qcloud.tuikit.tuichat.bean.GroupMemberBean
import com.tencent.qcloud.tuikit.tuichat.bean.WorkBean import com.tencent.qcloud.tuikit.tuichat.bean.WorkBean
import com.tencent.qcloud.tuikit.tuichat.classicui.interfaces.OnParamedicOperateListener
import com.tencent.qcloud.tuikit.tuichat.presenter.ParamedicOperateHelper
import com.xjjk.healthyclients.R import com.xjjk.healthyclients.R
import com.xjjk.healthyclients.base.BaseVMBFragment import com.xjjk.healthyclients.base.BaseVMBFragment
import com.xjjk.healthyclients.bean.emergency.EmergencyDictBean import com.xjjk.healthyclients.bean.emergency.EmergencyDictBean
import com.xjjk.healthyclients.bean.emergency.LocationResourceBean import com.xjjk.healthyclients.bean.emergency.LocationResourceBean
import com.xjjk.healthyclients.data.repository.EmergencyRepository
import com.xjjk.healthyclients.databinding.FragmentEmergencyBinding import com.xjjk.healthyclients.databinding.FragmentEmergencyBinding
import com.xjjk.healthyclients.superfuntion.getMarkerInfo import com.xjjk.healthyclients.superfuntion.getMarkerInfo
import com.xjjk.healthyclients.superfuntion.startGroupChat import com.xjjk.healthyclients.superfuntion.startGroupChat
@@ -43,8 +49,12 @@ import com.xjjk.healthyclients.utils.IMInputActionSettingUtils
import com.xjjk.healthyclients.utils.MapUtils import com.xjjk.healthyclients.utils.MapUtils
import com.xjjk.healthyclients.utils.SystemFuntion.goNavigation import com.xjjk.healthyclients.utils.SystemFuntion.goNavigation
import com.xjjk.healthyclients.utils.TUIUtils import com.xjjk.healthyclients.utils.TUIUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withTimeoutOrNull
/** /**
@@ -76,6 +86,93 @@ class EmergencyFragment :
) )
var mListener: LocationSource.OnLocationChangedListener? = null var mListener: LocationSource.OnLocationChangedListener? = null
private val paramedicOperateListener = object : OnParamedicOperateListener {
override fun addGroupUser(groupId: String, memberList: List<String>, memberType: Int) {
mViewModel.addGroupUser(groupId, memberList, memberType)
}
override fun removeGroupUser(groupId: String, memberList: List<String>) {
mViewModel.removeGroupUser(groupId, memberList)
}
override fun searchEmployee(
realname: String?,
orgCode: String?,
workNo: String?,
phone: String?,
excludeSessionId: String?,
pageNo: Int,
pageSize: Int
): List<EmployeeBean?>? {
return runBlocking(Dispatchers.IO) {
try {
val deferred = async {
EmergencyRepository.searchEmployee(
realname ?: "",
orgCode ?: "",
workNo ?: "",
phone ?: "",
excludeSessionId ?: "",
pageNo,
pageSize
)
}
val response = withTimeoutOrNull(10000L) { deferred.await() }
?: return@runBlocking emptyList<EmployeeBean>()
response.takeIf { it.success }?.result ?: emptyList()
} catch (e: Exception) {
emptyList()
}
}
}
override fun searchExpert(
realname: String?,
centerId: String?,
excludeSessionId: String?,
pageNo: Int,
pageSize: Int
): List<ExpertBean?>? {
return runBlocking(Dispatchers.IO) {
try {
val deferred = async {
EmergencyRepository.searchExpert(
realname ?: "",
centerId ?: "",
excludeSessionId ?: "",
pageNo,
pageSize
)
}
val response = withTimeoutOrNull(10000L) { deferred.await() }
?: return@runBlocking emptyList<ExpertBean>()
response.takeIf { it.success }?.result ?: emptyList()
} catch (e: Exception) {
emptyList()
}
}
}
override fun getActualGroupMemberList(sessionId: String): List<GroupMemberBean?>? {
return runBlocking(Dispatchers.IO) {
try {
val deferred = async {
EmergencyRepository.getActualGroupMemberList(sessionId)
}
val response = withTimeoutOrNull(10000L) { deferred.await() }
?: return@runBlocking emptyList<GroupMemberBean>()
response.takeIf { it.success }?.result ?: emptyList()
} catch (e: Exception) {
emptyList()
}
}
}
}
override fun initView(root: View?, savedInstanceState: Bundle?) { override fun initView(root: View?, savedInstanceState: Bundle?) {
// fitTransparentStatusBar(mBinding.tvTitle) // fitTransparentStatusBar(mBinding.tvTitle)
MapsInitializer.updatePrivacyShow(context, true, true) MapsInitializer.updatePrivacyShow(context, true, true)
@@ -88,6 +185,8 @@ class EmergencyFragment :
PermissionX.init(this@EmergencyFragment) PermissionX.init(this@EmergencyFragment)
.permissions(mPermissionList) .permissions(mPermissionList)
.request { allGranted, grantedList, deniedList -> setUpMap() } .request { allGranted, grantedList, deniedList -> setUpMap() }
ParamedicOperateHelper.getInstance().setListener(paramedicOperateListener)
} }
override fun initData() { override fun initData() {
@@ -288,7 +387,7 @@ class EmergencyFragment :
} }
R.id.fragment_emergency_paramedic -> { R.id.fragment_emergency_paramedic -> {
mViewModel.getIMGroupInfo( mViewModel.getParamedicGroupInfo(
getString(R.string.title_paramedic_group), getString(R.string.title_paramedic_group),
mCurrentLon, mCurrentLon,
mCurrentLat, mCurrentLat,
@@ -1,5 +1,8 @@
package com.xjjk.healthyclients.ui.viewmodel package com.xjjk.healthyclients.ui.viewmodel
import com.tencent.qcloud.tuikit.tuichat.bean.EmployeeBean
import com.tencent.qcloud.tuikit.tuichat.bean.ExpertBean
import com.tencent.qcloud.tuikit.tuichat.bean.GroupMemberBean
import com.xjjk.healthyclients.base.viewmodel.BaseViewModel import com.xjjk.healthyclients.base.viewmodel.BaseViewModel
import com.xjjk.healthyclients.bean.emergency.EmergencyGroupInfo import com.xjjk.healthyclients.bean.emergency.EmergencyGroupInfo
import com.xjjk.healthyclients.bean.emergency.LocationResourceBean import com.xjjk.healthyclients.bean.emergency.LocationResourceBean
@@ -99,6 +102,89 @@ class EmergencyViewModel : BaseViewModel() {
}) })
}) })
} }
fun getParamedicGroupInfo(
groupName: String, longitude: Double,
latitude: Double, successCall: (EmergencyGroupInfo) -> Unit = {}
) {
launch({
handleRequest(
EmergencyRepository.getParamedicGroupInfo(groupName, longitude, latitude),
successBlock = {
it.result?.let { it1 ->
successCall.invoke(it1)
}
})
})
}
fun searchExpert(
realname: String, centerId: String, excludeSessionId: String, pageNo: Int, pageSize: Int, successCall: (List<ExpertBean>) -> Unit = {}
) {
launch({
handleRequest(
EmergencyRepository.searchExpert(realname, centerId, excludeSessionId, pageNo, pageSize),
successBlock = {
it.result?.let { it1 ->
successCall.invoke(it1)
}
})
})
}
fun searchEmployee(
realname: String, orgCode: String, workNo: String, phone: String, excludeSessionId: String, pageNo: Int, pageSize: Int,
successCall: (List<EmployeeBean>) -> Unit = {}) {
launch({
handleRequest(
EmergencyRepository.searchEmployee(realname, orgCode, workNo, phone, excludeSessionId, pageNo, pageSize),
successBlock = {
it.result?.let { it1 ->
successCall.invoke(it1)
}
})
})
}
fun getActualGroupMemberList(sessionId: String, successCall: (List<GroupMemberBean>) -> Unit = {}
) {
launch({
handleRequest(
EmergencyRepository.getActualGroupMemberList(sessionId),
successBlock = {
it.result?.let { it1 ->
successCall.invoke(it1)
}
})
})
}
fun addGroupUser(groupId: String, memberList: List<String>,
memberType: Int, successCall: (Boolean) -> Unit = {}
) {
launch({
handleRequest(
EmergencyRepository.addGroupUser(groupId, memberList, memberType),
successBlock = {
it.result?.let { it1 ->
successCall.invoke(it1)
}
})
})
}
fun removeGroupUser(groupId: String, memberList: List<String>, successCall: (Boolean) -> Unit = {}
) {
launch({
handleRequest(
EmergencyRepository.removeGroupUser(groupId, memberList),
successBlock = {
it.result?.let { it1 ->
successCall.invoke(it1)
}
})
})
}
// //
// /** // /**
// * 附近医疗点 // * 附近医疗点