package com.iscs.bozzys.api import android.util.Log import androidx.compose.runtime.snapshots.SnapshotStateMap import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color 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 import com.iscs.bozzys.ui.pages.edit.step.compose.NodeUI import com.iscs.bozzys.utils.StringToListSerializer import kotlinx.serialization.Serializable import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json /** * 请求基础响应对象 * * @param code * @param msg * @param data */ open class Response(var code: Int = 500, var msg: String = "", var data: T? = null) /** * 账号登录接口响应数据 * * @param userId 用户id * @param username 用户名 * @param nickname 用户昵称 * @param accessToken 使用token * @param refreshToken 刷新token */ class LoginRsp( val userId: Int, val username: String, val nickname: String, val accessToken: String, val refreshToken: String ) /** * 获取分页数据 * * @param total 数据总数 * @param list 当前获取到的数据 */ class PageRsp(val total: Int, val list: List) /** * 获取用户角色 * * @param user 用户信息 * @param roles 角色信息 */ class PermissionRsp(val user: User, val roles: List) /** * 任务统计 */ data class TaskStatistics(val inProgressCount: Int, val completedCount: Int) /** * 点位基类 */ @Serializable data class IsolationPoint(val id: Int, val pointName: String, val pointNfc: String) /** * 用户 */ @Serializable data class User( val id: Int = 0, val userId: Int? = null, val username: String = "", val nickname: String = "", val cardNfc: String? = null, val remark: String = "", val deptId: Int = 0, val deptName: String? = null, val postIds: List = listOf(), val email: String? = null, val mobile: String? = null, val sex: Int = 0, val avatar: String = "", val status: Int = 0, val loginIp: String = "", val loginDate: Long = 0L, val createTime: Long = 0L, val workstationIds: List? = null, val type: String? = "", ) /** * 流程模板 */ @Serializable data class Sop( val id: Int, val name: String, val content: String, val nodeCount: Int, val status: Int, val description: String, val createTime: Long, val updateTime: Long ) /** * 字典结构 */ @Serializable data class Dict(val id: Int, val sort: Int, val label: String, val value: String, val status: Int) /** * 任务信息 */ @Serializable data class Node( val id: Int = 0, val workId: Int = 0, val uuid: String = "", val parentUuid: String = "", val childrenUuid: String = "", val nodeName: String = "", val nodeIcon: String = "", val type: String = "", val position: String = "", val data: String = "", val description: String? = null, val workerUserId: Int = 0, val workerGroupId: Int? = null, val formId: Int = 0, val formData: String? = null, 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? = null, // 隔离和解除隔离关联的上锁人和共锁人 val smsTemplateCode: String? = null, // 短信通知 val messageTemplateCode: String? = null, // 站内信息 val emailTemplateCode: String? = null, // 邮件通知 val appTemplateCode: String? = null, // app消息推送 ) { /** * 校验请求参数 * * @param forms 表单数据 */ fun checkCanSubmit(forms: List): String { forms.forEach { Log.d("NodeOption", "checkCanSubmit -> $it") if (it.required) { if ((it.value.getOrNull(0) ?: "").isEmpty()) { return (it.placeholder.getOrNull(0) ?: "").ifEmpty { "请填写${it.label}" } } } } return "" } /** * 构建请求参数 * * @param forms 表单数据 */ fun buildSubmitParams(forms: List, workFormList: List): MutableMap { val nodeName = forms.find { it.name == "nodeName" }?.value?.getOrNull(0) ?: "" val params = mutableMapOf("nodeId" to id, "nodeName" to nodeName) if (type != "createJob") { val workerUserId = forms.find { it.name == "workerUserId" }?.value?.getOrNull(0) ?: "0" 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 // 处理隔离点信息表单 if (listOf("isolation", "releaseIsolation").contains(type)) { val isolationType = forms.find { it.name == "isolationType" }?.value?.getOrNull(0) ?: "-1" val isolationPoints = forms.find { it.name == "isolationPoints" }?.value ?: emptyList() if (isolationType == "1") { // 移除负责人 params.remove("workerUserId") // 上锁人和共锁人 val locker = forms.find { it.name == "locker" }?.value?.getOrNull(0) ?: "-1" val group = forms.find { it.name == "group" }?.value ?: emptyList() val userList = mutableListOf>() if (locker.toInt() > 0) userList += mutableMapOf("userId" to locker.toInt(), "type" to "jtlocker") group.forEach { userList += mutableMapOf("userId" to it.toInt(), "type" to "jtcolocker") } params["nodeUserDOList"] = userList } params["isolationType"] = isolationType params["isolationPoints"] = isolationPoints.joinToString( prefix = "[", postfix = "]", separator = "," ) if (type == "releaseIsolation") { val isolationNode = forms.find { it.name == "isolationNode" }?.value?.getOrNull(0) // 解除隔离,这里需要填写关联的节点uuid 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 } /** * 更新节点信息 * * @param forms */ fun updateNodeInfo(forms: List, nodes: List): Node { val nodeName = forms.find { it.name == "nodeName" }?.value?.getOrNull(0) ?: "" if (type != "createJob") { // 解析关联表单 val formId = forms.find { it.name == "formId" }?.value?.getOrNull(0) ?: "-1" // 解析表单数据 val formData = forms.find { it.name == "formData" }?.value?.getOrNull(0) // 解析负责人 var workerUserId = forms.find { it.name == "workerUserId" }?.value?.getOrNull(0) ?: "0" // 解析隔离方式 var isolationType = forms.find { it.name == "isolationType" }?.value?.getOrNull(0) // 解析点位 var isolationPoints = forms.find { it.name == "isolationPoints" }?.value ?: emptyList() // 解析上锁人 val locker = forms.find { it.name == "locker" }?.value?.getOrNull(0) ?: "-1" // 解析共锁人 val group = forms.find { it.name == "group" }?.value ?: emptyList() // 构建上锁人和共锁人列表 val userList = mutableListOf() if (locker.toInt() > 0) userList += User(userId = locker.toInt(), type = "jtlocker") if (group.isNotEmpty()) group.forEach { userList += User(userId = it.toInt(), type = "jtcolocker") } // 解析隔离节点 val isolationNode = forms.find { it.name == "isolationNode" }?.value?.getOrNull(0) // 如果是解除隔离操作,隔离方式、隔离点位、负责人(或者上锁人、解锁人)需要从选中的点位中获取 if (type == "releaseIsolation") { nodes.find { it.node?.uuid == isolationNode }?.node?.let { // 从节点中将数据copy到当前节点 workerUserId = "${it.workerUserId}" isolationType = it.isolationType isolationPoints = if (it.isolationPoints.isNullOrEmpty()) { emptyList() } else { it.isolationPoints.replace("[", "").replace("]", "").split(",") } userList.addAll(it.nodeUserList ?: emptyList()) } } // 通知方式 val noticeType = forms.find { it.name == "noticeType" }?.value ?: emptyList() return Node( id = id, workId = workId, uuid = uuid, parentUuid = parentUuid, childrenUuid = childrenUuid, nodeName = nodeName, type = type, position = position, workerUserId = workerUserId.toInt(), formId = formId.toInt(), formData = formData, isolationType = isolationType, isolationPoints = isolationPoints.joinToString( prefix = "[", postfix = "]", separator = "," ), isolationNodeUuid = isolationNode, 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( id = id, workId = workId, uuid = uuid, parentUuid = parentUuid, childrenUuid = childrenUuid, nodeName = nodeName, type = type, position = position ) } /** * 构建表单列表 * * @param workerUserList 负责人列表 * @param lockerUserList 上锁人列表 * @param groupUserList 共锁人列表 * @param workFormList 工作表单列表 * @param isolationMethodList 获取隔离方式列表 * @param isolationList 点位列表 * @param isLocker 是否上锁人 */ fun buildFormList( workerUserList: List, lockerUserList: List, groupUserList: List, workFormList: List, isolationMethodList: List, isolationList: List, nodes: List = listOf(), isLocker: Boolean = false, ): List { Log.d("NodeOption", "buildFormList -> $this") val list = ArrayList() // 添加节点名称 list += FormField( id.toString(), "input", "节点名称", name = "nodeName", true, value = listOf(nodeName), placeholder = listOf("请输入节点名称") ) if (type == "createJob") return list // 添加节点负责人 list += FormField( id.toString(), "select", "负责人", "workerUserId", true, placeholder = listOf("请选择负责人"), options = workerUserList.map { FormOption(it.nickname, it.id.toString()) }, value = if (workerUserId > 0) listOf(workerUserId.toString()) else listOf() ) // 添加业务表单 list += FormField( id.toString(), "select", "业务表单", name = "formId", true, placeholder = listOf("请选择业务表单"), options = workFormList.map { FormOption(it.name ?: "", it.id.toString()) }, value = if (formId > 0) listOf(formId.toString()) else listOf() ) // 隔离配置 if (listOf("isolation", "releaseIsolation").contains(type)) { // 先移除负责人,需要在后面进行添加 list.removeIf { it.name == "workerUserId" } // 而且业务表单非必填 val formField = list.find { it.name == "formId" }?.copy(required = false) list.removeIf { it.name == "formId" } formField?.let { list.add(it) } val isolations = nodes.map { it.node }.filter { it != null && it.type == "isolation" } var enabled = true var isLockerTemp = isLocker // 如果是解除隔离操作,这里只能选择当前流程中已经存在的隔离点位 if (type == "releaseIsolation") { enabled = false // 增加隔离点选择列表 list += FormField( id.toString(), type = "select", label = "选择隔离节点", name = "isolationNode", required = true, placeholder = listOf("请选择隔离节点"), options = isolations.map { FormOption(it?.nodeName ?: "", value = it?.uuid ?: "") }, value = if (isolationNodeUuid.isNullOrEmpty()) listOf() else listOf(isolationNodeUuid) ) // 如果是解除隔离的操作,这里需要先校验选择的节点是否是拆除 isLockerTemp = isolationType == "1" } // 抽取隔离方式value值 val valueOfIsolationType = if (isolationType.isNullOrEmpty()) { listOf() // val type = dataMap["isolationMethod"] // if (type == null) listOf() else listOf("$type") } else listOf(isolationType) // 隔离方式 list += FormField( id.toString(), "select", "隔离方式", "isolationType", true, placeholder = listOf("请选择隔离方式"), options = isolationMethodList.map { FormOption(it.label, it.value) }, value = valueOfIsolationType, enabled = enabled ) // 抽取隔离点位value值 val points = if (isolationPoints.isNullOrEmpty()) "" else isolationPoints.replace("[", "").replace("]", "") val valueOfIsolationPoints = if (points.isNotEmpty()) { points.split(",") } else { listOf() // when (val value = dataMap["isolationPoints"]) { // // 如果是数组 // is List<*> -> value.map { "$it".toFloat().toInt().toString() } // // 如果是String // is String -> { // value.replace("[", "").replace("]", "").split(",") // } // // 其他不处理 // else -> listOf() // } } // 隔离点选择,可多选 list += FormField( id.toString(), "select", "隔离点选择(可多选)", "isolationPoints", true, listOf("请选择隔离点"), multiSelect = true, options = isolationList.map { FormOption(it.pointName, it.id.toString()) }, value = valueOfIsolationPoints, enabled = enabled ) if (isLockerTemp) { val locker = nodeUserList?.find { it.type == "jtlocker" } val group = nodeUserList?.filter { it.type == "jtcolocker" } ?: emptyList() // 上锁人 list += FormField( id.toString(), "select", "上锁人", "locker", true, placeholder = listOf("请选择上锁人"), options = lockerUserList.map { FormOption(it.nickname, it.id.toString()) }, value = if (locker != null) listOf(locker.userId.toString()) else listOf(), enabled = enabled ) // 共锁人 list += FormField( id.toString(), "select", "共锁人(可多选)", "group", false, placeholder = listOf("请选择共锁人"), options = groupUserList.map { FormOption(it.nickname, it.id.toString()) }, value = if (group.isNotEmpty()) group.map { it.userId.toString() } else listOf(), multiSelect = true, enabled = enabled ) } else { // 负责人 list += FormField( id.toString(), "select", "负责人", "workerUserId", true, placeholder = listOf("请选择负责人"), options = workerUserList.map { FormOption(it.nickname, it.id.toString()) }, value = if (workerUserId > 0) listOf(workerUserId.toString()) else listOf(), enabled = enabled ) } } val notices = arrayListOf() 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( // FormOption("任务负责人", "p1"), // FormOption("任务参与人", "p2"), // FormOption("任务负责和参与人", "all"), // FormOption("指定人", "custom"), // ) // ) // // 通知时间 // list += FormField( // id.toString(), "select", "通知时间", nodeName, false, listOf(), options = listOf( // FormOption("任务开始前30分钟", "0.5"), // FormOption("任务开始前1小时", "1"), // FormOption("任务开始前2小时", "2"), // FormOption("任务开始前5小时", "5"), // ) // ) return list } /** * 获取node图标,暂时本地通过type获取 */ fun getIcon(): Int { // Log.d("xiaoming", "节点类型:$type") return when (type) { // 开始/创建 "createJob" -> R.drawable.node_icon_create_job // 审核 "review" -> R.drawable.node_icon_review // 确认 "confirm" -> R.drawable.node_icon_confirm // 录入信息 "inputInfo" -> R.drawable.node_icon_input_info // 隔离 "isolation" -> R.drawable.node_icon_isolation // 解除隔离 "releaseIsolation" -> R.drawable.node_icon_release_isolation // 还锁 "returnLock" -> R.drawable.node_icon_return_lock // 结束 "complete" -> R.drawable.node_icon_complete else -> R.drawable.tips } } } /** * 任务表单信息 */ @Serializable data class TaskFormInfo( val id: Int = 0, val name: String? = null, val conf: String? = null, val fields: List? = null, val status: Int? = null, val remark: String? = null, val createTime: Long? = null ) /** * 作业基类信息 */ @Serializable data class Job( val id: Int = -1, // 作业Id val orderNo: String = "", // 作业编号 val name: String = "", // 作业名称 val type: String = "", // 作业类型 val typeName: String = "", // 作业类型名称,服务端不返回,本地使用 val urgencyLevel: String = "", // 紧急等级 val designId: Int = -1, // sopId val designName: String = "", // 设计模板 val designContent: String? = null, // sopInfo val description: String = "", // 描述 val initiatorId: Int = -1, // 创建者id val initiatorName: String = "", // 创建者名称 val initiatorTime: Long = -1L, // 创建者创建时间 val status: String = "", // 作业状态 val createTime: Long = -1L, // 创建时间 val currentNodeId: String? = null, val currentNodeName: String? = null, val completionTime: Long? = null, val cancellationTime: Long? = null, val cancellationReason: String? = null, val workflowWorkNodeDOList: List? = null ) : java.io.Serializable { /** * 将接口返回的node转换为可显示的Node */ fun toUINodeMap(): SnapshotStateMap { val json = Json { ignoreUnknownKeys = true } val map = SnapshotStateMap() workflowWorkNodeDOList?.forEach { val pos = json.decodeFromString(it.position) map[it.id.toString()] = NodeUI(it.id.toString(), Offset(pos.x, pos.y), node = it) } return map } fun String.toAnchor(): Anchor { return when (this) { "left-source" -> Anchor.LEFT "right-source" -> Anchor.RIGHT "top-source" -> Anchor.TOP "bottom-source" -> Anchor.BOTTOM "left-target" -> Anchor.LEFT "right-target" -> Anchor.RIGHT "top-target" -> Anchor.TOP "bottom-target" -> Anchor.BOTTOM else -> if (this.contains("source")) Anchor.LEFT else Anchor.RIGHT } } /** * 将api连线数据转化为UI层连线数据 */ fun toUIConnectList(): List { val connections = ArrayList() val json = Json { ignoreUnknownKeys = true } val nodeInfo: NodeInfo = json.decodeFromString(designContent?.ifEmpty { "{}" } ?: "{}") nodeInfo.edges.forEach { val fromUuid = it.target val toUuid = it.source val fromId = workflowWorkNodeDOList?.find { node -> node.uuid == fromUuid }?.id ?: 0 val fromAnchor = it.targetHandle.toAnchor() val toId = workflowWorkNodeDOList?.find { node -> node.uuid == toUuid }?.id ?: 0 val toAnchor = it.sourceHandle.toAnchor() connections.add(Connection(fromId.toString(), toId.toString(), fromAnchor, toAnchor)) } return connections } /** * 获取等级名称和背景色 */ fun getLevelNameAndColor(): Pair { return when (urgencyLevel) { "1" -> "紧急" to Color(0xFFFF9800) "2" -> "非常紧急" to Color(0xFFFF4500) else -> "正常" to Color(0xFF1E90FF) } } /** * 获取任务状态名称 */ fun getJobStatusName(): String { return when (status) { "unreleased" -> "待发布" "approved" -> "已审核" "running" -> "进行中" "completed" -> "已完成" else -> status } } } /** * Task任务 */ @Serializable data class Task( val workId: Int, val nodeId: Int, val orderNo: String, val name: String, val urgencyLevel: String, val completionTime: Long?, val cancellationTime: Long?, val cancellationReason: String?, val initiatorName: String?, // 发起人 val workerUserName: String?, // 当前执行人 val workTime: Long?, val currentNodeId: String?, val currentNodeName: String?, val approvalStatus: String ) : java.io.Serializable /** * 消息结构体 */ @Serializable data class Message( val id: Int = 0, val userId: Int = 0, val userType: Int = 0, val readStatus: Boolean = false, val createTime: Long = -1L, val title: String? = null, val templateContent: String = "", val isTitle: Boolean = false, ) /** * 表单需要的组件 * * @param id * @param type * @param label * @param name * @param required * @param placeholder * @param options * @param cardTitle * @param gridColumns * @param children */ @Serializable data class FormField( val id: String = "", val type: String = "", val label: String = "", val name: String = "", val required: Boolean = false, @Serializable(with = StringToListSerializer::class) val placeholder: List = listOf(), @Serializable(with = StringToListSerializer::class) var value: List = listOf(), var options: List = listOf(), val cardTitle: String = "", val gridColumns: Int = 0, val enabled: Boolean = true, val multiSelect: Boolean = false, val children: List = listOf() ) /** * 表单Option字段 * * @param label 标题 * @param value 数值 */ @Serializable data class FormOption(val label: String = "", val value: String = "") /** * 节点位置信息 */ @Serializable data class NodePositon(val x: Float, val y: Float) /** * 节点信息 */ @Serializable data class NodeInfo(val edgeCount: Int, val edges: List) /** * 节点连接线信息 */ @Serializable data class NodeConnection( val id: String, val source: String, val target: String, val sourceHandle: String, val targetHandle: String, val type: String )