Forráskód Böngészése

1. 任务节点消息通知支持
2. 首页支持获取未读消息个数
3. 首页底部最新消息支持获取最近三条消息
4. 点击消息支持跳转到消息内页

bjb 4 hónapja
szülő
commit
6af70aa7dd

+ 40 - 22
app/src/main/java/com/iscs/bozzys/api/ApiBean.kt

@@ -4,7 +4,6 @@ import android.util.Log
 import androidx.compose.runtime.snapshots.SnapshotStateMap
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.Color
-import com.google.gson.Gson
 import com.iscs.bozzys.R
 import com.iscs.bozzys.ui.pages.edit.step.compose.Anchor
 import com.iscs.bozzys.ui.pages.edit.step.compose.Connection
@@ -139,10 +138,14 @@ data class Node(
     val createTime: Long = 0L,
     val approvalStatus: String = "",
     val approvalOpinion: String = "",
-    val isolationType: String? = null,      // 隔离类型
-    val isolationPoints: String? = null,    // 解除隔离的点位
-    val isolationNodeUuid: String? = null,  // 解除隔离关联的节点uuid
-    val nodeUserList: List<User>? = null,   // 隔离和解除隔离关联的上锁人和共锁人
+    val isolationType: String? = null,          // 隔离类型
+    val isolationPoints: String? = null,        // 解除隔离的点位
+    val isolationNodeUuid: String? = null,      // 解除隔离关联的节点uuid
+    val nodeUserList: List<User>? = null,       // 隔离和解除隔离关联的上锁人和共锁人
+    val smsTemplateCode: String? = null,        // 短信通知
+    val messageTemplateCode: String? = null,    // 站内信息
+    val emailTemplateCode: String? = null,      // 邮件通知
+    val appTemplateCode: String? = null,        // app消息推送
 ) {
 
     /**
@@ -175,6 +178,7 @@ data class Node(
             val formId = forms.find { it.name == "formId" }?.value?.getOrNull(0) ?: "0"
             val findWork = workFormList.find { it.id == formId.toInt() }
             val formData = if (findWork != null) Json.encodeToString(findWork) else null
+            val noticeType = forms.find { it.name == "noticeType" }?.value ?: emptyList()
             params["workerUserId"] = workerUserId.toInt()
             params["formId"] = formId.toInt()
             params["formData"] = formData
@@ -205,6 +209,15 @@ data class Node(
                     params["isolationNodeUuid"] = isolationNode
                 }
             }
+            // 通知方式
+            // 短信
+            params["smsTemplateCode"] = if (noticeType.contains("sms")) "true" else "false"
+            // 站内信
+            params["messageTemplateCode"] = if (noticeType.contains("message")) "true" else "false"
+            // 邮件
+            params["emailTemplateCode"] = if (noticeType.contains("email")) "true" else "false"
+            // app通知
+            params["appTemplateCode"] = if (noticeType.contains("app")) "true" else "false"
         }
         return params
     }
@@ -251,7 +264,8 @@ data class Node(
                     userList.addAll(it.nodeUserList ?: emptyList())
                 }
             }
-
+            // 通知方式
+            val noticeType = forms.find { it.name == "noticeType" }?.value ?: emptyList()
             return Node(
                 id = id,
                 workId = workId,
@@ -271,7 +285,11 @@ data class Node(
                     separator = ","
                 ),
                 isolationNodeUuid = isolationNode,
-                nodeUserList = if (userList.isEmpty()) null else userList
+                nodeUserList = if (userList.isEmpty()) null else userList,
+                smsTemplateCode = if (noticeType.contains("sms")) "true" else "false",
+                messageTemplateCode = if (noticeType.contains("message")) "true" else "false",
+                emailTemplateCode = if (noticeType.contains("email")) "true" else "false",
+                appTemplateCode = if (noticeType.contains("app")) "true" else "false"
             )
         }
         return Node(
@@ -309,11 +327,6 @@ data class Node(
     ): List<FormField> {
         Log.d("NodeOption", "buildFormList -> $this")
         val list = ArrayList<FormField>()
-        val gson = Gson()
-        // 暂不解析,等后端确认后再解析
-        // val dataMap: MutableMap<String, Any?> = gson.fromJson(data.ifEmpty { "{}" }, object : TypeToken<MutableMap<String, Any?>>() {}.type)
-        // 携带数据
-        // Log.d("NodeOption", "dataMap -> ${gson.toJson(dataMap)}")
         // 添加节点名称
         list += FormField(
             id.toString(),
@@ -466,16 +479,21 @@ data class Node(
                 )
             }
         }
-//        // 通知方式
-//        list += FormField(
-//            id.toString(), "checkbox", "通知方式", nodeName, false, listOf(), listOf(nodeName),
-//            options = listOf(
-//                FormOption("短信", "message"),
-//                FormOption("站内信", "sms"),
-//                FormOption("邮件", "email"),
-//                FormOption("App通知", "app"),
-//            )
-//        )
+        val notices = arrayListOf<String>()
+        if (smsTemplateCode == "true") notices += "sms"
+        if (messageTemplateCode == "true") notices += "message"
+        if (emailTemplateCode == "true") notices += "email"
+        if (appTemplateCode == "true") notices += "app"
+        // 通知方式
+        list += FormField(
+            id.toString(), "checkbox", "通知方式", "noticeType", false, listOf(), notices,
+            options = listOf(
+                FormOption("短信", "sms"),
+                FormOption("站内信", "message"),
+                FormOption("邮件", "email"),
+                FormOption("App通知", "app"),
+            )
+        )
 //        // 通知人
 //        list += FormField(
 //            id.toString(), "select", "通知人", nodeName, false, listOf(), options = listOf(

+ 7 - 0
app/src/main/java/com/iscs/bozzys/api/ApiRequest.kt

@@ -271,6 +271,13 @@ object ApiRequest {
         return requestApi { api.getMessages(params) }
     }
 
+    /**
+     * 获取未读消息数量
+     */
+    suspend fun getMessageUnread(): Result<Response<Int>> {
+        return requestApi { api.getMessagesUnread() }
+    }
+
     /**
      * 更新消息为已读
      *

+ 8 - 1
app/src/main/java/com/iscs/bozzys/api/ApiService.kt

@@ -217,12 +217,19 @@ interface ApiService {
      * 获取通知消息
      */
     @Headers("Content-Type: application/x-www-form-urlencoded")
-    @GET("/admin-api/system/app-notify-message/page")
+    @GET("/admin-api/system/app-notify-message/my-page")
     suspend fun getMessages(
         @QueryMap params: MutableMap<String, Any>,
         @HeaderMap headers: Map<String, String> = ApiRequest.getUserHeaders()
     ): Response<PageRsp<Message>>
 
+    /**
+     * 获取未读通知消息
+     */
+    @Headers("Content-Type: application/x-www-form-urlencoded")
+    @GET("/admin-api/system/app-notify-message/get-unread-count")
+    suspend fun getMessagesUnread(@HeaderMap headers: Map<String, String> = ApiRequest.getUserHeaders()): Response<Int>
+
     /**
      * 更新消息为已读
      */

+ 3 - 0
app/src/main/java/com/iscs/bozzys/event/Event.kt

@@ -46,4 +46,7 @@ sealed class RefreshEvent(var any: Any = Any()) {
 
     // 刷新Job详情数据
     object JobDetail : RefreshEvent()
+
+    // 通知事件
+    object Notification : RefreshEvent()
 }

+ 7 - 2
app/src/main/java/com/iscs/bozzys/service/AliPushService.kt

@@ -3,6 +3,9 @@ package com.iscs.bozzys.service
 import android.content.Context
 import com.alibaba.sdk.android.push.AliyunMessageIntentService
 import com.alibaba.sdk.android.push.notification.CPushMessage
+import com.iscs.bozzys.event.RefreshEvent
+import com.iscs.bozzys.event.RefreshEventBus
+import com.iscs.bozzys.utils.LogUtil
 
 class AliPushService : AliyunMessageIntentService() {
 
@@ -14,8 +17,10 @@ class AliPushService : AliyunMessageIntentService() {
 
     }
 
-    override fun onNotificationOpened(p0: Context?, p1: String?, p2: String?, p3: String?) {
-
+    override fun onNotificationOpened(ctx: Context?, title: String?, content: String?, params: String?) {
+        LogUtil.i("MPS", "AliPushService -> onNotificationOpened $title $content $params")
+        // 这里执行跳转到消息详情页面
+        RefreshEventBus.onRefreshData(RefreshEvent.Notification)
     }
 
     override fun onNotificationClickedWithNoAction(p0: Context?, p1: String?, p2: String?, p3: String?) {

+ 25 - 7
app/src/main/java/com/iscs/bozzys/ui/common/PageBase.kt

@@ -24,7 +24,11 @@ import androidx.lifecycle.lifecycleScope
 import androidx.lifecycle.viewmodel.compose.viewModel
 import com.iscs.bozzys.event.AuthEvent
 import com.iscs.bozzys.event.AuthEventBus
+import com.iscs.bozzys.event.RefreshEvent
+import com.iscs.bozzys.event.RefreshEventBus
+import com.iscs.bozzys.ui.pages.home.PageHome
 import com.iscs.bozzys.ui.pages.login.openPageLogin
+import com.iscs.bozzys.ui.pages.message.openPageMessage
 import com.iscs.bozzys.ui.pages.vm.StateLoading
 import com.iscs.bozzys.ui.pages.vm.VMLoading
 import com.iscs.bozzys.ui.theme.BozzysTheme
@@ -83,13 +87,27 @@ abstract class PageBase(
      */
     private fun initEvent() {
         lifecycleScope.launch {
-            AuthEventBus.events.collect { event ->
-                when (event) {
-                    is AuthEvent.TokenExpired -> {
-                        // 打开登录页面
-                        openPageLogin()
-                        // 关闭当前页面
-                        destroyDelay(500)
+            async {
+                AuthEventBus.events.collect { event ->
+                    when (event) {
+                        is AuthEvent.TokenExpired -> {
+                            // 打开登录页面
+                            openPageLogin()
+                            // 关闭当前页面
+                            destroyDelay(500)
+                        }
+                    }
+                }
+            }
+            async {
+                RefreshEventBus.events.collect { event ->
+                    when (event) {
+                        is RefreshEvent.Notification -> {
+                            delay(1000)
+                            if (this@PageBase is PageHome) openPageMessage()
+                        }
+                        // 其他操作
+                        else -> {}
                     }
                 }
             }

+ 50 - 36
app/src/main/java/com/iscs/bozzys/ui/pages/home/HomeCompose.kt

@@ -1,7 +1,6 @@
 package com.iscs.bozzys.ui.pages.home
 
 import androidx.compose.foundation.background
-import androidx.compose.foundation.border
 import androidx.compose.foundation.clickable
 import androidx.compose.foundation.interaction.MutableInteractionSource
 import androidx.compose.foundation.layout.Arrangement
@@ -54,11 +53,12 @@ import com.iscs.bozzys.ui.pages.compose.TaskListItem
 import com.iscs.bozzys.ui.pages.message.openPageMessage
 import com.iscs.bozzys.ui.pages.vm.VMHome
 import com.iscs.bozzys.ui.theme.Text
+import com.iscs.bozzys.utils.DateUtil.getShowDateOrTime
 
 @OptIn(ExperimentalMaterial3Api::class)
 @Composable
-fun HomeCompose(pv: PaddingValues, zIndex: Float, vmHome: VMHome) {
-    val state by vmHome.state.collectAsState()
+fun HomeCompose(pv: PaddingValues, zIndex: Float, vm: VMHome) {
+    val state by vm.state.collectAsState()
     Column(
         modifier = Modifier
             .fillMaxSize()
@@ -67,14 +67,14 @@ fun HomeCompose(pv: PaddingValues, zIndex: Float, vmHome: VMHome) {
             .pointerInput(Unit) {},
     ) {
         // 顶部工具栏
-        TopToolBar(pv, vmHome)
+        TopToolBar(pv, vm)
         PullToRefreshBox(state.isHomeTabRefresh, onRefresh = {
-            vmHome.onRefreshHomeTab()
+            vm.onRefreshHomeTab()
         }) {
             Column(Modifier.fillMaxSize()) {
                 Box {
-                    TaskList(pv, vmHome)
-                    TODO(vmHome)
+                    TaskList(pv, vm)
+                    TODO(vm)
                 }
             }
         }
@@ -85,9 +85,9 @@ fun HomeCompose(pv: PaddingValues, zIndex: Float, vmHome: VMHome) {
  * 顶部工具栏
  */
 @Composable
-private fun TopToolBar(pv: PaddingValues, vmHome: VMHome) {
+private fun TopToolBar(pv: PaddingValues, vm: VMHome) {
     val ctx = LocalContext.current
-    val state by vmHome.state.collectAsState()
+    val state by vm.state.collectAsState()
     Column(
         modifier = Modifier
             .fillMaxWidth()
@@ -134,8 +134,8 @@ private fun TopToolBar(pv: PaddingValues, vmHome: VMHome) {
                         .padding(9.dp),
                     tint = Color.White
                 )
-                Text(
-                    "5", color = Color.White, modifier = Modifier
+                if (state.msgCount > 0) Text(
+                    if (state.msgCount > 99) "${state.msgCount}+" else "${state.msgCount}", color = Color.White, modifier = Modifier
                         .offset(x = 8.dp, y = 15.dp)
                         .defaultMinSize(14.dp, 14.dp)
                         .clip(RoundedCornerShape(14.dp))
@@ -337,36 +337,50 @@ private fun TaskList(pv: PaddingValues, vm: VMHome) {
             }
         }
         Column(Modifier.fillMaxWidth()) {
-            state.messageList.forEach { msg -> key(msg.id) { MessageListItem(msg) } }
+            state.messageList.forEach { msg -> key(msg.id) { MessageListItem(msg, vm) } }
         }
     }
 }
 
 @Composable
-fun MessageListItem(msg: Message) {
-    Column(
-        Modifier
-            .padding(bottom = 15.dp)
-            .fillMaxWidth()
-            .height(102.dp)
-            .border(1.dp, color = Color(0xFFEEEEEE), shape = RoundedCornerShape(12.dp))
-            .clip(RoundedCornerShape(12.dp))
-            .background(Color(0xFFF8F9FA))
-            .clickable(onClick = {})
-            .padding(16.dp)
-    ) {
-        Text(
-            "系统通知:您有新的作业任务待处理,请及时查看并执行操作任务。请及时查看并执行操作任务。请及时查看并执行操作任务。",
-            modifier = Modifier.fillMaxWidth(),
-            fontSize = 14.sp,
-            color = Text,
-            maxLines = 2,
-            overflow = TextOverflow.Ellipsis
-        )
-        Spacer(Modifier.weight(1f))
-        Row(Modifier.fillMaxWidth()) {
-            Spacer(Modifier.weight(1f))
-            Text("5分钟前", fontSize = 12.sp, color = Color(0xFF999999))
+fun MessageListItem(msg: Message, vm: VMHome) {
+    CardContainer(modifier = Modifier.padding(vertical = 8.dp)) {
+        Row(
+            Modifier
+                .fillMaxWidth()
+                .height(122.dp)
+                .clickable(onClick = { vm.onMessage(msg) })
+                .padding(16.dp)
+        ) {
+            Column(
+                Modifier
+                    .padding(end = 10.dp)
+                    .fillMaxHeight()
+                    .weight(1f)
+            ) {
+                Text(msg.title ?: "提醒", fontSize = 15.sp, fontWeight = FontWeight.Medium, color = Text)
+                Text(
+                    msg.templateContent,
+                    fontSize = 13.sp,
+                    color = Color(0xFF666666),
+                    overflow = TextOverflow.Ellipsis,
+                    maxLines = 2,
+                    modifier = Modifier.fillMaxWidth()
+                )
+                Spacer(Modifier.weight(1f))
+                Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
+                    Text("作业管理", fontSize = 12.sp, color = Color(0xFF999999))
+                    Spacer(Modifier.weight(1f))
+                    Text(msg.createTime.getShowDateOrTime(), fontSize = 14.sp, color = Text.copy(alpha = 0.6f))
+                }
+            }
+            Spacer(
+                Modifier
+                    .size(8.dp)
+                    .clip(RoundedCornerShape(50))
+                    .background(if (msg.readStatus) Color.Transparent else Color(0xFFFF4500))
+                    .align(Alignment.CenterVertically)
+            )
         }
     }
 }

+ 14 - 12
app/src/main/java/com/iscs/bozzys/ui/pages/message/PageMessage.kt

@@ -142,6 +142,7 @@ class PageMessage : PageBase() {
                     textAlign = TextAlign.Center
                 )
                 Row(Modifier.width(80.dp)) {
+                    Spacer(modifier = Modifier.weight(1f))
                     Icon(
                         painter = painterResource(R.drawable.read_all),
                         contentDescription = null,
@@ -152,16 +153,17 @@ class PageMessage : PageBase() {
                             .padding(8.dp),
                         tint = Color.White
                     )
-                    Icon(
-                        painter = painterResource(R.drawable.delete_all),
-                        contentDescription = null,
-                        modifier = Modifier
-                            .size(36.dp)
-                            .clip(RoundedCornerShape(6.dp))
-                            .clickable(onClick = { vm.onDeleteAll() })
-                            .padding(9.dp),
-                        tint = Color.White
-                    )
+                    // 后端暂不支持删除消息
+//                    Icon(
+//                        painter = painterResource(R.drawable.delete_all),
+//                        contentDescription = null,
+//                        modifier = Modifier
+//                            .size(36.dp)
+//                            .clip(RoundedCornerShape(6.dp))
+//                            .clickable(onClick = { vm.onDeleteAll() })
+//                            .padding(9.dp),
+//                        tint = Color.White
+//                    )
                 }
             }
         }
@@ -273,7 +275,7 @@ private fun MessageListItemContent(vm: VMMessage, msg: Message) {
                     .fillMaxHeight()
                     .weight(1f)
             ) {
-                Text("新任务分配", fontSize = 15.sp, fontWeight = FontWeight.Medium, color = Text)
+                Text(msg.title ?: "提醒", fontSize = 15.sp, fontWeight = FontWeight.Medium, color = Text)
                 Text(
                     msg.templateContent,
                     fontSize = 13.sp,
@@ -286,7 +288,7 @@ private fun MessageListItemContent(vm: VMMessage, msg: Message) {
                 Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
                     Text(msg.createTime.getShowDateOrTime(), fontSize = 12.sp, color = Color(0xFF999999))
                     Spacer(Modifier.weight(1f))
-                    Text("作业管理", fontSize = 12.sp, color = Color(0xFF999999))
+                    Text("作业管理", fontSize = 13.sp, color = Color(0xFF999999))
                 }
             }
             Spacer(

+ 39 - 7
app/src/main/java/com/iscs/bozzys/ui/pages/vm/VMHome.kt

@@ -113,6 +113,15 @@ class VMHome : VMBase() {
                 toast.emit(statisticsRsp.msg)
                 return@launch
             }
+            // 获取未读消息数量
+            val msgCountRsp = ApiRequest.getMessageUnread().getOrElse { it.getResponse() }
+            if (!msgCountRsp.code.isCodeOk()) {
+                _state.value = _state.value.copy(isHomeTabRefresh = false)
+                delay(500)
+                loading.emit(StateLoading())
+                toast.emit(msgCountRsp.msg)
+                return@launch
+            }
             // 查询进行中的作业
             val jobRsp =
                 ApiRequest.getJobs(hashMapOf("pageNo" to 1, "pageSize" to 1, "status" to "running"))
@@ -124,7 +133,7 @@ class VMHome : VMBase() {
                 toast.emit(jobRsp.msg)
                 return@launch
             }
-            // 查询待审核的任务
+            // 查询待审核的任务,目前只读最新3条
             val tasksRsp = ApiRequest.getTasks(
                 hashMapOf(
                     "pageNo" to 1, "pageSize" to 3, "approvalStatus" to "running"
@@ -137,16 +146,24 @@ class VMHome : VMBase() {
                 toast.emit(statisticsRsp.msg)
                 return@launch
             }
-            val tasks = tasksRsp.data?.list ?: emptyList()
-            // 后面看需求是否需要限制
-            // val maxLimitList = if (list.isEmpty()) list else list.subList(0, min(list.size, 3))
+            // 获取最新三条消息
+            val msgRsp = ApiRequest.getMessage(mutableMapOf("pageNo" to 1, "pageSize" to 3)).getOrElse { it.getResponse() }
+            if (!msgRsp.code.isCodeOk()) {
+                _state.value = _state.value.copy(isHomeTabRefresh = false)
+                delay(500)
+                loading.emit(StateLoading())
+                toast.emit(msgRsp.msg)
+                return@launch
+            }
             _state.value = _state.value.copy(
-                homeTasks = ArrayList(tasks),
+                homeTasks = ArrayList(tasksRsp.data?.list ?: emptyList()),
+                messageList = ArrayList(msgRsp.data?.list ?: emptyList()),
                 todoCountRunning = statisticsRsp.data?.inProgressCount ?: 0,
                 todoCountFinish = statisticsRsp.data?.completedCount ?: 0,
                 isHomeTabRefresh = false,
                 roles = permissionRsp.data?.roles ?: emptyList(),
-                user = permissionRsp.data?.user ?: User()
+                user = permissionRsp.data?.user ?: User(),
+                msgCount = msgCountRsp.data ?: 0
             )
             // 更新进行中的任务数字
             updateUnReadCount(1, jobRsp.data?.total ?: 0)
@@ -220,6 +237,20 @@ class VMHome : VMBase() {
         }
     }
 
+    /**
+     * 消息被点击操作,主要是已读操作
+     */
+    fun onMessage(msg: Message) {
+        // 已读不做处理
+        if (msg.readStatus) return
+        viewModelScope.launch {
+            ApiRequest.updateMessageRead(mutableMapOf("ids" to "${msg.id}")).onSuccess {
+                val newList = _state.value.messageList.map { if (it.id == msg.id) it.copy(readStatus = true) else it }.toMutableList()
+                _state.value = _state.value.copy(messageList = newList)
+            }
+        }
+    }
+
     /**
      * 退出登录操作
      */
@@ -272,7 +303,7 @@ class VMHome : VMBase() {
  * @param todoCountFinish   已完成
  * @param roles             用户角色
  * @param user              当前用户
- * @param isRefreshHome     是否刷新首页数据
+ * @param msgCount          未读消息数
  */
 data class StateHome(
     val navs: List<NavBarItem> = arrayListOf(),
@@ -288,6 +319,7 @@ data class StateHome(
     val todoCountFinish: Int = 0,
     val roles: List<String> = listOf(),
     val user: User = User(),
+    val msgCount: Int = 0,
 )
 
 /**

+ 4 - 0
app/src/main/java/com/iscs/bozzys/ui/pages/vm/VMMessage.kt

@@ -3,6 +3,8 @@ package com.iscs.bozzys.ui.pages.vm
 import androidx.lifecycle.viewModelScope
 import com.iscs.bozzys.api.ApiRequest
 import com.iscs.bozzys.api.Message
+import com.iscs.bozzys.event.RefreshEvent
+import com.iscs.bozzys.event.RefreshEventBus
 import com.iscs.bozzys.ui.common.StateTips
 import com.iscs.bozzys.ui.common.VMBase
 import com.iscs.bozzys.utils.DateUtil.getShowDate
@@ -57,6 +59,7 @@ class VMMessage : VMBase() {
             ApiRequest.updateMessageRead(mutableMapOf("ids" to "${msg.id}")).onSuccess {
                 val newList = _state.value.messages.map { if (it.id == msg.id) it.copy(readStatus = true) else it }.toMutableList()
                 _state.value = _state.value.copy(messages = newList)
+                RefreshEventBus.onRefreshData(RefreshEvent.HomeData)
             }
         }
     }
@@ -81,6 +84,7 @@ class VMMessage : VMBase() {
                 ApiRequest.updateMessageRead(mutableMapOf()).onSuccess {
                     val newList = _state.value.messages.map { it.copy(readStatus = true) }.toMutableList()
                     _state.value = _state.value.copy(messages = newList)
+                    RefreshEventBus.onRefreshData(RefreshEvent.HomeData)
                 }
             }
         }