Procházet zdrojové kódy

feat(数据管理)
- 新增数据库备份Worker
- 新增步骤动作枚举
- 调整钥匙归还逻辑,增加强制上传数据选项

refactor(用户)
- 优化指纹登录和指纹校验逻辑,提高匹配效率
- 调整设置指纹界面的数据展示和删除逻辑

fix(流程)
- 修复流程导入时SHA256校验不一致的问题
- 修复作业票待办事项组装逻辑,确保不同类型的步骤能正确生成和关联

style(提示)
- 优化解压进度的文本展示格式
- 调整部分中英文提示文案

周文健 před 3 měsíci
rodič
revize
d5d7c34e5a
55 změnil soubory, kde provedl 1034 přidání a 172 odebrání
  1. 10 5
      app/src/main/java/com/grkj/iscs/features/init/activity/InitActivity.kt
  2. 21 15
      app/src/main/java/com/grkj/iscs/features/login/activity/LoginActivity.kt
  3. 10 5
      app/src/main/java/com/grkj/iscs/features/main/activity/MainActivity.kt
  4. 10 6
      app/src/main/java/com/grkj/iscs/features/main/activity/QuickEntryActivity.kt
  5. 50 0
      app/src/main/java/com/grkj/iscs/features/main/dialog/ColockOperationTipDialog.kt
  6. 37 0
      app/src/main/java/com/grkj/iscs/features/main/entity/WorkflowModeImport.kt
  7. 91 0
      app/src/main/java/com/grkj/iscs/features/main/entity/WorkflowModeStepImport.kt
  8. 4 0
      app/src/main/java/com/grkj/iscs/features/main/fragment/hardware_manage/CardManageFragment.kt
  9. 4 0
      app/src/main/java/com/grkj/iscs/features/main/fragment/hardware_manage/KeyManageFragment.kt
  10. 4 0
      app/src/main/java/com/grkj/iscs/features/main/fragment/hardware_manage/LockManageFragment.kt
  11. 4 0
      app/src/main/java/com/grkj/iscs/features/main/fragment/hardware_manage/RfidTokenManageFragment.kt
  12. 4 0
      app/src/main/java/com/grkj/iscs/features/main/fragment/job_manage/JobExecuteFragment.kt
  13. 142 12
      app/src/main/java/com/grkj/iscs/features/main/fragment/job_manage/MyTodoListFragment.kt
  14. 7 0
      app/src/main/java/com/grkj/iscs/features/main/fragment/job_manage/WorkflowManageFragment.kt
  15. 8 9
      app/src/main/java/com/grkj/iscs/features/main/fragment/user_info/SetFingerprintFragment.kt
  16. 8 7
      app/src/main/java/com/grkj/iscs/features/main/fragment/user_info/UserInfoFragment.kt
  17. 47 7
      app/src/main/java/com/grkj/iscs/features/main/viewmodel/WorkflowViewModel.kt
  18. 2 0
      app/src/main/java/com/grkj/iscs/features/main/viewmodel/exception_manage/ExceptionJobViewModel.kt
  19. 2 0
      app/src/main/java/com/grkj/iscs/features/main/viewmodel/job_manage/JobExecuteViewModel.kt
  20. 79 37
      app/src/main/java/com/grkj/iscs/features/main/viewmodel/job_manage/MyTodoViewModel.kt
  21. 7 7
      app/src/main/java/com/grkj/iscs/features/main/viewmodel/user_info/UserInfoViewModel.kt
  22. 12 12
      app/src/main/java/com/grkj/iscs/features/splash/activity/SplashActivity.kt
  23. 53 0
      app/src/main/res/layout-land/dialog_colock_operation_tip.xml
  24. 1 1
      app/src/main/res/layout/dialog_add_lock.xml
  25. 53 0
      app/src/main/res/layout/dialog_colock_operation_tip.xml
  26. 17 0
      app/src/main/res/layout/fragment_card_manage.xml
  27. 17 0
      app/src/main/res/layout/fragment_key_manage.xml
  28. 17 0
      app/src/main/res/layout/fragment_lock_manage.xml
  29. 16 8
      app/src/main/res/layout/fragment_reset_password.xml
  30. 22 2
      app/src/main/res/layout/fragment_rfid_token_manage.xml
  31. 9 0
      app/src/main/res/layout/fragment_workflow_manage.xml
  32. 7 0
      app/src/main/res/layout/item_card_manage.xml
  33. 7 0
      app/src/main/res/layout/item_key_manage.xml
  34. 7 0
      app/src/main/res/layout/item_lock_manage.xml
  35. 7 0
      app/src/main/res/layout/item_rfid_token_manage.xml
  36. 9 0
      app/src/main/res/layout/item_workflow_manage.xml
  37. 4 0
      app/src/main/res/values-en/strings.xml
  38. 4 0
      app/src/main/res/values-zh/strings.xml
  39. 4 0
      app/src/main/res/values/strings.xml
  40. 1 1
      data/src/main/java/com/grkj/data/dao/JobTicketDao.kt
  41. 13 1
      data/src/main/java/com/grkj/data/dao/UserDao.kt
  42. 14 1
      data/src/main/java/com/grkj/data/dao/WorkflowStepDao.kt
  43. 8 0
      data/src/main/java/com/grkj/data/data/CommonConstants.kt
  44. 2 2
      data/src/main/java/com/grkj/data/enums/OperationTypeEnum.kt
  45. 1 0
      data/src/main/java/com/grkj/data/model/local/TodoStepJoin.kt
  46. 5 0
      data/src/main/java/com/grkj/data/model/vo/TodoItemVo.kt
  47. 16 1
      data/src/main/java/com/grkj/data/repository/IUserRepository.kt
  48. 10 0
      data/src/main/java/com/grkj/data/repository/IWorkflowRepository.kt
  49. 16 0
      data/src/main/java/com/grkj/data/repository/impl/network/NetworkUserRepository.kt
  50. 8 0
      data/src/main/java/com/grkj/data/repository/impl/network/NetworkWorkflowRepository.kt
  51. 1 1
      data/src/main/java/com/grkj/data/repository/impl/standard/HardwareRepository.kt
  52. 93 32
      data/src/main/java/com/grkj/data/repository/impl/standard/JobTicketRepository.kt
  53. 16 0
      data/src/main/java/com/grkj/data/repository/impl/standard/UserRepository.kt
  54. 8 0
      data/src/main/java/com/grkj/data/repository/impl/standard/WorkflowRepository.kt
  55. 5 0
      ui-base/src/main/java/com/grkj/ui_base/business/BleBusinessManager.kt

+ 10 - 5
app/src/main/java/com/grkj/iscs/features/init/activity/InitActivity.kt

@@ -43,11 +43,16 @@ class InitActivity : BaseActivity<ActivityInitBinding>() {
             // 检测到回车开始处理
             if (event.keyCode == 66) {
                 logger.info("Swipe card login Origin: $cardNo")
-                cardNo = cardNo.toLong().toByteArrays().toHexStrings(false)
-                logger.info("Swipe card login: $cardNo")
-                CardSwipeEvent.sendCardSwipeEvent(cardNo)
-                // 重置cardNo
-                cardNo = ""
+                try {
+                    cardNo = cardNo.toLong().toByteArrays().toHexStrings(false)
+                    logger.info("Swipe card login: $cardNo")
+                    CardSwipeEvent.sendCardSwipeEvent(cardNo)
+                    // 重置cardNo
+                    cardNo = ""
+                } catch (e: Exception) {
+                    cardNo = ""
+                    logger.info("读卡失败: ${e.toString()}")
+                }
                 return super.dispatchKeyEvent(event)
             }
             cardNo += event.keyCharacterMap.getDisplayLabel(event.keyCode)

+ 21 - 15
app/src/main/java/com/grkj/iscs/features/login/activity/LoginActivity.kt

@@ -31,6 +31,7 @@ import com.grkj.ui_base.utils.extension.getAppVersionName
 import com.grkj.shared.utils.extension.toByteArrays
 import com.grkj.shared.utils.extension.toHexStrings
 import com.grkj.ui_base.config.ISCSConfig
+import com.grkj.ui_base.utils.event.RFIDCardReadEvent
 import com.grkj.ui_base.utils.extension.tip
 import com.grkj.ui_base.utils.fingerprint.FingerprintUtil
 import com.kongzue.dialogx.dialogs.PopTip
@@ -101,7 +102,7 @@ class LoginActivity : BaseActivity<ActivityLoginBinding>() {
         super.initData()
         viewModel.registerFaceFeature().observe(this) {}
         //todo 测试用,直接创建管理员账号
-        viewModel.insertAdminAccount().observe(this) {}
+//        viewModel.insertAdminAccount().observe(this) {}
         requestPermissionsIfNeeded(*Constants.needPermission) {
             if (it) {
                 logger.info("权限获取成功")
@@ -183,22 +184,27 @@ class LoginActivity : BaseActivity<ActivityLoginBinding>() {
             // 检测到回车开始处理
             if (event.keyCode == 66) {
                 logger.info("Swipe card login Origin: $cardNo")
-                cardNo = cardNo.toLong().toByteArrays().toHexStrings(false)
-                logger.info("Swipe card login: $cardNo")
-                LoadingEvent.sendLoadingEvent(
-                    CommonUtils.getStr(com.grkj.ui_base.R.string.doing_login),
-                    true
-                )
-                viewModel.loginWithCard(cardNo).observe(this) { isSuccess ->
-                    LoadingEvent.sendLoadingEvent()
-                    if (isSuccess) {
-                        startActivity(Intent(this@LoginActivity, MainActivity::class.java))
-                    } else {
-                        PopTip.tip(CommonUtils.getStr(R.string.login_failed))
+                try {
+                    cardNo = cardNo.toLong().toByteArrays().toHexStrings(false)
+                    logger.info("Swipe card login: $cardNo")
+                    LoadingEvent.sendLoadingEvent(
+                        CommonUtils.getStr(com.grkj.ui_base.R.string.doing_login),
+                        true
+                    )
+                    viewModel.loginWithCard(cardNo).observe(this) { isSuccess ->
+                        LoadingEvent.sendLoadingEvent()
+                        if (isSuccess) {
+                            startActivity(Intent(this@LoginActivity, MainActivity::class.java))
+                        } else {
+                            PopTip.tip(CommonUtils.getStr(R.string.login_failed))
+                        }
                     }
+                    // 重置cardNo
+                    cardNo = ""
+                } catch (e: Exception) {
+                    cardNo = ""
+                    logger.info("读卡失败: ${e.toString()}")
                 }
-                // 重置cardNo
-                cardNo = ""
                 return super.dispatchKeyEvent(event)
             }
             cardNo += event.keyCharacterMap.getDisplayLabel(event.keyCode)

+ 10 - 5
app/src/main/java/com/grkj/iscs/features/main/activity/MainActivity.kt

@@ -180,11 +180,16 @@ class MainActivity() : BaseActivity<ActivityMainBinding>() {
             // 检测到回车开始处理
             if (event.keyCode == 66) {
                 logger.info("Swipe card login Origin: $cardNo")
-                cardNo = cardNo.toLong().toByteArrays().toHexStrings(false)
-                logger.info("Swipe card login: $cardNo")
-                RFIDCardReadEvent.sendRFIDCardReadEvent(cardNo)
-                // 重置cardNo
-                cardNo = ""
+                try {
+                    cardNo = cardNo.toLong().toByteArrays().toHexStrings(false)
+                    logger.info("Swipe card login: $cardNo")
+                    RFIDCardReadEvent.sendRFIDCardReadEvent(cardNo)
+                    // 重置cardNo
+                    cardNo = ""
+                } catch (e: Exception) {
+                    cardNo = ""
+                    logger.info("读卡失败: ${e.toString()}")
+                }
                 return super.dispatchKeyEvent(event)
             }
             cardNo += event.keyCharacterMap.getDisplayLabel(event.keyCode)

+ 10 - 6
app/src/main/java/com/grkj/iscs/features/main/activity/QuickEntryActivity.kt

@@ -100,12 +100,16 @@ class QuickEntryActivity() : BaseActivity<ActivityQuickEntryBinding>() {
         if (event.action == KeyEvent.ACTION_UP && event.source == InputDevice.SOURCE_KEYBOARD) {
             // 检测到回车开始处理
             if (event.keyCode == 66) {
-                logger.info("Swipe card login Origin: $cardNo")
-                cardNo = cardNo.toLong().toByteArrays().toHexStrings(false)
-                logger.info("Swipe card login: $cardNo")
-                RFIDCardReadEvent.sendRFIDCardReadEvent(cardNo)
-                // 重置cardNo
-                cardNo = ""
+                try {
+                    cardNo = cardNo.toLong().toByteArrays().toHexStrings(false)
+                    logger.info("Swipe card login: $cardNo")
+                    RFIDCardReadEvent.sendRFIDCardReadEvent(cardNo)
+                    // 重置cardNo
+                    cardNo = ""
+                } catch (e: Exception) {
+                    cardNo = ""
+                    logger.info("读卡失败: ${e.toString()}")
+                }
                 return super.dispatchKeyEvent(event)
             }
             cardNo += event.keyCharacterMap.getDisplayLabel(event.keyCode)

+ 50 - 0
app/src/main/java/com/grkj/iscs/features/main/dialog/ColockOperationTipDialog.kt

@@ -0,0 +1,50 @@
+package com.grkj.iscs.features.main.dialog
+
+import android.view.View
+import com.grkj.iscs.R
+import com.grkj.iscs.databinding.DialogCheckFaceBinding
+import com.grkj.iscs.databinding.DialogColockOperationTipBinding
+import com.grkj.shared.utils.ArcSoftUtil
+import com.grkj.ui_base.utils.CommonUtils
+import com.kongzue.dialogx.dialogs.CustomDialog
+import com.kongzue.dialogx.interfaces.DialogLifecycleCallback
+import com.kongzue.dialogx.interfaces.OnBindView
+import com.sik.sikcore.SIKCore
+import com.sik.sikcore.extension.setDebouncedClickListener
+
+/**
+ * 检查人脸弹窗
+ */
+class ColockOperationTipDialog() : OnBindView<CustomDialog>(R.layout.dialog_colock_operation_tip) {
+    private val mPairList = mutableListOf(
+        Pair(
+            SIKCore.getApplication().getString(com.grkj.ui_base.R.string.please_swipe_card),
+            R.drawable.icon_login_menu_card
+        )
+    )
+
+    private lateinit var mBinding: DialogColockOperationTipBinding
+
+    override fun onBind(customDialog: CustomDialog, contentView: View) {
+        mBinding = DialogColockOperationTipBinding.bind(contentView)
+        customDialog?.setMaskColor(CommonUtils.getColor(com.grkj.ui_base.R.color.scrim))
+        mBinding.closeIv.setDebouncedClickListener {
+            customDialog.dismiss()
+        }
+        mBinding.llEasyContainer.visibility = View.VISIBLE
+        mBinding.ivIcon.setImageResource(mPairList[0].second)
+        mBinding.tvTip.text = mPairList[0].first
+    }
+
+    companion object {
+        /**
+         * 根据类型显示弹框
+         *
+         * @param loginType 0:人脸 1:指纹 2:工卡
+         */
+        @JvmStatic
+        fun show(): CustomDialog {
+            return CustomDialog.show(ColockOperationTipDialog())
+        }
+    }
+}

+ 37 - 0
app/src/main/java/com/grkj/iscs/features/main/entity/WorkflowModeImport.kt

@@ -0,0 +1,37 @@
+package com.grkj.iscs.features.main.entity
+
+/**
+ * 流程模式导入列表包装
+ */
+class WorkflowModeImport {
+
+    var id: Long = 0L
+
+    /** 模式编码 */
+    var modeCode: String? = null
+
+    /** 是否预置 */
+    var isPreset: Boolean = false
+
+    /** 模式名称 */
+    var modeName: String? = null
+
+    /** 模式标题 */
+    var modeTitle: String? = null
+
+    /** 模式描述信息 */
+    var modeDescription: String? = null
+
+    /** 是否支持共锁 */
+    var isColockSupport: Boolean = false
+
+    /**
+     * 步骤数据
+     */
+    var stepList: List<WorkflowModeStepImport> = mutableListOf()
+
+    /**
+     * 是否启用
+     */
+    var status: Boolean = false
+}

+ 91 - 0
app/src/main/java/com/grkj/iscs/features/main/entity/WorkflowModeStepImport.kt

@@ -0,0 +1,91 @@
+package com.grkj.iscs.features.main.entity
+
+import com.grkj.data.data.MainDomainData
+import com.sik.sikcore.date.TimeUtils
+
+/**
+ * 流程模式导入列表包装
+ */
+class WorkflowModeStepImport {
+
+    /** 自增主键,步骤 ID */
+    var stepId: Long = 0L
+
+    /** 所属模式 ID */
+    var modeId: Long = 0L
+
+    /** 步骤模板 ID */
+    var stepIcon: String? = null
+
+    /** 步骤序号 */
+    var stepIndex: Int = 0
+
+    /** 步骤名称 */
+    var stepName: String? = null
+
+    /** 步骤标题 */
+    var stepTitle: String? = null
+
+    /** 步骤短标题 */
+    var stepTitleShort: String? = null
+
+    /** 步骤描述信息 */
+    var stepDescription: String? = null
+
+    /** 确认类型,0=手动 */
+    var confirmType: Int = 0
+
+    /** 确认角色编码 */
+    var confirmRoleCode: String? = null
+
+    /** 确认人员 ID */
+    var confirmUser: Long? = null
+
+    /** 是否可取消作业 */
+    var enableCancelJob: Boolean = false
+
+    /** 是否可设置锁定人 */
+    var enableSetLocker: Boolean = false
+
+    /** 是否可设置共锁人 */
+    var enableSetColocker: Boolean = false
+
+    /** 是否可添加共锁人 */
+    var enableAddColocker: Boolean = false
+
+    /** 添加共锁人后跳转步骤序号 */
+    var gotoStepAfterAddingColocker: Int? = null
+
+    /** 是否可减少共锁人 */
+    var enableReduceColocker: Boolean = false
+
+    /** 是否可上锁 */
+    var enableLock: Boolean = false
+
+    /** 是否可共锁 */
+    var enableColock: Boolean = false
+
+    /** 是否可解除共锁 */
+    var enableReleaseColock: Boolean = false
+
+    /** 是否可解锁 */
+    var enableUnlock: Boolean = false
+
+    /** 是否可结束作业 */
+    var enableEndJob: Boolean = false
+
+    /** 创建时间,格式 yyyy-MM-dd HH:mm:ss */
+    var createTime: Long? = null
+
+    /** 更新者 */
+    var updater: String = MainDomainData.userInfo?.userName ?: ""
+
+    /** 更新时间,格式 yyyy-MM-dd HH:mm:ss */
+    var updateTime: String = TimeUtils.nowString(TimeUtils.DEFAULT_DATE_HOUR_MIN_SEC_FORMAT)
+
+    /** 是否删除 */
+    var deleted: Boolean = false
+
+    /** 租户编号 */
+    var tenantId: Long = 0
+}

+ 4 - 0
app/src/main/java/com/grkj/iscs/features/main/fragment/hardware_manage/CardManageFragment.kt

@@ -60,6 +60,10 @@ class CardManageFragment : BaseFragment<FragmentCardManageBinding>() {
                 }
             }
         }
+        binding.reset.setDebouncedClickListener {
+            viewModel.cardFilterData = null
+            loadCards(true)
+        }
         binding.filter.setDebouncedClickListener {
             viewModel.getAllUserData().observe(this) {
                 FilterCardDialog.show(it) {

+ 4 - 0
app/src/main/java/com/grkj/iscs/features/main/fragment/hardware_manage/KeyManageFragment.kt

@@ -40,6 +40,10 @@ class KeyManageFragment : BaseFragment<FragmentKeyManageBinding>() {
         binding.delete.setDebouncedClickListener {
             deleteSelected()
         }
+        binding.reset.setDebouncedClickListener{
+            viewModel.keyFilterData=null
+            getKeyData(false)
+        }
         binding.add.setDebouncedClickListener {
             AddKeyDialog.show { data, dialog ->
                 viewModel.validateKeyData(data.keyNfc ?: "", data.macAddress ?: "").observe(this) {

+ 4 - 0
app/src/main/java/com/grkj/iscs/features/main/fragment/hardware_manage/LockManageFragment.kt

@@ -60,6 +60,10 @@ class LockManageFragment : BaseFragment<FragmentLockManageBinding>() {
                 }
             }
         }
+        binding.reset.setDebouncedClickListener{
+            viewModel.lockFilterData = null
+            loadLocks(true)
+        }
         binding.filter.setDebouncedClickListener {
             FilterLockDialog.show {
                 viewModel.lockFilterData = it

+ 4 - 0
app/src/main/java/com/grkj/iscs/features/main/fragment/hardware_manage/RfidTokenManageFragment.kt

@@ -57,6 +57,10 @@ class RfidTokenManageFragment : BaseFragment<FragmentRfidTokenManageBinding>() {
                 }
             }
         }
+        binding.reset.setDebouncedClickListener{
+            viewModel.rfidTokenFilterData = null
+            loadTokens(true)
+        }
         binding.filter.setDebouncedClickListener {
             FilterRfidTokenDialog.show {
                 viewModel.rfidTokenFilterData = it

+ 4 - 0
app/src/main/java/com/grkj/iscs/features/main/fragment/job_manage/JobExecuteFragment.kt

@@ -43,6 +43,7 @@ import com.grkj.ui_base.utils.extension.toggleExpandView
 import com.kongzue.dialogx.dialogs.BottomMenu
 import com.kongzue.dialogx.dialogs.PopTip
 import com.sik.sikcore.data.GlobalDataTempStore
+import com.sik.sikcore.date.TimeUtils
 import com.sik.sikcore.extension.setDebouncedClickListener
 import dagger.hilt.android.AndroidEntryPoint
 
@@ -299,6 +300,7 @@ class JobExecuteFragment : BaseFragment<FragmentJobExecuteBinding>() {
                         workflowStep?.stepTitleShort.toString()
                     ).toString(), onConfirmClick = {
                         item.stepStatus = "1"
+                        item.updateTime = TimeUtils.nowString(TimeUtils.DEFAULT_DATE_HOUR_MIN_SEC_FORMAT)
                         viewModel.updateStepStatus(item).observe(this@JobExecuteFragment) {
                             if (it == false) {
                                 PopTip.build().tip(R.string.step_confirm_failed)
@@ -700,6 +702,7 @@ class JobExecuteFragment : BaseFragment<FragmentJobExecuteBinding>() {
                     (viewModel.ticketData?.createBy == MainDomainData.userInfo?.userName || workflowStep.currentUserCanConfirm())
                 ) {
                     viewModel.currentStepData?.stepStatus = "1"
+                    viewModel.currentStepData?.updateTime = TimeUtils.nowString(TimeUtils.DEFAULT_DATE_HOUR_MIN_SEC_FORMAT)
                     viewModel.currentStepData?.let {
                         viewModel.updateStepStatus(it).observe(this@JobExecuteFragment) {
                             if (it == false) {
@@ -719,6 +722,7 @@ class JobExecuteFragment : BaseFragment<FragmentJobExecuteBinding>() {
                             it.stepIndex >= workflowStep.gotoStepAfterAddingColocker!!
                         }.sortedByDescending { it.stepIndex }.forEach {
                             it.stepStatus = "0"
+                            it.updateTime = TimeUtils.nowString(TimeUtils.DEFAULT_DATE_HOUR_MIN_SEC_FORMAT)
                             viewModel.updateStepStatusBack(it).observe(this@JobExecuteFragment) {
                                 if (it == false) {
                                     PopTip.build().tip(R.string.step_confirm_failed)

+ 142 - 12
app/src/main/java/com/grkj/iscs/features/main/fragment/job_manage/MyTodoListFragment.kt

@@ -23,17 +23,22 @@ import com.grkj.iscs.common.DataTransferConstants
 import com.grkj.iscs.databinding.FragmentMyTodoListBinding
 import com.grkj.iscs.databinding.ItemMyTodoBinding
 import com.grkj.iscs.features.main.dialog.CheckFaceDialog
+import com.grkj.iscs.features.main.dialog.ColockOperationTipDialog
 import com.grkj.iscs.features.main.dialog.TextDropDownDialog
 import com.grkj.iscs.features.main.dialog.job_manage.TodoPointDetailDialog
 import com.grkj.iscs.features.main.viewmodel.job_manage.JobExecuteViewModel
 import com.grkj.iscs.features.main.viewmodel.job_manage.MyTodoViewModel
 import com.grkj.shared.model.EventBean
+import com.grkj.shared.utils.ArcSoftUtil
 import com.grkj.ui_base.base.BaseFragment
 import com.grkj.ui_base.dialog.TipDialog
 import com.grkj.ui_base.utils.CommonUtils
+import com.grkj.ui_base.utils.event.RFIDCardReadEvent
 import com.grkj.ui_base.utils.event.UiEvent
 import com.grkj.ui_base.utils.extension.tip
+import com.kongzue.dialogx.dialogs.CustomDialog
 import com.kongzue.dialogx.dialogs.PopTip
+import com.kongzue.dialogx.interfaces.DialogLifecycleCallback
 import com.loper7.date_time_picker.dialog.CardDatePickerDialog
 import com.sik.sikcore.data.GlobalDataTempStore
 import com.sik.sikcore.date.TimeUtils
@@ -117,7 +122,7 @@ class MyTodoListFragment : BaseFragment<FragmentMyTodoListBinding>() {
                                 TimeUtils.DEFAULT_DATE_HOUR_MIN_SEC_FORMAT
                             )
                             updateTimeDate?.let {
-                                TimeUtils.calcDayNum(it, Date()) <= 1
+                                TimeUtils.calcDayNum(it, Date()) <= 7
                             } ?: false
                         }.sortedByDescending { it.stepUpdateTime }
                     }
@@ -286,21 +291,19 @@ class MyTodoListFragment : BaseFragment<FragmentMyTodoListBinding>() {
         itemBinding.btnPointDetail.isVisible =
             item.todoType == OperationTypeEnum.LOCK_TAKE_KEY || item.todoType == OperationTypeEnum.UNLOCK_TAKE_KEY || item.todoType == OperationTypeEnum.LOCK_RETURN_KEY || item.todoType == OperationTypeEnum.UNLOCK_RETURN_KEY
         itemBinding.btnHandle.isVisible = if (item.todoType == OperationTypeEnum.UNLOCK_TAKE_KEY) {
-            val currentStep =
-                jobExecuteViewModel.currentStepData
-            val canUnLock = currentStep?.enableUnlock == true &&
-                    jobExecuteViewModel.ticketPoints.any { it.pointStatus == "1" } &&
-                    (jobExecuteViewModel.workflowModes.find { it.modeId == jobExecuteViewModel.ticketData?.modeId }?.isColockSupport == false ||
-                            (jobExecuteViewModel.workflowModes.find { it.modeId == jobExecuteViewModel.ticketData?.modeId }?.isColockSupport == true &&
-                                    jobExecuteViewModel.ticketUser.filter { it.userRole == RoleEnum.JTCOLOCKER.roleKey }
-                                        .all { it.jobStatus == "2" }))
+            val canUnLock =
+                item.enableUnlock && jobExecuteViewModel.ticketPoints.filter { it.groupId == item.groupId }
+                    .all { it.pointStatus == "1" } && ((jobExecuteViewModel.workflowModes.find { it.modeId == jobExecuteViewModel.ticketData?.modeId }?.isColockSupport == false) ||
+                        ((jobExecuteViewModel.workflowModes.find { it.modeId == jobExecuteViewModel.ticketData?.modeId }?.isColockSupport == true) &&
+                                jobExecuteViewModel.ticketUser.filter { it.userRole == RoleEnum.JTCOLOCKER.roleKey }
+                                    .all { it.jobStatus == "2" }))
             item.isCurrentStep && item.todoType != OperationTypeEnum.LOCK_RETURN_KEY && item.todoType != OperationTypeEnum.UNLOCK_RETURN_KEY && canUnLock
         } else {
             item.isCurrentStep && item.todoType != OperationTypeEnum.LOCK_RETURN_KEY && item.todoType != OperationTypeEnum.UNLOCK_RETURN_KEY
         }
         itemBinding.btnPointDetail.setDebouncedClickListener {
             viewModel.getTicketPointsData(item.ticketId).observe(this) {
-                TodoPointDetailDialog.show(it)
+                TodoPointDetailDialog.show(it.filter { it.groupId == item.groupId })
             }
         }
         itemBinding.btnHandle.setDebouncedClickListener {
@@ -321,6 +324,28 @@ class MyTodoListFragment : BaseFragment<FragmentMyTodoListBinding>() {
                                 jobExecuteViewModel.toUnLock(it).observe(this) {}
                             } ?: showToast(getString(R.string.not_group_can_unlock))
 
+                            OperationTypeEnum.COLOCK -> {
+                                viewModel.startReadCard = true
+                                ColockOperationTipDialog.show().setDialogLifecycleCallback(object :
+                                    DialogLifecycleCallback<CustomDialog>() {
+                                    override fun onDismiss(dialog: CustomDialog?) {
+                                        viewModel.startReadCard = false
+                                        super.onDismiss(dialog)
+                                    }
+                                })
+                            }
+
+                            OperationTypeEnum.RELEASE_COLOCK -> {
+                                viewModel.startReadCard = true
+                                ColockOperationTipDialog.show().setDialogLifecycleCallback(object :
+                                    DialogLifecycleCallback<CustomDialog>() {
+                                    override fun onDismiss(dialog: CustomDialog?) {
+                                        viewModel.startReadCard = false
+                                        super.onDismiss(dialog)
+                                    }
+                                })
+                            }
+
                             OperationTypeEnum.CONFIRM -> {
                                 jobExecuteViewModel.currentStepData
                                     ?.let { stepVo ->
@@ -374,6 +399,84 @@ class MyTodoListFragment : BaseFragment<FragmentMyTodoListBinding>() {
     override fun onEvent(event: EventBean<Any>) {
         super.onEvent(event)
         when (event.code) {
+            EventConstants.EVENT_RFID_CARD_READ -> {
+                if (MainDomainData.userInfo?.userName != jobExecuteViewModel.ticketData?.createBy) {
+                    showToast(
+                        CommonUtils.getStr(com.grkj.ui_base.R.string.no_permission_to_handle)
+                            .toString()
+                    )
+                    return
+                }
+                if (!viewModel.startReadCard) {
+                    return
+                }
+                (event.data as RFIDCardReadEvent).let {
+                    val currentWorkflowStep = jobExecuteViewModel.currentStepData
+                    //当前步骤能加共锁并且锁已经上锁或者当前步骤能解共锁并且锁已经上锁
+                    if ((currentWorkflowStep?.enableColock == true || currentWorkflowStep?.enableReleaseColock == true) && jobExecuteViewModel.ticketPoints.all { it.pointStatus == "1" }) {
+                        jobExecuteViewModel.getUserIdByCardRfid(it.rfidNo).observe(this) { userId ->
+                            userId?.let {
+                                val isJobCardUser = jobExecuteViewModel.ticketUser.filter {
+                                    it.userRole?.contains(
+                                        RoleEnum.JTCOLOCKER.roleKey
+                                    ) == true
+                                }.find { it.userId == userId }
+                                isJobCardUser?.let { colocker ->
+                                    if (colocker.jobStatus == "0" && currentWorkflowStep.enableColock) {
+                                        logger.info("添加共锁")
+                                        TipDialog.showInfo(
+                                            msg = CommonUtils.getStr(
+                                                com.grkj.ui_base.R.string.confirm_to_colock,
+                                                colocker.nickName ?: ""
+                                            ).toString(), countDownTime = 10, onConfirmClick = {
+                                                colocker.jobStatus = "1"
+                                                jobExecuteViewModel.colockerStatusChange(colocker)
+                                                    .observe(this) {
+                                                        if (it) {
+                                                            PopTip.build()
+                                                                .tip(R.string.colock_complete)
+                                                            checkStepComplete()
+                                                            getData()
+                                                        } else {
+                                                            PopTip.build()
+                                                                .tip(R.string.colock_failed)
+                                                        }
+                                                    }
+                                            })
+                                    } else if (colocker.jobStatus == "1" && currentWorkflowStep.enableReleaseColock) {
+                                        logger.info("解除共锁")
+                                        TipDialog.showInfo(
+                                            msg = CommonUtils.getStr(
+                                                com.grkj.ui_base.R.string.confirm_to_uncolock,
+                                                colocker.nickName ?: ""
+                                            ).toString(), countDownTime = 10, onConfirmClick = {
+                                                colocker.jobStatus = "2"
+                                                jobExecuteViewModel.colockerStatusChange(colocker)
+                                                    .observe(this) {
+                                                        if (it) {
+                                                            PopTip.build()
+                                                                .tip(R.string.uncolock_complete)
+                                                            checkStepComplete()
+                                                            getData()
+                                                        } else {
+                                                            PopTip.build()
+                                                                .tip(R.string.uncolock_failed)
+                                                        }
+                                                    }
+                                            })
+                                    } else {
+                                        PopTip.build()
+                                            .tip(R.string.currently_unable_to_lock_together)
+                                    }
+                                } ?: PopTip.build().tip(R.string.invalid_user)
+                            } ?: PopTip.build().tip(R.string.invalid_card)
+                        }
+                    } else {
+                        PopTip.build().tip(R.string.currently_unable_to_lock_together)
+                    }
+                }
+            }
+
             EventConstants.EVENT_UPDATE_TICKET_PROGRESS -> {
                 getData()
             }
@@ -394,6 +497,7 @@ class MyTodoListFragment : BaseFragment<FragmentMyTodoListBinding>() {
         if (!checkSelectMember(workflowStep)) {
             if (jobExecuteViewModel.stepConditionsComplete(workflowStep)) {
                 item.stepStatus = "1"
+                item.updateTime = TimeUtils.nowString(TimeUtils.DEFAULT_DATE_HOUR_MIN_SEC_FORMAT)
                 jobExecuteViewModel.updateStepStatus(item).observe(this@MyTodoListFragment) {
                     if (it == false) {
                         showToast(getString(R.string.handle_failed))
@@ -472,7 +576,6 @@ class MyTodoListFragment : BaseFragment<FragmentMyTodoListBinding>() {
     override fun onResume() {
         super.onResume()
         if (!isFirstEnter) {
-
             if (GlobalDataTempStore.getInstance()
                     .hasData(DataTransferConstants.KEY_SELECTED_MEMBER_LOCKER_DATA)
             ) {
@@ -499,7 +602,7 @@ class MyTodoListFragment : BaseFragment<FragmentMyTodoListBinding>() {
                         jobExecuteViewModel.currentStepData?.let {
                             jobExecuteViewModel.updateStepStatus(it)
                                 .observe(this@MyTodoListFragment) {
-
+                                    getData()
                                 }
                         }
                     }
@@ -511,6 +614,8 @@ class MyTodoListFragment : BaseFragment<FragmentMyTodoListBinding>() {
                                 it.stepIndex >= workflowStep.gotoStepAfterAddingColocker!!
                             }.sortedByDescending { it.stepIndex }.forEach {
                                 it.stepStatus = "0"
+                                it.updateTime =
+                                    TimeUtils.nowString(TimeUtils.DEFAULT_DATE_HOUR_MIN_SEC_FORMAT)
                                 jobExecuteViewModel.updateStepStatusBack(it)
                                     .observe(this@MyTodoListFragment) {
                                         if (it == false) {
@@ -518,6 +623,7 @@ class MyTodoListFragment : BaseFragment<FragmentMyTodoListBinding>() {
                                         } else {
                                             jobExecuteViewModel.currentStepData =
                                                 jobExecuteViewModel.ticketStep.firstOrNull { it.stepStatus == "0" }
+                                            getData()
                                         }
                                     }
                             }
@@ -529,6 +635,30 @@ class MyTodoListFragment : BaseFragment<FragmentMyTodoListBinding>() {
         getData()
     }
 
+    /**
+     * 检查流程是否完成
+     */
+    private fun checkStepComplete() {
+        val workflowStep = jobExecuteViewModel.currentStepData
+        if (jobExecuteViewModel.stepConditionsComplete(workflowStep) && workflowStep?.confirmType != 0) {
+            jobExecuteViewModel.currentStepData =
+                jobExecuteViewModel.ticketStep.firstOrNull { it.stepStatus == "0" }
+            logger.info("当前步骤数据:${jobExecuteViewModel.currentStepData}")
+            jobExecuteViewModel.currentStepData?.let {
+                it.stepStatus = "1"
+                it.updateTime = TimeUtils.nowString(TimeUtils.DEFAULT_DATE_HOUR_MIN_SEC_FORMAT)
+                jobExecuteViewModel.updateStepStatus(it).observe(this@MyTodoListFragment) {
+                    if (it == false) {
+                        PopTip.build().tip(R.string.step_confirm_failed)
+                    } else {
+                        logger.info("检查更新步骤状态完成")
+                        getData()
+                    }
+                }
+            }
+        }
+    }
+
     private fun getData() {
         viewModel.getMyTodoList().observe(this) {
             for (tabPosition in 0 until binding.tabLayout.tabCount) {

+ 7 - 0
app/src/main/java/com/grkj/iscs/features/main/fragment/job_manage/WorkflowManageFragment.kt

@@ -125,6 +125,13 @@ class WorkflowManageFragment : BaseFragment<FragmentWorkflowManageBinding>() {
                 viewModel.workflowModeData.filter { !it.isPreset }.all { it.isSelected }
             setSelectAllListener()
         }
+        itemBinding.delete.setDebouncedClickListener{
+            viewModel.deleteWorkflowMode(item).observe(this@WorkflowManageFragment){
+                TipDialog.showSuccess(CommonUtils.getStr(R.string.delete_success).toString(),onConfirmClick={
+                    getData()
+                })
+            }
+        }
         itemBinding.root.setDebouncedClickListener {
             GlobalDataTempStore.getInstance()
                 .saveData(

+ 8 - 9
app/src/main/java/com/grkj/iscs/features/main/fragment/user_info/SetFingerprintFragment.kt

@@ -76,18 +76,16 @@ class SetFingerprintFragment : BaseFragment<FragmentSetFingerprintBinding>() {
                 ).toString(),
                 countDownTime = 10,
                 onConfirmClick = {
-                    viewModel.fingerprintData.filter { it.isSelected }.forEach { item ->
-                        if (item.fingerprintData.isNotEmpty()) {
-                            item.fingerprintData.forEach {
-                                it.content.deleteIfExists()
-                            }
-                        }
-                    }
-                    viewModel.deleteFingerprintByIds(viewModel.fingerprintData.flatMap { it.fingerprintData }
+                    viewModel.deleteFingerprintByIds(viewModel.fingerprintData.filter { it.isSelected }
+                        .flatMap { it.fingerprintData }
                         .map { it.recordId })
                         .observe(this) {
                             if (it) {
-                                TipDialog.showSuccess(getString(R.string.delete_success))
+                                TipDialog.showSuccess(
+                                    getString(R.string.delete_success),
+                                    onConfirmClick = {
+                                        getData()
+                                    })
                             }
                         }
                 }
@@ -117,6 +115,7 @@ class SetFingerprintFragment : BaseFragment<FragmentSetFingerprintBinding>() {
                                 .observe(this@SetFingerprintFragment) {
                                     if (it) {
                                         TipDialog.showSuccess(getString(R.string.delete_success))
+                                        getData()
                                     }
                                 }
                         }

+ 8 - 7
app/src/main/java/com/grkj/iscs/features/main/fragment/user_info/UserInfoFragment.kt

@@ -21,6 +21,7 @@ import com.kongzue.dialogx.dialogs.PopTip
 import com.sik.sikcore.date.TimeUtils
 import com.sik.sikcore.extension.file
 import com.sik.sikcore.extension.setDebouncedClickListener
+import com.sik.sikcore.string.RegexUtils
 import com.sik.sikimage.ImageConvertUtils
 import dagger.hilt.android.AndroidEntryPoint
 
@@ -119,12 +120,8 @@ class UserInfoFragment : BaseFragment<FragmentUserInfoBinding>() {
                 ).observe(this) {
                     if (it) {
                         TipDialog.showSuccess(
-                            CommonUtils.getStr(R.string.update_user_succeed).toString(),
-                            onConfirmClick = {
-                                LogoutEvent.sendLogoutEvent()
-                            }, onCancelClick = {
-                                LogoutEvent.sendLogoutEvent()
-                            })
+                            CommonUtils.getStr(R.string.update_user_succeed).toString()
+                        )
                     } else {
                         TipDialog.showError(
                             CommonUtils.getStr(R.string.update_user_failed).toString()
@@ -140,7 +137,7 @@ class UserInfoFragment : BaseFragment<FragmentUserInfoBinding>() {
         getData()
     }
 
-    private fun getData(){
+    private fun getData() {
         binding.username.text = MainDomainData.userInfo?.userName
         binding.nicknameEt.setText(MainDomainData.userInfo?.nickName)
         binding.phoneEt.setText(MainDomainData.userInfo?.phoneNumber)
@@ -164,6 +161,10 @@ class UserInfoFragment : BaseFragment<FragmentUserInfoBinding>() {
             PopTip.tip(R.string.please_input_phone)
             return false
         }
+        if (!RegexUtils.isMatch(binding.phoneEt.text.toString(), CommonConstants.REGEX_MOBILE)) {
+            PopTip.tip(R.string.please_input_correct_phone)
+            return false
+        }
         return true
     }
 

+ 47 - 7
app/src/main/java/com/grkj/iscs/features/main/viewmodel/WorkflowViewModel.kt

@@ -6,11 +6,11 @@ import com.google.common.reflect.TypeToken
 import com.google.gson.Gson
 import com.grkj.data.data.CommonConstants
 import com.grkj.data.model.dos.WorkflowMode
+import com.grkj.data.model.dos.WorkflowStep
 import com.grkj.data.model.vo.WorkflowModeVo
 import com.grkj.data.repository.IWorkflowRepository
 import com.grkj.iscs.R
-import com.grkj.iscs.features.main.entity.WorkflowDataBundle
-import com.grkj.iscs.features.main.entity.WorkflowExportPackage
+import com.grkj.iscs.features.main.entity.WorkflowModeImport
 import com.grkj.shared.config.AESConfig
 import com.grkj.ui_base.base.BaseViewModel
 import com.grkj.ui_base.dialog.TipDialog
@@ -62,6 +62,19 @@ class WorkflowViewModel @Inject constructor(val workflowRepository: IWorkflowRep
         }
     }
 
+    /**
+     * 删除流程模式
+     */
+    fun deleteWorkflowMode(workflowMode: WorkflowModeVo): LiveData<Boolean> {
+        return liveData(Dispatchers.IO) {
+            workflowRepository.updateWorkflowModeDeleted(
+                listOf(workflowMode.modeId),
+                true
+            )
+            emit(true)
+        }
+    }
+
     fun updateWorkflowMode(item: WorkflowModeVo): LiveData<Boolean> {
         return liveData(Dispatchers.IO) {
             val isSuccess = workflowRepository.updateWorkflowMode(
@@ -115,7 +128,8 @@ class WorkflowViewModel @Inject constructor(val workflowRepository: IWorkflowRep
                                 return
                             }
                             if (MessageDigestUtils.getMode(MessageDigestTypes.SHA256)
-                                    .digestToHex(workflowModeData.readBytes()) != workflowModeSha.readText().uppercase()
+                                    .digestToHex(workflowModeData.readBytes()) != workflowModeSha.readText()
+                                    .uppercase()
                             ) {
                                 LoadingEvent.sendLoadingEvent()
                                 TipDialog.showError(
@@ -129,13 +143,39 @@ class WorkflowViewModel @Inject constructor(val workflowRepository: IWorkflowRep
                                         .decryptFromHex(workflowModeData.readText())
                                 logger.info("流程模式字符串: $workflowModeDataJson")
                                 try {
-                                    val workflowExportPackage =
-                                        Gson().fromJson<WorkflowExportPackage<WorkflowDataBundle>>(
+//                                    val workflowExportPackage =
+//                                        Gson().fromJson<WorkflowExportPackage<WorkflowDataBundle>>(
+//                                            workflowModeDataJson,
+//                                            object :
+//                                                TypeToken<WorkflowExportPackage<WorkflowDataBundle>>() {}.type
+//                                        )
+                                    val workflowImportData =
+                                        Gson().fromJson<List<WorkflowModeImport>>(
                                             workflowModeDataJson,
                                             object :
-                                                TypeToken<WorkflowExportPackage<WorkflowDataBundle>>() {}.type
+                                                TypeToken<List<WorkflowModeImport>>() {}.type
                                         )
-
+                                    workflowImportData.forEach { workflowImportBean ->
+                                        BeanUtils.copyProperties(
+                                            workflowImportBean,
+                                            WorkflowMode::class.java
+                                        )?.let {
+                                            it.isPreset = false
+                                            val modeId = workflowRepository.insertWorkflowMode(it)
+                                            workflowImportBean.stepList.mapNotNull {
+                                                BeanUtils.copyProperties(
+                                                    it,
+                                                    WorkflowStep::class.java
+                                                )?.apply { this.modeId = modeId }
+                                            }.let {
+                                                workflowRepository.insertSteps(it)
+                                            }
+                                        }
+                                        LoadingEvent.sendLoadingEvent()
+                                        TipDialog.showSuccess(
+                                            CommonUtils.getStr(R.string.import_success).toString()
+                                        )
+                                    }
                                 } catch (e: Exception) {
                                     LoadingEvent.sendLoadingEvent()
                                     TipDialog.showError(

+ 2 - 0
app/src/main/java/com/grkj/iscs/features/main/viewmodel/exception_manage/ExceptionJobViewModel.kt

@@ -29,6 +29,7 @@ import com.grkj.ui_base.base.BaseViewModel
 import com.grkj.ui_base.business.DataBusiness
 import com.grkj.ui_base.utils.CommonUtils
 import com.sik.sikcore.data.BeanUtils
+import com.sik.sikcore.date.TimeUtils
 import dagger.hilt.android.lifecycle.HiltViewModel
 import kotlinx.coroutines.Dispatchers
 import javax.inject.Inject
@@ -119,6 +120,7 @@ class ExceptionJobViewModel @Inject constructor(
                 //如果是自动确认并且没有操作的功能,则更新步骤
                 if (workflowStep?.confirmType != 0 && workflowStep?.hasAnyOperationFunction() == false) {
                     stepDataVo.stepStatus = "1"
+                    stepDataVo.updateTime = TimeUtils.nowString(TimeUtils.DEFAULT_DATE_HOUR_MIN_SEC_FORMAT)
                     BeanUtils.copyProperties(
                         stepDataVo, IsJobTicketStep::class.java
                     )?.let { jobTicketStep ->

+ 2 - 0
app/src/main/java/com/grkj/iscs/features/main/viewmodel/job_manage/JobExecuteViewModel.kt

@@ -36,6 +36,7 @@ import com.grkj.ui_base.utils.modbus.DeviceConst
 import com.grkj.ui_base.utils.modbus.ModBusController
 import com.kongzue.dialogx.dialogs.PopTip
 import com.sik.sikcore.data.BeanUtils
+import com.sik.sikcore.date.TimeUtils
 import com.sik.sikcore.thread.ThreadUtils
 import dagger.hilt.android.lifecycle.HiltViewModel
 import javax.inject.Inject
@@ -426,6 +427,7 @@ class JobExecuteViewModel @Inject constructor(
                 //如果是自动确认并且没有操作的功能,则更新步骤
                 if (stepDataVo.confirmType != 0 && stepDataVo.hasAnyOperationFunction() == false) {
                     stepDataVo.stepStatus = "1"
+                    stepDataVo.updateTime = TimeUtils.nowString(TimeUtils.DEFAULT_DATE_HOUR_MIN_SEC_FORMAT)
                     BeanUtils.copyProperties(
                         stepDataVo, IsJobTicketStep::class.java
                     )?.let { jobTicketStep ->

+ 79 - 37
app/src/main/java/com/grkj/iscs/features/main/viewmodel/job_manage/MyTodoViewModel.kt

@@ -4,36 +4,19 @@ import androidx.lifecycle.LiveData
 import androidx.lifecycle.liveData
 import com.grkj.data.data.DictConstants
 import com.grkj.data.data.MainDomainData
-import com.grkj.data.di.RepositoryManager
-import com.grkj.data.di.RepositoryManager.workflowRepository
 import com.grkj.data.enums.OperationTypeEnum
-import com.grkj.data.enums.RoleEnum
-import com.grkj.data.model.dos.WorkflowStep
-import com.grkj.data.model.local.DeviceTakeUpdate
 import com.grkj.data.model.local.TodoStepJoin
 import com.grkj.data.model.local.isMyTodo
 import com.grkj.data.model.res.CommonDictRes
 import com.grkj.data.model.vo.IsJobTicketPointsDataVo
-import com.grkj.data.model.vo.IsJobTicketUserDataVo
 import com.grkj.data.model.vo.TodoItemVo
 import com.grkj.data.repository.IJobTicketRepository
 import com.grkj.iscs.R
 import com.grkj.ui_base.base.BaseViewModel
-import com.grkj.ui_base.business.BleBusinessManager
 import com.grkj.ui_base.business.DataBusiness
-import com.grkj.ui_base.business.ModbusBusinessManager
-import com.grkj.ui_base.dialog.TipDialog
 import com.grkj.ui_base.utils.CommonUtils
-import com.grkj.ui_base.utils.event.LoadingEvent
-import com.grkj.ui_base.utils.event.UiEvent
-import com.grkj.ui_base.utils.extension.tip
-import com.grkj.ui_base.utils.modbus.DeviceConst
-import com.grkj.ui_base.utils.modbus.ModBusController
-import com.kongzue.dialogx.dialogs.PopTip
-import com.sik.sikcore.thread.ThreadUtils
 import dagger.hilt.android.lifecycle.HiltViewModel
 import kotlinx.coroutines.Dispatchers
-import java.util.concurrent.atomic.AtomicInteger
 import javax.inject.Inject
 
 /**
@@ -43,6 +26,10 @@ import javax.inject.Inject
 class MyTodoViewModel @Inject constructor(
     val jobTicketRepository: IJobTicketRepository,
 ) : BaseViewModel() {
+    /**
+     * 开始读卡
+     */
+    var startReadCard: Boolean = false
 
     /**
      * 开始时间
@@ -87,39 +74,81 @@ class MyTodoViewModel @Inject constructor(
     }
 
     private fun splitTodoSteps(
-        all: List<TodoItemVo>, waitLimit: Int = Int.MAX_VALUE // 支持限制 waitData 条数
+        all: List<TodoItemVo>,
+        waitLimit: Int = Int.MAX_VALUE        // 限制“批次”数量
     ) {
-        // 初始化容器
         val tempTodo = mutableListOf<TodoItemVo>()
         val tempWait = mutableListOf<TodoItemVo>()
         val tempDone = mutableListOf<TodoItemVo>()
 
-        // 1. 先按 ticketId 分组
+        /* —— 1. 按 ticketId 分组 —— */
         val grouped = all.groupBy { it.ticketId }
 
         for ((_, steps) in grouped) {
             val doneSteps = steps.filter { it.stepStatus == "1" }
             val pendingSteps = steps.filter { it.stepStatus == "0" }
 
-            // 当前步骤:isCurrentStep = true
-            val todoStep = pendingSteps.filter { it.isCurrentStep }
-            todoStep.let { tempTodo.addAll(it) }
+            /* —— 当前步骤 → todoData —— */
+            val todoStep =
+                pendingSteps.filter { it.isCurrentStep }
+            tempTodo += todoStep
+
+            /* —— 其它待办 → waitData —— */
+            val otherPending = pendingSteps.filter { it !in todoStep }
+
+            /* ① 按 (stepIndex, actionKey) 分批 —— */
+            val byBatch = otherPending
+                .groupBy { it.stepIndex to actionKey(it) }             // <─ 新增动作维度
+                .toSortedMap(
+                    compareBy<Pair<Int, String>> { it.first }          // stepIndex 升序
+                        .thenBy { it.second }                          // actionKey 字典序
+                )
+
+            var pickedBatch = 0
+            for ((_, batchList) in byBatch) {
+                if (pickedBatch >= waitLimit) break
+                if (batchList.isEmpty()) continue
+
+                val action = actionKey(batchList[0])  // 同批次动作一致,取任意一条判断即可
+
+                if (action == "LOCK" || action == "UNLOCK") {
+                    // ✅ 上锁/解锁 → 同一 stepIndex 下多个 group 要全部保留
+                    tempWait += batchList
+                } else {
+                    // ✅ 其他类型 → 只保留一条代表
+                    tempWait.add(batchList.first())
+                }
+
+                pickedBatch++
+            }
 
-            // 剩余非当前步骤的 wait 步骤,最多 waitLimit 条
-            val waitSteps =
-                pendingSteps.filter { it !in todoStep }
-                    .sortedBy { it.stepIndex } // 可选:stepIndex小的优先
-                    .take(waitLimit)
+            tempDone += doneSteps
+        }
 
-            tempWait.addAll(waitSteps)
+        /* —— 结果赋值 —— */
+        todoData = tempTodo
+            .sortedBy { it.ticketId }
+            .toMutableList()
 
-            tempDone.addAll(doneSteps)
-        }
+        waitData = tempWait
+            .sortedWith(compareBy<TodoItemVo> { it.ticketId }.thenBy { it.stepIndex })
+            .toMutableList()
+
+        doneData = tempDone
+            .sortedByDescending { it.stepUpdateTime }
+            .toMutableList()
+    }
 
-        // 应用 limit 到 waitData
-        todoData = tempTodo.sortedBy { it.ticketId }.toMutableList()
-        waitData = tempWait.sortedBy { it.ticketId }.toMutableList()
-        doneData = tempDone.sortedByDescending { it.stepUpdateTime }.toMutableList()
+    /* -------------------------------------------------------------
+     * 根据唯一的 enableXXX 返回动作标识
+     * ----------------------------------------------------------- */
+    private fun actionKey(item: TodoItemVo): String = when {
+        item.enableLock -> "LOCK"
+        item.enableUnlock -> "UNLOCK"
+        item.enableColock -> "COLOCK"
+        item.enableReleaseColock -> "RELEASE_COLOCK"
+        item.enableEndJob -> "END_JOB"
+        else -> "CONFIRM"
     }
 
 
@@ -137,8 +166,14 @@ class MyTodoViewModel @Inject constructor(
             this.isCurrentStep = sameTicketStepJoinData.filter { it.stepStatus == "0" }
                 .minByOrNull { it.stepIndex }?.stepIndex == temp.stepIndex
             this.todoStatus = when {
-                temp.stepStatus == "0" && this.isCurrentStep -> CommonUtils.getStr(R.string.todo_header)
-                temp.stepStatus == "0" && !this.isCurrentStep -> CommonUtils.getStr(R.string.wait_header)
+                (temp.stepStatus == "0" || (temp.stepStatus == "1" && type == OperationTypeEnum.END)) && this.isCurrentStep -> CommonUtils.getStr(
+                    R.string.todo_header
+                )
+
+                (temp.stepStatus == "0" || (temp.stepStatus == "1" && type == OperationTypeEnum.END)) && !this.isCurrentStep -> CommonUtils.getStr(
+                    R.string.wait_header
+                )
+
                 temp.stepStatus == "1" -> CommonUtils.getStr(R.string.done_header)
                 else -> ""
             }.toString()
@@ -149,6 +184,7 @@ class MyTodoViewModel @Inject constructor(
             this.groupName = temp.groupName
             this.stepStatus = temp.stepStatus
             this.stepUpdateTime = temp.stepUpdateTime
+            this.colockerStatus = temp.colockerStatus
             this.createTime = temp.ticketStartTime
             this.enableSetLocker = temp.enableSetLocker
             this.enableSetColocker = temp.enableSetColocker
@@ -180,6 +216,12 @@ class MyTodoViewModel @Inject constructor(
             OperationTypeEnum.UNLOCK_TAKE_KEY -> CommonUtils.getStr(R.string.handle_unlock_take_key)
                 .toString()
 
+            OperationTypeEnum.COLOCK -> CommonUtils.getStr(R.string.handle_colock)
+                .toString()
+
+            OperationTypeEnum.RELEASE_COLOCK -> CommonUtils.getStr(R.string.handle_release_colock)
+                .toString()
+
             OperationTypeEnum.CONFIRM -> CommonUtils.getStr(
                 R.string.handle_step_confirm, item.todoTitle.toString()
             ).toString()

+ 7 - 7
app/src/main/java/com/grkj/iscs/features/main/viewmodel/user_info/UserInfoViewModel.kt

@@ -42,10 +42,9 @@ class UserInfoViewModel @Inject constructor(
      */
     fun saveUserInfo(nickName: String, phone: String): LiveData<Boolean> {
         return liveData(Dispatchers.IO) {
-            MainDomainData.userInfo?.apply {
-                this.nickName = nickName
-                this.phoneNumber = phone
-                userRepository.updateUser(this)
+            MainDomainData.userInfo?.userId?.let {
+                userRepository.updateUserInfo(it, nickName, phone)
+                MainDomainData.userInfo = userRepository.getUserByUserId(it)
                 emit(true)
             } ?: emit(false)
         }
@@ -56,9 +55,10 @@ class UserInfoViewModel @Inject constructor(
      */
     fun updatePassword(newPassword: String): LiveData<Boolean> {
         return liveData(Dispatchers.IO) {
-            MainDomainData.userInfo?.apply {
-                this.password = BCryptUtils.encryptPassword(newPassword)
-                userRepository.updateUser(this)
+            MainDomainData.userInfo?.userId?.let {
+                val password = BCryptUtils.encryptPassword(newPassword)
+                logger.info("用户id:${it}")
+                userRepository.updatePassword(it, password)
                 emit(true)
             } ?: emit(false)
         }

+ 12 - 12
app/src/main/java/com/grkj/iscs/features/splash/activity/SplashActivity.kt

@@ -38,19 +38,19 @@ class SplashActivity : BaseActivity<ActivitySplashBinding>() {
         DialogX.titleTextInfo = dialogXTextInfo
         StartModbusEvent.sendStartModbusEvent()
         //todo 测试用,直接进入,不初始化
-        viewModel.checkSysMenuAndRole().observe(this) {
-            startActivity(Intent(this, LoginActivity::class.java))
-            finish()
-        }
 //        viewModel.checkSysMenuAndRole().observe(this) {
-//            val isAppInit = MMKVConstants.APP_INIT.getMMKVData(false)
-//            if (isAppInit) {
-//                startActivity(Intent(this, LoginActivity::class.java))
-//                finish()
-//            } else {
-//                startActivity(Intent(this, InitActivity::class.java))
-//                finish()
-//            }
+//            startActivity(Intent(this, LoginActivity::class.java))
+//            finish()
 //        }
+        viewModel.checkSysMenuAndRole().observe(this) {
+            val isAppInit = MMKVConstants.APP_INIT.getMMKVData(false)
+            if (isAppInit) {
+                startActivity(Intent(this, LoginActivity::class.java))
+                finish()
+            } else {
+                startActivity(Intent(this, InitActivity::class.java))
+                finish()
+            }
+        }
     }
 }

+ 53 - 0
app/src/main/res/layout-land/dialog_colock_operation_tip.xml

@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+
+    <com.google.android.material.card.MaterialCardView
+        android:layout_width="@dimen/login_dialog_width"
+        android:layout_height="@dimen/login_dialog_height"
+        android:gravity="center"
+        app:cardBackgroundColor="@color/dialog_card_login_bg"
+        app:strokeColor="@color/common_transparent">
+
+        <LinearLayout
+            android:id="@+id/ll_easy_container"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_gravity="center"
+            android:gravity="center"
+            android:orientation="vertical"
+            android:visibility="visible">
+
+            <ImageView
+                android:id="@+id/iv_icon"
+                android:layout_width="@dimen/dialog_common_root_height_normal"
+                android:layout_height="@dimen/dialog_common_root_height_normal"
+                android:layout_marginBottom="@dimen/common_spacing_small" />
+
+            <TextView
+                android:id="@+id/tv_tip"
+                style="@style/CommonTextView"
+                android:textSize="@dimen/common_text_size_big" />
+        </LinearLayout>
+
+        <FrameLayout
+            android:layout_width="match_parent"
+            android:layout_height="match_parent">
+
+            <TextureView
+                android:id="@+id/preview"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:visibility="invisible" />
+        </FrameLayout>
+
+        <ImageView
+            android:id="@+id/close_iv"
+            android:layout_width="@dimen/dialog_check_face_close_size"
+            android:layout_height="@dimen/dialog_check_face_close_size"
+            android:layout_gravity="right"
+            android:padding="@dimen/common_spacing"
+            android:src="@drawable/icon_close"
+            android:tint="@color/white" />
+    </com.google.android.material.card.MaterialCardView>
+</layout>

+ 1 - 1
app/src/main/res/layout/dialog_add_lock.xml

@@ -160,7 +160,7 @@
                 android:layout_height="wrap_content"
                 android:layout_marginLeft="@dimen/common_spacing"
                 android:background="@drawable/bg_common_input"
-                android:hint="@string/please_input_key_nfc"
+                android:hint="@string/please_input_remark"
                 android:maxLines="1"
                 android:paddingHorizontal="@dimen/common_spacing"
                 android:paddingVertical="2dp"

+ 53 - 0
app/src/main/res/layout/dialog_colock_operation_tip.xml

@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+
+    <com.google.android.material.card.MaterialCardView
+        android:layout_width="@dimen/login_dialog_width"
+        android:layout_height="@dimen/login_dialog_height"
+        android:gravity="center"
+        app:cardBackgroundColor="@color/dialog_card_login_bg"
+        app:strokeColor="@color/common_transparent">
+
+        <LinearLayout
+            android:id="@+id/ll_easy_container"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_gravity="center"
+            android:gravity="center"
+            android:orientation="vertical"
+            android:visibility="visible">
+
+            <ImageView
+                android:id="@+id/iv_icon"
+                android:layout_width="@dimen/dialog_common_root_height_normal"
+                android:layout_height="@dimen/dialog_common_root_height_normal"
+                android:layout_marginBottom="@dimen/common_spacing_small" />
+
+            <TextView
+                android:id="@+id/tv_tip"
+                style="@style/CommonTextView"
+                android:textSize="@dimen/common_text_size_big" />
+        </LinearLayout>
+
+        <FrameLayout
+            android:layout_width="match_parent"
+            android:layout_height="match_parent">
+
+            <TextureView
+                android:id="@+id/preview"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:visibility="invisible" />
+        </FrameLayout>
+
+        <ImageView
+            android:id="@+id/close_iv"
+            android:layout_width="@dimen/dialog_check_face_close_size"
+            android:layout_height="@dimen/dialog_check_face_close_size"
+            android:layout_gravity="right"
+            android:padding="@dimen/common_spacing"
+            android:src="@drawable/icon_close"
+            android:tint="@color/white" />
+    </com.google.android.material.card.MaterialCardView>
+</layout>

+ 17 - 0
app/src/main/res/layout/fragment_card_manage.xml

@@ -84,6 +84,16 @@
                 android:textColor="@color/black"
                 android:textSize="@dimen/common_btn_text_size" />
 
+            <TextView
+                android:id="@+id/reset"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="@dimen/common_spacing"
+                android:background="@drawable/common_btn"
+                android:paddingHorizontal="@dimen/common_spacing_2x"
+                android:text="@string/reset"
+                android:textColor="@color/black"
+                android:textSize="@dimen/common_btn_text_size" />
 
             <View
                 android:layout_width="0dp"
@@ -141,6 +151,13 @@
                 android:gravity="center"
                 android:text="@string/username"
                 android:textSize="@dimen/common_text_size" />
+            <TextView
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1"
+                android:gravity="center"
+                android:text="@string/detail"
+                android:textSize="@dimen/common_text_size" />
         </LinearLayout>
 
         <com.scwang.smart.refresh.layout.SmartRefreshLayout

+ 17 - 0
app/src/main/res/layout/fragment_key_manage.xml

@@ -84,6 +84,16 @@
                 android:textColor="@color/black"
                 android:textSize="@dimen/common_btn_text_size" />
 
+            <TextView
+                android:id="@+id/reset"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="@dimen/common_spacing"
+                android:background="@drawable/common_btn"
+                android:paddingHorizontal="@dimen/common_spacing_2x"
+                android:text="@string/reset"
+                android:textColor="@color/black"
+                android:textSize="@dimen/common_btn_text_size" />
 
             <View
                 android:layout_width="0dp"
@@ -141,6 +151,13 @@
                 android:gravity="center"
                 android:text="@string/key_mac"
                 android:textSize="@dimen/common_text_size" />
+            <TextView
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1"
+                android:gravity="center"
+                android:text="@string/detail"
+                android:textSize="@dimen/common_text_size" />
         </LinearLayout>
 
         <com.scwang.smart.refresh.layout.SmartRefreshLayout

+ 17 - 0
app/src/main/res/layout/fragment_lock_manage.xml

@@ -84,6 +84,16 @@
                 android:textColor="@color/black"
                 android:textSize="@dimen/common_btn_text_size" />
 
+            <TextView
+                android:id="@+id/reset"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="@dimen/common_spacing"
+                android:background="@drawable/common_btn"
+                android:paddingHorizontal="@dimen/common_spacing_2x"
+                android:text="@string/reset"
+                android:textColor="@color/black"
+                android:textSize="@dimen/common_btn_text_size" />
 
             <View
                 android:layout_width="0dp"
@@ -141,6 +151,13 @@
                 android:gravity="center"
                 android:text="@string/status"
                 android:textSize="@dimen/common_text_size" />
+            <TextView
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1"
+                android:gravity="center"
+                android:text="@string/detail"
+                android:textSize="@dimen/common_text_size" />
         </LinearLayout>
 
         <com.scwang.smart.refresh.layout.SmartRefreshLayout

+ 16 - 8
app/src/main/res/layout/fragment_reset_password.xml

@@ -30,8 +30,8 @@
                 android:layout_weight="1"
                 android:text="@string/reset_password_title"
                 android:textColor="@color/black"
-                android:textStyle="bold"
-                android:textSize="@dimen/normal_text_size_25" />
+                android:textSize="@dimen/normal_text_size_25"
+                android:textStyle="bold" />
 
             <TextView
                 android:id="@+id/back"
@@ -70,7 +70,7 @@
                 android:text="@string/old_password"
                 android:textColor="@color/black"
                 android:textSize="@dimen/common_text_size"
-                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintEnd_toEndOf="@+id/end_line"
                 app:layout_constraintTop_toTopOf="parent" />
 
             <EditText
@@ -88,7 +88,7 @@
                 android:textColor="@color/black"
                 android:textSize="@dimen/common_text_size"
                 app:layout_constraintEnd_toEndOf="parent"
-                app:layout_constraintStart_toEndOf="@+id/old_password_tv"
+                app:layout_constraintStart_toEndOf="@+id/end_line"
                 app:layout_constraintTop_toTopOf="@+id/old_password_tv" />
 
             <TextView
@@ -99,7 +99,7 @@
                 android:text="@string/new_password"
                 android:textColor="@color/black"
                 android:textSize="@dimen/common_text_size"
-                app:layout_constraintEnd_toEndOf="@+id/old_password_tv"
+                app:layout_constraintEnd_toEndOf="@+id/end_line"
                 app:layout_constraintTop_toBottomOf="@+id/old_password_tv" />
 
             <EditText
@@ -117,7 +117,7 @@
                 android:textColor="@color/black"
                 android:textSize="@dimen/common_text_size"
                 app:layout_constraintEnd_toEndOf="parent"
-                app:layout_constraintStart_toEndOf="@+id/new_password_tv"
+                app:layout_constraintStart_toEndOf="@+id/end_line"
                 app:layout_constraintTop_toTopOf="@+id/new_password_tv" />
 
 
@@ -129,7 +129,8 @@
                 android:text="@string/repeat_new_password"
                 android:textColor="@color/black"
                 android:textSize="@dimen/common_text_size"
-                app:layout_constraintEnd_toEndOf="@+id/new_password_tv"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintEnd_toEndOf="@+id/end_line"
                 app:layout_constraintTop_toBottomOf="@+id/new_password_tv" />
 
             <EditText
@@ -147,9 +148,16 @@
                 android:textColor="@color/black"
                 android:textSize="@dimen/common_text_size"
                 app:layout_constraintEnd_toEndOf="parent"
-                app:layout_constraintStart_toEndOf="@+id/repeat_new_password_tv"
+                app:layout_constraintStart_toEndOf="@+id/end_line"
                 app:layout_constraintTop_toTopOf="@+id/repeat_new_password_tv" />
 
+            <androidx.constraintlayout.widget.Barrier
+                android:id="@+id/end_line"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                app:barrierDirection="end"
+                app:constraint_referenced_ids="repeat_new_password_tv,new_password_tv,old_password_tv" />
+
         </androidx.constraintlayout.widget.ConstraintLayout>
 
         <View

+ 22 - 2
app/src/main/res/layout/fragment_rfid_token_manage.xml

@@ -28,8 +28,8 @@
                 android:layout_weight="1"
                 android:text="@string/rfid_token_manage_title"
                 android:textColor="@color/black"
-                android:textStyle="bold"
-                android:textSize="@dimen/normal_text_size_25" />
+                android:textSize="@dimen/normal_text_size_25"
+                android:textStyle="bold" />
 
             <TextView
                 android:id="@+id/back"
@@ -84,6 +84,16 @@
                 android:textColor="@color/black"
                 android:textSize="@dimen/common_btn_text_size" />
 
+            <TextView
+                android:id="@+id/reset"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="@dimen/common_spacing"
+                android:background="@drawable/common_btn"
+                android:paddingHorizontal="@dimen/common_spacing_2x"
+                android:text="@string/reset"
+                android:textColor="@color/black"
+                android:textSize="@dimen/common_btn_text_size" />
 
             <View
                 android:layout_width="0dp"
@@ -141,6 +151,14 @@
                 android:gravity="center"
                 android:text="@string/status"
                 android:textSize="@dimen/common_text_size" />
+
+            <TextView
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1"
+                android:gravity="center"
+                android:text="@string/detail"
+                android:textSize="@dimen/common_text_size" />
         </LinearLayout>
 
         <com.scwang.smart.refresh.layout.SmartRefreshLayout
@@ -149,11 +167,13 @@
             android:layout_height="match_parent"
             android:layout_marginHorizontal="@dimen/common_spacing_2x"
             android:layout_marginBottom="@dimen/common_spacing">
+
             <com.drake.statelayout.StateLayout
                 android:id="@+id/state"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
                 android:background="@drawable/common_card_bg">
+
                 <androidx.recyclerview.widget.RecyclerView
                     android:id="@+id/list_rv"
                     android:layout_width="match_parent"

+ 9 - 0
app/src/main/res/layout/fragment_workflow_manage.xml

@@ -127,6 +127,15 @@
                 android:paddingVertical="@dimen/common_spacing"
                 android:text="@string/detail"
                 android:textSize="@dimen/common_text_size" />
+
+            <TextView
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1"
+                android:gravity="center"
+                android:paddingVertical="@dimen/common_spacing"
+                android:text="@string/operation"
+                android:textSize="@dimen/common_text_size" />
         </LinearLayout>
 
 

+ 7 - 0
app/src/main/res/layout/item_card_manage.xml

@@ -36,5 +36,12 @@
             android:layout_weight="1"
             android:gravity="center"
             android:textSize="@dimen/common_text_size" />
+        <TextView
+            android:layout_width="0dp"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            android:gravity="center"
+            android:text="@string/view"
+            android:textSize="@dimen/common_text_size" />
     </LinearLayout>
 </layout>

+ 7 - 0
app/src/main/res/layout/item_key_manage.xml

@@ -36,5 +36,12 @@
             android:layout_weight="1"
             android:gravity="center"
             android:textSize="@dimen/common_text_size" />
+        <TextView
+            android:layout_width="0dp"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            android:gravity="center"
+            android:text="@string/view"
+            android:textSize="@dimen/common_text_size" />
     </LinearLayout>
 </layout>

+ 7 - 0
app/src/main/res/layout/item_lock_manage.xml

@@ -36,5 +36,12 @@
             android:layout_weight="1"
             android:gravity="center"
             android:textSize="@dimen/common_text_size" />
+        <TextView
+            android:layout_width="0dp"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            android:gravity="center"
+            android:text="@string/view"
+            android:textSize="@dimen/common_text_size" />
     </LinearLayout>
 </layout>

+ 7 - 0
app/src/main/res/layout/item_rfid_token_manage.xml

@@ -36,5 +36,12 @@
             android:layout_weight="1"
             android:gravity="center"
             android:textSize="@dimen/common_text_size" />
+        <TextView
+            android:layout_width="0dp"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            android:gravity="center"
+            android:text="@string/view"
+            android:textSize="@dimen/common_text_size" />
     </LinearLayout>
 </layout>

+ 9 - 0
app/src/main/res/layout/item_workflow_manage.xml

@@ -47,5 +47,14 @@
             android:gravity="center"
             android:text="@string/user_manage_view"
             android:textSize="@dimen/common_text_size" />
+
+        <TextView
+            android:id="@+id/delete"
+            android:layout_width="0dp"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            android:gravity="center"
+            android:text="@string/delete"
+            android:textSize="@dimen/common_text_size" />
     </LinearLayout>
 </layout>

+ 4 - 0
app/src/main/res/values-en/strings.xml

@@ -528,5 +528,9 @@
     <string name="data_decrypt_failed">Data decrypt failed</string>
     <string name="data_content_error">Data content error</string>
     <string name="current_sop_has_job_in_progress">Current sop has job in progress</string>
+    <string name="handle_colock">Please confirm whether to proceed with the Co-Lock</string>
+    <string name="handle_release_colock">Please confirm whether to proceed with the Release Co-Lock</string>
+    <string name="please_input_correct_phone">Please input correct phone</string>
+    <string name="import_success">Import success</string>
 
 </resources>

+ 4 - 0
app/src/main/res/values-zh/strings.xml

@@ -528,5 +528,9 @@
     <string name="data_decrypt_failed">数据解密失败</string>
     <string name="data_content_error">数据内容错误</string>
     <string name="current_sop_has_job_in_progress">当前SOP存在进行中的作业</string>
+    <string name="handle_colock">请确认是否要进行添加共锁</string>
+    <string name="handle_release_colock">请确认是否要进行解除共锁</string>
+    <string name="please_input_correct_phone">请输入正确的手机号</string>
+    <string name="import_success">导入成功</string>
 
 </resources>

+ 4 - 0
app/src/main/res/values/strings.xml

@@ -531,5 +531,9 @@
     <string name="data_decrypt_failed">数据解密失败</string>
     <string name="data_content_error">数据内容错误</string>
     <string name="current_sop_has_job_in_progress">当前SOP存在进行中的作业</string>
+    <string name="handle_colock">请确认是否要进行添加共锁</string>
+    <string name="handle_release_colock">请确认是否要进行解除共锁</string>
+    <string name="please_input_correct_phone">请输入正确的手机号</string>
+    <string name="import_success">导入成功</string>
 
 </resources>

+ 1 - 1
data/src/main/java/com/grkj/data/dao/JobTicketDao.kt

@@ -666,7 +666,7 @@ interface JobTicketDao {
         FROM is_job_ticket ijt
         LEFT JOIN sys_user su ON ijt.create_by = su.user_name
         LEFT JOIN is_job_ticket_user ijtu ON ijtu.ticket_id = ijt.ticket_id
-        WHERE ijt.ticket_status IN ('1','2','3','4','7')
+        WHERE ijt.ticket_status not IN ('6')
           AND (
                 su.user_id = :userId         -- 是创建者
              OR ijtu.user_id = :userId       -- 是参与人

+ 13 - 1
data/src/main/java/com/grkj/data/dao/UserDao.kt

@@ -211,7 +211,7 @@ interface UserDao {
     /**
      * 更新用户数据
      */
-    @Update
+    @Update(onConflict = OnConflictStrategy.REPLACE)
     fun updateUserData(userDo: SysUserDo)
 
     /**
@@ -292,4 +292,16 @@ interface UserDao {
      */
     @Query("select * from sys_user where user_name = :username")
     fun getUsersByUsername(username: String): List<SysUserDo>
+
+    /**
+     * 更新用户信息
+     */
+    @Query("update sys_user set nick_name = :nickName, phonenumber = :phone where user_id = :userId")
+    fun updateUserInfo(userId: Long, nickName: String, phone: String)
+
+    /**
+     * 根据用户id更新用户密码
+     */
+    @Query("update sys_user set password = :password where user_id = :userId")
+    fun updateUserPassword(userId: Long, password: String)
 }

+ 14 - 1
data/src/main/java/com/grkj/data/dao/WorkflowStepDao.kt

@@ -3,6 +3,7 @@ package com.grkj.data.dao
 import androidx.room.Dao
 import androidx.room.Delete
 import androidx.room.Insert
+import androidx.room.OnConflictStrategy
 import androidx.room.Query
 import androidx.room.Update
 import com.grkj.data.model.dos.WorkflowMode
@@ -91,5 +92,17 @@ interface WorkflowStepDao {
      * 删除流程模式
      */
     @Query("update is_workflow_mode set deleted = :deleted where mode_id in (:workflowModeId)")
-    fun updateWorkflowModeDeleted(workflowModeId: List<Long>,deleted: Boolean)
+    fun updateWorkflowModeDeleted(workflowModeId: List<Long>, deleted: Boolean)
+
+    /**
+     * 添加流程模式
+     */
+    @Insert(onConflict = OnConflictStrategy.REPLACE)
+    fun insertWorkflowMode(workflowMode: WorkflowMode): Long
+
+    /**
+     * 添加步骤
+     */
+    @Insert(onConflict = OnConflictStrategy.REPLACE)
+    fun insertSteps(step: List<WorkflowStep>)
 }

+ 8 - 0
data/src/main/java/com/grkj/data/data/CommonConstants.kt

@@ -34,5 +34,13 @@ object CommonConstants {
      */
     const val AVATAR_FOLDER = "avatar"
 
+    /**
+     * 导入文件位置
+     */
     val workflowModeZipFilePath = "/sdcard/iscs/workflowMode.zip"
+
+    /**
+     * 手机号正则
+     */
+    const val REGEX_MOBILE = "^1(3\\d|4[5-9]|5[0-35-9]|6[2567]|7[0-8]|8\\d|9[0-35-9])\\d{8}$"
 }

+ 2 - 2
data/src/main/java/com/grkj/data/enums/OperationTypeEnum.kt

@@ -27,9 +27,9 @@ enum class OperationTypeEnum(val code: String, val desc: String, val todoTitle:
             return when {
                 (j.enableSetLocker || j.enableSetColocker) && isCreator -> CONFIRM
                 j.enableLock && j.userRole == RoleEnum.JTLOCKER.roleKey && j.keyStatus == "0" -> LOCK_TAKE_KEY
-                j.enableLock && j.userRole == RoleEnum.JTLOCKER.roleKey && j.keyStatus == "1" -> LOCK_RETURN_KEY
+                j.enableLock && j.userRole == RoleEnum.JTLOCKER.roleKey && (j.keyStatus == "1" || j.keyStatus == "2") -> LOCK_RETURN_KEY
                 j.enableUnlock && j.userRole == RoleEnum.JTLOCKER.roleKey && j.keyStatus == "0" -> UNLOCK_TAKE_KEY
-                j.enableUnlock && j.userRole == RoleEnum.JTLOCKER.roleKey && j.keyStatus == "1" -> UNLOCK_RETURN_KEY
+                j.enableUnlock && j.userRole == RoleEnum.JTLOCKER.roleKey && (j.keyStatus == "1" || j.keyStatus == "2") -> UNLOCK_RETURN_KEY
                 j.enableColock && isCreator -> COLOCK
                 j.enableReleaseColock && isCreator -> RELEASE_COLOCK
                 j.enableEndJob && isCreator -> END

+ 1 - 0
data/src/main/java/com/grkj/data/model/local/TodoStepJoin.kt

@@ -37,6 +37,7 @@ data class TodoStepJoin(
     val userRole: String?,   // JTLOCKER/JTCOLOCKER
     val lockerUserId: Long?, //上锁人id
     val colockerUserId: List<Long> = mutableListOf(), //共锁人id
+    val colockerStatus: List<String> = mutableListOf(), //共锁人状态
     val keyStatus: String?,   // 0/1/2 未取 已取 已还
 
     var groupId: Long? = null,

+ 5 - 0
data/src/main/java/com/grkj/data/model/vo/TodoItemVo.kt

@@ -72,6 +72,11 @@ class TodoItemVo {
      */
     var groupId: Long? = null
 
+    /**
+     * 共锁人状态
+     */
+    var colockerStatus:List<String> = mutableListOf()
+
     /**
      * 分组名称
      */

+ 16 - 1
data/src/main/java/com/grkj/data/repository/IUserRepository.kt

@@ -32,7 +32,7 @@ interface IUserRepository {
     /**
      * 指纹登录
      */
-    suspend fun loginWithFingerprint(fingerprint: String): Boolean
+     suspend fun loginWithFingerprint(fingerprint: String): Boolean
 
     /**
      * 检查指纹
@@ -157,4 +157,19 @@ interface IUserRepository {
      * 根据用户名获取用户
      */
     fun getUserByUserName(username: String): List<SysUserDo>
+
+    /**
+     * 根据用户id更新用户
+     */
+    fun updateUserInfo(userId: Long, nickName: String, phone: String)
+
+    /**
+     * 更新密码
+     */
+    fun updatePassword(userId: Long, password: String)
+
+    /**
+     * 根据用户id获取用户
+     */
+    fun getUserByUserId(userId: Long): SysUserDo?
 }

+ 10 - 0
data/src/main/java/com/grkj/data/repository/IWorkflowRepository.kt

@@ -54,4 +54,14 @@ interface IWorkflowRepository {
      * 删除流程模式
      */
     fun updateWorkflowModeDeleted(workflowModeId: List<Long>, deleted: Boolean)
+
+    /**
+     * 添加流程模式
+     */
+    fun insertWorkflowMode(workflowMode: WorkflowMode):Long
+
+    /**
+     * 添加步骤
+     */
+    fun insertSteps(workflowSteps: List<WorkflowStep>)
 }

+ 16 - 0
data/src/main/java/com/grkj/data/repository/impl/network/NetworkUserRepository.kt

@@ -113,6 +113,22 @@ class NetworkUserRepository @Inject constructor() : BaseRepository(), IUserRepos
         TODO("Not yet implemented")
     }
 
+    override fun updateUserInfo(
+        userId: Long,
+        nickName: String,
+        phone: String
+    ) {
+        TODO("Not yet implemented")
+    }
+
+    override fun updatePassword(userId: Long, password: String) {
+        TODO("Not yet implemented")
+    }
+
+    override fun getUserByUserId(userId: Long): SysUserDo? {
+        TODO("Not yet implemented")
+    }
+
     override fun getAllUsersWithRole(): List<SysUserVo> {
         TODO("Not yet implemented")
     }

+ 8 - 0
data/src/main/java/com/grkj/data/repository/impl/network/NetworkWorkflowRepository.kt

@@ -55,4 +55,12 @@ class NetworkWorkflowRepository @Inject constructor()  : BaseRepository(), IWork
     override fun updateWorkflowModeDeleted(workflowModeId: List<Long>, deleted: Boolean) {
         TODO("Not yet implemented")
     }
+
+    override fun insertWorkflowMode(workflowMode: WorkflowMode): Long {
+        TODO("Not yet implemented")
+    }
+
+    override fun insertSteps(workflowSteps: List<WorkflowStep>) {
+        TODO("Not yet implemented")
+    }
 }

+ 1 - 1
data/src/main/java/com/grkj/data/repository/impl/standard/HardwareRepository.kt

@@ -274,7 +274,7 @@ class HardwareRepository @Inject constructor(
     }
 
     override fun deleteLockByLockIds(lockIds: List<Long>) {
-        hardwareDao.deleteKeyByKeyIds(lockIds)
+        hardwareDao.deleteLockByLockIds(lockIds)
     }
 
     override fun deleteCardByCardIds(cardIds: List<Long>) {

+ 93 - 32
data/src/main/java/com/grkj/data/repository/impl/standard/JobTicketRepository.kt

@@ -38,6 +38,7 @@ import com.grkj.data.model.vo.JobTicketGroupDataVo
 import com.grkj.data.model.vo.JobTicketManageVo
 import com.grkj.data.model.vo.JobUserVo
 import com.grkj.data.model.vo.LockedPointVo
+import com.grkj.data.model.vo.TodoItemVo
 import com.grkj.data.repository.BaseRepository
 import com.grkj.data.repository.IJobTicketRepository
 import com.sik.sikcore.data.BeanUtils
@@ -130,6 +131,7 @@ class JobTicketRepository @Inject constructor(
                 val isJobTicketStep =
                     BeanUtils.copyProperties(workflowStep, IsJobTicketStep::class.java)
                         ?: IsJobTicketStep()
+                isJobTicketStep.stepId = 0L
                 isJobTicketStep.ticketId = ticketId
                 isJobTicketStep.stepIndex = workflowStep.stepIndex
                 isJobTicketStep.stepContent = workflowStep.stepTitle
@@ -228,6 +230,7 @@ class JobTicketRepository @Inject constructor(
                 val isJobTicketStep =
                     BeanUtils.copyProperties(workflowStep, IsJobTicketStep::class.java)
                         ?: IsJobTicketStep()
+                isJobTicketStep.stepId = 0L
                 isJobTicketStep.ticketId = ticketId
                 isJobTicketStep.stepIndex = workflowStep.stepIndex
                 isJobTicketStep.stepContent = workflowStep.stepTitle
@@ -313,35 +316,54 @@ class JobTicketRepository @Inject constructor(
 
                 /* 1️⃣ Lock / Unlock(按 group 拆) */
                 if (stepDef.enableLock || stepDef.enableUnlock) {
-                    val type = if (stepDef.enableLock) 0 else 1
-                    val keys = keyRecords.filter { it.ticketId == ticketId && it.ticketType == type && it.delFlag == "0" }
-
-                    val lockJoins = if (keys.isEmpty()) {
-                        // 没取钥匙 → 每个 group 生成一条占位
-                        groupList.map { g ->
-                            buildTodoStepJoin(stepDef, ticket, stepDef, lockerList, colockerList,
-                                g.id, g.groupName, null).only(if (stepDef.enableLock) StepAction.LOCK else StepAction.UNLOCK)
-                        }
-                    } else {
-                        // 有钥匙记录
-                        keys.map { k ->
-                            buildTodoStepJoin(stepDef, ticket, stepDef, lockerList, colockerList,
-                                k.groupId, groupList.firstOrNull { it.id == k.groupId }?.groupName, k).only(if (stepDef.enableLock) StepAction.LOCK else StepAction.UNLOCK)   // ⚠️
+                    val action  = if (stepDef.enableLock) StepAction.LOCK else StepAction.UNLOCK
+                    val type    = if (stepDef.enableLock) 0 else 1          // key.ticketType
+                    val keysOfStep = keyRecords.filter {                     // 全部钥匙记录
+                        it.ticketId == ticketId && it.ticketType == type && it.delFlag == "0"
+                    }
+
+                    /* —— 核心改动:flatMap 每个 group 各自判断 —— */
+                    val lockJoins = groupList.flatMap { g ->
+                        val keysOfGroup = keysOfStep.filter { it.groupId == g.id }
+
+                        if (keysOfGroup.isEmpty()) {
+                            // 该 group 还没取钥匙 → 占位 Todo
+                            listOf(
+                                buildTodoStepJoin(
+                                    stepDef, ticket, stepDef, lockerList, colockerList,
+                                    g.id, g.groupName, null
+                                ).only(action)
+                            )
+                        } else {
+                            // 该 group 已有钥匙记录 → 每把钥匙一条 Todo
+                            keysOfGroup.map { k ->
+                                buildTodoStepJoin(
+                                    stepDef, ticket, stepDef, lockerList, colockerList,
+                                    k.groupId, g.groupName, k
+                                ).only(action)
+                            }
                         }
                     }
+
                     lockJoins.forEach(::collect)
                 }
 
                 /* 2️⃣ Colock / Release(不拆 group) */
-                if (stepDef.enableColock || stepDef.enableReleaseColock) {
+                if (stepDef.enableColock) {
                     collect(
-                        buildTodoStepJoin(stepDef, ticket, stepDef, lockerList, colockerList,
-                            null, "", null).only(
-                            if (stepDef.enableColock)
-                                StepAction.COLOCK
-                            else
-                                StepAction.RELEASE_COLOCK
-                        ) // ⚠️
+                        buildTodoStepJoin(
+                            stepDef, ticket, stepDef, lockerList, colockerList,
+                            null, "", null
+                        ).only(StepAction.COLOCK)
+                    )
+                }
+
+                if (stepDef.enableReleaseColock) {
+                    collect(
+                        buildTodoStepJoin(
+                            stepDef, ticket, stepDef, lockerList, colockerList,
+                            null, "", null
+                        ).only(StepAction.RELEASE_COLOCK)
                     )
                 }
 
@@ -411,6 +433,7 @@ class JobTicketRepository @Inject constructor(
             userRole = getUserRole(workflowStep),
             lockerUserId = lockers.find { it.groupId == groupId }?.userId,
             colockerUserId = colockers.map { it.userId },
+            colockerStatus = colockers.mapNotNull { it.jobStatus },
             keyStatus = getKeyStatusFromKey(key),
             previousTodoStepJoin = previousTodoStepJoin
         ).also {
@@ -461,27 +484,62 @@ class JobTicketRepository @Inject constructor(
         // 1. 先按 ticketId 分组
         val grouped = todoList.filter { it.isMyTodo(userId, userName) }.groupBy { it.ticketId }
 
-        grouped.forEach { (_, steps) ->
+        for ((_, steps) in grouped) {
             val doneSteps = steps.filter { it.stepStatus == "1" }
             val pendingSteps = steps.filter { it.stepStatus == "0" }
 
-            // 当前步骤 = 最小 stepIndex 的 pending
+            /* —— 当前步骤 → todoData —— */
             val todoStep = pendingSteps.filter { pendingStep ->
                 todoList.filter { it.stepStatus == "0" }
-                    .minByOrNull { it.stepIndex }?.stepIndex == pendingStep.stepIndex
-            }
+                    .minByOrNull { it.stepIndex }?.stepIndex == pendingStep.stepIndex }
             todoStep.let { tempTodo.addAll(it) }
 
-            // 剩下的就是 wait
-            val waitSteps = pendingSteps.filter { it !in todoStep }
-                .sortedBy { it.stepIndex } // 可选:stepIndex小的优先
-                .take(waitLimit)
-            tempWait.addAll(waitSteps)
-            tempDone.addAll(doneSteps)
+            /* —— 其它待办 → waitData —— */
+            val otherPending = pendingSteps.filter { it !in todoStep }
+
+            /* ① 按 (stepIndex, actionKey) 分批 —— */
+            val byBatch = otherPending
+                .groupBy { it.stepIndex to actionKey(it) }             // <─ 新增动作维度
+                .toSortedMap(
+                    compareBy<Pair<Int, String>> { it.first }          // stepIndex 升序
+                        .thenBy { it.second }                          // actionKey 字典序
+                )
+
+            var pickedBatch = 0
+            for ((_, batchList) in byBatch) {
+                if (pickedBatch >= waitLimit) break
+                if (batchList.isEmpty()) continue
+
+                val action = actionKey(batchList[0])  // 同批次动作一致,取任意一条判断即可
+
+                if (action == "LOCK" || action == "UNLOCK") {
+                    // ✅ 上锁/解锁 → 同一 stepIndex 下多个 group 要全部保留
+                    tempWait += batchList
+                } else {
+                    // ✅ 其他类型 → 只保留一条代表
+                    tempWait.add(batchList.first())
+                }
+
+                pickedBatch++
+            }
+
+            tempDone += doneSteps
         }
         return tempTodo.size + tempWait.size
     }
 
+    /* -------------------------------------------------------------
+     * 根据唯一的 enableXXX 返回动作标识
+     * ----------------------------------------------------------- */
+    private fun actionKey(item: TodoStepJoin): String = when {
+        item.enableLock           -> "LOCK"
+        item.enableUnlock         -> "UNLOCK"
+        item.enableColock         -> "COLOCK"
+        item.enableReleaseColock  -> "RELEASE_COLOCK"
+        item.enableEndJob         -> "END_JOB"
+        else                      -> "CONFIRM"
+    }
+
     override fun updateCoincideToUnLock(ticketDetail: TicketDetailRes, groupId: Long) {
         ticketDetail.noUnlockTicketPointsVOSet?.filter { it.groupId == groupId }
             ?.forEach { jobTicketPointsVO ->
@@ -613,6 +671,7 @@ class JobTicketRepository @Inject constructor(
             //如果是自动确认并且没有操作的功能,则更新步骤
             if (stepDataVo?.confirmType != 0 && stepDataVo?.hasAnyOperationFunction() == false) {
                 stepDataVo.stepStatus = "1"
+                stepDataVo.updateTime = TimeUtils.nowString(TimeUtils.DEFAULT_DATE_HOUR_MIN_SEC_FORMAT)
                 BeanUtils.copyProperties(
                     stepDataVo, IsJobTicketStep::class.java
                 )?.let { jobTicketStep ->
@@ -1062,6 +1121,7 @@ class JobTicketRepository @Inject constructor(
                 val isJobTicketStep =
                     BeanUtils.copyProperties(workflowStep, IsJobTicketStep::class.java)
                         ?: IsJobTicketStep()
+                isJobTicketStep.stepId = 0L
                 isJobTicketStep.ticketId = ticketId
                 isJobTicketStep.stepIndex = workflowStep.stepIndex
                 isJobTicketStep.stepContent = workflowStep.stepTitle
@@ -1144,6 +1204,7 @@ class JobTicketRepository @Inject constructor(
                 val isJobTicketStep =
                     BeanUtils.copyProperties(workflowStep, IsJobTicketStep::class.java)
                         ?: IsJobTicketStep()
+                isJobTicketStep.stepId = 0L
                 isJobTicketStep.ticketId = ticketId
                 isJobTicketStep.stepIndex = workflowStep.stepIndex
                 isJobTicketStep.stepContent = workflowStep.stepTitle

+ 16 - 0
data/src/main/java/com/grkj/data/repository/impl/standard/UserRepository.kt

@@ -94,6 +94,22 @@ class UserRepository @Inject constructor(
         }
     }
 
+    override fun getUserByUserId(userId: Long): SysUserDo? {
+        return userDao.getUserInfoByUserId(userId.toString())
+    }
+
+    override fun updatePassword(userId: Long, password: String) {
+        userDao.updateUserPassword(userId, password)
+    }
+
+    override fun updateUserInfo(
+        userId: Long,
+        nickName: String,
+        phone: String
+    ) {
+        userDao.updateUserInfo(userId, nickName, phone)
+    }
+
     override fun checkCard(cardNo: String): Boolean {
         val userId = hardwareDao.getUserIdByCardNfc(cardNo)?.toString()
         return userId?.let {

+ 8 - 0
data/src/main/java/com/grkj/data/repository/impl/standard/WorkflowRepository.kt

@@ -33,6 +33,14 @@ class WorkflowRepository @Inject constructor(val workflowStepDao: WorkflowStepDa
         } ?: false
     }
 
+    override fun insertWorkflowMode(workflowMode: WorkflowMode): Long {
+        return workflowStepDao.insertWorkflowMode(workflowMode)
+    }
+
+    override fun insertSteps(workflowSteps: List<WorkflowStep>) {
+        workflowStepDao.insertSteps(workflowSteps)
+    }
+
     override fun updateWorkflowModeDeleted(workflowModeId: List<Long>, deleted: Boolean) {
         workflowStepDao.updateWorkflowModeDeleted(workflowModeId, deleted)
     }

+ 5 - 0
ui-base/src/main/java/com/grkj/ui_base/business/BleBusinessManager.kt

@@ -40,6 +40,7 @@ import com.grkj.ui_base.utils.modbus.ModBusController
 import com.kongzue.dialogx.dialogs.PopTip
 import com.sik.sikcore.SIKCore
 import com.sik.sikcore.data.BeanUtils
+import com.sik.sikcore.date.TimeUtils
 import com.sik.sikcore.thread.ThreadUtils
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.async
@@ -605,6 +606,8 @@ object BleBusinessManager {
                     logger.info("当前解锁和解除共锁在同一步骤或者没有全部点位解锁,不更新步骤状态")
                 } else {
                     ticketStepDataVo?.stepStatus = "1"
+                    ticketStepDataVo?.updateTime =
+                        TimeUtils.nowString(TimeUtils.DEFAULT_DATE_HOUR_MIN_SEC_FORMAT)
                     val isTicketStepData = BeanUtils.copyProperties(
                         ticketStepDataVo, IsJobTicketStep::class.java
                     )
@@ -725,6 +728,8 @@ object BleBusinessManager {
                                     logger.info("当前上锁和共锁或者解锁和解除共锁在同一步骤,或者所有点位的状态不统一,不更新步骤状态")
                                 } else {
                                     ticketStepDataVo?.stepStatus = "1"
+                                    ticketStepDataVo?.updateTime =
+                                        TimeUtils.nowString(TimeUtils.DEFAULT_DATE_HOUR_MIN_SEC_FORMAT)
                                     val isTicketStepData = BeanUtils.copyProperties(
                                         ticketStepDataVo, IsJobTicketStep::class.java
                                     )