Ver código fonte

refactor(用户管理):
- 优化`UserDao`中用户查询逻辑:
- 新增`getUserManagePage`方法,通过SQL直接实现用户管理列表的分页和筛选(昵称、状态、卡号、岗位名),替代原先在`UserLogic`中获取全量数据后再进行内存筛选的方式,大幅提升查询效率。指纹数量统计口径调整为按`group`去重。
- 新增`getJobUsersByIdsAndTicket`方法,通过SQL直接查询指定作业票、用户ID列表和用户角色的用户信息,并包含用户所属分组名。
- 新增`getAllUsersWithWorkstation`方法,通过SQL直接按岗位ID查询所有相关用户信息。指纹数量统计口径调整为直接按指纹记录数。
- 在上述新查询中,角色、岗位、工卡信息均在SQL层面通过`GROUP_CONCAT`聚合,并由`TypeConverter`转换为List,简化了数据处理。
- `UserLogic`中对应的数据获取方法 (`getUserManagerData`, `getJobUserDataByUserIdAndTicketId`, `getAllUserDataWithWorkstation`) 改为直接调用`UserRepository`中新的查询方法。
- `UserRepository`及其实现类`UserRepositoryImpl`和`NetworkUserRepositoryImpl`同步更新接口和实现,以支持新的DAO方法。
- `UpdateUserDataVo`和`UserManageVo`中的`cardNfc`字段类型从`String`修改为`String?`,以允许NFC卡号为空。
- `UserManageViewModel`中更新用户工卡时,增加对`updateUserDataVo.cardNfc`是否为空的判断。

周文健 1 mês atrás
pai
commit
08d437aa1f

+ 1 - 1
app/src/main/java/com/grkj/iscs/features/main/viewmodel/data_manage/UserManageViewModel.kt

@@ -143,7 +143,7 @@ class UserManageViewModel @Inject constructor(
                     it
                 )
             }
-            updateUserDataVo.cardNfc.let {
+            updateUserDataVo.cardNfc?.let {
                 if (it.isNotEmpty()) {
                     hardwareRepository.updateUserJobCard(it, updateUserDataVo.userId)
                 }

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

@@ -21,7 +21,6 @@ import com.grkj.data.repository.JobTicketRepository
 import com.grkj.data.repository.RoleRepository
 import com.grkj.data.repository.SysMenuRepository
 import com.grkj.data.repository.UserRepository
-import com.grkj.data.repository.WorkflowRepository
 import com.grkj.data.repository.WorkstationRepository
 import com.grkj.shared.utils.BCryptUtils
 import com.grkj.shared.utils.BiometricVerifier
@@ -314,59 +313,7 @@ class UserLogic @Inject constructor(
         current: Int,
         size: Int
     ): List<UserManageVo> {
-        //1.根据分页获取用户信息
-        val sysUserDos = userRepository.getUserInfoPage(size, current * size)
-        //2.获取所有区域信息和绑定信息
-        val workstationDos = workstationRepository.getAllWorkstation()
-        val userWorkstationDos = hardwareRepository.getAllUserWorkstations()
-        //3.获取所有角色信息和用户角色关联信息
-        val roleDos = roleRepository.getAllRole()
-        val userRoleDos = roleRepository.getAllUserRoles()
-        val userCharacteristicDos = userRepository.getAllUserCharacteristic()
-        //4.获取所有卡片数据
-        val jobCardDos = hardwareRepository.getAllJobCard()
-        val userManageVos = BeanUtils.copyList(sysUserDos, UserManageVo::class.java)
-        userManageVos.forEach { user ->
-            val userRoles = roleDos.filter {
-                it.roleId in userRoleDos.filter { it.userId == user.userId }.map { it.roleId }
-            }
-            user.roleKeys = userRoles.map { it.roleKey }
-            user.roleIds = userRoles.map { it.roleId }
-            user.roleNames = userRoles.map {
-                val i18NRoleName = I18nManager.t(it.roleKey)
-                if (i18NRoleName == it.roleKey || i18NRoleName.isEmpty()) {
-                    it.roleName
-                } else {
-                    i18NRoleName
-                }
-            }
-            val userWorkstations = workstationDos.filter {
-                it.workstationId in userWorkstationDos.filter { it.userId == user.userId }
-                    .map { it.workstationId }
-            }
-            user.workstationIds = userWorkstations.map { it.workstationId }
-            user.workstationNames = userWorkstations.map { it.workstationName }
-            user.cardNfc = jobCardDos.find { it.userId == user.userId }?.cardNfc ?: ""
-            user.fingerprintSize =
-                userCharacteristicDos.groupBy { it.group }
-                    .count { it.value.any { it.userId == user.userId && it.type == "1" } }
-        }
-        val nickname = userManageFilterData?.nickname
-        val status = userManageFilterData?.status
-        val cardNfc = userManageFilterData?.cardNfc
-        val workstationName = userManageFilterData?.workstationName
-
-        val filteredList = userManageVos.filter { user ->
-            (nickname.isNullOrEmpty() || user.nickName.contains(nickname)) &&
-                    (status == null || user.status == (if (status) "1" else "0")) &&
-                    (cardNfc.isNullOrEmpty() || user.cardNfc.contains(cardNfc)) &&
-                    (workstationName.isNullOrEmpty() || user.workstationNames.any {
-                        it?.contains(
-                            workstationName
-                        ) == true
-                    })
-        }
-        return filteredList
+        return userRepository.getUserManagePage(userManageFilterData, current, size)
     }
 
 
@@ -375,74 +322,11 @@ class UserLogic @Inject constructor(
         userIds: List<Long>,
         userRole: String
     ): List<JobUserVo> {
-        //1.根据用户id获取用户数据
-        val sysUserDos = userRepository.getUserInfosByUserIds(userIds)
-        //2.获取所有区域信息和绑定信息
-        val workstationDos = workstationRepository.getAllWorkstation()
-        val userWorkstationDos = hardwareRepository.getAllUserWorkstations()
-        //3.获取所有角色信息和用户角色关联信息
-        val roleDos = roleRepository.getAllRole()
-        val userRoleDos = roleRepository.getAllUserRoles()
-        //4.获取所有卡片数据
-        val jobCardDos = hardwareRepository.getAllJobCard()
-        //5.根据作业票id获取用户数据
-        val jobTicketUsers = jobTicketRepository.getJobTicketUsersByTicketId(ticketId)
-        val needJobTicketUser =
-            jobTicketUsers.filter { it.userId in userIds && it.userRole == userRole }
-        //6.根据作业票id获取分组数据
-        val jobTicketGroups = jobTicketRepository.getJobTicketGroupsByTicketId(ticketId)
-        val userManageVos = BeanUtils.copyList(sysUserDos, JobUserVo::class.java)
-        userManageVos.forEach { user ->
-            val userRoles = roleDos.filter {
-                it.roleId in userRoleDos.filter { it.userId == user.userId }.map { it.roleId }
-            }
-            user.roleKeys = userRoles.map { it.roleKey }
-            user.roleIds = userRoles.map { it.roleId }
-            user.roleNames = userRoles.map { it.roleName }
-            val userWorkstations = workstationDos.filter {
-                it.workstationId in userWorkstationDos.filter { it.userId == user.userId }
-                    .map { it.workstationId }
-            }
-            user.workstationIds = userWorkstations.map { it.workstationId }
-            user.workstationNames = userWorkstations.map { it.workstationName }
-            user.cardNfc = jobCardDos.find { it.userId == user.userId }?.cardNfc ?: ""
-            user.groupId = needJobTicketUser.find { it.userId == user.userId }?.groupId ?: 0
-            user.groupName = jobTicketGroups.find { it.id == user.groupId }?.groupName ?: ""
-        }
-        return userManageVos
+        return userRepository.getJobUserDataByUserIdAndTicketId(ticketId, userIds, userRole)
     }
 
     override fun getAllUserDataWithWorkstation(workstationId: Long): List<UserManageVo> {
-        //1.获取用户信息
-        val sysUserDos = userRepository.getAllUserInfos()
-        //2.获取所有区域信息和绑定信息
-        val workstationDos = workstationRepository.getAllWorkstation()
-        val userWorkstationDos = hardwareRepository.getAllUserWorkstations()
-        //3.获取所有角色信息和用户角色关联信息
-        val roleDos = roleRepository.getAllRole()
-        val userRoleDos = roleRepository.getAllUserRoles()
-        val userCharacteristicDos = userRepository.getAllUserCharacteristic()
-        //4.获取所有卡片数据
-        val jobCardDos = hardwareRepository.getAllJobCard()
-        val userManageVos = BeanUtils.copyList(sysUserDos, UserManageVo::class.java)
-        userManageVos.forEach { user ->
-            val userRoles = roleDos.filter {
-                it.roleId in userRoleDos.filter { it.userId == user.userId }.map { it.roleId }
-            }
-            user.roleKeys = userRoles.map { it.roleKey }
-            user.roleIds = userRoles.map { it.roleId }
-            user.roleNames = userRoles.map { it.roleName }
-            val userWorkstations = workstationDos.filter {
-                it.workstationId in userWorkstationDos.filter { it.userId == user.userId }
-                    .map { it.workstationId }
-            }
-            user.workstationIds = userWorkstations.map { it.workstationId }
-            user.workstationNames = userWorkstations.map { it.workstationName }
-            user.cardNfc = jobCardDos.find { it.userId == user.userId }?.cardNfc ?: ""
-            user.fingerprintSize =
-                userCharacteristicDos.count { it.userId == user.userId && it.type == "1" }
-        }
-        return userManageVos.filter { it.workstationIds.contains(workstationId) }
+        return userRepository.getAllUsersWithWorkstation(workstationId)
     }
 
     override fun addUserData(addUserDataVo: AddUserDataVo): Long {

+ 1 - 1
data/src/main/java/com/grkj/data/domain/vo/UpdateUserDataVo.kt

@@ -8,7 +8,7 @@ data class UpdateUserDataVo(
     val username: String,
     val password: String,
     val nickname: String,
-    val cardNfc: String,
+    val cardNfc: String?,
     val roleId: List<Long>,
     val workstationId: List<Long>?,
     val status: Boolean,

+ 1 - 1
data/src/main/java/com/grkj/data/domain/vo/UserManageVo.kt

@@ -12,7 +12,7 @@ class UserManageVo {
     var userName: String = ""
     var password: String = BCryptUtils.encryptPassword("123456")
     var avatar: String? = null
-    var cardNfc: String = ""
+    var cardNfc: String? = ""
     var roleIds: List<Long?> = listOf()
     var roleNames: List<String?> = listOf()
     var roleKeys: List<String?> = listOf()

+ 181 - 0
data/src/main/java/com/grkj/data/local/dao/UserDao.kt

@@ -356,4 +356,185 @@ interface UserDao {
      */
     @Query("update sys_user_characteristic set del_flag = 1 where `group` not in (:fingerprintData) and user_id = :userId")
     fun deleteNoUseFingerprint(fingerprintData: List<String>, userId: Long)
+
+    /**
+     * ① 用户管理分页(昵称/状态/卡号/岗位名筛选)
+     * 指纹口径:按 group 去重后计数(与你原 getUserManagerData 保持一致)。
+     *
+     * @param nickname 模糊匹配
+     * @param status "1" 启用 / "0" 禁用;null 不筛选
+     * @param cardNfc 模糊匹配
+     * @param workstationName 模糊匹配
+     * @param size 分页大小
+     * @param offset 偏移(current*size)
+     */
+    @Query("""
+        SELECT
+      u.user_id   AS userId,
+      u.nick_name AS nickName,
+      u.user_name AS userName,
+      u.password AS password,
+      u.avatar    AS avatar,
+      u.status    AS status,
+
+      /* 工卡:取任意一张(你要“最新”可换 MAX(create_time) 再取) */
+      (SELECT MIN(jc.card_nfc)
+         FROM is_job_card jc
+        WHERE jc.user_id = u.user_id) AS cardNfc,
+
+      /* 角色聚合(去重) */
+      (SELECT GROUP_CONCAT(DISTINCT r.role_id)
+         FROM sys_user_role ur
+         JOIN sys_role r ON r.role_id = ur.role_id
+        WHERE ur.user_id = u.user_id) AS roleIds,
+      (SELECT GROUP_CONCAT(DISTINCT r.role_name)
+         FROM sys_user_role ur
+         JOIN sys_role r ON r.role_id = ur.role_id
+        WHERE ur.user_id = u.user_id) AS roleNames,
+      (SELECT GROUP_CONCAT(DISTINCT r.role_key)
+         FROM sys_user_role ur
+         JOIN sys_role r ON r.role_id = ur.role_id
+        WHERE ur.user_id = u.user_id) AS roleKeys,
+
+      /* 岗位聚合(去重) */
+      (SELECT GROUP_CONCAT(DISTINCT w.workstation_id)
+         FROM is_user_workstation uw
+         JOIN is_workstation w ON w.workstation_id = uw.workstation_id
+        WHERE uw.user_id = u.user_id) AS workstationIds,
+      (SELECT GROUP_CONCAT(DISTINCT w.workstation_name)
+         FROM is_user_workstation uw
+         JOIN is_workstation w ON w.workstation_id = uw.workstation_id
+        WHERE uw.user_id = u.user_id) AS workstationNames,
+
+      /* 指纹数量:与 getUserManagerData 口径一致——按 group 去重 */
+      COALESCE((
+        SELECT COUNT(DISTINCT CASE WHEN c.type='1' THEN c.`group` END)
+          FROM sys_user_characteristic c
+         WHERE c.user_id = u.user_id
+      ), 0) AS fingerprintSize
+
+    FROM sys_user u
+    WHERE u.del_flag = '0'
+      AND (:nickname IS NULL OR u.nick_name LIKE '%'||:nickname||'%')
+      AND (:status   IS NULL OR u.status = :status)
+      AND (:cardNfc  IS NULL OR EXISTS (
+            SELECT 1 FROM is_job_card x
+             WHERE x.user_id = u.user_id AND x.card_nfc LIKE '%'||:cardNfc||'%'
+          ))
+      AND (:workstationName IS NULL OR EXISTS (
+            SELECT 1
+              FROM is_user_workstation uw
+              JOIN is_workstation w ON w.workstation_id = uw.workstation_id
+             WHERE uw.user_id = u.user_id AND w.workstation_name LIKE '%'||:workstationName||'%'
+          ))
+    ORDER BY u.user_id asc
+    LIMIT :size OFFSET :offset
+    """)
+    fun getUserManagePage(
+        nickname: String?,
+        status: String?,
+        cardNfc: String?,
+        workstationName: String?,
+        size: Int,
+        offset: Int
+    ): List<UserManageVo>
+
+    /**
+     * ② 作业票下的指定用户(限定 userIds + userRole),包含分组名。
+     * 角色/岗位/工卡同样聚合为 CSV,由 TypeConverter 映射到 List。
+     */
+    @Query("""
+        SELECT
+    u.user_id   AS userId,
+    u.nick_name AS nickName,
+    u.user_name AS userName,
+    u.avatar    AS avatar,
+    u.status    AS status,
+
+    (SELECT MIN(jc.card_nfc) FROM is_job_card jc WHERE jc.user_id = u.user_id) AS cardNfc,
+
+    (SELECT GROUP_CONCAT(DISTINCT r.role_id)
+       FROM sys_user_role ur JOIN sys_role r ON r.role_id = ur.role_id
+      WHERE ur.user_id = u.user_id) AS roleIds,
+    (SELECT GROUP_CONCAT(DISTINCT r.role_name)
+       FROM sys_user_role ur JOIN sys_role r ON r.role_id = ur.role_id
+      WHERE ur.user_id = u.user_id) AS roleNames,
+    (SELECT GROUP_CONCAT(DISTINCT r.role_key)
+       FROM sys_user_role ur JOIN sys_role r ON r.role_id = ur.role_id
+      WHERE ur.user_id = u.user_id) AS roleKeys,
+
+    (SELECT GROUP_CONCAT(DISTINCT w.workstation_id)
+       FROM is_user_workstation uw JOIN is_workstation w ON w.workstation_id = uw.workstation_id
+      WHERE uw.user_id = u.user_id) AS workstationIds,
+    (SELECT GROUP_CONCAT(DISTINCT w.workstation_name)
+       FROM is_user_workstation uw JOIN is_workstation w ON w.workstation_id = uw.workstation_id
+      WHERE uw.user_id = u.user_id) AS workstationNames,
+
+    jtu.group_id   AS groupId,
+    (SELECT jtg.group_name FROM is_job_ticket_group jtg WHERE jtg.id = jtu.group_id) AS groupName
+
+  FROM is_job_ticket_user jtu
+  JOIN sys_user u ON u.user_id = jtu.user_id AND u.del_flag='0'
+  WHERE jtu.ticket_id = :ticketId
+    AND jtu.user_role = :userRole
+    AND jtu.del_flag  = '0'
+    AND u.user_id IN (:userIds)
+  ORDER BY u.user_id asc
+    """)
+    fun getJobUsersByIdsAndTicket(
+        ticketId: Long,
+        userIds: List<Long>,
+        userRole: String
+    ): List<JobUserVo>
+
+    /**
+     * ③ 按岗位取全体用户(含角色/岗位/工卡)
+     * 指纹口径:直接按记录数计数(与你原 getAllUserDataWithWorkstation 保持一致)。
+     */
+    @Query("""
+        SELECT
+    u.user_id   AS userId,
+    u.nick_name AS nickName,
+    u.user_name AS userName,
+    u.password AS password,
+    u.avatar    AS avatar,
+    u.status    AS status,
+
+    (SELECT MIN(jc.card_nfc)
+       FROM is_job_card jc WHERE jc.user_id = u.user_id) AS cardNfc,
+
+    (SELECT GROUP_CONCAT(DISTINCT r.role_id)
+       FROM sys_user_role ur JOIN sys_role r ON r.role_id = ur.role_id
+      WHERE ur.user_id = u.user_id) AS roleIds,
+    (SELECT GROUP_CONCAT(DISTINCT r.role_name)
+       FROM sys_user_role ur JOIN sys_role r ON r.role_id = ur.role_id
+      WHERE ur.user_id = u.user_id) AS roleNames,
+    (SELECT GROUP_CONCAT(DISTINCT r.role_key)
+       FROM sys_user_role ur JOIN sys_role r ON r.role_id = ur.role_id
+      WHERE ur.user_id = u.user_id) AS roleKeys,
+
+    (SELECT GROUP_CONCAT(DISTINCT w.workstation_id)
+       FROM is_user_workstation uw JOIN is_workstation w ON w.workstation_id = uw.workstation_id
+      WHERE uw.user_id = u.user_id) AS workstationIds,
+    (SELECT GROUP_CONCAT(DISTINCT w.workstation_name)
+       FROM is_user_workstation uw JOIN is_workstation w ON w.workstation_id = uw.workstation_id
+      WHERE uw.user_id = u.user_id) AS workstationNames,
+
+    COALESCE((
+      SELECT SUM(CASE WHEN c.type='1' THEN 1 ELSE 0 END)
+        FROM sys_user_characteristic c
+       WHERE c.user_id = u.user_id
+    ), 0) AS fingerprintSize
+
+  FROM sys_user u
+  WHERE u.del_flag='0'
+    AND EXISTS (
+        SELECT 1 FROM is_user_workstation uw
+         WHERE uw.user_id=u.user_id AND uw.workstation_id = :workstationId
+    )
+  ORDER BY u.user_id asc
+    """)
+    fun getAllUsersWithWorkstation(
+        workstationId: Long
+    ): List<UserManageVo>
 }

+ 32 - 0
data/src/main/java/com/grkj/data/repository/UserRepository.kt

@@ -1,5 +1,8 @@
 package com.grkj.data.repository
 
+import com.grkj.data.domain.vo.JobUserVo
+import com.grkj.data.domain.vo.UserManageFilterVo
+import com.grkj.data.domain.vo.UserManageVo
 import com.grkj.data.local.dos.SysUserCharacteristicDo
 import com.grkj.data.local.dos.SysUserDo
 
@@ -131,4 +134,33 @@ interface UserRepository {
      * 删除不在列表中的指纹
      */
     fun deleteNoUseFingerprint(fingerprintData: List<String>, userId: Long)
+
+    /**
+     * 用户管理分页(带筛选)
+     *
+     * @param filter UserManageFilterVo(nickname/cardNfc/workstationName/status)
+     * @param current 页码(从 0 开始)
+     * @param size 页大小
+     */
+    fun getUserManagePage(
+        filter: UserManageFilterVo?,
+        current: Int,
+        size: Int
+    ): List<UserManageVo>
+
+    /**
+     * 指定作业票下用户(限定 userIds + userRole),包含分组名
+     */
+    fun getJobUserDataByUserIdAndTicketId(
+        ticketId: Long,
+        userIds: List<Long>,
+        userRoleuserRole: String
+    ): List<JobUserVo>
+
+    /**
+     * 按岗位获取全体用户
+     */
+    fun getAllUsersWithWorkstation(
+        workstationId: Long
+    ): List<UserManageVo>
 }

+ 23 - 0
data/src/main/java/com/grkj/data/repository/impl/network/NetworkUserRepositoryImpl.kt

@@ -1,5 +1,8 @@
 package com.grkj.data.repository.impl.network
 
+import com.grkj.data.domain.vo.JobUserVo
+import com.grkj.data.domain.vo.UserManageFilterVo
+import com.grkj.data.domain.vo.UserManageVo
 import com.grkj.data.local.dos.SysUserCharacteristicDo
 import com.grkj.data.local.dos.SysUserDo
 import com.grkj.data.repository.BaseRepository
@@ -103,6 +106,26 @@ class NetworkUserRepositoryImpl @Inject constructor() : BaseRepository(), UserRe
         TODO("Not yet implemented")
     }
 
+    override fun getUserManagePage(
+        filter: UserManageFilterVo?,
+        current: Int,
+        size: Int
+    ): List<UserManageVo> {
+        TODO("Not yet implemented")
+    }
+
+    override fun getJobUserDataByUserIdAndTicketId(
+        ticketId: Long,
+        userIds: List<Long>,
+        userRole: String
+    ): List<JobUserVo> {
+        TODO("Not yet implemented")
+    }
+
+    override fun getAllUsersWithWorkstation(workstationId: Long): List<UserManageVo> {
+        TODO("Not yet implemented")
+    }
+
     override fun bindUserCharacteristic(group: String, userId: Long) {
         TODO("Not yet implemented")
     }

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

@@ -1,5 +1,8 @@
 package com.grkj.data.repository.impl.standard
 
+import com.grkj.data.domain.vo.JobUserVo
+import com.grkj.data.domain.vo.UserManageFilterVo
+import com.grkj.data.domain.vo.UserManageVo
 import com.grkj.data.local.dao.UserDao
 import com.grkj.data.local.dos.SysUserCharacteristicDo
 import com.grkj.data.local.dos.SysUserDo
@@ -123,5 +126,45 @@ class UserRepositoryImpl @Inject constructor(
     override fun getSysUserCharacteristicDo(userId: Long): List<SysUserCharacteristicDo> {
         return userDao.getSysUserCharacteristicDo(userId)
     }
-
+    override fun getUserManagePage(
+        filter: UserManageFilterVo?,
+        current: Int,
+        size: Int
+    ): List<UserManageVo> {
+        val nickname        = filter?.nickname?.takeIf { it.isNotEmpty() }
+        val statusStr       = when (filter?.status) { true -> "1"; false -> "0"; null -> null }
+        val cardNfc         = filter?.cardNfc?.takeIf { it.isNotEmpty() }
+        val workstationName = filter?.workstationName?.takeIf { !it.isNullOrEmpty() }
+        val offset          = current * size
+
+        logger.debug(
+            "getUserManagePage(size={}, offset={}, nickname={}, status={}, cardNfc={}, workstationName={})",
+            size, offset, nickname, statusStr, cardNfc, workstationName
+        )
+        return userDao.getUserManagePage(
+            nickname = nickname,
+            status = statusStr,
+            cardNfc = cardNfc,
+            workstationName = workstationName,
+            size = size,
+            offset = offset
+        )
+    }
+
+    override fun getJobUserDataByUserIdAndTicketId(
+        ticketId: Long,
+        userIds: List<Long>,
+        userRole: String
+    ): List<JobUserVo> {
+        logger.debug(
+            "getJobUsersByIdsAndTicket(ticketId={}, userRole={}, userIds={})",
+            ticketId, userRole, userIds.size
+        )
+        return userDao.getJobUsersByIdsAndTicket(ticketId, userIds, userRole)
+    }
+
+    override fun getAllUsersWithWorkstation(workstationId: Long): List<UserManageVo> {
+        logger.debug("getAllUsersWithWorkstation(workstationId={})", workstationId)
+        return userDao.getAllUsersWithWorkstation(workstationId)
+    }
 }