You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@paimon.apache.org by ni...@apache.org on 2024/04/06 11:03:28 UTC
(paimon-webui) branch main updated: [Improvement] Improves the user management interface (#148)
This is an automated email from the ASF dual-hosted git repository.
nicholasjiang pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/paimon-webui.git
The following commit(s) were added to refs/heads/main by this push:
new a51f2bb [Improvement] Improves the user management interface (#148)
a51f2bb is described below
commit a51f2bb8f6f3f13ba195654e825f00ec9acbdddf
Author: s7monk <34...@users.noreply.github.com>
AuthorDate: Sat Apr 6 19:03:23 2024 +0800
[Improvement] Improves the user management interface (#148)
---
.../web/server/controller/UserController.java | 79 ++++++++-
.../web/server/data/dto/UserWithRolesDTO.java | 35 ++++
.../apache/paimon/web/server/data/model/User.java | 5 +
.../web/server/data/result/enums/Status.java | 2 +
.../data/{model/User.java => vo/UserVO.java} | 47 ++---
.../paimon/web/server/mapper/UserMapper.java | 14 +-
.../paimon/web/server/service/UserService.java | 53 +++++-
.../web/server/service/impl/UserServiceImpl.java | 96 +++++++++++
.../src/main/resources/i18n/messages.properties | 1 +
.../main/resources/i18n/messages_en_US.properties | 1 +
.../main/resources/i18n/messages_zh_CN.properties | 1 +
.../src/main/resources/mapper/UserMapper.xml | 58 ++++---
.../web/server/controller/PermissionTest.java | 4 +-
.../web/server/controller/UserControllerTest.java | 190 +++++++++++++++++++++
14 files changed, 523 insertions(+), 63 deletions(-)
diff --git a/paimon-web-server/src/main/java/org/apache/paimon/web/server/controller/UserController.java b/paimon-web-server/src/main/java/org/apache/paimon/web/server/controller/UserController.java
index 6cb1ceb..30158f8 100644
--- a/paimon-web-server/src/main/java/org/apache/paimon/web/server/controller/UserController.java
+++ b/paimon-web-server/src/main/java/org/apache/paimon/web/server/controller/UserController.java
@@ -19,17 +19,29 @@
package org.apache.paimon.web.server.controller;
import org.apache.paimon.web.server.data.model.User;
+import org.apache.paimon.web.server.data.result.PageR;
import org.apache.paimon.web.server.data.result.R;
+import org.apache.paimon.web.server.data.result.enums.Status;
+import org.apache.paimon.web.server.data.vo.UserVO;
import org.apache.paimon.web.server.service.UserService;
+import org.apache.paimon.web.server.util.PageSupport;
import cn.dev33.satoken.annotation.SaCheckPermission;
+import com.baomidou.mybatisplus.core.metadata.IPage;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
+import java.util.List;
+
import static org.apache.paimon.web.server.data.result.enums.Status.USER_NOT_EXIST;
/** User api controller. */
@@ -41,19 +53,76 @@ public class UserController {
@Autowired private UserService userService;
/**
- * get user by id.
+ * Get user by id.
*
* @param id user-id
- * @return {@link R} with {@link User}
+ * @return {@link R} with {@link UserVO}
*/
@SaCheckPermission("system:user:query")
@GetMapping("/{id}")
- public R<User> getUserById(@PathVariable("id") Long id) {
- User user = userService.getById(id);
+ public R<UserVO> getUser(@PathVariable("id") Integer id) {
+ UserVO user = userService.getUserById(id);
if (user == null) {
return R.failed(USER_NOT_EXIST);
}
- user.setPassword(null);
return R.succeed(user);
}
+
+ /**
+ * Get user views with pagination.
+ *
+ * @param user filter conditions
+ * @return paginated user view objects
+ */
+ @SaCheckPermission("system:user:list")
+ @GetMapping("/list")
+ public PageR<UserVO> listUsers(User user) {
+ IPage<User> page = PageSupport.startPage();
+ List<UserVO> list = userService.listUsers(page, user);
+ return PageR.<UserVO>builder().success(true).total(page.getTotal()).data(list).build();
+ }
+
+ /**
+ * Add a new user.
+ *
+ * @param user the user to be added, must not be null
+ * @return a {@code R<Void>} response indicating success or failure
+ */
+ @SaCheckPermission("system:user:add")
+ @PostMapping
+ public R<Void> add(@Validated @RequestBody User user) {
+ if (!userService.checkUserNameUnique(user)) {
+ return R.failed(Status.USER_NAME_ALREADY_EXISTS, user.getUsername());
+ }
+
+ return userService.insertUser(user) > 0 ? R.succeed() : R.failed();
+ }
+
+ /**
+ * Update an existing user's details.
+ *
+ * @param user the user with updated details, must not be null
+ * @return a {@code R<Void>} response indicating success or failure
+ */
+ @SaCheckPermission("system:user:update")
+ @PutMapping
+ public R<Void> update(@Validated @RequestBody User user) {
+ if (!userService.checkUserNameUnique(user)) {
+ return R.failed(Status.USER_NAME_ALREADY_EXISTS, user.getUsername());
+ }
+
+ return userService.updateUser(user) > 0 ? R.succeed() : R.failed();
+ }
+
+ /**
+ * Delete one or more users by user ID.
+ *
+ * @param userIds an array of user IDs to be deleted
+ * @return a {@code R<Void>} response indicating success or failure
+ */
+ @SaCheckPermission("system:user:delete")
+ @DeleteMapping("/{userIds}")
+ public R<Void> delete(@PathVariable Integer[] userIds) {
+ return userService.deleteUserByIds(userIds) > 0 ? R.succeed() : R.failed();
+ }
}
diff --git a/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/dto/UserWithRolesDTO.java b/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/dto/UserWithRolesDTO.java
new file mode 100644
index 0000000..77d4208
--- /dev/null
+++ b/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/dto/UserWithRolesDTO.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.paimon.web.server.data.dto;
+
+import org.apache.paimon.web.server.data.model.SysRole;
+import org.apache.paimon.web.server.data.model.User;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.List;
+
+/** DTO of UserWithRoles. */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class UserWithRolesDTO extends User {
+
+ private List<SysRole> roles;
+}
diff --git a/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/model/User.java b/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/model/User.java
index 3bbc2d7..9f3444a 100644
--- a/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/model/User.java
+++ b/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/model/User.java
@@ -20,6 +20,7 @@ package org.apache.paimon.web.server.data.model;
import org.apache.paimon.web.server.constant.Constants;
+import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableLogic;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -58,6 +59,10 @@ public class User extends BaseModel {
/** avatar url. */
private String url;
+ /** role ids. */
+ @TableField(exist = false)
+ private Integer[] roleIds;
+
public boolean isAdmin() {
return isAdmin(this.getId());
}
diff --git a/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/result/enums/Status.java b/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/result/enums/Status.java
index 9a28cef..0f6b314 100644
--- a/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/result/enums/Status.java
+++ b/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/result/enums/Status.java
@@ -40,10 +40,12 @@ public enum Status {
REQUEST_PARAMS_NOT_VALID_ERROR(4001, "invalid.request.parameter"),
REQUEST_PARAMS_ERROR(4002, "request.parameter.error"),
+ /** ------------user-----------------. */
USER_NOT_EXIST(10001, "user.not.exist"),
USER_PASSWORD_ERROR(10002, "user.password.error"),
USER_DISABLED_ERROR(10003, "user.is.disabled"),
USER_NOT_BING_TENANT(10004, "user.not.bing.tenant"),
+ USER_NAME_ALREADY_EXISTS(10005, "user.name.exist"),
/** ------------role-----------------. */
ROLE_IN_USED(10101, "role.in.used"),
ROLE_NAME_IS_EXIST(10102, "role.name.exist"),
diff --git a/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/model/User.java b/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/vo/UserVO.java
similarity index 52%
copy from paimon-web-server/src/main/java/org/apache/paimon/web/server/data/model/User.java
copy to paimon-web-server/src/main/java/org/apache/paimon/web/server/data/vo/UserVO.java
index 3bbc2d7..3f59429 100644
--- a/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/model/User.java
+++ b/paimon-web-server/src/main/java/org/apache/paimon/web/server/data/vo/UserVO.java
@@ -16,53 +16,42 @@
* limitations under the License.
*/
-package org.apache.paimon.web.server.data.model;
+package org.apache.paimon.web.server.data.vo;
-import org.apache.paimon.web.server.constant.Constants;
+import org.apache.paimon.web.server.data.model.SysRole;
-import com.baomidou.mybatisplus.annotation.TableLogic;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
import lombok.Data;
-import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
-/** user table model. */
+import java.time.LocalDateTime;
+import java.util.List;
+
+/** VO of User. */
@Data
-@EqualsAndHashCode(callSuper = true)
-public class User extends BaseModel {
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class UserVO {
- private static final long serialVersionUID = 1L;
+ private Integer id;
- /** username. */
private String username;
- /** password. */
- private String password;
-
- /** nickname. */
private String nickname;
- /** login type (0:LOCAL,1:LDAP). */
- private Integer userType;
+ private String userType;
- /** mobile phone. */
private String mobile;
- /** email. */
private String email;
- /** is enable. */
private Boolean enabled;
- /** is delete. */
- @TableLogic private Boolean isDelete;
-
- /** avatar url. */
- private String url;
+ private LocalDateTime createTime;
- public boolean isAdmin() {
- return isAdmin(this.getId());
- }
+ private LocalDateTime updateTime;
- public static boolean isAdmin(Integer userId) {
- return userId != null && Constants.ADMIN_ID == userId;
- }
+ private List<SysRole> roles;
}
diff --git a/paimon-web-server/src/main/java/org/apache/paimon/web/server/mapper/UserMapper.java b/paimon-web-server/src/main/java/org/apache/paimon/web/server/mapper/UserMapper.java
index a58b47c..7c7459d 100644
--- a/paimon-web-server/src/main/java/org/apache/paimon/web/server/mapper/UserMapper.java
+++ b/paimon-web-server/src/main/java/org/apache/paimon/web/server/mapper/UserMapper.java
@@ -18,10 +18,13 @@
package org.apache.paimon.web.server.mapper;
+import org.apache.paimon.web.server.data.dto.UserWithRolesDTO;
import org.apache.paimon.web.server.data.model.User;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
import java.util.List;
@@ -32,9 +35,10 @@ public interface UserMapper extends BaseMapper<User> {
* Query user list.
*
* @param user query params
+ * @param page paging params
* @return user list
*/
- List<User> selectUserList(User user);
+ List<UserWithRolesDTO> listUsers(IPage<User> page, @Param("user") User user);
/**
* Query user list by role ID.
@@ -61,10 +65,10 @@ public interface UserMapper extends BaseMapper<User> {
User selectUserByUserName(String username);
/**
- * Query user info by user ID.
+ * Retrieves a user along with their roles based on the user's ID.
*
- * @param userId user ID
- * @return user info
+ * @param userId the ID of the user to retrieve
+ * @return the UserWithRolesDTO containing user and role information
*/
- User selectUserById(Integer userId);
+ UserWithRolesDTO selectUserWithRolesById(Integer userId);
}
diff --git a/paimon-web-server/src/main/java/org/apache/paimon/web/server/service/UserService.java b/paimon-web-server/src/main/java/org/apache/paimon/web/server/service/UserService.java
index 626fc5c..1b637b5 100644
--- a/paimon-web-server/src/main/java/org/apache/paimon/web/server/service/UserService.java
+++ b/paimon-web-server/src/main/java/org/apache/paimon/web/server/service/UserService.java
@@ -22,7 +22,9 @@ import org.apache.paimon.web.server.data.dto.LoginDTO;
import org.apache.paimon.web.server.data.model.User;
import org.apache.paimon.web.server.data.result.exception.BaseException;
import org.apache.paimon.web.server.data.vo.UserInfoVO;
+import org.apache.paimon.web.server.data.vo.UserVO;
+import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
@@ -31,7 +33,32 @@ import java.util.List;
public interface UserService extends IService<User> {
/**
- * login by username and password.
+ * Get a user by ID.
+ *
+ * @param id the user ID
+ * @return the UserVO or null if not found
+ */
+ UserVO getUserById(Integer id);
+
+ /**
+ * Select users with pagination.
+ *
+ * @param page the pagination information
+ * @param user the filter criteria
+ * @return list of UserVO
+ */
+ List<UserVO> listUsers(IPage<User> page, User user);
+
+ /**
+ * Check if the username is unique.
+ *
+ * @param user the user to check
+ * @return true if unique, false otherwise
+ */
+ boolean checkUserNameUnique(User user);
+
+ /**
+ * Login by username and password.
*
* @param loginDTO login params
* @return {@link String}
@@ -53,4 +80,28 @@ public interface UserService extends IService<User> {
* @return user list
*/
List<User> selectUnallocatedList(User user);
+
+ /**
+ * Insert a new user.
+ *
+ * @param user the user to be inserted
+ * @return the number of rows affected
+ */
+ int insertUser(User user);
+
+ /**
+ * Update an existing user.
+ *
+ * @param user the user with updated information
+ * @return the number of rows affected
+ */
+ int updateUser(User user);
+
+ /**
+ * Delete users by user ID.
+ *
+ * @param userIds the ids of the users to delete
+ * @return the number of rows affected
+ */
+ int deleteUserByIds(Integer[] userIds);
}
diff --git a/paimon-web-server/src/main/java/org/apache/paimon/web/server/service/impl/UserServiceImpl.java b/paimon-web-server/src/main/java/org/apache/paimon/web/server/service/impl/UserServiceImpl.java
index 17828ab..83da9ff 100644
--- a/paimon-web-server/src/main/java/org/apache/paimon/web/server/service/impl/UserServiceImpl.java
+++ b/paimon-web-server/src/main/java/org/apache/paimon/web/server/service/impl/UserServiceImpl.java
@@ -19,6 +19,7 @@
package org.apache.paimon.web.server.service.impl;
import org.apache.paimon.web.server.data.dto.LoginDTO;
+import org.apache.paimon.web.server.data.dto.UserWithRolesDTO;
import org.apache.paimon.web.server.data.enums.UserType;
import org.apache.paimon.web.server.data.model.RoleMenu;
import org.apache.paimon.web.server.data.model.SysMenu;
@@ -30,7 +31,9 @@ import org.apache.paimon.web.server.data.result.exception.user.UserDisabledExcep
import org.apache.paimon.web.server.data.result.exception.user.UserNotExistsException;
import org.apache.paimon.web.server.data.result.exception.user.UserPasswordNotMatchException;
import org.apache.paimon.web.server.data.vo.UserInfoVO;
+import org.apache.paimon.web.server.data.vo.UserVO;
import org.apache.paimon.web.server.mapper.UserMapper;
+import org.apache.paimon.web.server.mapper.UserRoleMapper;
import org.apache.paimon.web.server.service.LdapService;
import org.apache.paimon.web.server.service.RoleMenuService;
import org.apache.paimon.web.server.service.SysMenuService;
@@ -38,17 +41,23 @@ import org.apache.paimon.web.server.service.SysRoleService;
import org.apache.paimon.web.server.service.TenantService;
import org.apache.paimon.web.server.service.UserRoleService;
import org.apache.paimon.web.server.service.UserService;
+import org.apache.paimon.web.server.util.StringUtils;
import cn.dev33.satoken.secure.SaSecureUtil;
import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
+import java.util.Objects;
import java.util.Optional;
+import java.util.stream.Collectors;
/** UserServiceImpl. */
@Service
@@ -61,6 +70,30 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
@Autowired private RoleMenuService roleMenuService;
@Autowired private SysMenuService sysMenuService;
@Autowired private TenantService tenantService;
+ @Autowired private UserRoleMapper userRoleMapper;
+
+ @Override
+ public UserVO getUserById(Integer id) {
+ UserWithRolesDTO userWithRolesDTO = userMapper.selectUserWithRolesById(id);
+ if (Objects.nonNull(userWithRolesDTO)) {
+ return toVo(userWithRolesDTO);
+ }
+ return null;
+ }
+
+ @Override
+ public List<UserVO> listUsers(IPage<User> page, User user) {
+ return userMapper.listUsers(page, user).stream()
+ .map(this::toVo)
+ .collect(Collectors.toList());
+ }
+
+ @Override
+ public boolean checkUserNameUnique(User user) {
+ int userId = user.getId() == null ? -1 : user.getId();
+ User info = this.lambdaQuery().eq(User::getUsername, user.getUsername()).one();
+ return info == null || info.getId() == userId;
+ }
/**
* login by username and password.
@@ -189,4 +222,67 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
public List<User> selectUnallocatedList(User user) {
return userMapper.selectUnallocatedList(user);
}
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public int insertUser(User user) {
+ this.save(user);
+ return insertUserRole(user);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public int updateUser(User user) {
+ this.updateById(user);
+ userRoleMapper.deleteUserRoleByUserId(user.getId());
+ return insertUserRole(user);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public int deleteUserByIds(Integer[] userIds) {
+ userRoleMapper.deleteUserRole(userIds);
+ return userMapper.deleteBatchIds(Arrays.asList(userIds));
+ }
+
+ private int insertUserRole(User user) {
+ int rows = 1;
+ if (user.getRoleIds() != null && user.getRoleIds().length > 0) {
+ List<UserRole> list = new ArrayList<>();
+ for (Integer roleId : user.getRoleIds()) {
+ UserRole userRole = new UserRole();
+ userRole.setUserId(user.getId());
+ userRole.setRoleId(roleId);
+ list.add(userRole);
+ }
+ if (!list.isEmpty()) {
+ rows = userRoleMapper.batchUserRole(list);
+ }
+ }
+ return rows;
+ }
+
+ private UserVO toVo(UserWithRolesDTO userWithRolesDTO) {
+ return UserVO.builder()
+ .id(userWithRolesDTO.getId())
+ .username(userWithRolesDTO.getUsername())
+ .nickname(
+ StringUtils.isNotEmpty(userWithRolesDTO.getNickname())
+ ? userWithRolesDTO.getNickname()
+ : "")
+ .userType(userWithRolesDTO.getUserType() == 0 ? "LOCAL" : "LDAP")
+ .mobile(
+ StringUtils.isNotEmpty(userWithRolesDTO.getMobile())
+ ? userWithRolesDTO.getMobile()
+ : "")
+ .email(
+ StringUtils.isNotEmpty(userWithRolesDTO.getEmail())
+ ? userWithRolesDTO.getEmail()
+ : "")
+ .enabled(userWithRolesDTO.getEnabled())
+ .createTime(userWithRolesDTO.getCreateTime())
+ .updateTime(userWithRolesDTO.getUpdateTime())
+ .roles(userWithRolesDTO.getRoles())
+ .build();
+ }
}
diff --git a/paimon-web-server/src/main/resources/i18n/messages.properties b/paimon-web-server/src/main/resources/i18n/messages.properties
index 8e74fbc..35f3a68 100644
--- a/paimon-web-server/src/main/resources/i18n/messages.properties
+++ b/paimon-web-server/src/main/resources/i18n/messages.properties
@@ -27,6 +27,7 @@ user.not.exist=User Not Exist
user.password.error=User Password Error
user.is.disabled=User Is Disabled
user.not.bing.tenant=User Not Bing Tenant
+user.name.exist=The username {0} already exists.
role.in.used=This role {0} is in used
role.name.exist=This role name {0} exists.
role.key.exist=This role key {0} exists.
diff --git a/paimon-web-server/src/main/resources/i18n/messages_en_US.properties b/paimon-web-server/src/main/resources/i18n/messages_en_US.properties
index 8e74fbc..35f3a68 100644
--- a/paimon-web-server/src/main/resources/i18n/messages_en_US.properties
+++ b/paimon-web-server/src/main/resources/i18n/messages_en_US.properties
@@ -27,6 +27,7 @@ user.not.exist=User Not Exist
user.password.error=User Password Error
user.is.disabled=User Is Disabled
user.not.bing.tenant=User Not Bing Tenant
+user.name.exist=The username {0} already exists.
role.in.used=This role {0} is in used
role.name.exist=This role name {0} exists.
role.key.exist=This role key {0} exists.
diff --git a/paimon-web-server/src/main/resources/i18n/messages_zh_CN.properties b/paimon-web-server/src/main/resources/i18n/messages_zh_CN.properties
index 8cffa97..96ca9fa 100644
--- a/paimon-web-server/src/main/resources/i18n/messages_zh_CN.properties
+++ b/paimon-web-server/src/main/resources/i18n/messages_zh_CN.properties
@@ -27,6 +27,7 @@ user.not.exist=\u7528\u6237\u4E0D\u5B58\u5728
user.password.error=\u5BC6\u7801\u9519\u8BEF
user.is.disabled=\u7528\u6237\u5DF2\u7981\u7528
user.not.bing.tenant=\u7528\u6237\u672A\u7ED1\u5B9A\u79DF\u6237
+user.name.exist=\u8BE5\u7528\u6237\u540D{0}\u5DF2\u7ECF\u5B58\u5728
role.in.used=\u6B64\u89D2\u8272{0}\u6B63\u5728\u4F7F\u7528\u4E2D
role.name.exist=\u6B64\u89D2\u8272\u540D\u79F0{0}\u5DF2\u4F7F\u7528
role.key.exist=\u6B64\u89D2\u8272\u5173\u952E\u5B57{0}\u5DF2\u4F7F\u7528
diff --git a/paimon-web-server/src/main/resources/mapper/UserMapper.xml b/paimon-web-server/src/main/resources/mapper/UserMapper.xml
index fc13d9f..a71b9a2 100644
--- a/paimon-web-server/src/main/resources/mapper/UserMapper.xml
+++ b/paimon-web-server/src/main/resources/mapper/UserMapper.xml
@@ -22,10 +22,27 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.apache.paimon.web.server.mapper.UserMapper">
+ <resultMap id="UserWithRolesResult" type="org.apache.paimon.web.server.data.dto.UserWithRolesDTO">
+ <id property="id" column="id" />
+ <result property="username" column="username" />
+ <result property="nickname" column="nickname" />
+ <result property="userType" column="user_type" />
+ <result property="email" column="email" />
+ <result property="mobile" column="mobile" />
+ <result property="url" column="url" />
+ <result property="enabled" column="enabled" />
+ <result property="password" column="password" />
+ <result property="isDelete" column="is_delete" />
+ <result property="createTime" column="create_time" />
+ <result property="updateTime" column="update_time" />
+ <collection property="roles" resultMap="RoleResult" />
+ </resultMap>
+
<resultMap type="org.apache.paimon.web.server.data.model.User" id="SysUserResult">
<id property="id" column="id" />
- <result property="username" column="user_name" />
- <result property="nickname" column="nick_name" />
+ <result property="username" column="username" />
+ <result property="nickname" column="nickname" />
+ <result property="userType" column="user_type" />
<result property="email" column="email" />
<result property="mobile" column="mobile" />
<result property="url" column="url" />
@@ -34,11 +51,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="isDelete" column="is_delete" />
<result property="createTime" column="create_time" />
<result property="updateTime" column="update_time" />
- <collection property="roles" javaType="java.util.List" resultMap="RoleResult" />
</resultMap>
<resultMap id="RoleResult" type="org.apache.paimon.web.server.data.model.SysRole">
- <id property="id" column="role_id" />
+ <id property="id" column="id" />
<result property="roleName" column="role_name" />
<result property="roleKey" column="role_key" />
<result property="sort" column="sort" />
@@ -46,33 +62,33 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</resultMap>
<sql id="selectUserVo">
- select u.id, u.username, u.nickname, u.email, u.url, u.mobile, u.password, u.is_delete, u.enabled, u.create_time,
+ select u.id, u.username, u.nickname, u.user_type, u.email, u.url, u.mobile, u.password, u.is_delete, u.enabled, u.create_time,
r.id, r.role_name, r.role_key, r.sort, r.enabled as role_status
from user u
left join user_role urole on u.id = urole.user_id
left join sys_role r on r.id = urole.role_id
</sql>
- <select id="selectUserList" parameterType="org.apache.paimon.web.server.data.model.User" resultMap="SysUserResult">
- select u.id, u.nickname, u.username, u.email, u.url, u.mobile, u.enabled, u.is_delete, u.create_time from user u
+ <select id="listUsers" parameterType="org.apache.paimon.web.server.data.model.User" resultMap="UserWithRolesResult">
+ <include refid="selectUserVo"/>
where u.is_delete = '0'
- <if test="id != null and id != 0">
- AND u.id = #{userId}
+ <if test="user.id != null and user.id != 0">
+ AND u.id = #{user.id}
</if>
- <if test="username != null and username != ''">
- AND u.username like concat('%', #{userName}, '%')
+ <if test="user.username != null and user.username != ''">
+ AND u.username like concat('%', #{user.username}, '%')
</if>
- <if test="enabled != null and enabled != ''">
- AND u.enabled = #{enabled}
+ <if test="user.enabled != null and user.enabled != ''">
+ AND u.enabled = #{user.enabled}
</if>
- <if test="mobile != null and mobile != ''">
- AND u.mobile like concat('%', #{mobile}, '%')
+ <if test="user.mobile != null and user.mobile != ''">
+ AND u.mobile like concat('%', #{user.mobile}, '%')
</if>
- <if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
- AND date_format(u.create_time,'%y%m%d') >= date_format(#{params.beginTime},'%y%m%d')
+ <if test="user.params.beginTime != null and user.params.beginTime != ''"><!-- Start time search -->
+ AND date_format(u.create_time,'%y%m%d') >= date_format(#{user.params.beginTime},'%y%m%d')
</if>
- <if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
- AND date_format(u.create_time,'%y%m%d') <= date_format(#{params.endTime},'%y%m%d')
+ <if test="user.params.endTime != null and user.params.endTime != ''"><!-- End time search -->
+ AND date_format(u.create_time,'%y%m%d') <= date_format(#{user.params.endTime},'%y%m%d')
</if>
</select>
@@ -109,8 +125,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<include refid="selectUserVo"/>
where u.username = #{username} and u.is_delete = '0'
</select>
-
- <select id="selectUserById" parameterType="Long" resultMap="SysUserResult">
+
+ <select id="selectUserWithRolesById" parameterType="Integer" resultMap="UserWithRolesResult">
<include refid="selectUserVo"/>
where u.id = #{id}
</select>
diff --git a/paimon-web-server/src/test/java/org/apache/paimon/web/server/controller/PermissionTest.java b/paimon-web-server/src/test/java/org/apache/paimon/web/server/controller/PermissionTest.java
index 58a6596..55c5e96 100644
--- a/paimon-web-server/src/test/java/org/apache/paimon/web/server/controller/PermissionTest.java
+++ b/paimon-web-server/src/test/java/org/apache/paimon/web/server/controller/PermissionTest.java
@@ -18,9 +18,9 @@
package org.apache.paimon.web.server.controller;
-import org.apache.paimon.web.server.data.model.User;
import org.apache.paimon.web.server.data.result.R;
import org.apache.paimon.web.server.data.result.enums.Status;
+import org.apache.paimon.web.server.data.vo.UserVO;
import org.apache.paimon.web.server.util.ObjectMapperUtils;
import com.fasterxml.jackson.core.type.TypeReference;
@@ -54,7 +54,7 @@ public class PermissionTest extends ControllerTestBase {
.getResponse()
.getContentAsString();
- R<User> r = ObjectMapperUtils.fromJSON(responseString, new TypeReference<R<User>>() {});
+ R<UserVO> r = ObjectMapperUtils.fromJSON(responseString, new TypeReference<R<UserVO>>() {});
assertEquals(Status.SUCCESS.getCode(), r.getCode());
}
}
diff --git a/paimon-web-server/src/test/java/org/apache/paimon/web/server/controller/UserControllerTest.java b/paimon-web-server/src/test/java/org/apache/paimon/web/server/controller/UserControllerTest.java
new file mode 100644
index 0000000..eebeb95
--- /dev/null
+++ b/paimon-web-server/src/test/java/org/apache/paimon/web/server/controller/UserControllerTest.java
@@ -0,0 +1,190 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.paimon.web.server.controller;
+
+import org.apache.paimon.web.server.data.model.User;
+import org.apache.paimon.web.server.data.result.PageR;
+import org.apache.paimon.web.server.data.result.R;
+import org.apache.paimon.web.server.data.vo.UserVO;
+import org.apache.paimon.web.server.util.ObjectMapperUtils;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestMethodOrder;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.http.MediaType;
+import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
+import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
+import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/** Test for {@link UserController}. */
+@SpringBootTest
+@AutoConfigureMockMvc
+@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
+public class UserControllerTest extends ControllerTestBase {
+
+ private static final String userPath = "/api/user";
+
+ private static final int userId = 3;
+ private static final String username = "test";
+
+ @Test
+ @Order(1)
+ public void testAddUser() throws Exception {
+ User user = new User();
+ user.setId(userId);
+ user.setUsername(username);
+ user.setNickname(username);
+ user.setUserType(0);
+ user.setEnabled(true);
+ user.setIsDelete(false);
+
+ mockMvc.perform(
+ MockMvcRequestBuilders.post(userPath)
+ .cookie(cookie)
+ .content(ObjectMapperUtils.toJSON(user))
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .accept(MediaType.APPLICATION_JSON_VALUE))
+ .andExpect(MockMvcResultMatchers.status().isOk())
+ .andDo(MockMvcResultHandlers.print());
+ }
+
+ @Test
+ @Order(2)
+ public void testGetUser() throws Exception {
+ String responseString =
+ mockMvc.perform(
+ MockMvcRequestBuilders.get(userPath + "/" + userId)
+ .cookie(cookie)
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .accept(MediaType.APPLICATION_JSON_VALUE))
+ .andExpect(MockMvcResultMatchers.status().isOk())
+ .andDo(MockMvcResultHandlers.print())
+ .andReturn()
+ .getResponse()
+ .getContentAsString();
+
+ R<UserVO> r = ObjectMapperUtils.fromJSON(responseString, new TypeReference<R<UserVO>>() {});
+ assertEquals(200, r.getCode());
+ assertNotNull(r.getData());
+ assertEquals(r.getData().getUsername(), username);
+ }
+
+ @Test
+ @Order(3)
+ public void testUpdateUser() throws Exception {
+ String newUserName = username + "-edit";
+ User user = new User();
+ user.setId(userId);
+ user.setUsername(newUserName);
+ user.setNickname(newUserName);
+ user.setUserType(0);
+ user.setEnabled(true);
+ user.setIsDelete(false);
+
+ mockMvc.perform(
+ MockMvcRequestBuilders.put(userPath)
+ .cookie(cookie)
+ .content(ObjectMapperUtils.toJSON(user))
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .accept(MediaType.APPLICATION_JSON_VALUE))
+ .andExpect(MockMvcResultMatchers.status().isOk());
+
+ String responseString =
+ mockMvc.perform(
+ MockMvcRequestBuilders.get(userPath + "/" + userId)
+ .cookie(cookie)
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .accept(MediaType.APPLICATION_JSON_VALUE))
+ .andExpect(MockMvcResultMatchers.status().isOk())
+ .andDo(MockMvcResultHandlers.print())
+ .andReturn()
+ .getResponse()
+ .getContentAsString();
+
+ R<UserVO> r = ObjectMapperUtils.fromJSON(responseString, new TypeReference<R<UserVO>>() {});
+ assertEquals(200, r.getCode());
+ assertNotNull(r.getData());
+ assertEquals(r.getData().getUsername(), newUserName);
+ }
+
+ @Test
+ @Order(4)
+ public void testListUsers() throws Exception {
+ String responseString =
+ mockMvc.perform(
+ MockMvcRequestBuilders.get(userPath + "/list")
+ .cookie(cookie)
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .accept(MediaType.APPLICATION_JSON_VALUE))
+ .andExpect(MockMvcResultMatchers.status().isOk())
+ .andDo(MockMvcResultHandlers.print())
+ .andReturn()
+ .getResponse()
+ .getContentAsString();
+
+ PageR<UserVO> r =
+ ObjectMapperUtils.fromJSON(responseString, new TypeReference<PageR<UserVO>>() {});
+ assertTrue(
+ r.getData() != null
+ && ((r.getTotal() > 0 && r.getData().size() > 0)
+ || (r.getTotal() == 0 && r.getData().size() == 0)));
+
+ UserVO firstUser = r.getData().get(0);
+ assertEquals("admin", firstUser.getUsername());
+ assertEquals("Admin", firstUser.getNickname());
+ assertEquals("admin@paimon.com", firstUser.getEmail());
+ assertEquals("LOCAL", firstUser.getUserType());
+ assertTrue(firstUser.getEnabled());
+
+ UserVO secondUser = r.getData().get(1);
+ assertEquals("common", secondUser.getUsername());
+ assertEquals("common", secondUser.getNickname());
+ assertEquals("common@paimon.com", secondUser.getEmail());
+ assertEquals("LOCAL", secondUser.getUserType());
+ assertTrue(secondUser.getEnabled());
+ }
+
+ @Test
+ @Order(5)
+ public void testDeleteUser() throws Exception {
+ String delResponseString =
+ mockMvc.perform(
+ MockMvcRequestBuilders.delete(
+ userPath + "/" + userId + "," + userId)
+ .cookie(cookie)
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .accept(MediaType.APPLICATION_JSON_VALUE))
+ .andExpect(MockMvcResultMatchers.status().isOk())
+ .andDo(MockMvcResultHandlers.print())
+ .andReturn()
+ .getResponse()
+ .getContentAsString();
+
+ R<?> result = ObjectMapperUtils.fromJSON(delResponseString, R.class);
+ assertEquals(200, result.getCode());
+ }
+}