Bladeren bron

Merge branch '84-integrate' into 'develop'

84 integrate



See merge request !22

linwenhua 2 jaren geleden
bovenliggende
commit
0ea970867e
38 gewijzigde bestanden met toevoegingen van 1980 en 12 verwijderingen
  1. 4 0
      smsb-customer-manager-adapter/pom.xml
  2. 92 4
      smsb-customer-manager-adapter/src/main/java/com/inspur/customer/web/controller/keyclaok/KeycloakController.java
  3. 127 0
      smsb-customer-manager-adapter/src/main/java/com/inspur/customer/web/controller/org/SmsbDepartmentController.java
  4. 6 0
      smsb-customer-manager-app/pom.xml
  5. 191 3
      smsb-customer-manager-app/src/main/java/com/inspur/customer/service/keycloak/KeycloakServiceImpl.java
  6. 239 0
      smsb-customer-manager-app/src/main/java/com/inspur/customer/service/org/SmsbDepartmentServiceImpl.java
  7. 117 0
      smsb-customer-manager-app/src/main/java/com/inspur/customer/service/org/SmsbDepartmentUserServiceImpl.java
  8. 7 0
      smsb-customer-manager-app/src/main/resources/email/CameraSnapPicReleaseWarn.html
  9. 7 0
      smsb-customer-manager-app/src/main/resources/email/CameraSnapPicWarn.html
  10. 24 0
      smsb-customer-manager-app/src/main/resources/weChat/CameraSnapPicReleaseWarn.json
  11. 24 0
      smsb-customer-manager-app/src/main/resources/weChat/CameraSnapPicWarn.json
  12. 4 0
      smsb-customer-manager-client/pom.xml
  13. 142 0
      smsb-customer-manager-client/src/main/java/com/inspur/customer/client/keycloak/KeycloakService.java
  14. 94 0
      smsb-customer-manager-client/src/main/java/com/inspur/customer/client/org/SmsbDepartmentService.java
  15. 87 0
      smsb-customer-manager-client/src/main/java/com/inspur/customer/client/org/SmsbDepartmentUserService.java
  16. 74 0
      smsb-customer-manager-client/src/main/java/com/inspur/customer/constant/Constant.java
  17. 9 1
      smsb-customer-manager-client/src/main/java/com/inspur/customer/context/inform/InformTypeEnum.java
  18. 11 4
      smsb-customer-manager-client/src/main/java/com/inspur/customer/context/inform/MessageTemplateEnum.java
  19. 29 0
      smsb-customer-manager-client/src/main/java/com/inspur/customer/object/keycloak/SwitchDTO.java
  20. 32 0
      smsb-customer-manager-client/src/main/java/com/inspur/customer/object/keycloak/UsersRoleMappingDTO.java
  21. 24 0
      smsb-customer-manager-client/src/main/java/com/inspur/customer/object/org/OperationAuthority.java
  22. 29 0
      smsb-customer-manager-client/src/main/java/com/inspur/customer/object/org/SmsbDepartmentAdd.java
  23. 74 0
      smsb-customer-manager-client/src/main/java/com/inspur/customer/object/org/SmsbDepartmentCO.java
  24. 42 0
      smsb-customer-manager-client/src/main/java/com/inspur/customer/object/org/SmsbDepartmentCmd.java
  25. 30 0
      smsb-customer-manager-client/src/main/java/com/inspur/customer/object/org/SmsbDepartmentUpdate.java
  26. 22 0
      smsb-customer-manager-client/src/main/java/com/inspur/customer/object/org/SmsbRegrouping.java
  27. 52 0
      smsb-customer-manager-client/src/main/java/com/inspur/customer/object/org/SmsbSimpleUserCO.java
  28. 48 0
      smsb-customer-manager-client/src/main/java/com/inspur/customer/object/org/SmsbUserAdd.java
  29. 54 0
      smsb-customer-manager-client/src/main/java/com/inspur/customer/object/org/SmsbUserMigration.java
  30. 29 0
      smsb-customer-manager-client/src/main/java/com/inspur/customer/object/org/SmsbUserUpdate.java
  31. 10 0
      smsb-customer-manager-infrastructure/pom.xml
  32. 44 0
      smsb-customer-manager-infrastructure/src/main/java/com/inspur/customer/infrastructure/convertor/SmsbDepartmentConvertor.java
  33. 14 0
      smsb-customer-manager-infrastructure/src/main/java/com/inspur/customer/infrastructure/mapper/org/SmsbDepartmentMapper.java
  34. 15 0
      smsb-customer-manager-infrastructure/src/main/java/com/inspur/customer/infrastructure/mapper/org/SmsbDepartmentUserMapper.java
  35. 88 0
      smsb-customer-manager-infrastructure/src/main/java/com/inspur/customer/infrastructure/object/org/SmsbDepartmentDO.java
  36. 75 0
      smsb-customer-manager-infrastructure/src/main/java/com/inspur/customer/infrastructure/object/org/SmsbDepartmentUserDO.java
  37. 5 0
      smsb-customer-manager-infrastructure/src/main/resources/mapper/org/SmsbDepartmentMapper.xml
  38. 5 0
      smsb-customer-manager-infrastructure/src/main/resources/mapper/org/SmsbDepartmentUserMapper.xml

+ 4 - 0
smsb-customer-manager-adapter/pom.xml

@@ -38,6 +38,10 @@
             <groupId>org.projectlombok</groupId>
             <artifactId>lombok</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-core</artifactId>
+        </dependency>
     </dependencies>
 
 </project>

+ 92 - 4
smsb-customer-manager-adapter/src/main/java/com/inspur/customer/web/controller/keyclaok/KeycloakController.java

@@ -5,15 +5,28 @@ import com.alibaba.cola.dto.SingleResponse;
 import com.alibaba.fastjson.JSONObject;
 import com.inspur.customer.client.keycloak.KeycloakService;
 import com.inspur.customer.client.wechat.IWeChatService;
+import com.inspur.customer.constant.Constant;
+import com.inspur.customer.object.keycloak.SwitchDTO;
+import com.inspur.customer.object.keycloak.UsersRoleMappingDTO;
 import com.inspur.customer.object.wechat.SubscribeDto;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.dubbo.config.Constants;
 import org.apache.dubbo.config.annotation.DubboReference;
+import org.keycloak.representations.idm.CredentialRepresentation;
+import org.keycloak.representations.idm.GroupRepresentation;
+import org.keycloak.representations.idm.RoleRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.scheduling.annotation.EnableScheduling;
 import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.util.CollectionUtils;
 import org.springframework.web.bind.annotation.*;
 
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
 /**
  * 微信模块
  * @author wangbo13
@@ -21,7 +34,6 @@ import org.springframework.web.bind.annotation.*;
 @Slf4j
 @RestController
 @EnableScheduling
-@RequestMapping("/keycloak")
 public class KeycloakController {
 
     @Value("${system.appKey}")
@@ -33,7 +45,7 @@ public class KeycloakController {
     @DubboReference
     private IWeChatService iWeChatService;
 
-    @PostMapping("update/user")
+    @PostMapping("/keycloak/update/user")
     public Response updateUser(@RequestBody String json){
         SubscribeDto subscribe = JSONObject.parseObject(json, SubscribeDto.class);
         if(StringUtils.isEmpty(subscribe.getUserId())){
@@ -43,13 +55,13 @@ public class KeycloakController {
         return Response.buildSuccess();
     }
 
-    @GetMapping("query/ticket/{userId}")
+    @GetMapping("/keycloak/query/ticket/{userId}")
     public SingleResponse getTicket(@PathVariable("userId") String userId,
                                     @RequestHeader("Authorization") String authorization) {
         return SingleResponse.of(iWeChatService.queryTicket(appKey, userId, authorization));
     }
 
-    @PutMapping("changePassword")
+    @PutMapping("/keycloak/changePassword")
     public Response changePassword(@RequestHeader("userId") String userId, String newPassword) {
         if (StringUtils.isEmpty(userId)) {
             return Response.buildFailure("400", "密码不能为空");
@@ -70,4 +82,80 @@ public class KeycloakController {
             }
         }
     }
+
+    @PostMapping("/admin/group/{id}/children")
+    public Response addKeycloakGroup(@PathVariable("id") String id , @RequestBody GroupRepresentation groupRepresentation){
+        if(StringUtils.isEmpty(groupRepresentation.getName())){
+            return Response.buildFailure("500","name不能为空!");
+        }
+        if(CollectionUtils.isEmpty(groupRepresentation.getAttributes())){
+            return Response.buildFailure("500","attributes不能为空!");
+        }
+        return keycloakService.addKeycloakGroup(id , groupRepresentation);
+    }
+
+    @PutMapping("/admin/group/{id}")
+    public Response updateKeycloakGroup(@PathVariable("id") String id , @RequestBody GroupRepresentation groupRepresentation){
+        if(StringUtils.isEmpty(groupRepresentation.getName())){
+            return Response.buildFailure("500","name不能为空!");
+        }
+        if(CollectionUtils.isEmpty(groupRepresentation.getAttributes())){
+            return Response.buildFailure("500","attributes不能为空!");
+        }
+        return keycloakService.updateKeycloakGroup(id , groupRepresentation);
+    }
+
+    @DeleteMapping("/admin/group/{id}")
+    public Response removeGroup(@PathVariable("id") String id){
+        return keycloakService.removeGroup(id);
+    }
+
+    @PutMapping("/admin/users/{userId}/resetPassword")
+    public Response resetPassword(@PathVariable("userId") String userId , @RequestBody CredentialRepresentation credential){
+        if(StringUtils.isEmpty(credential.getType()) || !credential.getType().equals(Constant.type)){
+            return Response.buildFailure("500","type参数不合法!");
+        }
+        if(StringUtils.isEmpty(credential.getValue())){
+            return Response.buildFailure("500","value不能为空!");
+        }
+        return keycloakService.updateUser(userId , credential , Constant.RESET_PASSWORD);
+    }
+
+    @PutMapping("/admin/users/role/configure")
+    public Response roleMapping(@RequestBody UsersRoleMappingDTO ssersRoleMappingDTO){
+        return keycloakService.roleMapping(ssersRoleMappingDTO);
+    }
+
+    @GetMapping("/admin/users/{userId}/credentials")
+    public Response queryUserCredentials(@PathVariable("userId")String userId){
+        return keycloakService.queryUserCredentials(userId);
+    }
+
+    @DeleteMapping("/admin/users/{userId}/credentials/{credentialId}")
+    public Response removeUserCredentials(@PathVariable("userId")String userId,
+                                          @PathVariable("credentialId")String credentialId){
+        return keycloakService.removeUserCredentials(userId ,credentialId);
+    }
+
+    @PutMapping("/keycloak/users/attribute")
+    public Response updateUserAttribute(@RequestHeader String userId , @RequestBody UserRepresentation representation){
+        return keycloakService.updateUser(userId , representation ,Constant.RESET_ATTRBUTE);
+    }
+
+    @PostMapping("/super/admin/tenant")
+    public Response addKeyclaokTenant(@RequestBody GroupRepresentation groupRepresentation){
+        if(StringUtils.isEmpty(groupRepresentation.getName())){
+            return Response.buildFailure("500","name不能为空!");
+        }
+        if(CollectionUtils.isEmpty(groupRepresentation.getAttributes())){
+            return Response.buildFailure("500","attributes不能为空!");
+        }
+        return keycloakService.addKeyclaokTenant(groupRepresentation);
+    }
+
+    @DeleteMapping("/super/admin/tenant/{id}")
+    public Response addKeyclaokTenant(@PathVariable("id")String id){
+        return keycloakService.removeGroup(id);
+    }
+
 }

+ 127 - 0
smsb-customer-manager-adapter/src/main/java/com/inspur/customer/web/controller/org/SmsbDepartmentController.java

@@ -0,0 +1,127 @@
+package com.inspur.customer.web.controller.org;
+
+import com.alibaba.cola.dto.PageResponse;
+import com.alibaba.cola.dto.Response;
+import com.alibaba.cola.dto.SingleResponse;
+import com.inspur.customer.client.keycloak.KeycloakService;
+import com.inspur.customer.client.org.SmsbDepartmentService;
+import com.inspur.customer.client.org.SmsbDepartmentUserService;
+import com.inspur.customer.constant.Constant;
+import com.inspur.customer.object.keycloak.SwitchDTO;
+import com.inspur.customer.object.org.*;
+import org.apache.dubbo.config.annotation.DubboReference;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+
+/**
+ * @Author wangbo13
+ * @Date 2022/11/25 15:09
+ * @Version 1.0
+ */
+@RestController
+public class SmsbDepartmentController {
+
+    @DubboReference
+    private SmsbDepartmentService departmentService;
+    @DubboReference
+    private SmsbDepartmentUserService userService;
+    @DubboReference
+    private KeycloakService service;
+
+    @PostMapping("/admin/department")
+    public Response addDepartment(@RequestHeader String userId, @Valid @RequestBody SmsbDepartmentAdd smsbDepartmentAdd){
+        Response checkResult = departmentService.checkOperationAuthority(userId, smsbDepartmentAdd.getTenant());
+        if(!checkResult.isSuccess()){return checkResult;}
+        return departmentService.addDepartment(userId ,smsbDepartmentAdd);
+    }
+
+    @DeleteMapping("/admin/department/{id}")
+    public Response DeleteDepartment(@RequestHeader String userId , @PathVariable("id")Long id){
+        Response checkResponse = departmentService.checkOperationAuthority(userId, departmentService.getOneById(id).getTenant());
+        if(!checkResponse.isSuccess()){return checkResponse;}
+        departmentService.deleteDepartment(userId , id);
+        return SingleResponse.buildSuccess();
+    }
+
+    @PutMapping("/admin/department")
+    public Response updateDepartment(@RequestHeader String userId , @Valid @RequestBody SmsbDepartmentUpdate departmentUpdate){
+        Response checkResponse = departmentService.checkOperationAuthority(userId, departmentService.getOneById(departmentUpdate.getId()).getTenant());
+        if(!checkResponse.isSuccess()){return checkResponse;}
+        return departmentService.updateDepartment(userId , departmentUpdate);
+    }
+
+    @PostMapping("/admin/department/list")
+    public Response queryDepartmentList(@RequestHeader String userId , @Valid @RequestBody OperationAuthority operationAuthority){
+        Response checkResponse = departmentService.checkOperationAuthority(userId, operationAuthority.getTenant());
+        if(!checkResponse.isSuccess()){return checkResponse;}
+        return departmentService.queryDepartmentList(operationAuthority.getTenant());
+    }
+
+    @PostMapping("/admin/users")
+    public Response addKeyClaokUser(@RequestHeader String userId ,@Valid @RequestBody SmsbUserAdd smsbUserAdd){
+        Response checkResponse = departmentService.checkOperationAuthority(userId, smsbUserAdd.getTenant());
+        if(!checkResponse.isSuccess()){return checkResponse;}
+        return departmentService.addKeyClaokUser(userId ,smsbUserAdd);
+    }
+
+    @PutMapping("/admin/users/regrouping")
+    public Response regrouping(@RequestHeader String userId,
+                               @Valid @RequestBody SmsbRegrouping smsbRegrouping){
+        Response checkResponse = departmentService.checkOperationAuthority(userId, smsbRegrouping.getTenant());
+        if(!checkResponse.isSuccess()){return checkResponse;}
+        return departmentService.regrouping(smsbRegrouping);
+    }
+
+    @DeleteMapping("/admin/users/{uId}")
+    public Response enableSwitch(@RequestHeader String userId,
+                                 @PathVariable("uId") String uId,
+                                 @Valid @RequestBody OperationAuthority authority){
+        Response checkResponse = departmentService.checkOperationAuthority(userId, authority.getTenant());
+        if(!checkResponse.isSuccess()){return checkResponse;}
+        if(userService.removeUser(uId)){
+            return service.removeUser(uId);
+        }else{
+            return Response.buildFailure("500","注销用户失败!");
+        }
+    }
+
+    @PostMapping("/admin/department/user/list")
+    public PageResponse<SmsbSimpleUserCO> queryDepartmentUserList(@RequestHeader String userId , @Valid @RequestBody SmsbDepartmentCmd departmentCmd){
+        Response checkResponse = departmentService.checkOperationAuthority(userId, departmentCmd.getTenant());
+        if(!checkResponse.isSuccess()){return PageResponse.buildFailure(checkResponse.getErrCode(),checkResponse.getErrMessage());}
+        return departmentService.queryDepartmentUserList(departmentCmd);
+    }
+
+    @PostMapping("/admin/users/temporary/migration")
+    public Response migration(@RequestHeader String userId , @RequestBody SmsbUserMigration userMigration){
+        Response checkResponse = departmentService.checkOperationAuthority(userId, userMigration.getTenant());
+        if(!checkResponse.isSuccess()){return checkResponse;}
+        return userService.migration(userMigration);
+    }
+
+    @PutMapping("/admin/users/enabled")
+    public Response enableSwitch(@RequestHeader String userId,
+                                 @RequestBody SwitchDTO switchDTO){
+        Response checkResponse = departmentService.checkOperationAuthority(userId, switchDTO.getTenant());
+        if(!checkResponse.isSuccess()){return checkResponse;}
+        Boolean flag = userService.enableSwitch(switchDTO);
+        if(flag){
+            return service.updateUser(switchDTO.getUserId() , switchDTO , Constant.ENABLE);
+        }
+        return Response.buildFailure("500","账号启用/禁用失败!");
+    }
+
+    @PutMapping("/admin/users/update")
+    public Response updateUserFirstName(@RequestHeader String userId ,
+                                        @RequestBody SmsbUserUpdate userUpdate){
+        Response checkResponse = departmentService.checkOperationAuthority(userId, userUpdate.getTenant());
+        if(!checkResponse.isSuccess()){return checkResponse;}
+        Boolean flag = userService.updateUserFirstName(userUpdate);
+        if(flag){
+            return service.updateUser(userUpdate.getUserId() , userUpdate.getFamilyName() , Constant.UPDATE_USER_FIRSTNAME);
+        }
+        return Response.buildFailure("500","用户名修改失败!");
+    }
+
+}

+ 6 - 0
smsb-customer-manager-app/pom.xml

@@ -73,6 +73,12 @@
             <groupId>com.google.guava</groupId>
             <artifactId>guava</artifactId>
         </dependency>
+        <dependency>
+            <groupId>com.inspur</groupId>
+            <artifactId>smsb-device-manager-client</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+            <scope>compile</scope>
+        </dependency>
     </dependencies>
 
 </project>

+ 191 - 3
smsb-customer-manager-app/src/main/java/com/inspur/customer/service/keycloak/KeycloakServiceImpl.java

@@ -1,14 +1,22 @@
 package com.inspur.customer.service.keycloak;
 
+import com.alibaba.cola.dto.PageResponse;
+import com.alibaba.cola.dto.Response;
+import com.alibaba.cola.dto.SingleResponse;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
 import com.inspur.customer.client.keycloak.KeycloakService;
 import com.inspur.customer.object.keycloak.KeycloakUserCO;
+import com.inspur.customer.object.keycloak.SwitchDTO;
+import com.inspur.customer.object.keycloak.UsersRoleMappingDTO;
+import com.inspur.customer.object.org.SmsbDepartmentCmd;
+import com.inspur.customer.object.org.SmsbUserAdd;
 import com.inspur.customer.object.wechat.Pair;
+import com.inspur.customer.constant.Constant;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.dubbo.config.annotation.DubboService;
-import org.keycloak.admin.client.resource.RealmResource;
-import org.keycloak.admin.client.resource.UserResource;
+import org.keycloak.admin.client.resource.*;
 import org.keycloak.representations.idm.*;
-import org.springframework.beans.factory.annotation.Value;
 import org.springframework.cache.annotation.Cacheable;
 import org.springframework.util.CollectionUtils;
 import org.springframework.util.StringUtils;
@@ -17,6 +25,7 @@ import javax.annotation.Resource;
 import java.util.*;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 @Slf4j
 @DubboService
@@ -240,4 +249,183 @@ public class KeycloakServiceImpl implements KeycloakService {
         });
         return userMap;
     }
+
+    @Override
+    public Response addKeycloakGroup(String id , GroupRepresentation groupRepresentation) {
+        return SingleResponse.of(realmResource.groups().group(id).subGroup(groupRepresentation).getDate());
+    }
+
+    @Override
+    public Response updateKeycloakGroup(String id, GroupRepresentation groupRepresentation) {
+        realmResource.groups().group(id).update(groupRepresentation);
+        return SingleResponse.buildSuccess();
+    }
+
+    @Override
+    public Response removeGroup(String id) {
+        realmResource.groups().group(id).remove();
+        return SingleResponse.buildSuccess();
+    }
+
+    @Override
+    public SingleResponse addKeyClaokUser(SmsbUserAdd user) {
+        UserRepresentation userRepresentation = new UserRepresentation();
+        userRepresentation.setUsername(user.getUsername());
+        userRepresentation.setGroups(Collections.singletonList(user.getTenant()));
+        userRepresentation.setEnabled(Boolean.TRUE);
+        userRepresentation.setCredentials(user.getCredentials());
+        userRepresentation.setFirstName(user.getFamilyName());
+        if(Objects.nonNull(getUserIdByName(userRepresentation.getUsername()))){
+            return SingleResponse.buildFailure("400","该用户名已存在!");
+        }
+        int status = realmResource.users().create(userRepresentation).getStatus();
+        if(status != 201){
+            return SingleResponse.buildFailure("400","账号创建失败!");
+        }
+        return SingleResponse.of(getUserIdByName(userRepresentation.getUsername()));
+    }
+
+    /**
+     * 通过用户名获取用户id
+     *
+     * @param username
+     * @return
+     */
+    private String getUserIdByName(String username){
+        Optional<String> optional = realmResource.users().search(username)
+            .stream().filter(user -> user.getUsername().equals(username))
+            .map(i -> i.getId())
+            .findFirst();
+        return optional.isEmpty()?null:optional.get();
+    }
+
+    @Override
+    public Response updateUser(String userId, Object representation ,Integer oprateType) {
+        UserResource userResource = realmResource.users().get(userId);
+        UserRepresentation user = userResource.toRepresentation();
+        switch (oprateType){
+            case Constant.ENABLE:
+                SwitchDTO switchDTO = (SwitchDTO) representation;
+                user.setEnabled(switchDTO.getEnabled());
+                break;
+            case Constant.RESET_PASSWORD:
+                CredentialRepresentation credential = (CredentialRepresentation) representation;
+                user.setCredentials(Stream.of(credential).collect(Collectors.toList()));
+                break;
+            case Constant.RESET_ATTRBUTE:
+                UserRepresentation userRepresentation = (UserRepresentation) representation;
+                user.setEmail(Optional.ofNullable(userRepresentation.getEmail()).orElse(null));
+                user.setFirstName(userRepresentation.getFirstName());
+                user.setAttributes(userRepresentation.getAttributes());
+            case Constant.UPDATE_USER_FIRSTNAME:
+                user.setFirstName((String) representation);
+            default:
+                break;
+        }
+        userResource.update(user);
+        return SingleResponse.buildSuccess();
+    }
+
+    @Override
+    public Response removeUser(String userId) {
+        realmResource.users().get(userId).remove();
+        return Response.buildSuccess();
+    }
+
+    @Override
+    public Response regrouping(String userId, String groupId) {
+        UserResource userResource = realmResource.users().get(userId);
+        List<GroupRepresentation> groups = userResource.groups();
+        // 移除
+        groups.forEach(groupRepresentation->{userResource.leaveGroup(groupRepresentation.getId());});
+        // 添加
+        GroupsResource initGroups = realmResource.groups();
+        List<String> targetGroups = Arrays.asList(initGroups.group(groupId).toRepresentation().getPath().split("/"))
+            .stream().filter(t -> Objects.nonNull(t)).collect(Collectors.toList());
+
+        getGroupId(targetGroups ,initGroups.groups()).forEach(targetGroupId->{userResource.joinGroup(targetGroupId);});
+        return SingleResponse.buildSuccess();
+    }
+
+    private List<String>  getGroupId(List<String> targetGroups ,  List<GroupRepresentation> groups){
+        List<String> groupIds = Lists.newArrayList();
+        GroupRepresentation group = groups.stream().filter(ele -> targetGroups.contains(ele.getName())).findFirst().get();
+        groupIds.add(group.getId());
+        if(!CollectionUtils.isEmpty(group.getSubGroups())){
+            groupIds.addAll(getGroupId(targetGroups , group.getSubGroups()));
+        }
+        return groupIds;
+    }
+
+    @Override
+    public Response roleMapping(UsersRoleMappingDTO ssersRoleMappingDTO) {
+        RoleScopeResource roleScopeResource = realmResource.users().get(ssersRoleMappingDTO.getUserId()).roles().realmLevel();
+        roleScopeResource.add(Optional.ofNullable(ssersRoleMappingDTO.getAddRoleList()).orElse(Lists.newArrayList()));
+        roleScopeResource.remove(Optional.ofNullable(ssersRoleMappingDTO.getRemoveRoleList()).orElse(Lists.newArrayList()));
+        return SingleResponse.buildSuccess();
+    }
+
+    @Override
+    public Response queryUserCredentials(String userId) {
+        return SingleResponse.of(realmResource.users().get(userId).credentials());
+    }
+
+    @Override
+    public Response removeUserCredentials(String userId , String credentialId) {
+        realmResource.users().get(userId).removeCredential(credentialId);
+        return SingleResponse.buildSuccess();
+    }
+
+    @Override
+    public Response addKeyclaokTenant(GroupRepresentation groupRepresentation) {
+        return SingleResponse.of(realmResource.groups().add(groupRepresentation).getDate());
+    }
+
+    @Override
+    public Boolean isExitTargetRole(String userId ,String role) {
+        List<String> roleList = realmResource.users().get(userId).roles().realmLevel().listAll()
+            .stream().map(i -> i.getName()).collect(Collectors.toList());
+        return roleList.contains(role);
+    }
+
+    @Override
+    @Cacheable(value = "msr:user:tenant", key = "#userId")
+    public String queryUserTenant(String userId) {
+        List<GroupRepresentation> groupList = realmResource.users().get(userId).groups()
+            .stream()
+            .sorted(Comparator.comparing(i -> i.getPath().length()))
+            .collect(Collectors.toList());
+        return CollectionUtils.isEmpty(groupList) ? "" : groupList.stream().findFirst().get().getPath();
+    }
+
+    @Override
+    public Boolean addAttributesByUserId(String userId, String key, List<String> values) {
+        UserResource userResource = realmResource.users().get(userId);
+        UserRepresentation user = userResource.toRepresentation();
+        Map<String, List<String>> attributes = Optional.ofNullable(user.getAttributes()).orElse(new HashMap<>());
+        attributes.put(key ,values);
+        user.setAttributes(attributes);
+        userResource.update(user);
+        return Boolean.TRUE;
+    }
+
+    @Override
+    public PageResponse<UserRepresentation> getUserListByIds(SmsbDepartmentCmd departmentCmd , List<String> userId) {
+        if(CollectionUtils.isEmpty(userId)){
+            GroupResource groupResource = realmResource.groups()
+                .group(realmResource.getGroupByPath(departmentCmd.getTenant()).getId());
+            log.info("查询分页参数:first:{},max:{}",departmentCmd.getOffset() ,departmentCmd.getPageSize());
+            return PageResponse.of(
+                groupResource.members(departmentCmd.getOffset() ,departmentCmd.getPageSize())
+                , groupResource.members().size() ,departmentCmd.getPageSize() ,departmentCmd.getPageIndex());
+        }else{
+            List<UserRepresentation> userRepresentations = realmResource.groups()
+                .group(realmResource.getGroupByPath(departmentCmd.getTenant()).getId())
+                .members()
+                .stream()
+                .filter(i -> userId.contains(i.getId()))
+                .collect(Collectors.toList());
+            return PageResponse.of(userRepresentations ,0 ,0,0);
+        }
+    }
 }

+ 239 - 0
smsb-customer-manager-app/src/main/java/com/inspur/customer/service/org/SmsbDepartmentServiceImpl.java

@@ -0,0 +1,239 @@
+package com.inspur.customer.service.org;
+
+import com.alibaba.cola.dto.PageResponse;
+import com.alibaba.cola.dto.Response;
+import com.alibaba.cola.dto.SingleResponse;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.google.common.collect.Lists;
+import com.inspur.customer.client.keycloak.KeycloakService;
+import com.inspur.customer.client.org.SmsbDepartmentService;
+import com.inspur.customer.client.org.SmsbDepartmentUserService;
+import com.inspur.customer.constant.Constant;
+import com.inspur.customer.infrastructure.convertor.SmsbDepartmentConvertor;
+import com.inspur.customer.infrastructure.mapper.org.SmsbDepartmentMapper;
+import com.inspur.customer.infrastructure.object.org.SmsbDepartmentDO;
+import com.inspur.customer.object.org.*;
+import com.inspur.device.client.core.relation.DeviceAllocateService;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.dubbo.config.annotation.DubboReference;
+import org.apache.dubbo.config.annotation.DubboService;
+import org.springframework.beans.BeanUtils;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.CollectionUtils;
+
+import java.time.LocalDateTime;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+/**
+ * @Author wangbo13
+ * @Date 2022/11/25 14:58
+ * @Version 1.0
+ */
+@Slf4j
+@DubboService(interfaceClass = SmsbDepartmentService.class)
+public class SmsbDepartmentServiceImpl extends ServiceImpl<SmsbDepartmentMapper , SmsbDepartmentDO> implements SmsbDepartmentService {
+
+    @DubboReference
+    private SmsbDepartmentUserService departmentUserService;
+    @DubboReference
+    private KeycloakService service;
+    @DubboReference
+    private SmsbDepartmentUserService userService;
+    @DubboReference
+    private DeviceAllocateService deviceAllocateService;
+
+    @Override
+    public Response addDepartment(String userId , SmsbDepartmentAdd smsbDepartmentAdd) {
+        List<SmsbDepartmentDO> departments = this.list(new LambdaQueryWrapper<>(SmsbDepartmentDO.class)
+            .eq(SmsbDepartmentDO::getTenant, smsbDepartmentAdd.getTenant())
+            .eq(SmsbDepartmentDO::getMark, smsbDepartmentAdd.getMark()));
+        if(!CollectionUtils.isEmpty(departments)){return Response.buildFailure("500",
+            "部门标识["+smsbDepartmentAdd.getMark()+"]在租户["+smsbDepartmentAdd.getTenant()+"]中已存在!");}
+        SmsbDepartmentDO department = SmsbDepartmentConvertor.toDataObjectForCreate(userId, smsbDepartmentAdd);
+        department.setPath(smsbDepartmentAdd.getTenant()+pathFactory(smsbDepartmentAdd.getParentId(),smsbDepartmentAdd.getMark()));
+        department.setOrderNo(orderNmFactory(smsbDepartmentAdd.getParentId()));
+        super.save(department);
+        return SingleResponse.of(department);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void deleteDepartment(String userId, Long id) {
+        SmsbDepartmentDO department = super.getById(id);
+        // 1、删除用户
+        userService.removeUserDepartmentRelation(department.getPath());
+        // 2、删除设备关系
+        deviceAllocateService.deleteRelatedOrg(department.getPath());
+        // 3、删除部门
+        removeDepartment(userId , Collections.singletonList(id));
+    }
+
+    private void removeDepartment(String userId, List<Long> ids){
+        super.update(new LambdaUpdateWrapper<>(SmsbDepartmentDO.class)
+            .set(SmsbDepartmentDO::getIsDel , 1)
+            .set(SmsbDepartmentDO::getUpdateBy , userId)
+            .set(SmsbDepartmentDO::getUpdateTime , LocalDateTime.now())
+            .in(SmsbDepartmentDO::getId , ids));
+        List<SmsbDepartmentDO> nextDepartment = super.list(new LambdaQueryWrapper<>(SmsbDepartmentDO.class)
+            .in(SmsbDepartmentDO::getParentId, ids));
+        if(!CollectionUtils.isEmpty(nextDepartment)){
+            removeDepartment(userId , nextDepartment.stream().map(i -> i.getId()).collect(Collectors.toList()));
+        }
+    }
+
+    @Override
+    public Response updateDepartment(String userId, SmsbDepartmentUpdate departmentUpdate) {
+        boolean flag = super.update(new LambdaUpdateWrapper<>(SmsbDepartmentDO.class)
+            .set(StringUtils.isNotBlank(departmentUpdate.getName()), SmsbDepartmentDO::getName, departmentUpdate.getName())
+            .set(SmsbDepartmentDO::getUpdateBy, userId)
+            .set(SmsbDepartmentDO::getUpdateTime, LocalDateTime.now())
+            .eq(SmsbDepartmentDO::getId, departmentUpdate.getId()));
+        return flag ? Response.buildSuccess() : Response.buildFailure("500","部门更新失败!");
+    }
+
+    /**
+     * 获取当前部门路径
+     *
+     * @param parentId 父节点id(可为空)
+     * @param mark 当前节点名称
+     * @return 当前路径
+     */
+    private String pathFactory(Long parentId , String mark){
+        if(Objects.isNull(parentId)){
+            return Constant.OBLIQUE_LINE.concat(mark);
+        }else{
+            SmsbDepartmentDO department = super.getById(parentId);
+            return pathFactory(department.getParentId() , department.getMark().concat(Constant.OBLIQUE_LINE).concat(mark));
+        }
+    }
+
+    /**
+     * 获取序号
+     *
+     * @param parentId
+     * @return
+     */
+    private Integer orderNmFactory(Long parentId){
+        if(Objects.isNull(parentId)) return 1;
+        SmsbDepartmentDO department = super.getOne(new LambdaQueryWrapper<>(SmsbDepartmentDO.class)
+            .eq(SmsbDepartmentDO::getParentId, parentId)
+            .orderByDesc(SmsbDepartmentDO::getOrderNo)
+            .last("limit 1"));
+        return Objects.isNull(department)?1:department.getOrderNo()+1;
+    }
+
+    @Override
+    public Response queryDepartmentList(String tenant) {
+        List<SmsbDepartmentDO> departmentList = super.list(new LambdaQueryWrapper<>(SmsbDepartmentDO.class)
+            .eq(SmsbDepartmentDO::getIsDel , 0)
+            .eq(SmsbDepartmentDO::getTenant, tenant)
+            .isNull(SmsbDepartmentDO::getParentId));
+        List<SmsbDepartmentCO> departmentTree = Lists.newArrayList();
+        departmentList.forEach(department->{
+            SmsbDepartmentCO smsbDepartmentCO = new SmsbDepartmentCO();
+            BeanUtils.copyProperties(department , smsbDepartmentCO);
+            smsbDepartmentCO.setChildren(this.getChilde(department.getId()));
+            departmentTree.add(smsbDepartmentCO);
+        });
+        log.info("tree num is :{}",departmentTree.size());
+        return SingleResponse.of(departmentTree);
+    }
+
+    @Override
+    public PageResponse<SmsbSimpleUserCO> queryDepartmentUserList(SmsbDepartmentCmd departmentCmd) {
+        if(Objects.nonNull(departmentCmd.getDepartmentId())){
+            // 部门id不为空,path过滤
+            departmentCmd.setTenant(super.getById(departmentCmd.getDepartmentId()).getPath());
+            return departmentUserService.queryUserPageByDeprIds(departmentCmd , 1);
+        }else{
+            // 部门id为空,tenant过滤
+            return departmentUserService.queryUserPageByDeprIds(departmentCmd , 2);
+        }
+    }
+
+    private List<SmsbDepartmentCO> getChilde(Long id){
+        List<SmsbDepartmentDO> childList = this.list(new LambdaQueryWrapper<>(SmsbDepartmentDO.class)
+            .eq(SmsbDepartmentDO::getIsDel ,0)
+            .eq(SmsbDepartmentDO::getParentId, id));
+        List<SmsbDepartmentCO> finalChilds = Lists.newArrayList();
+        childList.forEach(child ->{
+            SmsbDepartmentCO smsbDepartmentCO = new SmsbDepartmentCO();
+            BeanUtils.copyProperties(child , smsbDepartmentCO);
+            smsbDepartmentCO.setChildren(this.getChilde(child.getId()));
+            finalChilds.add(smsbDepartmentCO);
+        });
+        return finalChilds;
+    }
+
+    /**
+     * 鉴权
+     *
+     * @param tenant
+     * @param userId
+     * @return
+     */
+    @Override
+    public Response checkOperationAuthority(String userId , String tenant){
+        String real = service.queryUserTenant(userId);
+        if(service.isExitTargetRole(userId ,Constant.SUPER_ADMIN) ||
+            (StringUtils.isNotEmpty(real) && real.equals(tenant) && service.isExitTargetRole(userId , Constant.ROLE_ADMIN))){
+            return Response.buildSuccess();
+        }
+        return Response.buildFailure("401","无权操作!");
+    }
+
+    @Override
+    public SmsbDepartmentCO getOneById(Long id) {
+        SmsbDepartmentDO department = super.getById(id);
+        SmsbDepartmentCO departmentDO = new SmsbDepartmentCO();
+        BeanUtils.copyProperties(department , departmentDO);
+        return departmentDO;
+    }
+
+    @Override
+    public Response addKeyClaokUser(String userId ,SmsbUserAdd smsbUserAdd) {
+        // 1、keycloak新增账号
+        SingleResponse addUserResponse = service.addKeyClaokUser(smsbUserAdd);
+        if(!addUserResponse.isSuccess()){return addUserResponse;}
+        String addUserId = (String)addUserResponse.getData();
+        log.info("new user id:{}",addUserId);
+        // 2、是否传递所属部门,不传递不做处理
+        if(Objects.nonNull(smsbUserAdd.getDepartmentId())){
+            // 3、将部门信息写入keucloak的attibute中
+            smsbUserAdd.setPath(super.getById(smsbUserAdd.getDepartmentId()).getPath());
+            service.addAttributesByUserId(addUserId , "org" , Collections.singletonList(smsbUserAdd.getPath()));
+        }
+        // 4、保存用户与部门关系
+        userService.addUserDepartment(addUserId , smsbUserAdd);
+        return SingleResponse.of(addUserId);
+    }
+
+    @Override
+    public Response regrouping(SmsbRegrouping smsbRegrouping) {
+        // 1、清空原绑定关系,建立新绑定关系
+        Boolean result;
+        String path;
+        if(Objects.nonNull(smsbRegrouping.getDepartmentId())){
+            path = super.getById(smsbRegrouping.getDepartmentId()).getPath();
+            result = departmentUserService.updateRelation(smsbRegrouping.getUserId(),
+                smsbRegrouping.getDepartmentId(),path);
+        }else {
+            path = smsbRegrouping.getTenant();
+            result = departmentUserService.updateRelation(smsbRegrouping.getUserId(), null ,null);
+        }
+        // 2、更新attribute
+        if(result){
+            service.addAttributesByUserId(smsbRegrouping.getUserId() , "org" ,
+                Collections.singletonList(path));
+            return Response.buildSuccess();
+        }else{
+            return Response.buildFailure("500","用户重分组失败!");
+        }
+    }
+}

+ 117 - 0
smsb-customer-manager-app/src/main/java/com/inspur/customer/service/org/SmsbDepartmentUserServiceImpl.java

@@ -0,0 +1,117 @@
+package com.inspur.customer.service.org;
+
+import com.alibaba.cola.dto.PageResponse;
+import com.alibaba.cola.dto.Response;
+import com.alibaba.cola.dto.SingleResponse;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.google.common.collect.Lists;
+import com.inspur.customer.client.org.SmsbDepartmentUserService;
+import com.inspur.customer.constant.Constant;
+import com.inspur.customer.infrastructure.convertor.SmsbDepartmentConvertor;
+import com.inspur.customer.infrastructure.mapper.org.SmsbDepartmentUserMapper;
+import com.inspur.customer.infrastructure.object.org.SmsbDepartmentUserDO;
+import com.inspur.customer.object.keycloak.SwitchDTO;
+import com.inspur.customer.object.org.*;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.dubbo.config.annotation.DubboService;
+import org.springframework.beans.BeanUtils;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+/**
+ * @Author wangbo13
+ * @Date 2022/11/25 15:06
+ * @Version 1.0
+ */
+@Slf4j
+@DubboService(interfaceClass = SmsbDepartmentUserService.class)
+public class SmsbDepartmentUserServiceImpl extends ServiceImpl<SmsbDepartmentUserMapper, SmsbDepartmentUserDO> implements  SmsbDepartmentUserService{
+
+    @Override
+    public Boolean addUserDepartment(String userId, SmsbUserAdd smsbUserAdd) {
+        return super.save(SmsbDepartmentConvertor.toDataObjectForCreate(userId , smsbUserAdd));
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean updateRelation(String userId, Long departmentId ,String path) {
+        return super.update(new LambdaUpdateWrapper<>(SmsbDepartmentUserDO.class)
+            .set(SmsbDepartmentUserDO::getDepartmentId , departmentId)
+            .set(SmsbDepartmentUserDO::getPath , path)
+            .eq(SmsbDepartmentUserDO::getUserId , userId));
+    }
+
+    @Override
+    public PageResponse<SmsbSimpleUserCO> queryUserPageByDeprIds(SmsbDepartmentCmd departmentCmd, Integer oprateFlag) {
+        Page<SmsbDepartmentUserDO> page = new Page<>(departmentCmd.getPageIndex(), departmentCmd.getPageSize());
+        LambdaQueryWrapper<SmsbDepartmentUserDO> queryWrapper = Wrappers.lambdaQuery();
+        switch (oprateFlag){
+            case 1:
+                switch (departmentCmd.getRecursive()){
+                    case 0: queryWrapper.eq(SmsbDepartmentUserDO::getPath , departmentCmd.getTenant());break;
+                    case 1: queryWrapper.likeRight(SmsbDepartmentUserDO::getPath , departmentCmd.getTenant());break;
+                    default: break;
+                }
+                break;
+            case 2: queryWrapper.eq(SmsbDepartmentUserDO::getTenant , departmentCmd.getTenant());break;
+            default: break;
+        }
+        queryWrapper.orderByDesc(SmsbDepartmentUserDO::getCreateTime);
+        page = this.page(page, queryWrapper);
+        List<SmsbSimpleUserCO> smsbSimpleUserList = Lists.newArrayListWithCapacity((int) page.getSize());
+        page.getRecords().forEach(user->{
+            SmsbSimpleUserCO smsbSimpleUserCO = new SmsbSimpleUserCO();
+            BeanUtils.copyProperties(user, smsbSimpleUserCO);
+            smsbSimpleUserList.add(smsbSimpleUserCO);
+        });
+        return PageResponse.of(smsbSimpleUserList, (int) page.getTotal(), (int) page.getSize(), (int) page.getCurrent());
+    }
+
+    @Override
+    public Boolean removeUser(String userId) {
+        return super.remove(new LambdaQueryWrapper<>(SmsbDepartmentUserDO.class)
+            .eq(SmsbDepartmentUserDO::getUserId , userId));
+    }
+
+    @Override
+    public Response migration(SmsbUserMigration userMigration) {
+        List<SmsbDepartmentUserDO> exitUser = super.list(new LambdaQueryWrapper<>(SmsbDepartmentUserDO.class)
+            .eq(SmsbDepartmentUserDO::getUserId, userMigration.getUserId()));
+        if(!CollectionUtils.isEmpty(exitUser)){
+            return Response.buildFailure("500","用户已迁移!");
+        }
+        SmsbDepartmentUserDO smsbDepartmentUserDO = new SmsbDepartmentUserDO();
+        BeanUtils.copyProperties(userMigration , smsbDepartmentUserDO);
+        super.save(smsbDepartmentUserDO);
+        return SingleResponse.of(smsbDepartmentUserDO);
+    }
+
+    @Override
+    public Boolean enableSwitch(SwitchDTO switchDTO) {
+        return super.update(new LambdaUpdateWrapper<>(SmsbDepartmentUserDO.class)
+            .set(SmsbDepartmentUserDO::getEnabled, switchDTO.getEnabled()?
+                Constant.AccountNumberEnum.ENABLE.value() :Constant.AccountNumberEnum.DISABLE.value())
+            .eq(SmsbDepartmentUserDO::getUserId, switchDTO.getUserId()));
+    }
+
+    @Override
+    public Boolean removeUserDepartmentRelation(String path) {
+        return super.update(new LambdaUpdateWrapper<>(SmsbDepartmentUserDO.class)
+            .set(SmsbDepartmentUserDO::getDepartmentId , null)
+            .set(SmsbDepartmentUserDO::getPath , null)
+            .likeRight(SmsbDepartmentUserDO::getPath , path));
+    }
+
+    @Override
+    public Boolean updateUserFirstName(SmsbUserUpdate userUpdate) {
+        return super.update(new LambdaUpdateWrapper<>(SmsbDepartmentUserDO.class)
+            .set(SmsbDepartmentUserDO::getFamilyName , userUpdate.getFamilyName())
+            .eq(SmsbDepartmentUserDO::getUserId , userUpdate.getUserId()));
+    }
+}

+ 7 - 0
smsb-customer-manager-app/src/main/resources/email/CameraSnapPicReleaseWarn.html

@@ -0,0 +1,7 @@
+尊敬的用户,您好!<br>
+<br>
+&nbsp;&nbsp;您的摄像头有一条{MSR_LEVEL}预警,相关信息如下:<br>
+&nbsp;&nbsp;&nbsp;摄像头名称:{MSR_NAME}<br>
+&nbsp;&nbsp;&nbsp;连续检测次数:{MSR_TIMES}<br>
+&nbsp;&nbsp;&nbsp;连续检测时间:{MSR_START_TIME}-{MSR_END_TIME}<br>
+&nbsp;&nbsp;&nbsp;连续检测结果: 设备黑屏恢复<br>

+ 7 - 0
smsb-customer-manager-app/src/main/resources/email/CameraSnapPicWarn.html

@@ -0,0 +1,7 @@
+尊敬的用户,您好!<br>
+<br>
+&nbsp;&nbsp;您的摄像头有一条{MSR_LEVEL}预警,相关信息如下:<br>
+&nbsp;&nbsp;&nbsp;摄像头名称:{MSR_NAME}<br>
+&nbsp;&nbsp;&nbsp;连续检测次数:{MSR_TIMES}<br>
+&nbsp;&nbsp;&nbsp;连续检测时间:{MSR_START_TIME} - {MSR_END_TIME}<br>
+&nbsp;&nbsp;&nbsp;连续检测结果: 设备黑屏<br>

+ 24 - 0
smsb-customer-manager-app/src/main/resources/weChat/CameraSnapPicReleaseWarn.json

@@ -0,0 +1,24 @@
+{
+    "data": {
+        "first": {
+            "value": "【浪潮安播云】您的 AI 机审有一条 {0} 预警,请及时关注,谢谢。",
+            "color": "#173177"
+        },
+        "keyword1": {
+            "value": "摄像头检测黑屏恢复",
+            "color": "#173177"
+        },
+        "keyword2": {
+            "value": "无",
+            "color": "#173177"
+        },
+        "keyword3": {
+            "value": "摄像头名称: {0}, 连续检测次数: {1}",
+            "color": "#173177"
+        },
+        "keyword4": {
+            "value": "{0}-{1}",
+            "color": "#173177"
+        }
+    }
+}

+ 24 - 0
smsb-customer-manager-app/src/main/resources/weChat/CameraSnapPicWarn.json

@@ -0,0 +1,24 @@
+{
+    "data": {
+        "first": {
+            "value": "【浪潮安播云】您的 AI 机审有一条 {0} 预警,请及时关注,谢谢。",
+            "color": "#173177"
+        },
+        "keyword1": {
+            "value": "摄像头检测黑屏",
+            "color": "#173177"
+        },
+        "keyword2": {
+            "value": "无",
+            "color": "#173177"
+        },
+        "keyword3": {
+            "value": "摄像头名称: {0}, 连续检测次数: {1}",
+            "color": "#173177"
+        },
+        "keyword4": {
+            "value": "{0}-{1}",
+            "color": "#173177"
+        }
+    }
+}

+ 4 - 0
smsb-customer-manager-client/pom.xml

@@ -62,6 +62,10 @@
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-json</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-core</artifactId>
+        </dependency>
     </dependencies>
 
 </project>

+ 142 - 0
smsb-customer-manager-client/src/main/java/com/inspur/customer/client/keycloak/KeycloakService.java

@@ -1,6 +1,17 @@
 package com.inspur.customer.client.keycloak;
+import com.alibaba.cola.dto.PageResponse;
+import com.alibaba.cola.dto.Response;
+import com.alibaba.cola.dto.SingleResponse;
 import com.inspur.customer.object.keycloak.KeycloakUserCO;
+import com.inspur.customer.object.keycloak.UsersRoleMappingDTO;
+import com.inspur.customer.object.org.SmsbDepartmentCmd;
+import com.inspur.customer.object.org.SmsbUserAdd;
 import com.inspur.customer.object.wechat.Pair;
+import org.keycloak.representations.idm.CredentialRepresentation;
+import org.keycloak.representations.idm.GroupRepresentation;
+import org.keycloak.representations.idm.RoleRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
+import org.springframework.web.bind.annotation.RequestBody;
 
 import java.util.List;
 import java.util.Map;
@@ -79,4 +90,135 @@ public interface KeycloakService {
      * @return
      */
     Map<String, String> getUsersMap();
+
+    /**
+     * 该租户下添加部门
+     *
+     * @param id
+     * @param groupRepresentation
+     * @return
+     */
+    Response addKeycloakGroup(String id , GroupRepresentation groupRepresentation);
+
+    /**
+     * 该租户下修改部门
+     *
+     * @param id
+     * @param groupRepresentation
+     * @return
+     */
+    Response updateKeycloakGroup(String id , GroupRepresentation groupRepresentation);
+
+    /**
+     * 该租户下删除部门
+     *
+     * @param id
+     * @return
+     */
+    Response removeGroup(String id);
+
+    /**
+     * 添加用户
+     *
+     * @param smsbUserAdd
+     * @return
+     */
+    SingleResponse addKeyClaokUser(SmsbUserAdd smsbUserAdd);
+
+    /**
+     * 更新用户信息
+     *
+     * @param userId
+     * @param representation
+     * @param oprateType 1:账号启用/禁用 2:重置密码
+     * @return
+     */
+    Response updateUser(String userId , Object representation ,Integer oprateType);
+
+    /**
+     * 注销用户
+     *
+     * @param userId
+     * @return
+     */
+    Response removeUser(String userId);
+
+    /**
+     * 用户重分组
+     *
+     * @param userId
+     * @param groupId
+     * @return
+     */
+    Response regrouping(String userId , String groupId);
+
+    /**
+     * 用户角色分配
+     *
+     * @param ssersRoleMappingDTO
+     * @return
+     */
+    Response roleMapping(UsersRoleMappingDTO ssersRoleMappingDTO);
+
+    /**
+     * 获取用户凭证
+     *
+     * @param userId
+     * @return
+     */
+    Response queryUserCredentials(String userId);
+
+    /**
+     * 删除用户凭证
+     *
+     * @param userId
+     * @param credentialId
+     * @return
+     */
+    Response removeUserCredentials(String userId ,String credentialId);
+
+    /**
+     * 新增租户
+     *
+     * @param groupRepresentation
+     * @return
+     */
+    Response addKeyclaokTenant(GroupRepresentation groupRepresentation);
+
+    /**
+     * 判断用户是否具有某角色
+     *
+     * @param userId
+     * @param role
+     * @return
+     */
+    Boolean isExitTargetRole(String userId ,String role);
+
+    /**
+     * 获取用户所属租户
+     *
+     * @param userId
+     * @return 租户
+     */
+    String queryUserTenant(String userId);
+
+    /**
+     * 新增或修改用户attributes
+     *
+     * @param userId
+     * @param key key
+     * @param values values
+     * @return if success
+     */
+    Boolean addAttributesByUserId(String userId , String key , List<String> values);
+
+    /**
+     * 获取用户列表
+     *
+     * @param departmentCmd
+     * @param userId
+     * @return
+     */
+    PageResponse<UserRepresentation> getUserListByIds(SmsbDepartmentCmd departmentCmd , List<String> userId);
+
 }

+ 94 - 0
smsb-customer-manager-client/src/main/java/com/inspur/customer/client/org/SmsbDepartmentService.java

@@ -0,0 +1,94 @@
+package com.inspur.customer.client.org;
+
+import com.alibaba.cola.dto.PageResponse;
+import com.alibaba.cola.dto.Response;
+import com.inspur.customer.object.org.*;
+
+import java.util.List;
+
+/**
+ * @Author wangbo13
+ * 需要检验用户是否租户权限,租户是否一致
+ * 或是否具备超级用户权限
+ *
+ * @Date 2022/11/25 14:57
+ * @Version 1.0
+ */
+public interface SmsbDepartmentService {
+
+    /**
+     * 新增部门
+     *
+     * @param userId
+     * @param smsbDepartmentAdd
+     * @return
+     */
+    Response addDepartment(String userId , SmsbDepartmentAdd smsbDepartmentAdd);
+
+    /**
+     * 删除部门
+     *
+     * @param userId
+     * @param id
+     */
+    void deleteDepartment(String userId , Long id);
+
+    /**
+     * 修改部门
+     *
+     * @param userId
+     * @param departmentUpdate
+     * @return
+     */
+    Response updateDepartment(String userId , SmsbDepartmentUpdate departmentUpdate);
+
+    /**
+     * 获取部门树
+     *
+     * @param tenant
+     * @return
+     */
+    Response queryDepartmentList(String tenant);
+
+    /**
+     * 获取员工列表
+     *
+     * @param departmentCmd
+     * @return
+     */
+    PageResponse<SmsbSimpleUserCO> queryDepartmentUserList(SmsbDepartmentCmd departmentCmd);
+
+    /**
+     * 检查用户是否超级管理员以及该租户权限
+     *
+     * @param userId
+     * @param tenant
+     * @return
+     */
+    Response checkOperationAuthority(String userId , String tenant);
+
+    /**
+     * 通过id获取一条
+     *
+     * @param id
+     * @return
+     */
+    SmsbDepartmentCO getOneById(Long id);
+
+    /**
+     * 添加用户到某部门
+     *
+     * @param userId
+     * @param smsbUserAdd
+     * @return
+     */
+    Response addKeyClaokUser(String userId ,SmsbUserAdd smsbUserAdd);
+
+    /**
+     * 用户重分组
+     *
+     * @param smsbRegrouping
+     * @return
+     */
+    Response regrouping(SmsbRegrouping smsbRegrouping);
+}

+ 87 - 0
smsb-customer-manager-client/src/main/java/com/inspur/customer/client/org/SmsbDepartmentUserService.java

@@ -0,0 +1,87 @@
+package com.inspur.customer.client.org;
+
+import com.alibaba.cola.dto.PageResponse;
+import com.alibaba.cola.dto.Response;
+import com.inspur.customer.object.keycloak.SwitchDTO;
+import com.inspur.customer.object.org.*;
+
+import java.util.List;
+
+/**
+ * @Author wangbo13
+ * @Date 2022/11/25 15:06
+ * @Version 1.0
+ */
+public interface SmsbDepartmentUserService {
+
+    /**
+     * 保存
+     *
+     * @param userId
+     * @param smsbUserAdd
+     * @return
+     */
+    Boolean addUserDepartment(String userId , SmsbUserAdd smsbUserAdd);
+
+    /**
+     * 清空并建立新绑定关系
+     *
+     * @param userId
+     * @param departmentId
+     * @return
+     */
+    Boolean updateRelation(String userId, Long departmentId ,String path);
+
+    /**
+     * 通过部门id获取用户id
+     * oprateFlag:1 path过滤
+     * oprateFlag:2 tenant过滤
+     *
+     * @param departmentCmd
+     * @param oprateFlag
+     * @return
+     */
+    PageResponse<SmsbSimpleUserCO> queryUserPageByDeprIds(SmsbDepartmentCmd departmentCmd ,Integer oprateFlag);
+
+
+    /**
+     * 注销用户
+     *
+     * @param userId
+     * @return
+     */
+    Boolean removeUser(String userId);
+
+    /**
+     * 注意:临时使用
+     * 迁移keycloak用户至smsb_department_user表中
+     *
+     * @param userMigration
+     * @return
+     */
+    Response migration(SmsbUserMigration userMigration);
+
+    /**
+     * 账号启用/禁用开关
+     *
+     * @param switchDTO
+     * @return
+     */
+    Boolean enableSwitch(SwitchDTO switchDTO);
+
+    /**
+     * 左关联匹配删除用户部门绑定关系
+     *
+     * @param path
+     * @return
+     */
+    Boolean removeUserDepartmentRelation(String path);
+
+    /**
+     * 修改用户familyName
+     *
+     * @param userUpdate
+     * @return
+     */
+    Boolean updateUserFirstName(SmsbUserUpdate userUpdate);
+}

+ 74 - 0
smsb-customer-manager-client/src/main/java/com/inspur/customer/constant/Constant.java

@@ -0,0 +1,74 @@
+package com.inspur.customer.constant;
+
+/**
+ * @Author wangbo13
+ * @Date 2022/11/22 16:35
+ * @Version 1.0
+ */
+public final class Constant {
+
+    /**
+     * 账号启用/禁用
+     */
+    public static final int ENABLE = 1;
+
+    /**
+     * 重置密码
+     */
+    public static final int RESET_PASSWORD = 2;
+
+    /**
+     * 重置用户属性
+     */
+    public static final int RESET_ATTRBUTE = 3;
+
+    /**
+     * 更新用户名称
+     */
+    public static final int UPDATE_USER_FIRSTNAME = 4;
+
+    /**
+     * 凭证类型
+     */
+    public static final String type = "password";
+
+    /**
+     * 斜线
+     */
+    public static final String OBLIQUE_LINE = "/";
+
+    /**
+     * 超级管理员角色
+     */
+    public static final String SUPER_ADMIN = "ROLE_SUPER_ADMIN";
+
+    /**
+     * 租户管理员角色
+     */
+    public static final String ROLE_ADMIN = "ROLE_ADMIN";
+
+    /**
+     * 账号启用/禁用
+     */
+    public enum AccountNumberEnum {
+        ENABLE(1, "启用"),
+        DISABLE(0, "禁用");
+
+        private final int value;
+        private final String message;
+
+        AccountNumberEnum(int value, String message) {
+            this.value = value;
+            this.message = message;
+        }
+
+        public int value() {
+            return this.value;
+        }
+
+        public String getMessage() {
+            return this.message;
+        }
+
+    }
+}

+ 9 - 1
smsb-customer-manager-client/src/main/java/com/inspur/customer/context/inform/InformTypeEnum.java

@@ -27,7 +27,15 @@ public enum InformTypeEnum {
     /**
      * 设备视频回采次数库存预警
      */
-    DEVICE_PIC_AUDIT_CREDIT_WARN("设备视频回采次数库存预警", InformLevelEnum.HINT, MessageTemplateEnum.AI_AUDIT_NON_COMPLIANCE)
+    DEVICE_PIC_AUDIT_CREDIT_WARN("设备视频回采次数库存预警", InformLevelEnum.HINT, MessageTemplateEnum.AI_AUDIT_NON_COMPLIANCE),
+    /**
+     * 摄像头检测黑屏预警
+     */
+    CAMERA_SNAP_PIC_WARN("摄像头检测黑屏预警", InformLevelEnum.TENANT, MessageTemplateEnum.CAMERA_SNAP_PIC_WARN),
+    /**
+     * 摄像头检测黑屏恢复预警
+     */
+    CAMERA_SNAP_PIC_RELEASE_WARN("摄像头检测黑屏恢复预警", InformLevelEnum.TENANT, MessageTemplateEnum.CAMERA_SNAP_PIC_RELEASE_WARN),
     ;
 
     /**

+ 11 - 4
smsb-customer-manager-client/src/main/java/com/inspur/customer/context/inform/MessageTemplateEnum.java

@@ -20,16 +20,23 @@ public enum MessageTemplateEnum {
     /**
      * AI 审核库存
      */
-    AI_AUDIT_CREDIT_INSUFFICIENT_WARN("AiAuditCreditInsufficientWarn", 5, "AiAuditCreditInsufficientWarn",  "appletMessage/AiAuditCreditInsufficientWarn.RTF"),
+    AI_AUDIT_CREDIT_INSUFFICIENT_WARN("AiAuditCreditInsufficientWarn", 5, "AiAuditCreditInsufficientWarn", "appletMessage/AiAuditCreditInsufficientWarn.RTF"),
     /**
      * 媒资 AI 不通过异常
      */
-    AI_AUDIT_NON_COMPLIANCE("AiAuditMediaWarn", 4, "AiAuditMediaWarn",  "appletMessage/AiAuditMediaWarn.RTF"),
+    AI_AUDIT_NON_COMPLIANCE("AiAuditMediaWarn", 4, "AiAuditMediaWarn", "appletMessage/AiAuditMediaWarn.RTF"),
     /**
      * 设备视频回传次数不足
      */
-    DEVICE_PIC_AUDIT_CREDIT_WARN("DevicePicAuditCreditWarn", 6, "DevicePicAuditCreditWarn", "appletMessage/DevicePicAuditCreditWarn.RTF")
-    ;
+    DEVICE_PIC_AUDIT_CREDIT_WARN("DevicePicAuditCreditWarn", 6, "DevicePicAuditCreditWarn", "appletMessage/DevicePicAuditCreditWarn.RTF"),
+    /**
+     * 摄像头检测黑屏预警
+     */
+    CAMERA_SNAP_PIC_WARN("CameraSnapPicWarn", 10, "CameraSnapPicWarn", "appletMessage/CameraSnapPicWarn.RTF"),
+    /**
+     * 摄像头检测黑屏恢复预警
+     */
+    CAMERA_SNAP_PIC_RELEASE_WARN("CameraSnapPicReleaseWarn", 11, "CameraSnapPicReleaseWarn", "appletMessage/CameraSnapPicReleaseWarn.RTF");
 
     @Getter
     private final String email;

+ 29 - 0
smsb-customer-manager-client/src/main/java/com/inspur/customer/object/keycloak/SwitchDTO.java

@@ -0,0 +1,29 @@
+package com.inspur.customer.object.keycloak;
+
+import com.inspur.customer.object.org.OperationAuthority;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+
+/**
+ * @Author wangbo13
+ * @Date 2022/11/22 15:29
+ * @Version 1.0
+ */
+@Data
+public class SwitchDTO extends OperationAuthority {
+    private static final long serialVersionUID = 3364299451602207573L;
+
+    /**
+     * 用户ID
+     */
+    @NotNull(message = "userId不能为空")
+    private String userId ;
+
+    /**
+     * 启用/禁用 true/false
+     */
+    @NotNull(message = "enabled不能为空")
+    private Boolean enabled;
+}

+ 32 - 0
smsb-customer-manager-client/src/main/java/com/inspur/customer/object/keycloak/UsersRoleMappingDTO.java

@@ -0,0 +1,32 @@
+package com.inspur.customer.object.keycloak;
+
+import lombok.Data;
+import org.keycloak.representations.idm.RoleRepresentation;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * @Author wangbo13
+ * @Date 2022/11/24 9:24
+ * @Version 1.0
+ */
+@Data
+public class UsersRoleMappingDTO implements Serializable {
+    private static final long serialVersionUID = -8477367953520065512L;
+
+    /**
+     * 用户Id
+     */
+    private String userId;
+
+    /**
+     * 新增角色
+     */
+    private List<RoleRepresentation> addRoleList;
+
+    /**
+     * 删除角色
+     */
+    private List<RoleRepresentation> removeRoleList;
+}

+ 24 - 0
smsb-customer-manager-client/src/main/java/com/inspur/customer/object/org/OperationAuthority.java

@@ -0,0 +1,24 @@
+package com.inspur.customer.object.org;
+
+import com.alibaba.cola.dto.Command;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * @Author wangbo13
+ * @Date 2022/11/30 16:10
+ * @Version 1.0
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class OperationAuthority extends Command {
+    private static final long serialVersionUID = 9032048848393075367L;
+
+    /**
+     * 租户
+     */
+    @NotNull(message = "tenant不能为空")
+    private String tenant;
+}

+ 29 - 0
smsb-customer-manager-client/src/main/java/com/inspur/customer/object/org/SmsbDepartmentAdd.java

@@ -0,0 +1,29 @@
+package com.inspur.customer.object.org;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * @Author wangbo13
+ * @Date 2022/11/25 16:16
+ * @Version 1.0
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class SmsbDepartmentAdd extends OperationAuthority {
+
+    private Long parentId;
+    /**
+     * 部门中文名称
+     */
+    @NotNull(message = "name不能为空")
+    private String name;
+
+    /**
+     * 部门唯一标识
+     */
+    @NotNull(message = "mark不能为空")
+    private String mark;
+}

+ 74 - 0
smsb-customer-manager-client/src/main/java/com/inspur/customer/object/org/SmsbDepartmentCO.java

@@ -0,0 +1,74 @@
+package com.inspur.customer.object.org;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import lombok.Data;
+import java.io.Serializable;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @Author wangbo13
+ * @Date 2022/11/29 18:10
+ * @Version 1.0
+ */
+@Data
+public class SmsbDepartmentCO implements Serializable {
+    private static final long serialVersionUID = 2989462647060311429L;
+
+    private Long id;
+    /**
+     * 父节点id
+     */
+    private Long parentId;
+    /**
+     * 部门中文名称
+     */
+    private String name;
+    /**
+     * 部门唯一标识
+     */
+    private String mark;
+
+    /**
+     * 路径
+     */
+    private String path;
+    /**
+     * 序号
+     */
+    private Integer orderNo;
+
+    /**
+     * 租户
+     */
+    private String tenant;
+
+    /**
+     * 删除标识
+     */
+    private Integer isDel;
+
+    /**
+     * 创建人
+     */
+    private String createBy;
+
+    /**
+     * 创建时间
+     */
+    private LocalDateTime createTime;
+
+    /**
+     * 修改人
+     */
+    private String updateBy;
+
+    /**
+     * 修改时间
+     */
+    private LocalDateTime updateTime;
+
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    private List<SmsbDepartmentCO> children = new ArrayList<>();
+}

+ 42 - 0
smsb-customer-manager-client/src/main/java/com/inspur/customer/object/org/SmsbDepartmentCmd.java

@@ -0,0 +1,42 @@
+package com.inspur.customer.object.org;
+
+import com.alibaba.cola.dto.PageQuery;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import javax.validation.constraints.NotNull;
+import java.util.Objects;
+
+/**
+ * @Author wangbo13
+ * @Date 2022/11/29 18:20
+ * @Version 1.0
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class SmsbDepartmentCmd extends PageQuery {
+    private static final long serialVersionUID = 5431163674119912344L;
+
+    /**
+     * 部门id
+     */
+    private Long departmentId;
+
+    /**
+     * 租户
+     */
+    @NotNull(message = "tenant不能为空")
+    private String tenant;
+
+    /**
+     * 开关
+     * 0:所有子部门集合包括当前部门
+     * 1:当前部门集合
+     * 默认:0
+     */
+    private Integer recursive;
+
+    public Integer getRecursive(){
+        return Objects.nonNull(this.recursive)?this.recursive:0;
+    }
+}

+ 30 - 0
smsb-customer-manager-client/src/main/java/com/inspur/customer/object/org/SmsbDepartmentUpdate.java

@@ -0,0 +1,30 @@
+package com.inspur.customer.object.org;
+
+import com.alibaba.cola.dto.Command;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * @Author wangbo13
+ * @Date 2022/11/28 18:20
+ * @Version 1.0
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class SmsbDepartmentUpdate extends Command {
+    private static final long serialVersionUID = 2058133250023880231L;
+
+    /**
+     * 主键
+     */
+    @NotNull(message = "id不能为空")
+    private Long id;
+
+    /**
+     * 部门名称
+     */
+    @NotNull(message = "name不能为空")
+    private String name;
+}

+ 22 - 0
smsb-customer-manager-client/src/main/java/com/inspur/customer/object/org/SmsbRegrouping.java

@@ -0,0 +1,22 @@
+package com.inspur.customer.object.org;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * @Author wangbo13
+ * @Date 2022/11/30 16:40
+ * @Version 1.0
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class SmsbRegrouping extends OperationAuthority{
+    private static final long serialVersionUID = 6339187897255504048L;
+
+    @NotNull(message = "userId不能为空")
+    private String userId;
+
+    private Long departmentId;
+}

+ 52 - 0
smsb-customer-manager-client/src/main/java/com/inspur/customer/object/org/SmsbSimpleUserCO.java

@@ -0,0 +1,52 @@
+package com.inspur.customer.object.org;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * @Author wangbo13
+ * @Date 2022/12/5 11:22
+ * @Version 1.0
+ */
+@Data
+public class SmsbSimpleUserCO implements Serializable {
+    private static final long serialVersionUID = -514586097597178413L;
+
+    /**
+     * 部门id
+     */
+    private Long departmentId;
+
+    /**
+     * 部门路径
+     */
+    private String path;
+
+    /**
+     * 用户id
+     */
+    private String userId;
+
+    /**
+     * 账号
+     */
+    private String userName;
+
+    /**
+     * 用户名
+     */
+    private String familyName;
+
+    /**
+     * 租户
+     */
+    private String tenant;
+
+    /**
+     * 状态
+     * 0:启用
+     * 1:禁用
+     */
+    private Integer enabled;
+}

+ 48 - 0
smsb-customer-manager-client/src/main/java/com/inspur/customer/object/org/SmsbUserAdd.java

@@ -0,0 +1,48 @@
+package com.inspur.customer.object.org;
+
+import com.alibaba.cola.dto.Command;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.keycloak.representations.idm.CredentialRepresentation;
+
+import javax.validation.constraints.NotNull;
+import java.util.List;
+
+/**
+ * @Author wangbo13
+ * @Date 2022/11/30 9:40
+ * @Version 1.0
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class SmsbUserAdd extends OperationAuthority {
+    private static final long serialVersionUID = 8931489400099446732L;
+
+    /**
+     * 账号
+     */
+    @NotNull(message = "username不能为空")
+    private String username;
+
+    /**
+     * 部门id
+     */
+    private Long departmentId;
+
+    /**
+     * 部门路径
+     */
+    private String path;
+
+    /**
+     * 用户中文名
+     */
+    @NotNull(message = "familyName不能为空")
+    private String familyName;
+
+    /**
+     * 权证
+     */
+    @NotNull(message = "credentials不能为空")
+    private List<CredentialRepresentation> credentials;
+}

+ 54 - 0
smsb-customer-manager-client/src/main/java/com/inspur/customer/object/org/SmsbUserMigration.java

@@ -0,0 +1,54 @@
+package com.inspur.customer.object.org;
+
+import com.alibaba.cola.dto.Command;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * @Author wangbo13
+ * @Date 2022/12/5 17:09
+ * @Version 1.0
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class SmsbUserMigration extends Command {
+    private static final long serialVersionUID = 2062076612804311850L;
+
+    /**
+     * 部门id
+     */
+    private Long departmentId;
+
+    /**
+     * 部门路径
+     */
+    private String path;
+
+    /**
+     * 用户id
+     */
+    @NotNull(message = "userId不能为空")
+    private String userId;
+
+    /**
+     * 账号
+     */
+    @NotNull(message = "userName不能为空")
+    private String userName;
+
+    /**
+     * 租户
+     */
+    @NotNull(message = "tenant不能为空")
+    private String tenant;
+
+    /**
+     * 状态
+     * 0:禁用
+     * 1:启用
+     */
+    @NotNull(message = "userId不能为空")
+    private Integer enabled;
+}

+ 29 - 0
smsb-customer-manager-client/src/main/java/com/inspur/customer/object/org/SmsbUserUpdate.java

@@ -0,0 +1,29 @@
+package com.inspur.customer.object.org;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * @Author wangbo13
+ * @Date 2022/12/5 18:12
+ * @Version 1.0
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class SmsbUserUpdate extends OperationAuthority{
+    private static final long serialVersionUID = -217457569019658548L;
+
+    /**
+     * 用户ID
+     */
+    @NotNull(message = "userId不能为空")
+    private String userId ;
+
+    /**
+     * 用户中文名
+     */
+    @NotNull(message = "familyName不能为空")
+    private String familyName;
+}

+ 10 - 0
smsb-customer-manager-infrastructure/pom.xml

@@ -26,6 +26,16 @@
             <groupId>mysql</groupId>
             <artifactId>mysql-connector-java</artifactId>
         </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-annotations</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.inspur</groupId>
+            <artifactId>smsb-customer-manager-client</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+            <scope>compile</scope>
+        </dependency>
     </dependencies>
 
 </project>

+ 44 - 0
smsb-customer-manager-infrastructure/src/main/java/com/inspur/customer/infrastructure/convertor/SmsbDepartmentConvertor.java

@@ -0,0 +1,44 @@
+package com.inspur.customer.infrastructure.convertor;
+
+import com.inspur.customer.constant.Constant;
+import com.inspur.customer.infrastructure.object.org.SmsbDepartmentDO;
+import com.inspur.customer.infrastructure.object.org.SmsbDepartmentUserDO;
+import com.inspur.customer.object.org.SmsbDepartmentAdd;
+import com.inspur.customer.object.org.SmsbUserAdd;
+import org.springframework.beans.BeanUtils;
+
+import java.time.LocalDateTime;
+
+/**
+ * @Author wangbo13
+ * @Date 2022/11/28 17:08
+ * @Version 1.0
+ */
+public class SmsbDepartmentConvertor {
+    private SmsbDepartmentConvertor() {
+        throw new IllegalStateException("Utility class");
+    }
+
+    public static SmsbDepartmentDO toDataObjectForCreate(String userId ,SmsbDepartmentAdd smsbDepartmentAdd){
+        SmsbDepartmentDO department = new SmsbDepartmentDO();
+        BeanUtils.copyProperties(smsbDepartmentAdd , department);
+        department.setCreateBy(userId);
+        department.setUpdateBy(userId);
+        department.setCreateTime(LocalDateTime.now());
+        department.setUpdateTime(LocalDateTime.now());
+        return department;
+    }
+
+    public static SmsbDepartmentUserDO toDataObjectForCreate(String userId, SmsbUserAdd smsbUserAdd){
+        SmsbDepartmentUserDO userRelation = new SmsbDepartmentUserDO();
+        userRelation.setUserId(userId);
+        userRelation.setFamilyName(smsbUserAdd.getFamilyName());
+        userRelation.setUserName(smsbUserAdd.getUsername());
+        userRelation.setDepartmentId(smsbUserAdd.getDepartmentId());
+        userRelation.setPath(smsbUserAdd.getPath());
+        userRelation.setEnabled(Constant.AccountNumberEnum.ENABLE.value());
+        userRelation.setTenant(smsbUserAdd.getTenant());
+        userRelation.setCreateTime(LocalDateTime.now());
+        return userRelation;
+    }
+}

+ 14 - 0
smsb-customer-manager-infrastructure/src/main/java/com/inspur/customer/infrastructure/mapper/org/SmsbDepartmentMapper.java

@@ -0,0 +1,14 @@
+package com.inspur.customer.infrastructure.mapper.org;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.inspur.customer.infrastructure.object.org.SmsbDepartmentDO;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * @Author wangbo13
+ * @Date 2022/11/25 14:49
+ * @Version 1.0
+ */
+@Mapper
+public interface SmsbDepartmentMapper extends BaseMapper<SmsbDepartmentDO> {
+}

+ 15 - 0
smsb-customer-manager-infrastructure/src/main/java/com/inspur/customer/infrastructure/mapper/org/SmsbDepartmentUserMapper.java

@@ -0,0 +1,15 @@
+package com.inspur.customer.infrastructure.mapper.org;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.inspur.customer.infrastructure.object.org.SmsbDepartmentUserDO;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * @Author wangbo13
+ * @Date 2022/11/25 14:49
+ * @Version 1.0
+ */
+@Mapper
+public interface SmsbDepartmentUserMapper extends BaseMapper<SmsbDepartmentUserDO> {
+
+}

+ 88 - 0
smsb-customer-manager-infrastructure/src/main/java/com/inspur/customer/infrastructure/object/org/SmsbDepartmentDO.java

@@ -0,0 +1,88 @@
+package com.inspur.customer.infrastructure.object.org;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * @Author wangbo13
+ * @Date 2022/11/25 14:23
+ * @Version 1.0
+ */
+@Data
+@TableName("smsb_department")
+public class SmsbDepartmentDO implements Serializable {
+    private static final long serialVersionUID = -2282747602419984961L;
+
+    @TableId
+    private Long id;
+    /**
+     * 父节点id
+     */
+    @TableField("parent_id")
+    private Long parentId;
+    /**
+     * 部门中文名称
+     */
+    private String name;
+    /**
+     * 部门唯一标识
+     */
+    private String mark;
+
+    /**
+     * 路径
+     */
+    private String path;
+    /**
+     * 序号
+     */
+    @TableField("order_no")
+    private Integer orderNo;
+
+    /**
+     * 租户
+     */
+    private String tenant;
+
+    /**
+     * 删除标识
+     */
+    @TableField("is_del")
+    private Integer isDel;
+
+    /**
+     * 创建人
+     */
+    @TableField("create_by")
+    private String createBy;
+
+    /**
+     * 创建时间
+     */
+    @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @TableField(value = "create_time", fill = FieldFill.INSERT)
+    private LocalDateTime createTime;
+
+    /**
+     * 修改人
+     */
+    @TableField("update_by")
+    private String updateBy;
+
+    /**
+     * 修改时间
+     */
+    @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
+    private LocalDateTime updateTime;
+}

+ 75 - 0
smsb-customer-manager-infrastructure/src/main/java/com/inspur/customer/infrastructure/object/org/SmsbDepartmentUserDO.java

@@ -0,0 +1,75 @@
+package com.inspur.customer.infrastructure.object.org;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * @Author wangbo13
+ * @Date 2022/11/25 14:40
+ * @Version 1.0
+ */
+@Data
+@TableName("smsb_department_user")
+public class SmsbDepartmentUserDO implements Serializable {
+    private static final long serialVersionUID = 814979903754300618L;
+
+    @TableId
+    private Long id;
+
+    /**
+     * 部门id
+     */
+    @TableField("department_id")
+    private Long departmentId;
+
+    /**
+     * 部门路径
+     */
+    private String path;
+
+    /**
+     * 用户id
+     */
+    @TableField("user_id")
+    private String userId;
+
+    /**
+     * 用户名
+     */
+    @TableField("family_name")
+    private String familyName;
+
+    /**
+     * 账号
+     */
+    @TableField("user_name")
+    private String userName;
+
+    /**
+     * 租户
+     */
+    private String tenant;
+
+    /**
+     * 状态
+     * 0:启用
+     * 1:禁用
+     */
+    private Integer enabled;
+
+    /**
+     * 创建时间
+     */
+    @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @TableField(value = "create_time", fill = FieldFill.INSERT)
+    private LocalDateTime createTime;
+}

+ 5 - 0
smsb-customer-manager-infrastructure/src/main/resources/mapper/org/SmsbDepartmentMapper.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.inspur.customer.infrastructure.mapper.org.SmsbDepartmentMapper">
+
+</mapper>

+ 5 - 0
smsb-customer-manager-infrastructure/src/main/resources/mapper/org/SmsbDepartmentUserMapper.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.inspur.customer.infrastructure.mapper.org.SmsbDepartmentUserMapper">
+
+</mapper>