Node.kt 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566
  1. package com.iscs.bozzys.api
  2. import android.util.Log
  3. import com.iscs.bozzys.R
  4. import com.iscs.bozzys.ui.pages.edit.step.compose.NodeUI
  5. import com.iscs.bozzys.utils.StringToListSerializer
  6. import kotlinx.serialization.Serializable
  7. import kotlinx.serialization.encodeToString
  8. import kotlinx.serialization.json.Json
  9. /**
  10. * 流程节点
  11. */
  12. @Serializable
  13. data class Node(
  14. val id: Int = 0,
  15. val workId: Int = 0,
  16. val uuid: String = "",
  17. val parentUuid: String = "",
  18. val childrenUuid: String = "",
  19. val nodeName: String = "",
  20. val nodeIcon: String = "",
  21. val type: String = "",
  22. val position: String = "",
  23. val data: String = "",
  24. val description: String? = null,
  25. val workerUserId: Int = 0,
  26. val workerGroupId: Int? = null,
  27. val formId: Int = 0,
  28. val formData: String? = null,
  29. val createTime: Long = 0L,
  30. val approvalStatus: String = "",
  31. val approvalOpinion: String = "",
  32. val isolationType: String? = null, // 隔离类型
  33. val isolationPoints: String? = null, // 解除隔离的点位
  34. val isolationNodeUuid: String? = null, // 解除隔离关联的节点uuid
  35. val nodeUserList: List<User>? = null, // 隔离和解除隔离关联的上锁人和共锁人
  36. val smsTemplateCode: String? = null, // 短信通知
  37. val messageTemplateCode: String? = null, // 站内信息
  38. val emailTemplateCode: String? = null, // 邮件通知
  39. val appTemplateCode: String? = null, // app消息推送
  40. val deviceNumber: String? = null, // 设备序列号
  41. val attachments: String? = null, // 携带的数据
  42. val points: List<IsolationPoint>? = null, // 隔离点位信息
  43. val keys: List<Key>? = null, // 隔离点位参与的挂锁
  44. val locks: List<Lock>? = null, // 隔离点位参与的钥匙
  45. ) {
  46. /**
  47. * 校验是否可以通过手机进行操作
  48. */
  49. fun checkCanOptionByPhone(): Boolean {
  50. return !(listOf("lock", "unlock", "coLock","unlockCoLock").contains(type) && !listOf("0", "2").contains(isolationType))
  51. }
  52. /**
  53. * 校验请求参数
  54. *
  55. * @param forms 表单数据
  56. */
  57. fun checkCanSubmit(forms: List<FormField>): String {
  58. forms.forEach {
  59. Log.d("NodeOption", "checkCanSubmit -> $it")
  60. if (it.required) {
  61. if ((it.value.getOrNull(0) ?: "").isEmpty()) {
  62. return (it.placeholder.getOrNull(0) ?: "").ifEmpty { "请填写${it.label}" }
  63. }
  64. }
  65. }
  66. return ""
  67. }
  68. /**
  69. * 构建请求参数
  70. *
  71. * @param forms 表单数据
  72. */
  73. fun buildSubmitParams(forms: List<FormField>, workFormList: List<TaskFormInfo>): MutableMap<String, Any?> {
  74. val nodeName = forms.find { it.name == "nodeName" }?.value?.getOrNull(0) ?: ""
  75. val params = mutableMapOf<String, Any?>("nodeId" to id, "nodeName" to nodeName)
  76. if (type != "createJob") {
  77. val workerUserId = forms.find { it.name == "workerUserId" }?.value?.getOrNull(0) ?: "0"
  78. val formId = forms.find { it.name == "formId" }?.value?.getOrNull(0) ?: "0"
  79. val findWork = workFormList.find { it.id == formId.toInt() }
  80. val formData = if (findWork != null) Json.encodeToString(findWork) else null
  81. val noticeType = forms.find { it.name == "noticeType" }?.value ?: emptyList()
  82. params["workerUserId"] = workerUserId.toInt()
  83. params["formId"] = formId.toInt()
  84. params["formData"] = formData
  85. // 处理隔离点信息表单
  86. if (listOf("isolation", "releaseIsolation").contains(type)) {
  87. val isolationType = forms.find { it.name == "isolationType" }?.value?.getOrNull(0) ?: "-1"
  88. val isolationPoints = forms.find { it.name == "isolationPoints" }?.value ?: emptyList()
  89. if (isolationType == "1") {
  90. // 移除负责人
  91. params.remove("workerUserId")
  92. // 上锁人和共锁人
  93. val locker = forms.find { it.name == "locker" }?.value?.getOrNull(0) ?: "-1"
  94. val group = forms.find { it.name == "group" }?.value ?: emptyList()
  95. val userList = mutableListOf<MutableMap<String, Any>>()
  96. if (locker.toInt() > 0) userList += mutableMapOf("userId" to locker.toInt(), "type" to "jtlocker")
  97. group.forEach { userList += mutableMapOf("userId" to it.toInt(), "type" to "jtcolocker") }
  98. params["nodeUserDOList"] = userList
  99. }
  100. params["isolationType"] = isolationType
  101. params["isolationPoints"] = isolationPoints.joinToString(
  102. prefix = "[",
  103. postfix = "]",
  104. separator = ","
  105. )
  106. if (type == "releaseIsolation") {
  107. val isolationNode = forms.find { it.name == "isolationNode" }?.value?.getOrNull(0)
  108. // 解除隔离,这里需要填写关联的节点uuid
  109. params["isolationNodeUuid"] = isolationNode
  110. }
  111. }
  112. // 通知方式
  113. // 短信
  114. params["smsTemplateCode"] = if (noticeType.contains("sms")) "true" else "false"
  115. // 站内信
  116. params["messageTemplateCode"] = if (noticeType.contains("message")) "true" else "false"
  117. // 邮件
  118. params["emailTemplateCode"] = if (noticeType.contains("email")) "true" else "false"
  119. // app通知
  120. params["appTemplateCode"] = if (noticeType.contains("app")) "true" else "false"
  121. }
  122. return params
  123. }
  124. /**
  125. * 更新节点信息
  126. *
  127. * @param forms
  128. */
  129. fun updateNodeInfo(forms: List<FormField>, nodes: List<NodeUI>): Node {
  130. val nodeName = forms.find { it.name == "nodeName" }?.value?.getOrNull(0) ?: ""
  131. if (type != "createJob") {
  132. // 解析关联表单
  133. val formId = forms.find { it.name == "formId" }?.value?.getOrNull(0) ?: "-1"
  134. // 解析表单数据
  135. val formData = forms.find { it.name == "formData" }?.value?.getOrNull(0)
  136. // 解析负责人
  137. var workerUserId = forms.find { it.name == "workerUserId" }?.value?.getOrNull(0) ?: "0"
  138. // 解析隔离方式
  139. var isolationType = forms.find { it.name == "isolationType" }?.value?.getOrNull(0)
  140. // 解析点位
  141. var isolationPoints = forms.find { it.name == "isolationPoints" }?.value ?: emptyList()
  142. // 解析上锁人
  143. val locker = forms.find { it.name == "locker" }?.value?.getOrNull(0) ?: "-1"
  144. // 解析共锁人
  145. val group = forms.find { it.name == "group" }?.value ?: emptyList()
  146. // 构建上锁人和共锁人列表
  147. val userList = mutableListOf<User>()
  148. if (locker.toInt() > 0) userList += User(userId = locker.toInt(), type = "jtlocker")
  149. if (group.isNotEmpty()) group.forEach { userList += User(userId = it.toInt(), type = "jtcolocker") }
  150. // 解析隔离节点
  151. val isolationNode = forms.find { it.name == "isolationNode" }?.value?.getOrNull(0)
  152. // 如果是解除隔离操作,隔离方式、隔离点位、负责人(或者上锁人、解锁人)需要从选中的点位中获取
  153. if (type == "releaseIsolation") {
  154. nodes.find { it.node?.uuid == isolationNode }?.node?.let {
  155. // 从节点中将数据copy到当前节点
  156. workerUserId = "${it.workerUserId}"
  157. isolationType = it.isolationType
  158. isolationPoints = if (it.isolationPoints.isNullOrEmpty()) {
  159. emptyList()
  160. } else {
  161. it.isolationPoints.replace("[", "").replace("]", "").split(",")
  162. }
  163. userList.addAll(it.nodeUserList ?: emptyList())
  164. }
  165. }
  166. // 通知方式
  167. val noticeType = forms.find { it.name == "noticeType" }?.value ?: emptyList()
  168. return Node(
  169. id = id,
  170. workId = workId,
  171. uuid = uuid,
  172. parentUuid = parentUuid,
  173. childrenUuid = childrenUuid,
  174. nodeName = nodeName,
  175. type = type,
  176. position = position,
  177. workerUserId = workerUserId.toInt(),
  178. formId = formId.toInt(),
  179. formData = formData,
  180. isolationType = isolationType,
  181. isolationPoints = isolationPoints.joinToString(
  182. prefix = "[",
  183. postfix = "]",
  184. separator = ","
  185. ),
  186. isolationNodeUuid = isolationNode,
  187. nodeUserList = if (userList.isEmpty()) null else userList,
  188. smsTemplateCode = if (noticeType.contains("sms")) "true" else "false",
  189. messageTemplateCode = if (noticeType.contains("message")) "true" else "false",
  190. emailTemplateCode = if (noticeType.contains("email")) "true" else "false",
  191. appTemplateCode = if (noticeType.contains("app")) "true" else "false"
  192. )
  193. }
  194. return Node(
  195. id = id,
  196. workId = workId,
  197. uuid = uuid,
  198. parentUuid = parentUuid,
  199. childrenUuid = childrenUuid,
  200. nodeName = nodeName,
  201. type = type,
  202. position = position
  203. )
  204. }
  205. /**
  206. * 构建表单列表
  207. *
  208. * @param workerUserList 负责人列表
  209. * @param lockerUserList 上锁人列表
  210. * @param groupUserList 共锁人列表
  211. * @param workFormList 工作表单列表
  212. * @param isolationMethodList 获取隔离方式列表
  213. * @param isolationList 点位列表
  214. * @param isLocker 是否上锁人
  215. */
  216. fun buildFormList(
  217. workerUserList: List<User>,
  218. lockerUserList: List<User>,
  219. groupUserList: List<User>,
  220. workFormList: List<TaskFormInfo>,
  221. isolationMethodList: List<Dict>,
  222. isolationList: List<IsolationPoint>,
  223. nodes: List<NodeUI> = listOf(),
  224. isLocker: Boolean = false,
  225. ): List<FormField> {
  226. Log.d("NodeOption", "buildFormList -> $this")
  227. val list = ArrayList<FormField>()
  228. // 添加节点名称
  229. list += FormField(
  230. id.toString(),
  231. "input",
  232. "节点名称",
  233. name = "nodeName",
  234. true,
  235. value = listOf(nodeName),
  236. placeholder = listOf("请输入节点名称")
  237. )
  238. if (type == "createJob") return list
  239. // 添加节点负责人
  240. list += FormField(
  241. id.toString(),
  242. "select",
  243. "负责人",
  244. "workerUserId",
  245. true,
  246. placeholder = listOf("请选择负责人"),
  247. options = workerUserList.map { FormOption(it.nickname, it.id.toString()) },
  248. value = if (workerUserId > 0) listOf(workerUserId.toString()) else listOf()
  249. )
  250. // 添加业务表单
  251. list += FormField(
  252. id.toString(),
  253. "select",
  254. "业务表单",
  255. name = "formId",
  256. true,
  257. placeholder = listOf("请选择业务表单"),
  258. options = workFormList.map { FormOption(it.name ?: "", it.id.toString()) },
  259. value = if (formId > 0) listOf(formId.toString()) else listOf()
  260. )
  261. // 隔离配置
  262. if (listOf("isolation", "releaseIsolation").contains(type)) {
  263. // 先移除负责人,需要在后面进行添加
  264. list.removeIf { it.name == "workerUserId" }
  265. // 而且业务表单非必填
  266. val formField = list.find { it.name == "formId" }?.copy(required = false)
  267. list.removeIf { it.name == "formId" }
  268. formField?.let { list.add(it) }
  269. val isolations = nodes.map { it.node }.filter { it != null && it.type == "isolation" }
  270. var enabled = true
  271. var isLockerTemp = isLocker
  272. // 如果是解除隔离操作,这里只能选择当前流程中已经存在的隔离点位
  273. if (type == "releaseIsolation") {
  274. enabled = false
  275. // 增加隔离点选择列表
  276. list += FormField(
  277. id.toString(),
  278. type = "select",
  279. label = "选择隔离节点",
  280. name = "isolationNode",
  281. required = true,
  282. placeholder = listOf("请选择隔离节点"),
  283. options = isolations.map { FormOption(it?.nodeName ?: "", value = it?.uuid ?: "") },
  284. value = if (isolationNodeUuid.isNullOrEmpty()) listOf() else listOf(isolationNodeUuid)
  285. )
  286. // 如果是解除隔离的操作,这里需要先校验选择的节点是否是拆除
  287. isLockerTemp = isolationType == "1"
  288. }
  289. // 抽取隔离方式value值
  290. val valueOfIsolationType = if (isolationType.isNullOrEmpty()) {
  291. listOf()
  292. // val type = dataMap["isolationMethod"]
  293. // if (type == null) listOf() else listOf("$type")
  294. } else listOf(isolationType)
  295. // 隔离方式
  296. list += FormField(
  297. id.toString(),
  298. "select",
  299. "隔离方式",
  300. "isolationType",
  301. true,
  302. placeholder = listOf("请选择隔离方式"),
  303. options = isolationMethodList.map { FormOption(it.label, it.value) },
  304. value = valueOfIsolationType,
  305. enabled = enabled
  306. )
  307. // 抽取隔离点位value值
  308. val points = if (isolationPoints.isNullOrEmpty()) "" else isolationPoints.replace("[", "").replace("]", "")
  309. val valueOfIsolationPoints = if (points.isNotEmpty()) {
  310. points.split(",")
  311. } else {
  312. listOf()
  313. // when (val value = dataMap["isolationPoints"]) {
  314. // // 如果是数组
  315. // is List<*> -> value.map { "$it".toFloat().toInt().toString() }
  316. // // 如果是String
  317. // is String -> {
  318. // value.replace("[", "").replace("]", "").split(",")
  319. // }
  320. // // 其他不处理
  321. // else -> listOf()
  322. // }
  323. }
  324. // 隔离点选择,可多选
  325. list += FormField(
  326. id.toString(),
  327. "select",
  328. "隔离点选择(可多选)",
  329. "isolationPoints",
  330. true,
  331. listOf("请选择隔离点"),
  332. multiSelect = true,
  333. options = isolationList.map { FormOption(it.pointName ?: "", it.id.toString()) },
  334. value = valueOfIsolationPoints,
  335. enabled = enabled
  336. )
  337. if (isLockerTemp) {
  338. var locker = nodeUserList?.find { it.type == "jtlocker" }
  339. var group = nodeUserList?.filter { it.type == "jtcolocker" } ?: emptyList()
  340. // 如果是解除隔离,且当前解锁和工作人列表为空,需要默认配置
  341. if (type == "releaseIsolation" && nodeUserList.isNullOrEmpty()) {
  342. val bindNode = nodes.find { it.node?.uuid == isolationNodeUuid && !isolationNodeUuid.isNullOrEmpty() }
  343. val nodeUserList = bindNode?.node?.nodeUserList
  344. locker = nodeUserList?.find { it.type == "jtlocker" }
  345. group = nodeUserList?.filter { it.type == "jtcolocker" } ?: emptyList()
  346. }
  347. // 上锁人
  348. list += FormField(
  349. id.toString(),
  350. "select",
  351. "上锁人",
  352. "locker",
  353. true,
  354. placeholder = listOf("请选择上锁人"),
  355. options = lockerUserList.map { FormOption(it.nickname, it.id.toString()) },
  356. value = if (locker != null) listOf(locker.userId.toString()) else listOf(),
  357. enabled = enabled
  358. )
  359. // 共锁人
  360. list += FormField(
  361. id.toString(),
  362. "select",
  363. "共锁人(可多选)",
  364. "group",
  365. false,
  366. placeholder = listOf("请选择共锁人"),
  367. options = groupUserList.map { FormOption(it.nickname, it.id.toString()) },
  368. value = if (group.isNotEmpty()) group.map { it.userId.toString() } else listOf(),
  369. multiSelect = true,
  370. enabled = enabled
  371. )
  372. } else {
  373. // 负责人
  374. list += FormField(
  375. id.toString(),
  376. "select",
  377. "负责人",
  378. "workerUserId",
  379. true,
  380. placeholder = listOf("请选择负责人"),
  381. options = workerUserList.map { FormOption(it.nickname, it.id.toString()) },
  382. value = if (workerUserId > 0) listOf(workerUserId.toString()) else listOf(),
  383. enabled = enabled
  384. )
  385. }
  386. }
  387. val notices = arrayListOf<String>()
  388. if (smsTemplateCode == "true") notices += "sms"
  389. if (messageTemplateCode == "true") notices += "message"
  390. if (emailTemplateCode == "true") notices += "email"
  391. if (appTemplateCode == "true") notices += "app"
  392. // 通知方式
  393. list += FormField(
  394. id.toString(), "checkbox", "通知方式", "noticeType", false, listOf(), notices,
  395. options = listOf(
  396. FormOption("短信", "sms"),
  397. FormOption("站内信", "message"),
  398. FormOption("邮件", "email"),
  399. FormOption("App通知", "app"),
  400. )
  401. )
  402. // // 通知人
  403. // list += FormField(
  404. // id.toString(), "select", "通知人", nodeName, false, listOf(), options = listOf(
  405. // FormOption("任务负责人", "p1"),
  406. // FormOption("任务参与人", "p2"),
  407. // FormOption("任务负责和参与人", "all"),
  408. // FormOption("指定人", "custom"),
  409. // )
  410. // )
  411. // // 通知时间
  412. // list += FormField(
  413. // id.toString(), "select", "通知时间", nodeName, false, listOf(), options = listOf(
  414. // FormOption("任务开始前30分钟", "0.5"),
  415. // FormOption("任务开始前1小时", "1"),
  416. // FormOption("任务开始前2小时", "2"),
  417. // FormOption("任务开始前5小时", "5"),
  418. // )
  419. // )
  420. return list
  421. }
  422. /**
  423. * 获取node图标,暂时本地通过type获取
  424. */
  425. fun getIcon(): Int {
  426. return when (type) {
  427. // 开始/创建
  428. "createJob" -> R.drawable.node_icon_create_job
  429. // 审核
  430. "review" -> R.drawable.node_icon_review
  431. // 确认
  432. "confirm" -> R.drawable.node_icon_confirm
  433. // 录入信息
  434. "inputInfo" -> R.drawable.node_icon_input_info
  435. // 隔离
  436. "isolation" -> R.drawable.node_icon_isolation
  437. // 解除隔离
  438. "releaseIsolation" -> R.drawable.node_icon_release_isolation
  439. // 还锁
  440. "returnLock" -> R.drawable.node_icon_return_lock
  441. // 结束
  442. "complete" -> R.drawable.node_icon_complete
  443. else -> R.drawable.tips
  444. }
  445. }
  446. /**
  447. * 将作业票转换为JSON
  448. */
  449. fun toKeyTicket(lockList: List<Lock>): String {
  450. val json = Json {
  451. // 格式化默认值
  452. encodeDefaults = true
  453. // 去除字段为null的字段
  454. explicitNulls = false
  455. }
  456. val target = if (type == "releaseIsolation") 1 else 0
  457. // 锁具id构成
  458. var lockId = 0
  459. val keyTicket = KeyTicket(
  460. data = listOf(
  461. TicketTask(
  462. taskId = id.toString(),
  463. codeId = id,
  464. dataList = points?.map { point ->
  465. TicketPoint(
  466. dataId = point.id,
  467. equipName = point.pointName ?: point.pointNfc,
  468. equipRfidNo = point.pointNfc,
  469. target = target,
  470. infoRfidNo = point.lockNfc
  471. )
  472. } ?: emptyList())
  473. ),
  474. lockList = lockList.map { lock ->
  475. lockId++
  476. TicketLock(lockId = lockId, rfid = lock.lockNfc)
  477. })
  478. return json.encodeToString(keyTicket)
  479. }
  480. }
  481. /**
  482. * 表单需要的组件
  483. *
  484. * @param id
  485. * @param type
  486. * @param label
  487. * @param name
  488. * @param required
  489. * @param placeholder
  490. * @param options
  491. * @param cardTitle
  492. * @param gridColumns
  493. * @param children
  494. */
  495. @Serializable
  496. data class FormField(
  497. val id: String = "",
  498. val type: String = "",
  499. val label: String = "",
  500. val name: String = "",
  501. val required: Boolean = false,
  502. @Serializable(with = StringToListSerializer::class)
  503. val placeholder: List<String> = listOf(),
  504. @Serializable(with = StringToListSerializer::class)
  505. var value: List<String> = listOf(),
  506. var options: List<FormOption> = listOf(),
  507. val cardTitle: String = "",
  508. val gridColumns: Int = 0,
  509. val enabled: Boolean = true,
  510. val multiSelect: Boolean = false,
  511. val children: List<FormField> = listOf(),
  512. val maxCount: Int? = null, // 文件上传的最大限制个数
  513. val uploadType: String? = "file" // 上传文件类型
  514. )
  515. /**
  516. * 表单Option字段
  517. *
  518. * @param label 标题
  519. * @param value 数值
  520. */
  521. @Serializable
  522. data class FormOption(val label: String = "", val value: String = "")
  523. /**
  524. * 节点位置信息
  525. */
  526. @Serializable
  527. data class NodePositon(val x: Float, val y: Float)
  528. /**
  529. * 节点信息
  530. */
  531. @Serializable
  532. data class NodeInfo(val edgeCount: Int, val edges: List<NodeConnection>)
  533. /**
  534. * 节点连接线信息
  535. */
  536. @Serializable
  537. data class NodeConnection(
  538. val id: String,
  539. val source: String,
  540. val target: String,
  541. val sourceHandle: String,
  542. val targetHandle: String,
  543. val type: String
  544. )
  545. /**
  546. * 拆除盲板需要的字段
  547. */
  548. @Serializable
  549. data class Attachment(val name: String = "", val url: String = "")