Эх сурвалжийг харах

add(iscs_mc):
增加物资柜模块

周文健 1 сар өмнө
parent
commit
457adf3c11
100 өөрчлөгдсөн 1826 нэмэгдсэн , 198 устгасан
  1. 0 11
      app/src/main/res/values-en/strings.xml
  2. 0 11
      app/src/main/res/values-zh/strings.xml
  3. 0 11
      app/src/main/res/values/strings.xml
  4. 1 1
      data/src/main/java/com/grkj/data/config/ISCSConfig.kt
  5. 9 0
      data/src/main/java/com/grkj/data/data/EventConstants.kt
  6. 4 0
      data/src/main/java/com/grkj/data/data/MMKVConstants.kt
  7. 2 0
      data/src/main/java/com/grkj/data/di/AppEntryPoint.kt
  8. 7 0
      data/src/main/java/com/grkj/data/di/DatabaseModule.kt
  9. 3 0
      data/src/main/java/com/grkj/data/di/LogicManager.kt
  10. 8 0
      data/src/main/java/com/grkj/data/di/LogicModule.kt
  11. 14 0
      data/src/main/java/com/grkj/data/di/RepositoryModule.kt
  12. 96 0
      data/src/main/java/com/grkj/data/domain/logic/IMaterialsLogic.kt
  13. 87 0
      data/src/main/java/com/grkj/data/domain/logic/impl/MaterialsLogic.kt
  14. 39 121
      data/src/main/java/com/grkj/data/domain/logic/impl/SysMenuLogic.kt
  15. 3 5
      data/src/main/java/com/grkj/data/domain/logic/impl/UserLogic.kt
  16. 2 2
      data/src/main/java/com/grkj/data/domain/vo/UserManageFilterVo.kt
  17. 16 0
      data/src/main/java/com/grkj/data/enums/LoginModeEnum.kt
  18. 24 0
      data/src/main/java/com/grkj/data/enums/RFIDScanMode.kt
  19. 5 0
      data/src/main/java/com/grkj/data/hardware/IHardwareHelper.kt
  20. 20 0
      data/src/main/java/com/grkj/data/hardware/IRFIDScanHelper.kt
  21. 117 15
      data/src/main/java/com/grkj/data/hardware/can/CanCommand.kt
  22. 14 1
      data/src/main/java/com/grkj/data/hardware/can/CanHardwareHelper.kt
  23. 15 0
      data/src/main/java/com/grkj/data/hardware/can/CanHelper.kt
  24. 23 15
      data/src/main/java/com/grkj/data/hardware/can/CanReadyPlugin.kt
  25. 4 0
      data/src/main/java/com/grkj/data/hardware/modbus/ModBusHardwareHelper.kt
  26. 135 0
      data/src/main/java/com/grkj/data/hardware/uhf/UHFRFIDScanHelper.kt
  27. 132 0
      data/src/main/java/com/grkj/data/local/dao/MaterialsDao.kt
  28. 20 4
      data/src/main/java/com/grkj/data/local/database/ISCSDatabase.kt
  29. 22 1
      data/src/main/java/com/grkj/data/local/database/ISCSMigrations.kt
  30. 102 0
      data/src/main/java/com/grkj/data/local/dos/IsMaterials.kt
  31. 53 0
      data/src/main/java/com/grkj/data/local/dos/IsMaterialsCabinet.kt
  32. 64 0
      data/src/main/java/com/grkj/data/local/dos/IsMaterialsChangeRecord.kt
  33. 41 0
      data/src/main/java/com/grkj/data/local/dos/IsMaterialsCheckPlan.kt
  34. 57 0
      data/src/main/java/com/grkj/data/local/dos/IsMaterialsCheckRecord.kt
  35. 53 0
      data/src/main/java/com/grkj/data/local/dos/IsMaterialsInstructions.kt
  36. 71 0
      data/src/main/java/com/grkj/data/local/dos/IsMaterialsLoan.kt
  37. 43 0
      data/src/main/java/com/grkj/data/local/dos/IsMaterialsPlanCabinet.kt
  38. 29 0
      data/src/main/java/com/grkj/data/local/dos/IsMaterialsProperty.kt
  39. 33 0
      data/src/main/java/com/grkj/data/local/dos/IsMaterialsPropertyValue.kt
  40. 33 0
      data/src/main/java/com/grkj/data/local/dos/IsMaterialsReminder.kt
  41. 45 0
      data/src/main/java/com/grkj/data/local/dos/IsMaterialsRestitutionRules.kt
  42. 77 0
      data/src/main/java/com/grkj/data/local/dos/IsMaterialsType.kt
  43. 9 0
      data/src/main/java/com/grkj/data/perms/RoleMenuPolicy.kt
  44. 16 0
      data/src/main/java/com/grkj/data/perms/RolePermBridge.kt
  45. 9 0
      data/src/main/java/com/grkj/data/perms/RolePermNode.kt
  46. 96 0
      data/src/main/java/com/grkj/data/repository/MaterialsRepository.kt
  47. 84 0
      data/src/main/java/com/grkj/data/repository/impl/network/NetworkMaterialsRepositoryImpl.kt
  48. 87 0
      data/src/main/java/com/grkj/data/repository/impl/standard/MaterialsRepositoryImpl.kt
  49. 2 0
      gradle/libs.versions.toml
  50. 0 0
      iscs_lock/.gitignore
  51. 0 0
      iscs_lock/build.gradle.kts
  52. 0 0
      iscs_lock/proguard-rules.pro
  53. 0 0
      iscs_lock/src/androidTest/java/com/grkj/ui_base/ExampleInstrumentedTest.kt
  54. 0 0
      iscs_lock/src/main/AndroidManifest.xml
  55. 0 0
      iscs_lock/src/main/assets/logback.xml
  56. 0 0
      iscs_lock/src/main/assets/map_factory.png
  57. 0 0
      iscs_lock/src/main/assets/preset/CN/preset_workflow_mode.json
  58. 0 0
      iscs_lock/src/main/assets/preset/CN/preset_workflow_step.json
  59. 0 0
      iscs_lock/src/main/assets/preset/US/preset_workflow_mode.json
  60. 0 0
      iscs_lock/src/main/assets/preset/US/preset_workflow_step.json
  61. 0 0
      iscs_lock/src/main/assets/preset/preset_sys_role.json
  62. 0 0
      iscs_lock/src/main/assets/themes/Default/icons/access-control.svg
  63. 0 0
      iscs_lock/src/main/assets/themes/Default/icons/arrow-down-strenght.svg
  64. 0 0
      iscs_lock/src/main/assets/themes/Default/icons/arrow-progress-alt.svg
  65. 0 0
      iscs_lock/src/main/assets/themes/Default/icons/back-up.svg
  66. 0 0
      iscs_lock/src/main/assets/themes/Default/icons/ballot-check.svg
  67. 0 0
      iscs_lock/src/main/assets/themes/Default/icons/ballot.svg
  68. 0 0
      iscs_lock/src/main/assets/themes/Default/icons/bolt-slash.svg
  69. 0 0
      iscs_lock/src/main/assets/themes/Default/icons/bolt.svg
  70. 0 0
      iscs_lock/src/main/assets/themes/Default/icons/cards-blank.png
  71. 0 0
      iscs_lock/src/main/assets/themes/Default/icons/chalkboard-user.svg
  72. 0 0
      iscs_lock/src/main/assets/themes/Default/icons/checkbox.svg
  73. 0 0
      iscs_lock/src/main/assets/themes/Default/icons/choose.svg
  74. 0 0
      iscs_lock/src/main/assets/themes/Default/icons/document.svg
  75. 0 0
      iscs_lock/src/main/assets/themes/Default/icons/external-world.png
  76. 0 0
      iscs_lock/src/main/assets/themes/Default/icons/face-id-svgrepo-com.svg
  77. 0 0
      iscs_lock/src/main/assets/themes/Default/icons/file-export.svg
  78. 0 0
      iscs_lock/src/main/assets/themes/Default/icons/fingerprint.svg
  79. 0 0
      iscs_lock/src/main/assets/themes/Default/icons/fire-flame-curved.svg
  80. 0 0
      iscs_lock/src/main/assets/themes/Default/icons/gas-pump.svg
  81. 0 0
      iscs_lock/src/main/assets/themes/Default/icons/heat.svg
  82. 0 0
      iscs_lock/src/main/assets/themes/Default/icons/icon_bottom_menu_data_manage.svg
  83. 0 0
      iscs_lock/src/main/assets/themes/Default/icons/icon_bottom_menu_exception_manage.svg
  84. 0 0
      iscs_lock/src/main/assets/themes/Default/icons/icon_bottom_menu_hardware_manage.svg
  85. 0 0
      iscs_lock/src/main/assets/themes/Default/icons/icon_bottom_menu_home.svg
  86. 0 0
      iscs_lock/src/main/assets/themes/Default/icons/icon_bottom_menu_job_manage.svg
  87. 0 0
      iscs_lock/src/main/assets/themes/Default/icons/icon_chevron_left.png
  88. 0 0
      iscs_lock/src/main/assets/themes/Default/icons/icon_data_export.png
  89. 0 0
      iscs_lock/src/main/assets/themes/Default/icons/icon_data_manage_switch_layout.svg
  90. 0 0
      iscs_lock/src/main/assets/themes/Default/icons/icon_delete_circle.png
  91. 0 0
      iscs_lock/src/main/assets/themes/Default/icons/icon_drop_down_tree_check.png
  92. 0 0
      iscs_lock/src/main/assets/themes/Default/icons/icon_drop_down_tree_collapse.png
  93. 0 0
      iscs_lock/src/main/assets/themes/Default/icons/icon_drop_down_tree_expand.png
  94. 0 0
      iscs_lock/src/main/assets/themes/Default/icons/icon_drop_down_tree_point.png
  95. 0 0
      iscs_lock/src/main/assets/themes/Default/icons/icon_hide.png
  96. 0 0
      iscs_lock/src/main/assets/themes/Default/icons/icon_login_menu_card.png
  97. 0 0
      iscs_lock/src/main/assets/themes/Default/icons/icon_login_menu_password.png
  98. 0 0
      iscs_lock/src/main/assets/themes/Default/icons/icon_logo.png
  99. 0 0
      iscs_lock/src/main/assets/themes/Default/icons/icon_overview_data.png
  100. 0 0
      iscs_lock/src/main/assets/themes/Default/icons/icon_realtime_data.png

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

@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
-    <string name="register_job_card">Register Job Card</string>
-    <string name="register_fingerprint">Register Fingerprint</string>
-    <string name="register_face">Register Face</string>
-    <string name="register_point_rfid">Register Point RFID</string>
-    <string name="swip_card_to_register">Swip Card to Register</string>
-    <string name="face">Face</string>
-    <string name="fingerprint">Fingerprint</string>
-    <string name="register">Register</string>
-</resources>

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

@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
-    <string name="register_job_card">录入工卡</string>
-    <string name="register_fingerprint">录入指纹</string>
-    <string name="register_face">录入人脸</string>
-    <string name="register_point_rfid">录入RFID</string>
-    <string name="swip_card_to_register">刷卡录入</string>
-    <string name="face">人脸</string>
-    <string name="fingerprint">指纹</string>
-    <string name="register">录入</string>
-</resources>

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

@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
-    <string name="register_job_card">录入工卡</string>
-    <string name="register_fingerprint">录入指纹</string>
-    <string name="register_face">录入人脸</string>
-    <string name="register_point_rfid">录入RFID</string>
-    <string name="swip_card_to_register">刷卡录入</string>
-    <string name="face">人脸</string>
-    <string name="fingerprint">指纹</string>
-    <string name="register">录入</string>
-</resources>

+ 1 - 1
data/src/main/java/com/grkj/data/config/ISCSConfig.kt

@@ -55,5 +55,5 @@ object ISCSConfig {
     /**
      * 设计图尺寸,这边以短边为主
      */
-    val designSize = 600f
+    val DESIGN_SIZE = 600f
 }

+ 9 - 0
data/src/main/java/com/grkj/data/data/EventConstants.kt

@@ -78,6 +78,11 @@ object EventConstants {
      */
     const val EVENT_START_LISTENER = 100_000_015
 
+    /**
+     * 底部提示事件
+     */
+    const val EVENT_BOTTOM_TIP: Int = 100_000_016
+
     //---------------------------作业票------------------------
     const val EVENT_GET_TICKET_STATUS: Int = 100_001_001
 
@@ -106,5 +111,9 @@ object EventConstants {
      */
     const val EVENT_RFID_CARD_READ: Int = 100_003_004
 
+    /**
+     * 物资柜关门事件
+     */
+    const val EVENT_CABINET_DOOR_CLOSED: Int = 100_003_005
 
 }

+ 4 - 0
data/src/main/java/com/grkj/data/data/MMKVConstants.kt

@@ -83,4 +83,8 @@ object MMKVConstants {
      * 最小用户名长度
      */
     const val KEY_USERNAME_MIN_SIZE = "KEY_USERNAME_MIN_SIZE"
+    /**
+     * RFID扫描模式
+     */
+    const val KEY_RFID_SCAN_MODE = "key_rfid_scan_mode"
 }

+ 2 - 0
data/src/main/java/com/grkj/data/di/AppEntryPoint.kt

@@ -5,6 +5,7 @@ import com.grkj.data.domain.logic.IExceptionLogic
 import com.grkj.data.domain.logic.IHardwareLogic
 import com.grkj.data.domain.logic.IIsolationPointLogic
 import com.grkj.data.domain.logic.IJobTicketLogic
+import com.grkj.data.domain.logic.IMaterialsLogic
 import com.grkj.data.domain.logic.IRoleLogic
 import com.grkj.data.domain.logic.ISopLogic
 import com.grkj.data.domain.logic.ISysMenuLogic
@@ -29,4 +30,5 @@ interface AppEntryPoint {
     fun workflowLogic(): IWorkflowLogic
     fun exceptionLogic(): IExceptionLogic
     fun dataExportLogic(): IDataExportLogic
+    fun materialsLogic(): IMaterialsLogic
 }

+ 7 - 0
data/src/main/java/com/grkj/data/di/DatabaseModule.kt

@@ -5,6 +5,7 @@ import com.grkj.data.local.dao.HardwareDao
 import com.grkj.data.local.dao.IsSopDao
 import com.grkj.data.local.dao.IsolationPointDao
 import com.grkj.data.local.dao.JobTicketDao
+import com.grkj.data.local.dao.MaterialsDao
 import com.grkj.data.local.dao.RfidTokenDao
 import com.grkj.data.local.dao.RoleDao
 import com.grkj.data.local.dao.SysMenuDao
@@ -69,4 +70,10 @@ object DatabaseModule {
 
     @Provides
     fun provideExceptionDao(db: ISCSDatabase): ExceptionDao = db.exceptionDao()
+
+    /**
+     * 物资表提供
+     */
+    @Provides
+    fun provideMaterialsDao(db: ISCSDatabase): MaterialsDao = db.materialsDao()
 }

+ 3 - 0
data/src/main/java/com/grkj/data/di/LogicManager.kt

@@ -6,6 +6,7 @@ import com.grkj.data.domain.logic.IExceptionLogic
 import com.grkj.data.domain.logic.IHardwareLogic
 import com.grkj.data.domain.logic.IIsolationPointLogic
 import com.grkj.data.domain.logic.IJobTicketLogic
+import com.grkj.data.domain.logic.IMaterialsLogic
 import com.grkj.data.domain.logic.IRoleLogic
 import com.grkj.data.domain.logic.ISopLogic
 import com.grkj.data.domain.logic.ISysMenuLogic
@@ -29,6 +30,7 @@ object LogicManager {
     lateinit var workflowLogic: IWorkflowLogic
     lateinit var exceptionLogic: IExceptionLogic
     lateinit var dataExportLogic: IDataExportLogic
+    lateinit var materialsLogic: IMaterialsLogic
 
     fun init(app: Application) {
         val ep = EntryPointAccessors.fromApplication(app, AppEntryPoint::class.java)
@@ -43,5 +45,6 @@ object LogicManager {
         workflowLogic = ep.workflowLogic()
         exceptionLogic = ep.exceptionLogic()
         dataExportLogic = ep.dataExportLogic()
+        materialsLogic = ep.materialsLogic()
     }
 }

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

@@ -5,6 +5,7 @@ import com.grkj.data.domain.logic.IExceptionLogic
 import com.grkj.data.domain.logic.IHardwareLogic
 import com.grkj.data.domain.logic.IIsolationPointLogic
 import com.grkj.data.domain.logic.IJobTicketLogic
+import com.grkj.data.domain.logic.IMaterialsLogic
 import com.grkj.data.domain.logic.IRoleLogic
 import com.grkj.data.domain.logic.ISopLogic
 import com.grkj.data.domain.logic.ISysMenuLogic
@@ -16,6 +17,7 @@ import com.grkj.data.domain.logic.impl.ExceptionLogic
 import com.grkj.data.domain.logic.impl.HardwareLogic
 import com.grkj.data.domain.logic.impl.IsolationPointLogic
 import com.grkj.data.domain.logic.impl.JobTicketLogic
+import com.grkj.data.domain.logic.impl.MaterialsLogic
 import com.grkj.data.domain.logic.impl.RoleLogic
 import com.grkj.data.domain.logic.impl.SopLogic
 import com.grkj.data.domain.logic.impl.SysMenuLogic
@@ -100,4 +102,10 @@ object LogicModule {
     fun provideDataExportLogic(
         standard: DataExportLogic,
     ): IDataExportLogic = standard
+
+    @Provides
+    @Singleton
+    fun provideMaterialsLogic(
+        standard: MaterialsLogic,
+    ): IMaterialsLogic = standard
 }

+ 14 - 0
data/src/main/java/com/grkj/data/di/RepositoryModule.kt

@@ -11,6 +11,7 @@ import com.grkj.data.repository.ExceptionRepository
 import com.grkj.data.repository.HardwareRepository
 import com.grkj.data.repository.IsolationPointRepository
 import com.grkj.data.repository.JobTicketRepository
+import com.grkj.data.repository.MaterialsRepository
 import com.grkj.data.repository.RoleRepository
 import com.grkj.data.repository.SopRepository
 import com.grkj.data.repository.SysMenuRepository
@@ -21,6 +22,7 @@ import com.grkj.data.repository.impl.network.NetworkExceptionRepositoryImpl
 import com.grkj.data.repository.impl.network.NetworkHardwareRepositoryImpl
 import com.grkj.data.repository.impl.network.NetworkIsolationPointRepositoryImpl
 import com.grkj.data.repository.impl.network.NetworkJobTicketRepositoryImpl
+import com.grkj.data.repository.impl.network.NetworkMaterialsRepositoryImpl
 import com.grkj.data.repository.impl.network.NetworkRoleRepositoryImpl
 import com.grkj.data.repository.impl.network.NetworkSopRepository
 import com.grkj.data.repository.impl.network.NetworkSysMenuRepositoryImpl
@@ -31,6 +33,7 @@ import com.grkj.data.repository.impl.standard.ExceptionRepositoryImpl
 import com.grkj.data.repository.impl.standard.HardwareRepositoryImpl
 import com.grkj.data.repository.impl.standard.IsolationPointRepositoryImpl
 import com.grkj.data.repository.impl.standard.JobTicketRepositoryImpl
+import com.grkj.data.repository.impl.standard.MaterialsRepositoryImpl
 import com.grkj.data.repository.impl.standard.RoleRepositoryImpl
 import com.grkj.data.repository.impl.standard.SopRepositoryImpl
 import com.grkj.data.repository.impl.standard.SysMenuRepositoryImpl
@@ -165,4 +168,15 @@ object RepositoryModule {
         network: NetworkRoleRepositoryImpl
     ): RoleRepository =
         if (MMKVConstants.SERVER_ADDRESS.getMMKVData("").isNotEmpty()) network else standard
+
+    /**
+     * 物资仓储(本地/网络二选一)
+     */
+    @Provides
+    @Singleton
+    fun provideMaterialsRepository(
+        standard: MaterialsRepositoryImpl,
+        network: NetworkMaterialsRepositoryImpl
+    ): MaterialsRepository =
+        if (MMKVConstants.SERVER_ADDRESS.getMMKVData("").isNotEmpty()) network else standard
 }

+ 96 - 0
data/src/main/java/com/grkj/data/domain/logic/IMaterialsLogic.kt

@@ -0,0 +1,96 @@
+package com.grkj.data.domain.logic
+
+import com.grkj.data.local.dos.IsMaterials
+import com.grkj.data.local.dos.IsMaterialsInstructions
+import com.grkj.data.local.dos.IsMaterialsLoan
+import com.grkj.data.local.dos.IsMaterialsType
+
+/**
+ * 物资相关业务
+ */
+interface IMaterialsLogic {
+    /**
+     * 获取物资类型数据
+     */
+    fun getMaterialsType(): List<IsMaterialsType>
+
+    /**
+     * 新增物资类型
+     */
+    fun insertMaterialsType(materialsType: IsMaterialsType)
+
+    /**
+     * 更新物资类型
+     */
+    fun updateMaterialsType(materialsType: IsMaterialsType)
+
+    /**
+     * 删除物资类型
+     */
+    fun deleteMaterialsType(materialTypeIds: List<Long>)
+
+    /**
+     * 检查是否有正在使用的物资类型
+     */
+    fun checkMaterialsTypeInBorrowed(materialsTypeId: List<Long>): Boolean
+
+    /**
+     * 新增物资
+     */
+    fun insertMaterials(isMaterials: IsMaterials)
+
+    /**
+     * 批量新增物资
+     */
+    fun insertMaterials(isMaterials: List<IsMaterials>)
+
+    /**
+     * 更新物资
+     */
+    fun updateMaterials(isMaterials: IsMaterials)
+
+    /**
+     * 批量更新物资
+     */
+    fun updateMaterials(isMaterials: List<IsMaterials>)
+
+    /**
+     * 获取物资
+     */
+    fun getMaterials(): List<IsMaterials>
+
+    /**
+     * 删除物资
+     */
+    fun deleteMaterials(materialsId: List<Long>)
+
+    /**
+     * 检查是否有正在使用的物资
+     */
+    fun checkMaterialsInBorrowed(materialsId: List<Long>): Boolean
+
+    /**
+     * 获取借出物资
+     */
+    fun getMaterialsLoan(): List<IsMaterialsLoan>
+
+    /**
+     * 添加领取物资
+     */
+    fun borrowMaterials(materialsLoan: List<IsMaterialsLoan>)
+
+    /**
+     * 归还物资
+     */
+    fun returnMaterials(materialsLoan: List<IsMaterialsLoan>)
+
+    /**
+     * 取出异常物资
+     */
+    fun borrowExceptionMaterials(isMaterials: List<Long>)
+
+    /**
+     * 获取物资说明列表
+     */
+    fun getMaterialsInstructionList(): List<IsMaterialsInstructions>
+}

+ 87 - 0
data/src/main/java/com/grkj/data/domain/logic/impl/MaterialsLogic.kt

@@ -0,0 +1,87 @@
+package com.grkj.data.domain.logic.impl
+
+import com.grkj.data.domain.logic.BaseLogic
+import com.grkj.data.domain.logic.IMaterialsLogic
+import com.grkj.data.local.dos.IsMaterials
+import com.grkj.data.local.dos.IsMaterialsInstructions
+import com.grkj.data.local.dos.IsMaterialsLoan
+import com.grkj.data.local.dos.IsMaterialsType
+import com.grkj.data.repository.MaterialsRepository
+import javax.inject.Inject
+import javax.inject.Singleton
+
+/**
+ * 物资相关业务
+ */
+@Singleton
+class MaterialsLogic @Inject constructor(
+    val materialsRepository: MaterialsRepository
+) : BaseLogic(), IMaterialsLogic {
+    override fun getMaterialsType(): List<IsMaterialsType> {
+        return materialsRepository.getMaterialsType()
+    }
+
+    override fun insertMaterialsType(materialsType: IsMaterialsType) {
+        materialsRepository.insertMaterialsType(materialsType)
+    }
+
+    override fun insertMaterials(isMaterials: IsMaterials) {
+        materialsRepository.insertMaterials(isMaterials)
+    }
+
+    override fun insertMaterials(isMaterials: List<IsMaterials>) {
+        materialsRepository.insertMaterials(isMaterials)
+    }
+
+    override fun updateMaterials(isMaterials: List<IsMaterials>) {
+        materialsRepository.updateMaterials(isMaterials)
+    }
+
+    override fun getMaterials(): List<IsMaterials> {
+        return materialsRepository.getMaterials()
+    }
+
+    override fun deleteMaterials(materialsId: List<Long>) {
+        materialsRepository.deleteMaterials(materialsId)
+    }
+
+    override fun checkMaterialsInBorrowed(materialsId: List<Long>): Boolean {
+        return materialsRepository.checkMaterialsInBorrowed(materialsId)
+    }
+
+    override fun getMaterialsLoan(): List<IsMaterialsLoan> {
+        return materialsRepository.getMaterialsLoan()
+    }
+
+    override fun borrowExceptionMaterials(isMaterials: List<Long>) {
+        materialsRepository.borrowExceptionMaterials(isMaterials)
+    }
+
+    override fun getMaterialsInstructionList(): List<IsMaterialsInstructions> {
+        return materialsRepository.getMaterialsInstructionList()
+    }
+
+    override fun borrowMaterials(materialsLoan: List<IsMaterialsLoan>) {
+        materialsRepository.updateMaterialsLoan(materialsLoan)
+    }
+
+    override fun returnMaterials(materialsLoan: List<IsMaterialsLoan>) {
+        materialsRepository.updateMaterialsLoan(materialsLoan)
+    }
+
+    override fun updateMaterials(isMaterials: IsMaterials) {
+        materialsRepository.updateMaterials(isMaterials)
+    }
+
+    override fun updateMaterialsType(materialsType: IsMaterialsType) {
+        materialsRepository.updateMaterialsType(materialsType)
+    }
+
+    override fun deleteMaterialsType(materialTypeIds: List<Long>) {
+        materialsRepository.deleteMaterialsType(materialTypeIds)
+    }
+
+    override fun checkMaterialsTypeInBorrowed(materialsTypeId: List<Long>): Boolean {
+        return materialsRepository.checkMaterialsTypeInBorrowed(materialsTypeId)
+    }
+}

+ 39 - 121
data/src/main/java/com/grkj/data/domain/logic/impl/SysMenuLogic.kt

@@ -2,10 +2,11 @@ package com.grkj.data.domain.logic.impl
 
 import com.grkj.data.domain.logic.BaseLogic
 import com.grkj.data.domain.logic.ISysMenuLogic
-import com.grkj.data.enums.RoleEnum
-import com.grkj.data.enums.RoleFunctionalPermissionsEnum
 import com.grkj.data.local.dos.SysMenu
 import com.grkj.data.local.dos.SysRoleMenu
+import com.grkj.data.perms.RoleMenuPolicy
+import com.grkj.data.perms.RolePermBridge
+import com.grkj.data.perms.RolePermNode
 import com.grkj.data.repository.RoleRepository
 import com.grkj.data.repository.SysMenuRepository
 import com.grkj.shared.utils.i18n.I18nManager
@@ -18,95 +19,37 @@ import javax.inject.Singleton
 @Singleton
 class SysMenuLogic @Inject constructor(
     val sysMenuRepository: SysMenuRepository,
-    val roleRepository: RoleRepository
+    val roleRepository: RoleRepository,
+    private val permBridge: RolePermBridge,
+    private val roleMenuPolicy: RoleMenuPolicy
 ) :
     BaseLogic(), ISysMenuLogic {
     override fun checkSysMenuAndRole() {
-        // 找出所有顶层菜单(level == 0),依次递归插入/更新
-        RoleFunctionalPermissionsEnum.values()
-            .filter { it.level == 0 }
-            .forEach { topEnum ->
-                processMenuEnumRecursive(topEnum, parentId = null)
-            }
+        // 先灌根节点
+        permBridge.roots().forEach { top ->
+            processMenuNodeRecursive(top, parentId = null)
+        }
+
         val sysMenuData = sysMenuRepository.getAll()
-        roleRepository.getRoleData().filter { it.roleKey in RoleEnum.values().map { it.roleKey } }
+
+        roleRepository.getRoleData()
             .forEach { roleData ->
-                when (roleData.roleKey) {
-                    //超管权限
-                    RoleEnum.ADMIN.roleKey -> {
-                        val roleMenuData = mutableListOf<SysRoleMenu>().apply {
-                            for (permissionsEnum in RoleFunctionalPermissionsEnum.except()) {
-                                sysMenuData.find { it.perms == permissionsEnum.functionalPermission }?.menuId?.let { menuId ->
-                                    val sysRoleMenu = SysRoleMenu()
-                                    sysRoleMenu.roleId = roleData.roleId
-                                    sysRoleMenu.menuId = menuId
-                                    add(sysRoleMenu)
-                                }
-                            }
-                        }
-                        sysMenuRepository.insertRoleMenus(roleMenuData)
-                    }
-                    //作业管理员权限
-                    RoleEnum.JTDRAWER.roleKey -> {
-                        val roleMenuData = mutableListOf<SysRoleMenu>().apply {
-                            for (permissionsEnum in RoleFunctionalPermissionsEnum.except(
-                                RoleFunctionalPermissionsEnum.USER_MANAGE,
-                                RoleFunctionalPermissionsEnum.ROLE_MANAGE,
-                                RoleFunctionalPermissionsEnum.BACKUP_AND_RESTORE,
-                                RoleFunctionalPermissionsEnum.HARDWARE_HOME_MANAGE,
-                                RoleFunctionalPermissionsEnum.SETTINGS
-                            )) {
-                                sysMenuData.find { it.perms == permissionsEnum.functionalPermission }?.menuId?.let { menuId ->
-                                    val sysRoleMenu = SysRoleMenu()
-                                    sysRoleMenu.roleId = roleData.roleId
-                                    sysRoleMenu.menuId = menuId
-                                    add(sysRoleMenu)
-                                }
-                            }
-                        }
-                        sysMenuRepository.insertRoleMenus(roleMenuData)
-                    }
-                    //作业负责人权限
-                    RoleEnum.JTLOCKER.roleKey -> {
-                        val roleMenuData = mutableListOf<SysRoleMenu>().apply {
-                            for (permissionsEnum in RoleFunctionalPermissionsEnum.except(
-                                RoleFunctionalPermissionsEnum.DATA_HOME_MANAGE,
-                                RoleFunctionalPermissionsEnum.HARDWARE_HOME_MANAGE,
-                                RoleFunctionalPermissionsEnum.EXCEPTION_JOB,
-                                RoleFunctionalPermissionsEnum.EXCEPTION_MANAGE,
-                                RoleFunctionalPermissionsEnum.SETTINGS,
-                            )) {
-                                sysMenuData.find { it.perms == permissionsEnum.functionalPermission }?.menuId?.let { menuId ->
-                                    val sysRoleMenu = SysRoleMenu()
-                                    sysRoleMenu.roleId = roleData.roleId
-                                    sysRoleMenu.menuId = menuId
-                                    add(sysRoleMenu)
-                                }
-                            }
-                        }
-                        sysMenuRepository.insertRoleMenus(roleMenuData)
-                    }
-                    //作业参与人权限
-                    RoleEnum.JTCOLOCKER.roleKey -> {
-                        val roleMenuData = mutableListOf<SysRoleMenu>().apply {
-                            for (permissionsEnum in RoleFunctionalPermissionsEnum.except(
-                                RoleFunctionalPermissionsEnum.DATA_HOME_MANAGE,
-                                RoleFunctionalPermissionsEnum.HARDWARE_HOME_MANAGE,
-                                RoleFunctionalPermissionsEnum.EXCEPTION_JOB,
-                                RoleFunctionalPermissionsEnum.EXCEPTION_MANAGE,
-                                RoleFunctionalPermissionsEnum.SETTINGS,
-                            )) {
-                                sysMenuData.find { it.perms == permissionsEnum.functionalPermission }?.menuId?.let { menuId ->
-                                    val sysRoleMenu = SysRoleMenu()
-                                    sysRoleMenu.roleId = roleData.roleId
-                                    sysRoleMenu.menuId = menuId
-                                    add(sysRoleMenu)
-                                }
-                            }
+                val picked: List<RolePermNode> =
+                    roleMenuPolicy.menusFor(roleData.roleKey, permBridge)
+
+                val roleMenuData = buildList {
+                    for (node in picked) {
+                        val menuId =
+                            sysMenuData.find { it.perms == node.functionalPermission }?.menuId
+                        if (menuId != null) {
+                            add(SysRoleMenu().apply {
+                                roleId = roleData.roleId
+                                this.menuId = menuId
+                            })
                         }
-                        sysMenuRepository.insertRoleMenus(roleMenuData)
                     }
                 }
+                sysMenuRepository.insertRoleMenus(roleMenuData.toMutableList())
             }
     }
 
@@ -130,45 +73,25 @@ class SysMenuLogic @Inject constructor(
         return sysMenuRepository.getPermissionsByRoleIds(roleIds)
     }
 
-    /**
-     * 递归“插入或更新”一个枚举项,以及它的子节点。
-     *
-     * @param menuEnum 当前要处理的枚举项
-     * @param parentId 父菜单在数据库里的 menuId (顶层传 null)
-     * @return 返回在数据库里最终存在的 SysMenu 对象
-     */
-    private fun processMenuEnumRecursive(
-        menuEnum: RoleFunctionalPermissionsEnum,
-        parentId: Long?
-    ): SysMenu {
-        // 1. 查找数据库里是否已存在相同 perms 的菜单
-        val existing: SysMenu? = sysMenuRepository.findByPerms(menuEnum.functionalPermission)
-
-        val currentMenu: SysMenu = if (existing == null) {
-            // 不存在,构造一个新的实体并插入
+    /** 把递归函数的参数类型从枚举换成 Node 接口,逻辑不变 */
+    private fun processMenuNodeRecursive(menuNode: RolePermNode, parentId: Long?): SysMenu {
+        val existing = sysMenuRepository.findByPerms(menuNode.functionalPermission)
+        val current = if (existing == null) {
             val newMenu = SysMenu().apply {
-                menuName = I18nManager.t(menuEnum.description)
-                perms = menuEnum.functionalPermission
-                // 这里用 level 做 orderNum(仅示意),可根据业务自由调整
-                orderNum = menuEnum.level
+                menuName = I18nManager.t(menuNode.description)
+                perms = menuNode.functionalPermission
+                orderNum = menuNode.level
                 this.parentId = parentId
-                // 其它字段(path、component、isFrame、icon 等)可在此处赋值
-                // 例如:path = "/${menuEnum.functionalPermission.replace(":", "/")}"
             }
-            val newId: Long = sysMenuRepository.insert(newMenu)
+            val newId = sysMenuRepository.insert(newMenu)
             if (newId <= 0) {
-                // 如果 onConflict = IGNORE 导致插入被忽略(并发、重复等),则重新查询一次
-                sysMenuRepository.findByPerms(menuEnum.functionalPermission)
-                    ?: throw IllegalStateException(
-                        "插入 SysMenu(${menuEnum.functionalPermission}) 失败,且数据库中没有已存在记录"
-                    )
+                sysMenuRepository.findByPerms(menuNode.functionalPermission)
+                    ?: error("插入失败且未找到:${menuNode.functionalPermission}")
             } else {
-                // 插入成功,Room 会自动把自增主键分配给 newMenu.menuId
                 newMenu.menuId = newId
                 newMenu
             }
         } else {
-            // 已经存在,检查 parentId 是否需要更新
             if (existing.parentId != parentId) {
                 existing.parentId = parentId
                 sysMenuRepository.update(existing)
@@ -176,13 +99,8 @@ class SysMenuLogic @Inject constructor(
             existing
         }
 
-        // 2. 递归处理它的子节点
-        if (menuEnum.children.isNotEmpty()) {
-            menuEnum.children.forEach { childEnum ->
-                processMenuEnumRecursive(childEnum, parentId = currentMenu.menuId)
-            }
-        }
-
-        return currentMenu
+        // 递归子节点
+        menuNode.children.forEach { child -> processMenuNodeRecursive(child, current.menuId) }
+        return current
     }
 }

+ 3 - 5
data/src/main/java/com/grkj/data/domain/logic/impl/UserLogic.kt

@@ -3,7 +3,6 @@ package com.grkj.data.domain.logic.impl
 import com.grkj.data.data.MainDomainData
 import com.grkj.data.enums.LoginResultEnum
 import com.grkj.data.enums.RoleEnum
-import com.grkj.data.enums.RoleFunctionalPermissionsEnum
 import com.grkj.data.local.dos.SysUserCharacteristicDo
 import com.grkj.data.local.dos.SysUserDo
 import com.grkj.data.local.dos.SysUserRole
@@ -70,9 +69,7 @@ class UserLogic @Inject constructor(
             logger.info("卡号数据:${userCardList}")
             MainDomainData.roleKeys = roleDatas.joinToString(",") { it.roleKey }
             MainDomainData.permissions =
-                if (sysUserDo.nickName == "罗成") RoleFunctionalPermissionsEnum.values()
-                    .map { it.functionalPermission }.toMutableList() else
-                    sysMenuRepository.getPermissionsByRoleIds(roleDatas.map { it.roleId })
+                sysMenuRepository.getPermissionsByRoleIds(roleDatas.map { it.roleId })
             logger.info("用户信息:{}", MainDomainData.userInfo.toString())
             logger.info("用户角色:{}", MainDomainData.roleKeys)
             logger.info("用户权限:{}", MainDomainData.permissions)
@@ -189,7 +186,8 @@ class UserLogic @Inject constructor(
                 if (sysUserDo != null) {
                     // —— 缓存域数据 —— //
                     MainDomainData.userInfo = sysUserDo
-                    MainDomainData.userCardList = hardwareRepository.getIsJobCardByUserId(sysUserDo.userId)
+                    MainDomainData.userCardList =
+                        hardwareRepository.getIsJobCardByUserId(sysUserDo.userId)
                     MainDomainData.roleKeys =
                         roleRepository.getRoleDataByUserId(sysUserDo.userId)
                             .joinToString(",") { it.roleKey }

+ 2 - 2
data/src/main/java/com/grkj/data/domain/vo/UserManageFilterVo.kt

@@ -6,6 +6,6 @@ package com.grkj.data.domain.vo
 data class UserManageFilterVo(
     val nickname: String,
     val cardNfc: String,
-    val workstationName: String?,
-    val status: Boolean?
+    val workstationName: String? = null,
+    val status: Boolean? = null
 )

+ 16 - 0
data/src/main/java/com/grkj/data/enums/LoginModeEnum.kt

@@ -0,0 +1,16 @@
+package com.grkj.data.enums
+
+/**
+ * 登录模式
+ */
+enum class LoginModeEnum {
+    /**
+     * 用户模式
+     */
+    USER_MODE,
+
+    /**
+     * 管理员模式
+     */
+    ADMINISTRATOR_MODE;
+}

+ 24 - 0
data/src/main/java/com/grkj/data/enums/RFIDScanMode.kt

@@ -0,0 +1,24 @@
+package com.grkj.data.enums
+
+import com.grkj.data.data.MMKVConstants
+import com.grkj.data.hardware.IRFIDScanHelper
+import com.grkj.data.hardware.uhf.UHFRFIDScanHelper
+import com.sik.sikcore.extension.getMMKVData
+
+/**
+ * RFID扫描模式
+ */
+enum class RFIDScanMode(val iRfidScanHelper: IRFIDScanHelper) {
+    UHF(UHFRFIDScanHelper());
+
+    companion object {
+        /**
+         * 根据当前RFID扫描模式获取RFID扫描帮助类
+         */
+        fun getCurrentRFIDScanMode(): IRFIDScanHelper {
+            val currentRFIDScanMode =
+                valueOf(MMKVConstants.KEY_RFID_SCAN_MODE.getMMKVData(UHF.name))
+            return currentRFIDScanMode.iRfidScanHelper
+        }
+    }
+}

+ 5 - 0
data/src/main/java/com/grkj/data/hardware/IHardwareHelper.kt

@@ -247,4 +247,9 @@ interface IHardwareHelper {
      * 更新钥匙mac
      */
     fun updateKeyMac(addr: Int, idx: Int, macAddress: String)
+
+    /**
+     * 开门
+     */
+    suspend fun openDoor(left: Boolean? = null, right: Boolean? = null): Boolean
 }

+ 20 - 0
data/src/main/java/com/grkj/data/hardware/IRFIDScanHelper.kt

@@ -0,0 +1,20 @@
+package com.grkj.data.hardware
+
+/**
+ * RFID扫描帮助类
+ */
+interface IRFIDScanHelper {
+    /**
+     * 初始化
+     */
+    fun init()
+    /**
+     * 扫描物资
+     */
+    suspend fun scanMaterials():List<String>
+
+    /**
+     * 释放资源
+     */
+    fun release()
+}

+ 117 - 15
data/src/main/java/com/grkj/data/hardware/can/CanCommand.kt

@@ -93,17 +93,32 @@ object CanCommands {
     object Common {
         /** 版本 (R) → 0x6003/0x00, 4B: HW主,HW子,SW主,SW子 */
         fun getDeviceVersion(nodeId: Int): SdoRequest.Read =
-            SdoRequest.Read(nodeId, Command.VERSION, 0x00, timeoutMs = CustomCanConfig.instance.readTimeoutMs.toLong())
+            SdoRequest.Read(
+                nodeId,
+                Command.VERSION,
+                0x00,
+                timeoutMs = CustomCanConfig.instance.readTimeoutMs.toLong()
+            )
 
         /** 大多数设备复用的状态寄存器 (R) → 0x6010/0x00, 2B */
         fun getStatus(nodeId: Int): SdoRequest.Read =
-            SdoRequest.Read(nodeId, Command.STATUS, 0x00, timeoutMs = CustomCanConfig.instance.readTimeoutMs.toLong())
+            SdoRequest.Read(
+                nodeId,
+                Command.STATUS,
+                0x00,
+                timeoutMs = CustomCanConfig.instance.readTimeoutMs.toLong()
+            )
 
         /**
          * 获取设备类型
          */
         fun getDeviceType(nodeId: Int): SdoRequest.Read =
-            SdoRequest.Read(nodeId, Command.DEVICE_TYPE, 0x00, timeoutMs = CustomCanConfig.instance.readTimeoutMs.toLong())
+            SdoRequest.Read(
+                nodeId,
+                Command.DEVICE_TYPE,
+                0x00,
+                timeoutMs = CustomCanConfig.instance.readTimeoutMs.toLong()
+            )
     }
 
     /**
@@ -118,14 +133,26 @@ object CanCommands {
 
         /** 控制/状态 (R/W) 0x6011/0x00, 2B:写仅置相关位,其余写0;读回含工作位 */
         fun readControlReg(): SdoRequest.Read =
-            SdoRequest.Read(nodeId, Command.CONTROL_REG, 0x00, timeoutMs = CustomCanConfig.instance.readTimeoutMs.toLong())
+            SdoRequest.Read(
+                nodeId,
+                Command.CONTROL_REG,
+                0x00,
+                timeoutMs = CustomCanConfig.instance.readTimeoutMs.toLong()
+            )
 
         /** 设置左右卡扣(bit0=左卡扣,bit4=右卡扣) */
         fun setLatch(left: Boolean? = null, right: Boolean? = null): SdoRequest.Write {
             var v = 0
             if (left != null) v = v or ((if (left) 1 else 0) shl 0)
             if (right != null) v = v or ((if (right) 1 else 0) shl 4)
-            return SdoRequest.Write(nodeId, Command.CONTROL_REG, 0x00, shortLE(v, 0b0001_0001), 2, timeoutMs = CustomCanConfig.instance.readTimeoutMs.toLong())
+            return SdoRequest.Write(
+                nodeId,
+                Command.CONTROL_REG,
+                0x00,
+                shortLE(v, 0b0001_0001),
+                2,
+                timeoutMs = CustomCanConfig.instance.readTimeoutMs.toLong()
+            )
         }
 
         /** 设置左右充电(bit1=左充电,bit5=右充电) */
@@ -133,7 +160,14 @@ object CanCommands {
             var v = 0
             if (leftOn != null) v = v or ((if (leftOn) 1 else 0) shl 1)
             if (rightOn != null) v = v or ((if (rightOn) 1 else 0) shl 5)
-            return SdoRequest.Write(nodeId, Command.CONTROL_REG, 0x00, shortLE(v, 0b0010_0010), 2, timeoutMs = CustomCanConfig.instance.readTimeoutMs.toLong())
+            return SdoRequest.Write(
+                nodeId,
+                Command.CONTROL_REG,
+                0x00,
+                shortLE(v, 0b0010_0010),
+                2,
+                timeoutMs = CustomCanConfig.instance.readTimeoutMs.toLong()
+            )
         }
 
         /** 单侧卡扣语法糖:keySlotId: 0左/1右;status: 0解锁/1锁住 */
@@ -151,15 +185,33 @@ object CanCommands {
         }
 
         /** 左/右 RFID (R) 4B 小端(常见地址) */
-        fun getLeftRfid(): SdoRequest.Read = SdoRequest.Read(nodeId, Command.RFID, 0x00, timeoutMs = CustomCanConfig.instance.readTimeoutMs.toLong())
-        fun getRightRfid(): SdoRequest.Read = SdoRequest.Read(nodeId, Command.RIGHT_KEY_RFID, 0x00, timeoutMs = CustomCanConfig.instance.readTimeoutMs.toLong())
+        fun getLeftRfid(): SdoRequest.Read = SdoRequest.Read(
+            nodeId,
+            Command.RFID,
+            0x00,
+            timeoutMs = CustomCanConfig.instance.readTimeoutMs.toLong()
+        )
+
+        fun getRightRfid(): SdoRequest.Read = SdoRequest.Read(
+            nodeId,
+            Command.RIGHT_KEY_RFID,
+            0x00,
+            timeoutMs = CustomCanConfig.instance.readTimeoutMs.toLong()
+        )
 
         // ---- FiveLock / KeyCabinet(常见 1..5 位同构写法,寄存器通常与 0x6011 兼容) ----
 
         /** 一次写入 5 位控制(低5位有效),适配 5路/柜体同构 */
         fun setLatchBits_1to5(bits01to05: Int): SdoRequest.Write {
             val v = bits01to05 and 0b1_1111
-            return SdoRequest.Write(nodeId, Command.CONTROL_REG, 0x00, shortLE(v, 0b1_1111), 2, timeoutMs = CustomCanConfig.instance.readTimeoutMs.toLong())
+            return SdoRequest.Write(
+                nodeId,
+                Command.CONTROL_REG,
+                0x00,
+                shortLE(v, 0b1_1111),
+                2,
+                timeoutMs = CustomCanConfig.instance.readTimeoutMs.toLong()
+            )
         }
 
         /**
@@ -167,7 +219,14 @@ object CanCommands {
          */
         fun setLatchBits_1to5(target: Int, isOpen: Boolean): SdoRequest.Write {
             val v = if (isOpen) 0b0_0000 else 0b1_1111
-            return SdoRequest.Write(nodeId, Command.CONTROL_REG, 0x00, shortLE(v, target), 2, timeoutMs = CustomCanConfig.instance.readTimeoutMs.toLong())
+            return SdoRequest.Write(
+                nodeId,
+                Command.CONTROL_REG,
+                0x00,
+                shortLE(v, target),
+                2,
+                timeoutMs = CustomCanConfig.instance.readTimeoutMs.toLong()
+            )
         }
 
         /** 单位控制(1..5) */
@@ -183,18 +242,43 @@ object CanCommands {
             )
         }
 
+        /** 开门控制 */
+        fun controlDoorOpen(left: Boolean?, right: Boolean?): SdoRequest.Write {
+            var v = 0
+            if (left != null) v = v or (1 shl 0)
+            if (right != null) v = v or (1 shl 4)
+            return SdoRequest.Write(
+                nodeId,
+                Command.CONTROL_REG,
+                0x00,
+                shortLE(v, v),
+                2
+            )
+        }
+
         /** 1..5 位 RFID 常见映射:0x6020..0x6024 */
         fun getSlotRfid_1to5(slotIndex1to5: Int): SdoRequest.Read {
             require(slotIndex1to5 in 1..5) { "slotIndex must be 1..5" }
-            return SdoRequest.Read(nodeId, Command.RFID, 0x00 + (slotIndex1to5 - 1), timeoutMs = CustomCanConfig.instance.readTimeoutMs.toLong())
+            return SdoRequest.Read(
+                nodeId,
+                Command.RFID,
+                0x00 + (slotIndex1to5 - 1),
+                timeoutMs = CustomCanConfig.instance.readTimeoutMs.toLong()
+            )
         }
 
         // ---- MaterialCabinet(RGB/温湿度扩展) ----
 
         /** RGB 状态灯 (R/W) 0x6016/0x00, 4B: B[0..7],G[8..15],R[16..23], 模式[24..26], 时间[27..29], 单位[30], 锁定[31] */
-        fun getRgb(): SdoRequest.Read = SdoRequest.Read(nodeId, Command.LED_STRIP_CONTROL, 0x00, timeoutMs = CustomCanConfig.instance.readTimeoutMs.toLong())
+        fun getRgb(): SdoRequest.Read = SdoRequest.Read(
+            nodeId,
+            Command.LED_STRIP_CONTROL,
+            0x00,
+            timeoutMs = CustomCanConfig.instance.readTimeoutMs.toLong()
+        )
 
         fun setRgb(
+            leftLed: Boolean,
             r: Int, g: Int, b: Int,      // 0..255
             mode: Int,                    // 0关/1常亮/2闪烁/3呼吸/4流水
             timeStep: Int,                // 0..7(实际=+1)
@@ -214,12 +298,30 @@ object CanCommands {
             v = v or ((tt and 0x07) shl 27)
             v = v or ((if (secondsUnit) 1 else 0) shl 30)
             v = v or ((if (lockControl) 1 else 0) shl 31)
-            return SdoRequest.Write(nodeId, Command.LED_STRIP_CONTROL, 0x00, intLE(v), 4, timeoutMs = CustomCanConfig.instance.readTimeoutMs.toLong())
+            return SdoRequest.Write(
+                nodeId,
+                Command.LED_STRIP_CONTROL,
+                if (leftLed) 0x00 else 0x01,
+                intLE(v),
+                4,
+                timeoutMs = CustomCanConfig.instance.readTimeoutMs.toLong()
+            )
         }
 
         /** 温湿度(常见扩展) */
-        fun getTemperature(): SdoRequest.Read = SdoRequest.Read(nodeId, Command.TEMPERATURE, 0x00, timeoutMs = CustomCanConfig.instance.readTimeoutMs.toLong())
-        fun getHumidity(): SdoRequest.Read = SdoRequest.Read(nodeId, Command.HUMIDITY, 0x00, timeoutMs = CustomCanConfig.instance.readTimeoutMs.toLong())
+        fun getTemperature(): SdoRequest.Read = SdoRequest.Read(
+            nodeId,
+            Command.TEMPERATURE,
+            0x00,
+            timeoutMs = CustomCanConfig.instance.readTimeoutMs.toLong()
+        )
+
+        fun getHumidity(): SdoRequest.Read = SdoRequest.Read(
+            nodeId,
+            Command.HUMIDITY,
+            0x00,
+            timeoutMs = CustomCanConfig.instance.readTimeoutMs.toLong()
+        )
 
         // ---- 通用状态 ----
         fun getStatus(): SdoRequest.Read = Common.getStatus(nodeId)

+ 14 - 1
data/src/main/java/com/grkj/data/hardware/can/CanHardwareHelper.kt

@@ -7,6 +7,7 @@ import com.grkj.data.net.res.CabinetSlotsRecord
 import com.grkj.data.utils.event.StartListenerEvent
 import com.grkj.shared.utils.extension.toHexFromLe
 import com.grkj.shared.utils.i18n.I18nManager
+import com.sik.comm.impl_can.SdoOp
 import com.sik.comm.impl_can.toCommMessage
 import com.sik.sikcore.thread.ThreadUtils
 import org.slf4j.Logger
@@ -133,6 +134,18 @@ class CanHardwareHelper : IHardwareHelper {
         return getLockDockData().flatMap { it.lockData }.any { it.newHardware }
     }
 
+    override suspend fun openDoor(left: Boolean?, right: Boolean?): Boolean {
+        val materialCabinets =
+            CanHelper.getDeviceByDeviceType(CanDeviceConst.DEVICE_MATERIAL_CABINET_CONTROL_BOARD)
+        return if (materialCabinets.isNotEmpty()) {
+            val req = CanCommands.forDevice(materialCabinets.map { it.key }[0])
+                .controlDoorOpen(left, right)
+            CanHelper.writeTo(req)?.op != SdoOp.ERROR
+        } else {
+            false
+        }
+    }
+
     override fun getNewLockRFID(): List<String> {
         return getLockDockData().flatMap { it.lockData }.filter { it.newHardware }.map { it.rfid }
     }
@@ -232,7 +245,7 @@ class CanHardwareHelper : IHardwareHelper {
         val keys = CanHelper.getDeviceByDeviceType(CanDeviceConst.DEVICE_KEY_DOCK)
         keys.forEach {
             val req = CanCommands.forDevice(it.key).setCharge(false, false)
-            CanHelper.writeTo(req)
+            CanHelper.writeTo(req){}
         }
     }
 

+ 15 - 0
data/src/main/java/com/grkj/data/hardware/can/CanHelper.kt

@@ -198,6 +198,21 @@ object CanHelper {
         }
     }
 
+    /**
+     * 写入同步
+     */
+    suspend fun writeTo(req: SdoRequest.Write): SdoResponse.WriteAck? {
+        return runCatching {
+            ProtocolManager.getProtocol(CustomCanConfig.instance.deviceId)
+                .send(CustomCanConfig.instance.deviceId, req.toCommMessage())
+        }.onSuccess { rsp ->
+            rsp
+        }.onFailure {
+            logger.info("写入失败:${it}")
+            null
+        }.getOrNull()?.toSdoResponse() as? SdoResponse.WriteAck
+    }
+
     /**
      * 根据mac获取钥匙
      */

+ 23 - 15
data/src/main/java/com/grkj/data/hardware/can/CanReadyPlugin.kt

@@ -83,13 +83,15 @@ class CanReadyPlugin : CommPlugin {
                     val cmds = CanCommands.forDevice(nodeId)
 
                     // 1) 读状态 0x6010/00 (2B)
-                    safeRead(cmds.getStatus())?.let { rd ->
-                        deviceStatusListener.forEach {
-                            it.value.deviceStatus(
-                                nodeId,
-                                rd.index,
-                                rd.payload
-                            )
+                    if (CanHelper.getDeviceTypeByNodeId(nodeId) != CanDeviceConst.DEVICE_MATERIAL_CABINET_CONTROL_BOARD) {
+                        safeRead(cmds.getStatus())?.let { rd ->
+                            deviceStatusListener.forEach {
+                                it.value.deviceStatus(
+                                    nodeId,
+                                    rd.index,
+                                    rd.payload
+                                )
+                            }
                         }
                     }
                     safeRead(cmds.readControlReg())?.let { rd ->
@@ -119,13 +121,15 @@ class CanReadyPlugin : CommPlugin {
                         val cmds = CanCommands.forDevice(nodeId)
 
                         // 1) 读状态 0x6010/00 (2B)
-                        safeRead(cmds.getStatus())?.let { rd ->
-                            deviceStatusListener.forEach {
-                                it.value.deviceStatus(
-                                    nodeId,
-                                    rd.index,
-                                    rd.payload
-                                )
+                        if (CanHelper.getDeviceTypeByNodeId(nodeId) != CanDeviceConst.DEVICE_MATERIAL_CABINET_CONTROL_BOARD) {
+                            safeRead(cmds.getStatus())?.let { rd ->
+                                deviceStatusListener.forEach {
+                                    it.value.deviceStatus(
+                                        nodeId,
+                                        rd.index,
+                                        rd.payload
+                                    )
+                                }
                             }
                         }
                         safeRead(cmds.readControlReg())?.let { rd ->
@@ -247,7 +251,11 @@ class CanReadyPlugin : CommPlugin {
                                                 ToastEvent.sendToastEvent(I18nManager.t("get_key_info_fail"))
                                             }
                                             HardwareMode.getCurrentHardwareMode()
-                                                .controlKeyLockAndCharge(true, key.idx, dockBean.addr)
+                                                .controlKeyLockAndCharge(
+                                                    true,
+                                                    key.idx,
+                                                    dockBean.addr
+                                                )
                                         }
                                         val isKeyReady =
                                             HardwareMode.getCurrentHardwareMode().getKeyDockData()

+ 4 - 0
data/src/main/java/com/grkj/data/hardware/modbus/ModBusHardwareHelper.kt

@@ -125,6 +125,10 @@ class ModBusHardwareHelper : IHardwareHelper {
         ModBusController.updateKeyRfid(addr.toByte(), idx, macAddress)
     }
 
+    override suspend fun openDoor(left: Boolean?, right: Boolean?): Boolean {
+        return true
+    }
+
     override fun updateKeyRfid(addr: Int, idx: Int, rfid: String) {
         ModBusController.updateKeyRfid(addr.toByte(), idx, rfid)
     }

+ 135 - 0
data/src/main/java/com/grkj/data/hardware/uhf/UHFRFIDScanHelper.kt

@@ -0,0 +1,135 @@
+package com.grkj.data.hardware.uhf
+
+import com.grkj.data.hardware.IRFIDScanHelper
+import com.grkj.data.utils.event.ToastEvent
+import com.grkj.shared.utils.i18n.I18nManager
+import com.uhf.api.cls.Reader
+import com.uhf.api.cls.Reader.READER_ERR
+import com.uhf.api.cls.Reader.TAGINFO
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
+
+
+/**
+ * UHF
+ * 久佰特物资柜RFID资料
+ */
+class UHFRFIDScanHelper : IRFIDScanHelper {
+    private val logger: Logger = LoggerFactory.getLogger(UHFRFIDScanHelper::class.java)
+
+    companion object {
+        /**
+         * 串口地址
+         */
+        private const val ADDRESS: String = "/dev/USB"
+
+        /**
+         * 串口波特率
+         */
+        private const val ANT_PORT: Int = 921600
+
+        /**
+         * 天线数量
+         */
+        private const val ANT_SCNT: Int = 4
+
+        /**
+         * 盘点超时时间
+         */
+        private const val TIMEOUT = 200
+
+        /**
+         *
+         */
+        private var ants: IntArray = intArrayOf(1)
+
+        /**
+         *
+         */
+        private var tagCnt: IntArray = IntArray(1)
+
+        /**
+         *
+         */
+        private var err: READER_ERR? = null
+    }
+
+    /**
+     * 是否初始化完成
+     */
+    private var isInit: Boolean = false
+
+    /**
+     * 阅读器
+     */
+    val reader: Reader by lazy { Reader() }
+    override fun init() {
+        val errCode = reader.InitReader_Notype("$ADDRESS:$ANT_PORT", ANT_SCNT)
+        if (errCode != Reader.READER_ERR.MT_OK_ERR) {
+            logger.info("读取器初始化失败:${errCode}")
+            return
+        } else {
+            isInit = true
+        }
+        printRFIDScanData()
+        setParams()
+    }
+
+    private fun printRFIDScanData() {
+        val version = Reader.GetSDKVersion()
+        logger.info("版本号:${version}")
+    }
+
+    /**
+     * 设置参数
+     */
+    private fun setParams() {
+        val sessionParams = intArrayOf(1)
+        val qParams = intArrayOf(-1)
+        reader.ParamSet(Reader.Mtr_Param.MTR_PARAM_POTL_GEN2_SESSION, sessionParams)
+        reader.ParamSet(Reader.Mtr_Param.MTR_PARAM_POTL_GEN2_Q, qParams)
+    }
+
+    override suspend fun scanMaterials(): List<String> {
+        val materialsRfidSet = mutableSetOf<String>()
+        repeat(10) {
+            err = reader.TagInventory_Raw(ants, ants.size, TIMEOUT.toShort(), tagCnt)
+            if (err === READER_ERR.MT_OK_ERR) {
+                println("invenotry ---------------- tagcnt:" + tagCnt[0])
+                for (j in 0..<tagCnt[0]) {
+                    val tag_: TAGINFO = reader.TAGINFO()
+                    err = reader.GetNextTag(tag_)
+                    // 获取缓冲区标签一旦出错,不可以继续获取,重新盘点标签。
+                    // if failed when called getnexttag,you should inventory again.
+                    if (err !== READER_ERR.MT_OK_ERR) break
+                    materialsRfidSet.add(Reader.bytes_Hexstr(tag_.EpcId))
+                    logger.info(
+                        ("epcid:" + Reader.bytes_Hexstr(tag_.EpcId) + " ant:" + tag_.AntennaID + "  fre:"
+                                + tag_.Frequency + "  rssi:" + tag_.RSSI)
+                    )
+                }
+            }
+            if (err !== READER_ERR.MT_OK_ERR) {
+                logger.info("inventory tags err:" + err)
+            }
+        }
+        return materialsRfidSet.toList()
+    }
+
+    override fun release() {
+        if (checkInit()) {
+            reader.CloseReader()
+        }
+    }
+
+    /**
+     * 检查是否初始化
+     */
+    private fun checkInit(): Boolean {
+        if (!isInit) {
+            ToastEvent.sendToastEvent(I18nManager.t("rfid_helper_not_init"))
+            return false
+        }
+        return true
+    }
+}

+ 132 - 0
data/src/main/java/com/grkj/data/local/dao/MaterialsDao.kt

@@ -0,0 +1,132 @@
+package com.grkj.data.local.dao
+
+import androidx.room.Dao
+import androidx.room.Insert
+import androidx.room.OnConflictStrategy
+import androidx.room.Query
+import androidx.room.Update
+import com.grkj.data.local.dos.IsMaterials
+import com.grkj.data.local.dos.IsMaterialsInstructions
+import com.grkj.data.local.dos.IsMaterialsLoan
+import com.grkj.data.local.dos.IsMaterialsType
+
+/**
+ * 物资表
+ */
+@Dao
+interface MaterialsDao {
+
+    /**
+     * 新增物资类型
+     */
+    @Insert(onConflict = OnConflictStrategy.REPLACE)
+    fun insertMaterialsType(materialsType: IsMaterialsType)
+
+    /**
+     * 更新物资类型q
+     */
+    @Update(onConflict = OnConflictStrategy.REPLACE)
+    fun updateMaterialsType(materialsType: IsMaterialsType)
+
+    /**
+     * 获取物资类型
+     */
+    @Query("select * from is_materials_type where del_flag = 0")
+    fun getMaterialsType(): List<IsMaterialsType>
+
+    /**
+     * 删除物资类型
+     */
+    @Query("update is_materials_type set del_flag = 1 where materials_type_id in (:materialTypeIds)")
+    fun deleteMaterialsType(materialTypeIds: List<Long>)
+
+    /**
+     * 检查是否有正在使用的物资类型
+     */
+    @Query(
+        """
+        select count(*) from is_materials_loan iml
+        left join is_materials im on iml.materials_id = im.materials_id
+        left join is_materials_type imt on im.materials_type_id = imt.materials_type_id
+        where iml.status = 0 and iml.del_flag = 0 and imt.materials_type_id in (:materialsTypeId)
+    """
+    )
+    fun checkMaterialsTypeInBorrowed(materialsTypeId: List<Long>): Int
+
+    /**
+     * 新增物资
+     */
+    @Insert(onConflict = OnConflictStrategy.REPLACE)
+    fun insertMaterials(materials: IsMaterials)
+
+    /**
+     * 更新物资
+     */
+    @Update(onConflict = OnConflictStrategy.REPLACE)
+    fun updateMaterials(materials: IsMaterials)
+
+    /**
+     * 查询物资
+     */
+    @Query("select * from is_materials where del_flag = 0")
+    fun getMaterials(): List<IsMaterials>
+
+    /**
+     * 删除物资
+     */
+    @Query("update is_materials set del_flag = 1 where materials_id in (:materialIds)")
+    fun deleteMaterials(materialIds: List<Long>)
+
+    /**
+     * 检查是否有正在使用的物资
+     */
+    @Query(
+        """
+        select count(*) from is_materials_loan iml
+        left join is_materials im on iml.materials_id = im.materials_id
+        where iml.status = 0 and iml.del_flag = 0 and im.materials_id in (:materialsId)
+    """
+    )
+    fun checkMaterialsInBorrowed(materialsId: List<Long>): Int
+
+    /**
+     * 新增领取物资
+     */
+    @Insert(onConflict = OnConflictStrategy.REPLACE)
+    fun addMaterialsLoan(materialsLoan: List<IsMaterialsLoan>)
+
+    /**
+     * 更新领取物资
+     */
+    @Update(onConflict = OnConflictStrategy.REPLACE)
+    fun updateMaterialsLoan(materialsLoan: List<IsMaterialsLoan>)
+
+    /**
+     * 获取借出物资
+     */
+    @Query(
+        """
+        select * from is_materials_loan iml
+        where del_flag = 0 and iml.restitution_required = 0 and iml.status = 0
+    """
+    )
+    fun getMaterialsLoan(): List<IsMaterialsLoan>
+
+    /**
+     * 批量更新物资
+     */
+    @Update(onConflict = OnConflictStrategy.REPLACE)
+    fun updateMaterials(isMaterials: List<IsMaterials>)
+
+    /**
+     * 批量新增物资
+     */
+    @Insert(onConflict = OnConflictStrategy.REPLACE)
+    fun insertMaterials(materials: List<IsMaterials>)
+
+    /**
+     * 获取物资说明列表
+     */
+    @Query("select * from is_materials_instructions where del_flag = 0")
+    fun getMaterialsInstructionList(): List<IsMaterialsInstructions>
+}

+ 20 - 4
data/src/main/java/com/grkj/data/local/database/ISCSDatabase.kt

@@ -13,6 +13,7 @@ import com.grkj.data.local.dao.HardwareDao
 import com.grkj.data.local.dao.IsSopDao
 import com.grkj.data.local.dao.IsolationPointDao
 import com.grkj.data.local.dao.JobTicketDao
+import com.grkj.data.local.dao.MaterialsDao
 import com.grkj.data.local.dao.RfidTokenDao
 import com.grkj.data.local.dao.RoleDao
 import com.grkj.data.local.dao.SysMenuDao
@@ -47,7 +48,11 @@ import android.database.sqlite.SQLiteDatabase as SysDB
         IsKey::class, IsLock::class, IsLockCabinet::class, IsLockCabinetSlots::class, IsLocksetType::class, IsLockset::class,
         SysMenu::class, SysRoleMenu::class,
         WorkflowStep::class, WorkflowMode::class, WorkflowStepTemplate::class, IsMapPoint::class,
-                IsExceptionStandard ::class, IsExceptionSourceStandard::class, IsSopGroup::class, IsJobTicketGroup::class, IsSopWorkflowStep::class
+        IsExceptionStandard::class, IsExceptionSourceStandard::class, IsSopGroup::class, IsJobTicketGroup::class, IsSopWorkflowStep::class, IsMaterials::class,
+        IsMaterialsCabinet::class, IsMaterialsChangeRecord::class, IsMaterialsCheckPlan::class,
+        IsMaterialsCheckRecord::class, IsMaterialsInstructions::class, IsMaterialsLoan::class,
+        IsMaterialsPlanCabinet::class, IsMaterialsProperty::class, IsMaterialsPropertyValue::class,
+        IsMaterialsReminder::class, IsMaterialsRestitutionRules::class, IsMaterialsType::class
     ],
     version = ISCSMigrations.VERSION,
     exportSchema = true
@@ -66,6 +71,10 @@ abstract class ISCSDatabase : RoomDatabase() {
     abstract fun sysMenuDao(): SysMenuDao
     abstract fun workflowStepDao(): WorkflowDao
     abstract fun exceptionDao(): ExceptionDao
+    /**
+     * 物资表
+     */
+    abstract fun materialsDao(): MaterialsDao
 
     companion object {
         const val DB_NAME = "iscs_database.db"
@@ -76,9 +85,14 @@ abstract class ISCSDatabase : RoomDatabase() {
 
         // 外部存储目录路径
         val EXTERNAL_DB_FILE: File by lazy {
-            File(Environment.getExternalStorageDirectory(), "ISCS/database/$DB_NAME")
+            File(Environment.getExternalStorageDirectory(), "${DB_FOLDER}$DB_NAME")
         }
 
+        /**
+         * DB文件目录
+         */
+        var DB_FOLDER: String ="ISCS/database/"
+
         @Volatile
         private var _instance: ISCSDatabase? = null
 
@@ -98,7 +112,7 @@ abstract class ISCSDatabase : RoomDatabase() {
             val parentDir = EXTERNAL_DB_FILE.parentFile
             if (parentDir != null && !parentDir.exists()) {
                 if (!parentDir.mkdirs()) {
-                    Log.e("ISCSDatabase", "无法创建目录: ${parentDir.absolutePath}")
+                    logger.error("ISCSDatabase,无法创建目录: ${parentDir.absolutePath}")
                 } else {
                     logger.info("创建目录: ${parentDir.absolutePath}")
                 }
@@ -139,7 +153,9 @@ abstract class ISCSDatabase : RoomDatabase() {
             // 4) SQLCipher Hook
             val passphrase: ByteArray = AESConfig.defaultConfig.key()
             val hook = object : SQLiteDatabaseHook {
-                override fun preKey(connection: SQLiteConnection) { /* no-op */ }
+                override fun preKey(connection: SQLiteConnection) { /* no-op */
+                }
+
                 override fun postKey(connection: SQLiteConnection) {
                     connection.execute("PRAGMA foreign_keys=ON", null, null)
                     connection.executeForString("PRAGMA journal_mode=WAL", null, null)

+ 22 - 1
data/src/main/java/com/grkj/data/local/database/ISCSMigrations.kt

@@ -11,7 +11,7 @@ object ISCSMigrations {
     /**
      * 版本号
      */
-    const val VERSION = 7
+    const val VERSION = 8
 
     /**
      * 升级数据
@@ -24,6 +24,7 @@ object ISCSMigrations {
             migration4To5,
             migration5To6,
             migration6To7,
+            migration7To8,
         )
     }
 
@@ -218,4 +219,24 @@ object ISCSMigrations {
             )
         }
     }
+    /**
+     * 新增物资相关的表
+     */
+    var migration7To8 = object : Migration(7, 8) {
+        override fun migrate(db: SupportSQLiteDatabase) {
+            db.execSQL("CREATE TABLE IF NOT EXISTS `is_materials` (`materials_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `materials_code` TEXT, `materials_name` TEXT, `materials_type_id` INTEGER, `workarea_id` INTEGER, `materials_cabinet_id` INTEGER, `service_life` TEXT, `available_life` TEXT, `service_times` INTEGER, `available_times` INTEGER, `expiration_date` TEXT, `materials_rfid` TEXT, `supplier` TEXT, `start_time` TEXT, `properties` TEXT, `loan_state` TEXT NOT NULL DEFAULT '1', `status` TEXT NOT NULL DEFAULT '0', `del_flag` TEXT NOT NULL DEFAULT '0', `create_by` TEXT, `create_time` TEXT, `update_by` TEXT, `update_time` TEXT, `remark` TEXT)")
+            db.execSQL("CREATE TABLE IF NOT EXISTS `is_materials_cabinet` (`cabinet_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `cabinet_code` TEXT NOT NULL, `cabinet_name` TEXT NOT NULL, `hardware_id` INTEGER, `workarea_id` INTEGER, `workstation_id` INTEGER, `cabinet_icon` TEXT, `cabinet_picture` TEXT, `del_flag` TEXT NOT NULL DEFAULT '0', `status` TEXT NOT NULL DEFAULT '0', `create_by` TEXT, `create_time` TEXT, `update_by` TEXT, `update_time` TEXT, `remark` TEXT)")
+            db.execSQL("CREATE TABLE IF NOT EXISTS `is_materials_change_record` (`change_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `check_record_id` INTEGER, `old_materials_id` INTEGER, `old_materials_rfid` TEXT, `new_materials_id` INTEGER, `new_materials_rfid` TEXT, `change_user_id` INTEGER, `change_date` TEXT, `change_type` TEXT, `operate_type` TEXT, `status` TEXT, `del_flag` TEXT NOT NULL DEFAULT '0', `create_by` TEXT, `create_time` TEXT, `update_by` TEXT, `update_time` TEXT)")
+            db.execSQL("CREATE TABLE IF NOT EXISTS `is_materials_check_plan` (`plan_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `plan_name` TEXT, `workstation_id` INTEGER, `plan_date` TEXT NOT NULL, `check_user_id` INTEGER, `status` TEXT NOT NULL DEFAULT '0', `del_flag` TEXT NOT NULL DEFAULT '0', `create_by` TEXT, `create_time` TEXT, `update_by` TEXT, `update_time` TEXT, `remark` TEXT)")
+            db.execSQL("CREATE TABLE IF NOT EXISTS `is_materials_check_record` (`record_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `plan_id` INTEGER NOT NULL, `workstation_id` INTEGER, `cabinet_id` INTEGER, `materials_id` INTEGER NOT NULL, `check_user_id` INTEGER, `check_date` TEXT, `status` TEXT, `reason` TEXT, `measure` TEXT, `del_flag` TEXT NOT NULL DEFAULT '0', `create_by` TEXT, `create_time` TEXT, `update_by` TEXT, `update_time` TEXT, `remark` TEXT)")
+            db.execSQL("CREATE TABLE IF NOT EXISTS `is_materials_instructions` (`instructions_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `instructions_title` TEXT, `materials_type_id` INTEGER, `file_url` TEXT, `file_url_img` TEXT, `file_path_img` TEXT, `file_type` TEXT, `order_num` INTEGER NOT NULL DEFAULT 1, `status` TEXT NOT NULL DEFAULT '0', `del_flag` TEXT NOT NULL DEFAULT '0', `create_by` TEXT, `create_time` TEXT, `update_by` TEXT, `update_time` TEXT, `remark` TEXT)")
+            db.execSQL("CREATE TABLE IF NOT EXISTS `is_materials_loan` (`materials_loan_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `materials_id` INTEGER NOT NULL, `loan_user_id` INTEGER NOT NULL, `loan_from_id` INTEGER, `loan_time` TEXT, `reminder_time` TEXT, `restitution_user_id` INTEGER, `restitution_to_id` INTEGER, `restitution_time` TEXT, `actual_restitution_time` TEXT, `timeout_alarm` TEXT, `restitution_required` INTEGER NOT NULL DEFAULT 1, `del_flag` TEXT NOT NULL DEFAULT '0', `status` TEXT NOT NULL DEFAULT '0', `create_by` TEXT, `create_time` TEXT, `update_by` TEXT, `update_time` TEXT, `remark` TEXT)")
+            db.execSQL("CREATE TABLE IF NOT EXISTS `is_materials_plan_cabinet` (`plan_id` INTEGER NOT NULL, `cabinet_id` INTEGER NOT NULL, `check_user_id` INTEGER, `signature_img` TEXT, `signature_time` TEXT, `submit` TEXT NOT NULL DEFAULT '0', `status` TEXT NOT NULL DEFAULT '0', PRIMARY KEY(`plan_id`, `cabinet_id`))")
+            db.execSQL("CREATE TABLE IF NOT EXISTS `is_materials_property` (`property_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `property_name` TEXT, `del_flag` TEXT NOT NULL DEFAULT '0', `status` TEXT NOT NULL DEFAULT '0', `create_by` TEXT, `create_time` TEXT, `update_by` TEXT, `update_time` TEXT, `remark` TEXT)")
+            db.execSQL("CREATE TABLE IF NOT EXISTS `is_materials_property_value` (`record_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `property_id` INTEGER NOT NULL, `value_name` TEXT, `del_flag` TEXT NOT NULL DEFAULT '0', `status` TEXT NOT NULL DEFAULT '0', `create_by` TEXT, `create_time` TEXT, `update_by` TEXT, `update_time` TEXT, `remark` TEXT)")
+            db.execSQL("CREATE TABLE IF NOT EXISTS `is_materials_reminder` (`record_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `materials_loan_id` INTEGER NOT NULL, `reminder_type` INTEGER, `read_flag` INTEGER NOT NULL DEFAULT 0, `del_flag` TEXT NOT NULL DEFAULT '0', `create_by` TEXT, `create_time` TEXT, `update_by` TEXT, `update_time` TEXT, `remark` TEXT)")
+            db.execSQL("CREATE TABLE IF NOT EXISTS `is_materials_restitution_rules` (`rule_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `materials_type_id` INTEGER NOT NULL, `restitution_required` INTEGER NOT NULL DEFAULT 1, `restoration_required` INTEGER, `loan_duration` INTEGER, `reminder_time` INTEGER, `timeout_alarm` INTEGER, `del_flag` TEXT NOT NULL DEFAULT '0', `create_by` TEXT, `create_time` TEXT, `update_by` TEXT, `update_time` TEXT, `remark` TEXT)")
+            db.execSQL("CREATE TABLE IF NOT EXISTS `is_materials_type` (`materials_type_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `materials_type_code` TEXT, `materials_type_name` TEXT NOT NULL, `parent_id` INTEGER NOT NULL DEFAULT 0, `ancestors` TEXT NOT NULL, `enable_flag` TEXT NOT NULL DEFAULT 'Y', `service_life` TEXT, `available_life` TEXT, `service_times` INTEGER, `available_times` INTEGER, `materials_type_icon` TEXT, `materials_type_picture` TEXT, `check_standard` TEXT, `del_flag` TEXT NOT NULL DEFAULT '0', `status` TEXT, `property_ids` TEXT, `create_by` TEXT, `create_time` TEXT, `update_by` TEXT, `update_time` TEXT, `remark` TEXT)")
+        }
+    }
 }

+ 102 - 0
data/src/main/java/com/grkj/data/local/dos/IsMaterials.kt

@@ -0,0 +1,102 @@
+package com.grkj.data.local.dos
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+import com.grkj.data.utils.ExcelColumn
+import com.grkj.data.utils.ExcelIgnore
+import com.grkj.data.utils.ExcelSheet
+
+/**
+ * 物资表
+ */
+@ExcelSheet(name = "materials")
+@Entity(tableName = "is_materials")
+open class IsMaterials : BaseBean() {
+
+    @ExcelIgnore
+    @PrimaryKey(autoGenerate = true)
+    @ColumnInfo(name = "materials_id")
+    var materialsId: Long = 0L
+
+    @ExcelIgnore
+    @ColumnInfo(name = "materials_code")
+    var materialsCode: String? = null
+
+    /** 物资名称 */
+    @ExcelColumn(header = "materials_name", order = 0)
+    @ColumnInfo(name = "materials_name")
+    var materialsName: String? = null
+
+    /** 物资类型id */
+    @ExcelColumn(header = "materials_type_id", order = 1)
+    @ColumnInfo(name = "materials_type_id")
+    var materialsTypeId: Long? = null
+
+    @ExcelIgnore
+    @ColumnInfo(name = "workarea_id")
+    var workAreaId: Long? = null
+
+    @ExcelIgnore
+    @ColumnInfo(name = "materials_cabinet_id")
+    var materialsCabinetId: Long? = null
+
+    /** 可用寿命 */
+    @ExcelColumn(header = "service_life", order = 2)
+    @ColumnInfo(name = "service_life")
+    var serviceLife: String? = null
+
+    /** 剩余寿命 */
+    @ExcelColumn(header = "available_life", order = 3)
+    @ColumnInfo(name = "available_life")
+    var availableLife: String? = null
+
+    /** 可用次数 */
+    @ExcelColumn(header = "service_times", order = 4)
+    @ColumnInfo(name = "service_times")
+    var serviceTimes: Int? = null
+
+    /** 剩余次数 */
+    @ExcelColumn(header = "available_times", order = 5)
+    @ColumnInfo(name = "available_times")
+    var availableTimes: Int? = null
+
+    /** 有效期 */
+    @ExcelColumn(header = "expiration_date", order = 6)
+    @ColumnInfo(name = "expiration_date")
+    var expirationDate: String? = null
+
+    /** 物资RFID */
+    @ExcelColumn(header = "materials_rfid", order = 7)
+    @ColumnInfo(name = "materials_rfid")
+    var materialsRfid: String? = null
+
+    /** 供应商 */
+    @ExcelColumn(header = "supplier", order = 8)
+    @ColumnInfo(name = "supplier")
+    var supplier: String? = null
+
+    /** 启用时间 */
+    @ExcelColumn(header = "start_time", order = 9)
+    @ColumnInfo(name = "start_time")
+    var startTime: String? = null
+
+    @ExcelIgnore
+    @ColumnInfo(name = "properties")
+    var properties: String? = null
+
+    /** 借还状态(0-借出 1-柜中),默认 1 */
+    @ExcelIgnore
+    @ColumnInfo(name = "loan_state", defaultValue = "1")
+    var loanState: String = "1"
+
+    /** 物资状态(0正常 1损坏 2过期 3放错柜子),默认 0 */
+    @ExcelIgnore
+    @ColumnInfo(name = "status", defaultValue = "0")
+    var status: String = "0"
+
+    /** 删除标志(0存在 2删除),默认 0 */
+    @ExcelIgnore
+    @ColumnInfo(name = "del_flag", defaultValue = "0")
+    var delFlag: String = "0"
+}

+ 53 - 0
data/src/main/java/com/grkj/data/local/dos/IsMaterialsCabinet.kt

@@ -0,0 +1,53 @@
+package com.grkj.data.local.dos
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+
+/**
+ * 物资柜表
+ */
+@Entity(tableName = "is_materials_cabinet")
+open class IsMaterialsCabinet : BaseBean() {
+
+    /** 物资柜ID (PK, 自增) */
+    @PrimaryKey(autoGenerate = true)
+    @ColumnInfo(name = "cabinet_id")
+    var cabinetId: Long = 0L
+
+    /** 物资柜编号(NOT NULL) */
+    @ColumnInfo(name = "cabinet_code")
+    var cabinetCode: String = ""
+
+    /** 物资柜名称(NOT NULL) */
+    @ColumnInfo(name = "cabinet_name")
+    var cabinetName: String = ""
+
+    /** 硬件ID */
+    @ColumnInfo(name = "hardware_id")
+    var hardwareId: Long? = null
+
+    /** 区域ID */
+    @ColumnInfo(name = "workarea_id")
+    var workareaId: Long? = null
+
+    /** 所属岗位ID */
+    @ColumnInfo(name = "workstation_id")
+    var workstationId: Long? = null
+
+    /** 物资柜图标 */
+    @ColumnInfo(name = "cabinet_icon")
+    var cabinetIcon: String? = null
+
+    /** 物资柜略图 */
+    @ColumnInfo(name = "cabinet_picture")
+    var cabinetPicture: String? = null
+
+    /** 删除标志(0存在 2删除),默认 0 */
+    @ColumnInfo(name = "del_flag", defaultValue = "0")
+    var delFlag: String = "0"
+
+    /** 状态(0-正常 1-使用中 2-异常),默认 0 */
+    @ColumnInfo(name = "status", defaultValue = "0")
+    var status: String = "0"
+}

+ 64 - 0
data/src/main/java/com/grkj/data/local/dos/IsMaterialsChangeRecord.kt

@@ -0,0 +1,64 @@
+package com.grkj.data.local.dos
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+
+/**
+ * 物资更换记录
+ */
+@Entity(
+    tableName = "is_materials_change_record",
+    ignoredColumns = ["remark"] // 该表无 remark,BaseBean 有,需忽略
+)
+open class IsMaterialsChangeRecord : BaseBean() {
+
+    /** 物资更换id (PK, 自增) */
+    @PrimaryKey(autoGenerate = true)
+    @ColumnInfo(name = "change_id")
+    var changeId: Long = 0L
+
+    /** 物资检查记录id */
+    @ColumnInfo(name = "check_record_id")
+    var checkRecordId: Long? = null
+
+    /** 原物资id */
+    @ColumnInfo(name = "old_materials_id")
+    var oldMaterialsId: Long? = null
+
+    /** 原物资rfid */
+    @ColumnInfo(name = "old_materials_rfid")
+    var oldMaterialsRfid: String? = null
+
+    /** 新物资id */
+    @ColumnInfo(name = "new_materials_id")
+    var newMaterialsId: Long? = null
+
+    /** 新物资rfid */
+    @ColumnInfo(name = "new_materials_rfid")
+    var newMaterialsRfid: String? = null
+
+    /** 更换人id */
+    @ColumnInfo(name = "change_user_id")
+    var changeUserId: Long? = null
+
+    /** 更换时间(yyyy-MM-dd HH:mm:ss) */
+    @ColumnInfo(name = "change_date")
+    var changeDate: String? = null
+
+    /** 更换类型(0-手动更换 1-自动更换) */
+    @ColumnInfo(name = "change_type")
+    var changeType: String? = null
+
+    /** 操作类型(0-取出 1-放入 2-替换 3-维修) */
+    @ColumnInfo(name = "operate_type")
+    var operateType: String? = null
+
+    /** 状态 */
+    @ColumnInfo(name = "status")
+    var status: String? = null
+
+    /** 删除标志(0存在 2删除),默认 0 */
+    @ColumnInfo(name = "del_flag", defaultValue = "0")
+    var delFlag: String = "0"
+}

+ 41 - 0
data/src/main/java/com/grkj/data/local/dos/IsMaterialsCheckPlan.kt

@@ -0,0 +1,41 @@
+package com.grkj.data.local.dos
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+
+/**
+ * 物资检查计划表
+ */
+@Entity(tableName = "is_materials_check_plan")
+open class IsMaterialsCheckPlan : BaseBean() {
+
+    /** 物资计划id (PK, 自增) */
+    @PrimaryKey(autoGenerate = true)
+    @ColumnInfo(name = "plan_id")
+    var planId: Long = 0L
+
+    /** 计划名称 */
+    @ColumnInfo(name = "plan_name")
+    var planName: String? = null
+
+    /** 所属岗位ID */
+    @ColumnInfo(name = "workstation_id")
+    var workstationId: Long? = null
+
+    /** 计划日期(建议 yyyy-MM-dd) */
+    @ColumnInfo(name = "plan_date")
+    var planDate: String = ""
+
+    /** 检查员ID */
+    @ColumnInfo(name = "check_user_id")
+    var checkUserId: Long? = null
+
+    /** 检查状态(字典checking_status) - 默认 0 */
+    @ColumnInfo(name = "status", defaultValue = "0")
+    var status: String = "0"
+
+    /** 删除标志(0存在 2删除)- 默认 0 */
+    @ColumnInfo(name = "del_flag", defaultValue = "0")
+    var delFlag: String = "0"
+}

+ 57 - 0
data/src/main/java/com/grkj/data/local/dos/IsMaterialsCheckRecord.kt

@@ -0,0 +1,57 @@
+package com.grkj.data.local.dos
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+
+/**
+ * 物资检查记录表
+ */
+@Entity(tableName = "is_materials_check_record")
+open class IsMaterialsCheckRecord : BaseBean() {
+
+    /** 检查记录id (PK, 自增) */
+    @PrimaryKey(autoGenerate = true)
+    @ColumnInfo(name = "record_id")
+    var recordId: Long = 0L
+
+    /** 计划id */
+    @ColumnInfo(name = "plan_id")
+    var planId: Long = 0L
+
+    /** 所属岗位ID */
+    @ColumnInfo(name = "workstation_id")
+    var workstationId: Long? = null
+
+    /** 物资柜id */
+    @ColumnInfo(name = "cabinet_id")
+    var cabinetId: Long? = null
+
+    /** 物资id */
+    @ColumnInfo(name = "materials_id")
+    var materialsId: Long = 0L
+
+    /** 检查员 */
+    @ColumnInfo(name = "check_user_id")
+    var checkUserId: Long? = null
+
+    /** 检查时间(yyyy-MM-dd HH:mm:ss) */
+    @ColumnInfo(name = "check_date")
+    var checkDate: String? = null
+
+    /** 检查记录状态(字典checks_status) 0正常 1异常 */
+    @ColumnInfo(name = "status")
+    var status: String? = null
+
+    /** 异常原因(字典exceptions_status) 1损坏 2过期 */
+    @ColumnInfo(name = "reason")
+    var reason: String? = null
+
+    /** 措施 */
+    @ColumnInfo(name = "measure")
+    var measure: String? = null
+
+    /** 删除标志(0存在 2删除)- 默认 0 */
+    @ColumnInfo(name = "del_flag", defaultValue = "0")
+    var delFlag: String = "0"
+}

+ 53 - 0
data/src/main/java/com/grkj/data/local/dos/IsMaterialsInstructions.kt

@@ -0,0 +1,53 @@
+package com.grkj.data.local.dos
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+
+/**
+ * 物资使用说明表
+ */
+@Entity(tableName = "is_materials_instructions")
+open class IsMaterialsInstructions : BaseBean() {
+
+    /** 说明ID (PK, 自增) */
+    @PrimaryKey(autoGenerate = true)
+    @ColumnInfo(name = "instructions_id")
+    var instructionsId: Long = 0L
+
+    /** 标题 */
+    @ColumnInfo(name = "instructions_title")
+    var instructionsTitle: String? = null
+
+    /** 物资类型ID */
+    @ColumnInfo(name = "materials_type_id")
+    var materialsTypeId: Long? = null
+
+    /** 文件地址 */
+    @ColumnInfo(name = "file_url")
+    var fileUrl: String? = null
+
+    /** PDF转图片后的文件地址 */
+    @ColumnInfo(name = "file_url_img")
+    var fileUrlImg: String? = null
+
+    /** PDF转图片后的文件路径 */
+    @ColumnInfo(name = "file_path_img")
+    var filePathImg: String? = null
+
+    /** 文件类型 */
+    @ColumnInfo(name = "file_type")
+    var fileType: String? = null
+
+    /** 排序,默认 1 */
+    @ColumnInfo(name = "order_num", defaultValue = "1")
+    var orderNum: Int = 1
+
+    /** 物资状态(0-正常 1-异常 2-被更换),默认 0 */
+    @ColumnInfo(name = "status", defaultValue = "0")
+    var status: String = "0"
+
+    /** 删除标志(0存在 2删除),默认 0 */
+    @ColumnInfo(name = "del_flag", defaultValue = "0")
+    var delFlag: String = "0"
+}

+ 71 - 0
data/src/main/java/com/grkj/data/local/dos/IsMaterialsLoan.kt

@@ -0,0 +1,71 @@
+package com.grkj.data.local.dos
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+import com.grkj.data.data.MainDomainData
+import com.sik.sikcore.date.TimeUtils
+
+/**
+ * 物资借出表
+ */
+@Entity(tableName = "is_materials_loan")
+open class IsMaterialsLoan : BaseBean() {
+
+    /** 出借记录ID (PK, 自增) */
+    @PrimaryKey(autoGenerate = true)
+    @ColumnInfo(name = "materials_loan_id")
+    var materialsLoanId: Long = 0L
+
+    /** 物资ID */
+    @ColumnInfo(name = "materials_id")
+    var materialsId: Long = 0L
+
+    /** 领取人ID */
+    @ColumnInfo(name = "loan_user_id")
+    var loanUserId: Long = MainDomainData.userInfo?.userId ?: 0L
+
+    /** 领取柜ID */
+    @ColumnInfo(name = "loan_from_id")
+    var loanFromId: Long? = null
+
+    /** 领取时间 */
+    @ColumnInfo(name = "loan_time")
+    var loanTime: String? = TimeUtils.nowString(TimeUtils.DEFAULT_DATE_HOUR_MIN_SEC_FORMAT)
+
+    /** 提醒时间 */
+    @ColumnInfo(name = "reminder_time")
+    var reminderTime: String? = null
+
+    /** 归还人ID */
+    @ColumnInfo(name = "restitution_user_id")
+    var restitutionUserId: Long? = null
+
+    /** 归还柜ID */
+    @ColumnInfo(name = "restitution_to_id")
+    var restitutionToId: Long? = null
+
+    /** 理应归还时间 */
+    @ColumnInfo(name = "restitution_time")
+    var restitutionTime: String? = null
+
+    /** 实际归还时间 */
+    @ColumnInfo(name = "actual_restitution_time")
+    var actualRestitutionTime: String? = null
+
+    /** 超时报警时间 */
+    @ColumnInfo(name = "timeout_alarm")
+    var timeoutAlarm: String? = null
+
+    /** 是否需要归还 (1需要 0不需要),默认 1 */
+    @ColumnInfo(name = "restitution_required", defaultValue = "1")
+    var restitutionRequired: Int = 1
+
+    /** 删除标志(0存在 2删除),默认 0 */
+    @ColumnInfo(name = "del_flag", defaultValue = "0")
+    var delFlag: String = "0"
+
+    /** 状态(0-待归还 1-已归还 2-超时未归还),默认 0 */
+    @ColumnInfo(name = "status", defaultValue = "0")
+    var status: String = "0"
+}

+ 43 - 0
data/src/main/java/com/grkj/data/local/dos/IsMaterialsPlanCabinet.kt

@@ -0,0 +1,43 @@
+package com.grkj.data.local.dos
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+
+/**
+ * 物资检查计划关联物资柜表
+ */
+@Entity(
+    tableName = "is_materials_plan_cabinet",
+    primaryKeys = ["plan_id", "cabinet_id"],
+    ignoredColumns = ["create_by", "create_time", "update_by", "update_time", "remark"]
+)
+open class IsMaterialsPlanCabinet : BaseBean() {
+
+    /** 物资计划ID */
+    @ColumnInfo(name = "plan_id")
+    var planId: Long = 0L
+
+    /** 物资柜ID */
+    @ColumnInfo(name = "cabinet_id")
+    var cabinetId: Long = 0L
+
+    /** 检查员 */
+    @ColumnInfo(name = "check_user_id")
+    var checkUserId: Long? = null
+
+    /** 签名图片(长文本) */
+    @ColumnInfo(name = "signature_img")
+    var signatureImg: String? = null
+
+    /** 签名时间(yyyy-MM-dd HH:mm:ss) */
+    @ColumnInfo(name = "signature_time")
+    var signatureTime: String? = null
+
+    /** 是否提交(0-否 1-是),默认 0 */
+    @ColumnInfo(name = "submit", defaultValue = "0")
+    var submit: String = "0"
+
+    /** 柜子检查状态(字典checking_status),默认 0 */
+    @ColumnInfo(name = "status", defaultValue = "0")
+    var status: String = "0"
+}

+ 29 - 0
data/src/main/java/com/grkj/data/local/dos/IsMaterialsProperty.kt

@@ -0,0 +1,29 @@
+package com.grkj.data.local.dos
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+
+/**
+ * 物资属性项
+ */
+@Entity(tableName = "is_materials_property")
+open class IsMaterialsProperty : BaseBean() {
+
+    /** 编号 (PK, 自增) */
+    @PrimaryKey(autoGenerate = true)
+    @ColumnInfo(name = "property_id")
+    var propertyId: Long = 0L
+
+    /** 属性项名称 */
+    @ColumnInfo(name = "property_name")
+    var propertyName: String? = null
+
+    /** 删除标志(0存在 2删除),默认 0 */
+    @ColumnInfo(name = "del_flag", defaultValue = "0")
+    var delFlag: String = "0"
+
+    /** 物资状态(0-正常 1-异常 2-被更换),默认 0 */
+    @ColumnInfo(name = "status", defaultValue = "0")
+    var status: String = "0"
+}

+ 33 - 0
data/src/main/java/com/grkj/data/local/dos/IsMaterialsPropertyValue.kt

@@ -0,0 +1,33 @@
+package com.grkj.data.local.dos
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+
+/**
+ * 物资属性值
+ */
+@Entity(tableName = "is_materials_property_value")
+open class IsMaterialsPropertyValue : BaseBean() {
+
+    /** 编号 (PK, 自增) */
+    @PrimaryKey(autoGenerate = true)
+    @ColumnInfo(name = "record_id")
+    var recordId: Long = 0L
+
+    /** 属性项的编号 */
+    @ColumnInfo(name = "property_id")
+    var propertyId: Long = 0L
+
+    /** 属性值名称 */
+    @ColumnInfo(name = "value_name")
+    var valueName: String? = null
+
+    /** 删除标志(0存在 2删除),默认 0 */
+    @ColumnInfo(name = "del_flag", defaultValue = "0")
+    var delFlag: String = "0"
+
+    /** 物资状态(0-正常 1-异常 2-被更换),默认 0 */
+    @ColumnInfo(name = "status", defaultValue = "0")
+    var status: String = "0"
+}

+ 33 - 0
data/src/main/java/com/grkj/data/local/dos/IsMaterialsReminder.kt

@@ -0,0 +1,33 @@
+package com.grkj.data.local.dos
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+
+/**
+ * 物资借出提醒表
+ */
+@Entity(tableName = "is_materials_reminder")
+open class IsMaterialsReminder : BaseBean() {
+
+    /** 记录ID (PK, 自增) */
+    @PrimaryKey(autoGenerate = true)
+    @ColumnInfo(name = "record_id")
+    var recordId: Long = 0L
+
+    /** 出借记录ID */
+    @ColumnInfo(name = "materials_loan_id")
+    var materialsLoanId: Long = 0L
+
+    /** 提醒类型(0-提醒 1-警告) */
+    @ColumnInfo(name = "reminder_type")
+    var reminderType: Int? = null
+
+    /** 是否已读(0-未读 1-已读),默认 0 */
+    @ColumnInfo(name = "read_flag", defaultValue = "0")
+    var readFlag: Int = 0
+
+    /** 删除标志(0存在 2删除),默认 0 */
+    @ColumnInfo(name = "del_flag", defaultValue = "0")
+    var delFlag: String = "0"
+}

+ 45 - 0
data/src/main/java/com/grkj/data/local/dos/IsMaterialsRestitutionRules.kt

@@ -0,0 +1,45 @@
+package com.grkj.data.local.dos
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+
+/**
+ * 物资借出规则
+ */
+@Entity(tableName = "is_materials_restitution_rules")
+open class IsMaterialsRestitutionRules : BaseBean() {
+
+    /** 物资规则ID (PK, 自增) */
+    @PrimaryKey(autoGenerate = true)
+    @ColumnInfo(name = "rule_id")
+    var ruleId: Long = 0L
+
+    /** 物资规则编号/物资类型ID */
+    @ColumnInfo(name = "materials_type_id")
+    var materialsTypeId: Long = 0L
+
+    /** 是否需要归还(0-不需要 1-需要),默认 1 */
+    @ColumnInfo(name = "restitution_required", defaultValue = "1")
+    var restitutionRequired: Int = 1
+
+    /** 是否归还原位 */
+    @ColumnInfo(name = "restoration_required")
+    var restorationRequired: Int? = null
+
+    /** 出借时长 */
+    @ColumnInfo(name = "loan_duration")
+    var loanDuration: Int? = null
+
+    /** 提醒时间 */
+    @ColumnInfo(name = "reminder_time")
+    var reminderTime: Int? = null
+
+    /** 超时报警 */
+    @ColumnInfo(name = "timeout_alarm")
+    var timeoutAlarm: Int? = null
+
+    /** 删除标志(0存在 2删除),默认 0 */
+    @ColumnInfo(name = "del_flag", defaultValue = "0")
+    var delFlag: String = "0"
+}

+ 77 - 0
data/src/main/java/com/grkj/data/local/dos/IsMaterialsType.kt

@@ -0,0 +1,77 @@
+package com.grkj.data.local.dos
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+
+/**
+ * 物资类型表
+ */
+@Entity(tableName = "is_materials_type")
+open class IsMaterialsType : BaseBean() {
+
+    /** 物资类型ID (PK, 自增) */
+    @PrimaryKey(autoGenerate = true)
+    @ColumnInfo(name = "materials_type_id")
+    var materialsTypeId: Long = 0L
+
+    /** 物资类型编号 */
+    @ColumnInfo(name = "materials_type_code")
+    var materialsTypeCode: String? = null
+
+    /** 物资类型名称(NOT NULL) */
+    @ColumnInfo(name = "materials_type_name")
+    var materialsTypeName: String = ""
+
+    /** 父类型ID(NOT NULL,默认 0) */
+    @ColumnInfo(name = "parent_id", defaultValue = "0")
+    var parentId: Long = 0L
+
+    /** 所有父节点ID(NOT NULL) */
+    @ColumnInfo(name = "ancestors")
+    var ancestors: String = ""
+
+    /** 是否启用(NOT NULL,默认 Y) */
+    @ColumnInfo(name = "enable_flag", defaultValue = "Y")
+    var enableFlag: String = "Y"
+
+    /** 可用寿命 */
+    @ColumnInfo(name = "service_life")
+    var serviceLife: String? = null
+
+    /** 剩余寿命 */
+    @ColumnInfo(name = "available_life")
+    var availableLife: String? = null
+
+    /** 可用次数 */
+    @ColumnInfo(name = "service_times")
+    var serviceTimes: Int? = null
+
+    /** 剩余次数 */
+    @ColumnInfo(name = "available_times")
+    var availableTimes: Int? = null
+
+    /** 物资类型图标 */
+    @ColumnInfo(name = "materials_type_icon")
+    var materialsTypeIcon: String? = null
+
+    /** 物资类型缩略图 */
+    @ColumnInfo(name = "materials_type_picture")
+    var materialsTypePicture: String? = null
+
+    /** 物资检查标准 */
+    @ColumnInfo(name = "check_standard")
+    var checkStandard: String? = null
+
+    /** 删除标志(0存在 2删除),默认 0 */
+    @ColumnInfo(name = "del_flag", defaultValue = "0")
+    var delFlag: String = "0"
+
+    /** 状态 */
+    @ColumnInfo(name = "status")
+    var status: String? = null
+
+    /** 规格属性项 */
+    @ColumnInfo(name = "property_ids")
+    var propertyIds: String? = null
+}

+ 9 - 0
data/src/main/java/com/grkj/data/perms/RoleMenuPolicy.kt

@@ -0,0 +1,9 @@
+package com.grkj.data.perms
+
+interface RoleMenuPolicy {
+    /**
+     * 基于角色key计算“最终要入库/授权的菜单节点”
+     * data 不关心怎么算;上层可用 bridge.except(...)、include(...)随便玩
+     */
+    fun menusFor(roleKey: String, bridge: RolePermBridge): List<RolePermNode>
+}

+ 16 - 0
data/src/main/java/com/grkj/data/perms/RolePermBridge.kt

@@ -0,0 +1,16 @@
+package com.grkj.data.perms
+
+/** 上层提供实现;data 通过它拿“全部节点/根节点/排除视图”等 */
+interface RolePermBridge {
+    /** 全部节点(扁平) */
+    fun all(): List<RolePermNode>
+
+    /** 根节点(level == 0) */
+    fun roots(): List<RolePermNode>
+
+    /** 基于 ID 的排除,返回剩余节点(扁平) */
+    fun except(vararg excludePermissionIds: String): List<RolePermNode>
+
+    /** 可选:按 ID 查节点 */
+    fun findById(id: String): RolePermNode?
+}

+ 9 - 0
data/src/main/java/com/grkj/data/perms/RolePermNode.kt

@@ -0,0 +1,9 @@
+package com.grkj.data.perms
+
+/** data 只关心这四个属性与树形结构,不关心具体实现是枚举还是别的 */
+interface RolePermNode {
+    val functionalPermission: String
+    val description: String
+    val level: Int
+    val children: List<RolePermNode>
+}

+ 96 - 0
data/src/main/java/com/grkj/data/repository/MaterialsRepository.kt

@@ -0,0 +1,96 @@
+package com.grkj.data.repository
+
+import com.grkj.data.local.dos.IsMaterials
+import com.grkj.data.local.dos.IsMaterialsInstructions
+import com.grkj.data.local.dos.IsMaterialsLoan
+import com.grkj.data.local.dos.IsMaterialsType
+
+/**
+ * 物资仓储
+ */
+interface MaterialsRepository {
+    /**
+     * 获取物资类型
+     */
+    fun getMaterialsType(): List<IsMaterialsType>
+
+    /**
+     * 新增物资类型
+     */
+    fun insertMaterialsType(materialsType: IsMaterialsType)
+
+    /**
+     * 更新物资类型
+     */
+    fun updateMaterialsType(materialsType: IsMaterialsType)
+
+    /**
+     * 删除物资类型
+     */
+    fun deleteMaterialsType(materialTypeIds: List<Long>)
+
+    /**
+     * 检查是否有正在使用的物资类型
+     */
+    fun checkMaterialsTypeInBorrowed(materialsTypeId: List<Long>): Boolean
+
+    /**
+     * 新增物资
+     */
+    fun insertMaterials(materials: IsMaterials)
+
+    /**
+     * 更新物资
+     */
+    fun updateMaterials(materials: IsMaterials)
+
+    /**
+     * 获取物资
+     */
+    fun getMaterials(): List<IsMaterials>
+
+    /**
+     * 删除物资
+     */
+    fun deleteMaterials(materialsId: List<Long>)
+
+    /**
+     * 检查是否有正在使用的物资
+     */
+    fun checkMaterialsInBorrowed(materialsId: List<Long>): Boolean
+
+    /**
+     * 获取借出物资
+     */
+    fun getMaterialsLoan(): List<IsMaterialsLoan>
+
+    /**
+     * 新增领取物资
+     */
+    fun addMaterialsLoan(materialsLoan: List<IsMaterialsLoan>)
+
+    /**
+     * 更新领取物资
+     */
+    fun updateMaterialsLoan(materialsLoan: List<IsMaterialsLoan>)
+
+    /**
+     * 批量更新物资
+     */
+    fun updateMaterials(isMaterials: List<IsMaterials>)
+
+    /**
+     * 批量新增物资
+     */
+    fun insertMaterials(materials: List<IsMaterials>)
+
+    /**
+     * 取出异常物资
+     */
+    fun borrowExceptionMaterials(materials: List<Long>)
+
+    /**
+     * 获取物资说明列表
+     */
+    fun getMaterialsInstructionList(): List<IsMaterialsInstructions>
+}

+ 84 - 0
data/src/main/java/com/grkj/data/repository/impl/network/NetworkMaterialsRepositoryImpl.kt

@@ -0,0 +1,84 @@
+package com.grkj.data.repository.impl.network
+
+import com.grkj.data.local.dos.IsMaterials
+import com.grkj.data.local.dos.IsMaterialsInstructions
+import com.grkj.data.local.dos.IsMaterialsLoan
+import com.grkj.data.local.dos.IsMaterialsType
+import com.grkj.data.repository.BaseRepository
+import com.grkj.data.repository.MaterialsRepository
+import javax.inject.Inject
+import javax.inject.Singleton
+
+/**
+ * 物资联网仓储
+ */
+@Singleton
+class NetworkMaterialsRepositoryImpl @Inject constructor() : BaseRepository(), MaterialsRepository {
+    override fun getMaterialsType(): List<IsMaterialsType> {
+        TODO("Not yet implemented")
+    }
+
+    override fun insertMaterialsType(materialsType: IsMaterialsType) {
+        TODO("Not yet implemented")
+    }
+
+    override fun updateMaterialsType(materialsType: IsMaterialsType) {
+        TODO("Not yet implemented")
+    }
+
+    override fun deleteMaterialsType(materialTypeIds: List<Long>) {
+        TODO("Not yet implemented")
+    }
+
+    override fun checkMaterialsTypeInBorrowed(materialsTypeId: List<Long>): Boolean {
+        TODO("Not yet implemented")
+    }
+
+    override fun insertMaterials(materials: IsMaterials) {
+        TODO("Not yet implemented")
+    }
+
+    override fun updateMaterials(materials: IsMaterials) {
+        TODO("Not yet implemented")
+    }
+
+    override fun getMaterials(): List<IsMaterials> {
+        TODO("Not yet implemented")
+    }
+
+    override fun deleteMaterials(materialsId: List<Long>) {
+        TODO("Not yet implemented")
+    }
+
+    override fun checkMaterialsInBorrowed(materialsId: List<Long>): Boolean {
+        TODO("Not yet implemented")
+    }
+
+    override fun getMaterialsLoan(): List<IsMaterialsLoan> {
+        TODO("Not yet implemented")
+    }
+
+    override fun addMaterialsLoan(materialsLoan: List<IsMaterialsLoan>) {
+        TODO("Not yet implemented")
+    }
+
+    override fun updateMaterialsLoan(materialsLoan: List<IsMaterialsLoan>) {
+        TODO("Not yet implemented")
+    }
+
+    override fun updateMaterials(isMaterials: List<IsMaterials>) {
+        TODO("Not yet implemented")
+    }
+
+    override fun insertMaterials(materials: List<IsMaterials>) {
+        TODO("Not yet implemented")
+    }
+
+    override fun borrowExceptionMaterials(materials: List<Long>) {
+        TODO("Not yet implemented")
+    }
+
+    override fun getMaterialsInstructionList(): List<IsMaterialsInstructions> {
+        TODO("Not yet implemented")
+    }
+}

+ 87 - 0
data/src/main/java/com/grkj/data/repository/impl/standard/MaterialsRepositoryImpl.kt

@@ -0,0 +1,87 @@
+package com.grkj.data.repository.impl.standard
+
+import com.grkj.data.local.dao.MaterialsDao
+import com.grkj.data.local.dos.IsMaterials
+import com.grkj.data.local.dos.IsMaterialsInstructions
+import com.grkj.data.local.dos.IsMaterialsLoan
+import com.grkj.data.local.dos.IsMaterialsType
+import com.grkj.data.repository.BaseRepository
+import com.grkj.data.repository.MaterialsRepository
+import javax.inject.Inject
+import javax.inject.Singleton
+
+/**
+ * 物资本地仓储
+ */
+@Singleton
+class MaterialsRepositoryImpl @Inject constructor(
+    val materialsDao: MaterialsDao
+) : BaseRepository(), MaterialsRepository {
+    override fun getMaterialsType(): List<IsMaterialsType> {
+        return materialsDao.getMaterialsType()
+    }
+
+    override fun checkMaterialsTypeInBorrowed(materialsTypeId: List<Long>): Boolean {
+        return materialsDao.checkMaterialsTypeInBorrowed(materialsTypeId) > 0
+    }
+
+    override fun updateMaterials(materials: IsMaterials) {
+        materialsDao.updateMaterials(materials)
+    }
+
+    override fun deleteMaterials(materialsId: List<Long>) {
+        return materialsDao.deleteMaterials(materialsId)
+    }
+
+    override fun checkMaterialsInBorrowed(materialsId: List<Long>): Boolean {
+        return materialsDao.checkMaterialsInBorrowed(materialsId) > 0
+    }
+
+    override fun addMaterialsLoan(materialsLoan: List<IsMaterialsLoan>) {
+        materialsDao.addMaterialsLoan(materialsLoan)
+    }
+
+    override fun updateMaterialsLoan(materialsLoan: List<IsMaterialsLoan>) {
+        materialsDao.updateMaterialsLoan(materialsLoan)
+    }
+
+    override fun insertMaterials(materials: List<IsMaterials>) {
+        materialsDao.insertMaterials(materials)
+    }
+
+    override fun borrowExceptionMaterials(materials: List<Long>) {
+        materialsDao.deleteMaterials(materials)
+    }
+
+    override fun getMaterialsInstructionList(): List<IsMaterialsInstructions> {
+        return materialsDao.getMaterialsInstructionList()
+    }
+
+    override fun updateMaterials(isMaterials: List<IsMaterials>) {
+        materialsDao.updateMaterials(isMaterials)
+    }
+
+    override fun getMaterialsLoan(): List<IsMaterialsLoan> {
+        return materialsDao.getMaterialsLoan()
+    }
+
+    override fun getMaterials(): List<IsMaterials> {
+        return materialsDao.getMaterials()
+    }
+
+    override fun insertMaterials(materials: IsMaterials) {
+        materialsDao.insertMaterials(materials)
+    }
+
+    override fun insertMaterialsType(materialsType: IsMaterialsType) {
+        materialsDao.insertMaterialsType(materialsType)
+    }
+
+    override fun updateMaterialsType(materialsType: IsMaterialsType) {
+        materialsDao.updateMaterialsType(materialsType)
+    }
+
+    override fun deleteMaterialsType(materialTypeIds: List<Long>) {
+        materialsDao.deleteMaterialsType(materialTypeIds)
+    }
+}

+ 2 - 0
gradle/libs.versions.toml

@@ -66,6 +66,8 @@ androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref =
 android-navigation-fragment = { group = "androidx.navigation", name = "navigation-fragment", version.ref = "nav_version" }
 android-navigation-ui = { group = "androidx.navigation", name = "navigation-ui", version.ref = "nav_version" }
 android-navigation-dynamic-features-fragment = { group = "androidx.navigation", name = "navigation-dynamic-features-fragment", version.ref = "nav_version" }
+android-navigation-fragment-ktx = { group = "androidx.navigation", name = "navigation-fragment-ktx", version.ref = "nav_version" }
+android-navigation-ui-ktx = { group = "androidx.navigation", name = "navigation-ui-ktx", version.ref = "nav_version" }
 kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlin_serialization_json" }
 
 fastble = { group = "com.github.kanshenmekan", name = "FastBle", version.ref = "fastble" }

+ 0 - 0
app/.gitignore → iscs_lock/.gitignore


+ 0 - 0
app/build.gradle.kts → iscs_lock/build.gradle.kts


+ 0 - 0
app/proguard-rules.pro → iscs_lock/proguard-rules.pro


+ 0 - 0
app/src/androidTest/java/com/grkj/ui_base/ExampleInstrumentedTest.kt → iscs_lock/src/androidTest/java/com/grkj/ui_base/ExampleInstrumentedTest.kt


+ 0 - 0
app/src/main/AndroidManifest.xml → iscs_lock/src/main/AndroidManifest.xml


+ 0 - 0
app/src/main/assets/logback.xml → iscs_lock/src/main/assets/logback.xml


+ 0 - 0
app/src/main/assets/map_factory.png → iscs_lock/src/main/assets/map_factory.png


+ 0 - 0
app/src/main/assets/preset/CN/preset_workflow_mode.json → iscs_lock/src/main/assets/preset/CN/preset_workflow_mode.json


+ 0 - 0
app/src/main/assets/preset/CN/preset_workflow_step.json → iscs_lock/src/main/assets/preset/CN/preset_workflow_step.json


+ 0 - 0
app/src/main/assets/preset/US/preset_workflow_mode.json → iscs_lock/src/main/assets/preset/US/preset_workflow_mode.json


+ 0 - 0
app/src/main/assets/preset/US/preset_workflow_step.json → iscs_lock/src/main/assets/preset/US/preset_workflow_step.json


+ 0 - 0
app/src/main/assets/preset/preset_sys_role.json → iscs_lock/src/main/assets/preset/preset_sys_role.json


+ 0 - 0
app/src/main/assets/themes/Default/icons/access-control.svg → iscs_lock/src/main/assets/themes/Default/icons/access-control.svg


+ 0 - 0
app/src/main/assets/themes/Default/icons/arrow-down-strenght.svg → iscs_lock/src/main/assets/themes/Default/icons/arrow-down-strenght.svg


+ 0 - 0
app/src/main/assets/themes/Default/icons/arrow-progress-alt.svg → iscs_lock/src/main/assets/themes/Default/icons/arrow-progress-alt.svg


+ 0 - 0
app/src/main/assets/themes/Default/icons/back-up.svg → iscs_lock/src/main/assets/themes/Default/icons/back-up.svg


+ 0 - 0
app/src/main/assets/themes/Default/icons/ballot-check.svg → iscs_lock/src/main/assets/themes/Default/icons/ballot-check.svg


+ 0 - 0
app/src/main/assets/themes/Default/icons/ballot.svg → iscs_lock/src/main/assets/themes/Default/icons/ballot.svg


+ 0 - 0
app/src/main/assets/themes/Default/icons/bolt-slash.svg → iscs_lock/src/main/assets/themes/Default/icons/bolt-slash.svg


+ 0 - 0
app/src/main/assets/themes/Default/icons/bolt.svg → iscs_lock/src/main/assets/themes/Default/icons/bolt.svg


+ 0 - 0
app/src/main/assets/themes/Default/icons/cards-blank.png → iscs_lock/src/main/assets/themes/Default/icons/cards-blank.png


+ 0 - 0
app/src/main/assets/themes/Default/icons/chalkboard-user.svg → iscs_lock/src/main/assets/themes/Default/icons/chalkboard-user.svg


+ 0 - 0
app/src/main/assets/themes/Default/icons/checkbox.svg → iscs_lock/src/main/assets/themes/Default/icons/checkbox.svg


+ 0 - 0
app/src/main/assets/themes/Default/icons/choose.svg → iscs_lock/src/main/assets/themes/Default/icons/choose.svg


+ 0 - 0
app/src/main/assets/themes/Default/icons/document.svg → iscs_lock/src/main/assets/themes/Default/icons/document.svg


+ 0 - 0
app/src/main/assets/themes/Default/icons/external-world.png → iscs_lock/src/main/assets/themes/Default/icons/external-world.png


+ 0 - 0
app/src/main/assets/themes/Default/icons/face-id-svgrepo-com.svg → iscs_lock/src/main/assets/themes/Default/icons/face-id-svgrepo-com.svg


+ 0 - 0
app/src/main/assets/themes/Default/icons/file-export.svg → iscs_lock/src/main/assets/themes/Default/icons/file-export.svg


+ 0 - 0
app/src/main/assets/themes/Default/icons/fingerprint.svg → iscs_lock/src/main/assets/themes/Default/icons/fingerprint.svg


+ 0 - 0
app/src/main/assets/themes/Default/icons/fire-flame-curved.svg → iscs_lock/src/main/assets/themes/Default/icons/fire-flame-curved.svg


+ 0 - 0
app/src/main/assets/themes/Default/icons/gas-pump.svg → iscs_lock/src/main/assets/themes/Default/icons/gas-pump.svg


+ 0 - 0
app/src/main/assets/themes/Default/icons/heat.svg → iscs_lock/src/main/assets/themes/Default/icons/heat.svg


+ 0 - 0
app/src/main/assets/themes/Default/icons/icon_bottom_menu_data_manage.svg → iscs_lock/src/main/assets/themes/Default/icons/icon_bottom_menu_data_manage.svg


+ 0 - 0
app/src/main/assets/themes/Default/icons/icon_bottom_menu_exception_manage.svg → iscs_lock/src/main/assets/themes/Default/icons/icon_bottom_menu_exception_manage.svg


+ 0 - 0
app/src/main/assets/themes/Default/icons/icon_bottom_menu_hardware_manage.svg → iscs_lock/src/main/assets/themes/Default/icons/icon_bottom_menu_hardware_manage.svg


+ 0 - 0
app/src/main/assets/themes/Default/icons/icon_bottom_menu_home.svg → iscs_lock/src/main/assets/themes/Default/icons/icon_bottom_menu_home.svg


+ 0 - 0
app/src/main/assets/themes/Default/icons/icon_bottom_menu_job_manage.svg → iscs_lock/src/main/assets/themes/Default/icons/icon_bottom_menu_job_manage.svg


+ 0 - 0
app/src/main/assets/themes/Default/icons/icon_chevron_left.png → iscs_lock/src/main/assets/themes/Default/icons/icon_chevron_left.png


+ 0 - 0
app/src/main/assets/themes/Default/icons/icon_data_export.png → iscs_lock/src/main/assets/themes/Default/icons/icon_data_export.png


+ 0 - 0
app/src/main/assets/themes/Default/icons/icon_data_manage_switch_layout.svg → iscs_lock/src/main/assets/themes/Default/icons/icon_data_manage_switch_layout.svg


+ 0 - 0
app/src/main/assets/themes/Default/icons/icon_delete_circle.png → iscs_lock/src/main/assets/themes/Default/icons/icon_delete_circle.png


+ 0 - 0
app/src/main/assets/themes/Default/icons/icon_drop_down_tree_check.png → iscs_lock/src/main/assets/themes/Default/icons/icon_drop_down_tree_check.png


+ 0 - 0
app/src/main/assets/themes/Default/icons/icon_drop_down_tree_collapse.png → iscs_lock/src/main/assets/themes/Default/icons/icon_drop_down_tree_collapse.png


+ 0 - 0
app/src/main/assets/themes/Default/icons/icon_drop_down_tree_expand.png → iscs_lock/src/main/assets/themes/Default/icons/icon_drop_down_tree_expand.png


+ 0 - 0
app/src/main/assets/themes/Default/icons/icon_drop_down_tree_point.png → iscs_lock/src/main/assets/themes/Default/icons/icon_drop_down_tree_point.png


+ 0 - 0
app/src/main/assets/themes/Default/icons/icon_hide.png → iscs_lock/src/main/assets/themes/Default/icons/icon_hide.png


+ 0 - 0
app/src/main/assets/themes/Default/icons/icon_login_menu_card.png → iscs_lock/src/main/assets/themes/Default/icons/icon_login_menu_card.png


+ 0 - 0
app/src/main/assets/themes/Default/icons/icon_login_menu_password.png → iscs_lock/src/main/assets/themes/Default/icons/icon_login_menu_password.png


+ 0 - 0
app/src/main/assets/themes/Default/icons/icon_logo.png → iscs_lock/src/main/assets/themes/Default/icons/icon_logo.png


+ 0 - 0
app/src/main/assets/themes/Default/icons/icon_overview_data.png → iscs_lock/src/main/assets/themes/Default/icons/icon_overview_data.png


+ 0 - 0
app/src/main/assets/themes/Default/icons/icon_realtime_data.png → iscs_lock/src/main/assets/themes/Default/icons/icon_realtime_data.png


Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно