diff --git a/bonus-api/bonus-api-system/src/main/java/com/bonus/system/api/RemoteSbdUserService.java b/bonus-api/bonus-api-system/src/main/java/com/bonus/system/api/RemoteSbdUserService.java new file mode 100644 index 0000000..685d1df --- /dev/null +++ b/bonus-api/bonus-api-system/src/main/java/com/bonus/system/api/RemoteSbdUserService.java @@ -0,0 +1,37 @@ +package com.bonus.system.api; + +import com.bonus.common.core.constant.SecurityConstants; +import com.bonus.common.core.constant.ServiceNameConstants; +import com.bonus.common.core.domain.R; +import com.bonus.common.core.web.domain.AjaxResult; +import com.bonus.common.core.web.page.TableDataInfo; +import com.bonus.system.api.domain.SysDept; +import com.bonus.system.api.domain.SysUser; +import com.bonus.system.api.factory.RemoteUserFallbackFactory; +import com.bonus.system.api.model.LoginUser; +import com.bonus.system.api.model.SbdUser; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +/** + * 用户服务 + * + * @author bonus + */ +@FeignClient(contextId = "remoteSbdUserService", value = ServiceNameConstants.SYSTEM_SERVICE, fallbackFactory = RemoteUserFallbackFactory.class) +public interface RemoteSbdUserService { + + /** + * 查询 送变电用户及角色 + * + * @param source 请求来源 + * @return 用户信息 + */ + @GetMapping("/sbdUser/getInfo/{username}") + public R getInfo(@PathVariable("username") String username, @RequestHeader(SecurityConstants.FROM_SOURCE) String source); + + + + +} diff --git a/bonus-api/bonus-api-system/src/main/java/com/bonus/system/api/RemoteUserService.java b/bonus-api/bonus-api-system/src/main/java/com/bonus/system/api/RemoteUserService.java index 3158f54..46ab0b1 100644 --- a/bonus-api/bonus-api-system/src/main/java/com/bonus/system/api/RemoteUserService.java +++ b/bonus-api/bonus-api-system/src/main/java/com/bonus/system/api/RemoteUserService.java @@ -5,6 +5,7 @@ import com.bonus.common.core.constant.ServiceNameConstants; import com.bonus.common.core.domain.R; import com.bonus.common.core.web.domain.AjaxResult; import com.bonus.common.core.web.page.TableDataInfo; +import com.bonus.system.api.domain.RoleParams; import com.bonus.system.api.domain.SysDept; import com.bonus.system.api.domain.SysUser; import com.bonus.system.api.factory.RemoteUserFallbackFactory; @@ -159,6 +160,9 @@ public interface RemoteUserService { @PostMapping("/user/authRole/edit") public AjaxResult insertAuthRole(@PathVariable("userId") Long userId, @PathVariable("roleIds") Long[] roleIds, @RequestHeader(SecurityConstants.FROM_SOURCE) String source); + + @PostMapping("/user/authRole/insertRole") + public AjaxResult insertRole(@RequestBody RoleParams roleParams, @RequestHeader(SecurityConstants.FROM_SOURCE) String source); /** * 获取当前登录用户数据权限范围内的部门权限下的部门树列表 * diff --git a/bonus-api/bonus-api-system/src/main/java/com/bonus/system/api/domain/RoleParams.java b/bonus-api/bonus-api-system/src/main/java/com/bonus/system/api/domain/RoleParams.java new file mode 100644 index 0000000..3c9f91f --- /dev/null +++ b/bonus-api/bonus-api-system/src/main/java/com/bonus/system/api/domain/RoleParams.java @@ -0,0 +1,14 @@ +package com.bonus.system.api.domain; + +import lombok.Data; + +/** + * @author 黑子 + */ +@Data +public class RoleParams { + + private Long userId; + + private Long[] roleIds; +} diff --git a/bonus-api/bonus-api-system/src/main/java/com/bonus/system/api/factory/RemoteSbdUserFallbackFactory.java b/bonus-api/bonus-api-system/src/main/java/com/bonus/system/api/factory/RemoteSbdUserFallbackFactory.java new file mode 100644 index 0000000..8499cb7 --- /dev/null +++ b/bonus-api/bonus-api-system/src/main/java/com/bonus/system/api/factory/RemoteSbdUserFallbackFactory.java @@ -0,0 +1,45 @@ +package com.bonus.system.api.factory; + +import com.bonus.common.core.constant.HttpStatus; +import com.bonus.common.core.domain.R; +import com.bonus.common.core.web.domain.AjaxResult; +import com.bonus.common.core.web.page.TableDataInfo; +import com.bonus.system.api.RemoteSbdUserService; +import com.bonus.system.api.RemoteUserService; +import com.bonus.system.api.domain.SysDept; +import com.bonus.system.api.domain.SysUser; +import com.bonus.system.api.model.LoginUser; +import com.bonus.system.api.model.SbdUser; +import com.github.pagehelper.PageInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.cloud.openfeign.FallbackFactory; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; + +/** + * 用户服务降级处理 + * + * @author bonus + */ +@Component +public class RemoteSbdUserFallbackFactory implements FallbackFactory +{ + private static final Logger log = LoggerFactory.getLogger(RemoteSbdUserFallbackFactory.class); + + @Override + public RemoteSbdUserService create(Throwable throwable) + { + log.error("用户服务调用失败:{}", throwable.getMessage()); + return new RemoteSbdUserService() + { + + @Override + public R getInfo(String username, String source) { + return R.fail("获取送变电用户失败:" + throwable.getMessage()); + } + }; + } +} diff --git a/bonus-api/bonus-api-system/src/main/java/com/bonus/system/api/factory/RemoteUserFallbackFactory.java b/bonus-api/bonus-api-system/src/main/java/com/bonus/system/api/factory/RemoteUserFallbackFactory.java index 0694f32..e6ddd6c 100644 --- a/bonus-api/bonus-api-system/src/main/java/com/bonus/system/api/factory/RemoteUserFallbackFactory.java +++ b/bonus-api/bonus-api-system/src/main/java/com/bonus/system/api/factory/RemoteUserFallbackFactory.java @@ -3,6 +3,7 @@ package com.bonus.system.api.factory; import com.bonus.common.core.constant.HttpStatus; import com.bonus.common.core.web.domain.AjaxResult; import com.bonus.common.core.web.page.TableDataInfo; +import com.bonus.system.api.domain.RoleParams; import com.bonus.system.api.domain.SysDept; import com.github.pagehelper.PageInfo; import org.slf4j.Logger; @@ -131,6 +132,11 @@ public class RemoteUserFallbackFactory implements FallbackFactory list; + /** + * 岗位id + */ + private String postId; + /** + * 岗位名称 + */ + private String name; + /** + * 岗位 + */ + private String postName; + /** + * 部门名称 + */ + private String deptName; + + + + +} diff --git a/bonus-auth/src/main/java/com/bonus/auth/controller/TokenController.java b/bonus-auth/src/main/java/com/bonus/auth/controller/TokenController.java index 612c346..ef71070 100644 --- a/bonus-auth/src/main/java/com/bonus/auth/controller/TokenController.java +++ b/bonus-auth/src/main/java/com/bonus/auth/controller/TokenController.java @@ -33,6 +33,7 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; +import javax.annotation.PostConstruct; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.util.Collection; @@ -187,6 +188,19 @@ public class TokenController { return R.fail("登录失败!"); } + @PostConstruct + public void test(){ + sysLoginService.createNewUser("mengj7012"); + } + + + @PostMapping("testLogin") + public R testLogin(@RequestBody LoginBody form) { + + + return R.fail("登录失败!"); + } + /** * 获取手机验证码 * diff --git a/bonus-auth/src/main/java/com/bonus/auth/service/SysLoginService.java b/bonus-auth/src/main/java/com/bonus/auth/service/SysLoginService.java index 75c5110..7b11ee6 100644 --- a/bonus-auth/src/main/java/com/bonus/auth/service/SysLoginService.java +++ b/bonus-auth/src/main/java/com/bonus/auth/service/SysLoginService.java @@ -9,24 +9,31 @@ import com.bonus.common.core.constant.SecurityConstants; import com.bonus.common.core.constant.UserConstants; import com.bonus.common.core.domain.R; import com.bonus.common.core.exception.ServiceException; +import com.bonus.common.core.text.Convert; import com.bonus.common.core.utils.StringUtils; import com.bonus.common.core.utils.encryption.Sm4Utils; import com.bonus.common.core.web.domain.AjaxResult; import com.bonus.common.security.utils.SecurityUtils; import com.bonus.config.SystemConfig; import com.bonus.system.api.RemoteConfigService; +import com.bonus.system.api.RemoteSbdUserService; import com.bonus.system.api.RemoteUserService; +import com.bonus.system.api.domain.RoleParams; import com.bonus.system.api.domain.SysUser; import com.bonus.system.api.model.LoginUser; +import com.bonus.system.api.model.SbdUser; import com.hankcs.hanlp.HanLP; import lombok.extern.slf4j.Slf4j; +import org.apache.logging.log4j.util.Strings; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; import org.springframework.web.client.RestTemplate; import javax.annotation.Resource; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; /** @@ -38,6 +45,8 @@ import java.util.Map; public class SysLoginService { @Resource private RemoteUserService remoteUserService; + @Resource + private RemoteSbdUserService sbdUserService; @Autowired @@ -231,6 +240,61 @@ public class SysLoginService { return loginUser; } + //mengj7012 + public LoginUser createNewUser(String userName) { + //通过用户名获取人员信息 + LoginUser loginUserNew = new LoginUser(); + SysUser sysUserNew = new SysUser(); + R userResult = remoteUserService.getUserInfo(userName, SecurityConstants.INNER); + if (userResult.getData() == null || R.FAIL == userResult.getCode()) { + log.info("登录用户不存在,进行创建----"); + RegisterBody registerBody = new RegisterBody(); + //查询送变电用户 及橘色 + R sbdUserR = sbdUserService.getInfo(userName, SecurityConstants.INNER); + SbdUser sbdUser = sbdUserR.getData(); + registerBody.setUsername(sbdUser.getUserName()); + registerBody.setNickName(sbdUser.getNickName()); + registerBody.setMobile(sbdUser.getMobile()); + registerBody.setDeptId(sbdUser.getBnsDeptId()); + registerBody.setPassword("Bonus@Max2024"); + AjaxResult result = configService.getConfigKey("sys.user.initPassword"); + if ("200".equals(result.get("code").toString())) + { + registerBody.setPassword(result.get("msg").toString()); + } + log.info("开始进行注册{}",registerBody); + try { + register(registerBody); + log.info("注册成功!"); + //查询用户信息 + userResult = remoteUserService.getUserInfo(userName, SecurityConstants.INNER); + loginUserNew = userResult.getData(); + sysUserNew = loginUserNew.getSysUser(); + log.info("获取用户信息成功!{}",loginUserNew.getSysUser()); + //初始化一个角色 + List list=sbdUser.getList(); + String data=Strings.join(list,','); + Long[] datas= Convert.toLongArray(data); + // 将数组的第一个元素赋值为 36,流程角色 + log.info("开始绑定角色信息{}",datas); + RoleParams roleParams=new RoleParams(); + roleParams.setRoleIds(datas); + roleParams.setUserId(sysUserNew.getUserId()); + remoteUserService.insertRole(roleParams,SecurityConstants.INNER); + log.info("角色信息绑定成功!"); + }catch (Exception e){ + log.error(e.toString(),e); + throw new ServiceException("登录失败,请稍后重试"); + } + + + }else{ + loginUserNew = userResult.getData(); + } + return loginUserNew; + + } + /** * 先查,如果没有进行创建 * @param sysUser @@ -242,7 +306,6 @@ public class SysLoginService { SysUser sysUserNew = new SysUser(); //String resultName = convertAndAppend(sysUser.getNickName(), sysUser.getPhonenumber()); R userResult = remoteUserService.getUserInfo(sysUser.getUserName(), SecurityConstants.INNER); - if (userResult.getData() == null || R.FAIL == userResult.getCode()) { log.info("登录用户不存在,进行创建----"); RegisterBody registerBody = new RegisterBody(); diff --git a/bonus-modules/bonus-system/src/main/java/com/bonus/system/controller/SbdUserController.java b/bonus-modules/bonus-system/src/main/java/com/bonus/system/controller/SbdUserController.java new file mode 100644 index 0000000..5c88c2b --- /dev/null +++ b/bonus-modules/bonus-system/src/main/java/com/bonus/system/controller/SbdUserController.java @@ -0,0 +1,46 @@ +package com.bonus.system.controller; + +import com.bonus.common.core.domain.R; +import com.bonus.common.core.utils.StringUtils; +import com.bonus.common.security.annotation.InnerAuth; +import com.bonus.system.api.domain.SysDept; +import com.bonus.system.api.domain.SysUser; +import com.bonus.system.api.model.LoginUser; +import com.bonus.system.api.model.SbdUser; +import com.bonus.system.service.ISbdUserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.PostConstruct; +import java.util.Set; + + +/** + * 送变电用户信息 + * @author 黑子 + */ +@RestController +@RequestMapping("/sbdUser") +public class SbdUserController { + + @Autowired + private ISbdUserService service; + + /** + * 依据登录名查询用户角色 + * test shenyd6214/mengj7012 + */ + @InnerAuth + @GetMapping("/getInfo/{username}") + public R getInfo(@PathVariable("username") String username) { + SbdUser user=service.getUserInfo(username); + return R.ok(user); + } + + + + +} diff --git a/bonus-modules/bonus-system/src/main/java/com/bonus/system/mapper/SbdUserMapper.java b/bonus-modules/bonus-system/src/main/java/com/bonus/system/mapper/SbdUserMapper.java new file mode 100644 index 0000000..b8c483d --- /dev/null +++ b/bonus-modules/bonus-system/src/main/java/com/bonus/system/mapper/SbdUserMapper.java @@ -0,0 +1,77 @@ +package com.bonus.system.mapper; + +import com.bonus.system.api.model.SbdUser; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 送变电业务 数据层 + * @author 黑子 + */ +@Mapper +public interface SbdUserMapper { + /** + * 查询项目部用户人员信息登录 + * @param username + * @return + */ + SbdUser getDcUserInfo(@Param("userName") String username); + + /** + * 依据用户id查询工程信息 + * @param user + * @return + */ + List getUserRoleByPro(SbdUser user); + + /** + * 依据角色编码查询 角色 + * @param role + * @return + */ + List getRoleIdByCode(@Param("list") List role); + + /** + * 依据角色编码查询 角色 + * @param roleKey + * @return + */ + List getRoleIdByCodeKey(@Param("roleKey") String roleKey); + + /** + * 查询技术员角色 + * @param user + * @return + */ + int getJsyUserRole(SbdUser user); + + /** + * 查询送变电 + * @param username + * @return + */ + SbdUser getSbdUser(@Param("userName") String username); + + /** + * 查询 部门信息 + * @param user + * @return + */ + SbdUser getDeptById(SbdUser user); + + /** + * 查询用户角色岗位 + * @param user + * @return + */ + List getUserPost(SbdUser user); + + /** + * 查询分公司id + * @param user + * @return + */ + Long getBonusDeptId(SbdUser user); +} diff --git a/bonus-modules/bonus-system/src/main/java/com/bonus/system/service/ISbdUserService.java b/bonus-modules/bonus-system/src/main/java/com/bonus/system/service/ISbdUserService.java new file mode 100644 index 0000000..1f5c2f3 --- /dev/null +++ b/bonus-modules/bonus-system/src/main/java/com/bonus/system/service/ISbdUserService.java @@ -0,0 +1,16 @@ +package com.bonus.system.service; + +import com.bonus.system.api.model.SbdUser; + +/** + * 送变电 用户业务接口层 + * @author 黑子 + */ +public interface ISbdUserService { + /** + * 查询用户角色及用户信息 + * @param username + * @return + */ + SbdUser getUserInfo(String username); +} diff --git a/bonus-modules/bonus-system/src/main/java/com/bonus/system/service/ISysUserService.java b/bonus-modules/bonus-system/src/main/java/com/bonus/system/service/ISysUserService.java index 984e74d..4b651e9 100644 --- a/bonus-modules/bonus-system/src/main/java/com/bonus/system/service/ISysUserService.java +++ b/bonus-modules/bonus-system/src/main/java/com/bonus/system/service/ISysUserService.java @@ -239,4 +239,6 @@ public interface ISysUserService { List getList(SysUser user); + + void test(); } diff --git a/bonus-modules/bonus-system/src/main/java/com/bonus/system/service/impl/SbdUserServiceImpl.java b/bonus-modules/bonus-system/src/main/java/com/bonus/system/service/impl/SbdUserServiceImpl.java new file mode 100644 index 0000000..d59b317 --- /dev/null +++ b/bonus-modules/bonus-system/src/main/java/com/bonus/system/service/impl/SbdUserServiceImpl.java @@ -0,0 +1,223 @@ +package com.bonus.system.service.impl; + +import cn.hutool.core.collection.ListUtil; +import com.bonus.common.core.domain.R; +import com.bonus.system.api.model.SbdUser; +import com.bonus.system.mapper.SbdUserMapper; +import com.bonus.system.service.ISbdUserService; +import io.netty.util.internal.StringUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author 黑子 + * 送变电 用户角色 业务接口实现层 + * + */ +@Service +@Slf4j +public class SbdUserServiceImpl implements ISbdUserService { + + @Autowired + private SbdUserMapper mapper; + + /** + * 查询 sbd用户信息 + * @param username + * @return + */ + @Override + public SbdUser getUserInfo(String username) { + //第一种 普通用户查询及角色 + SbdUser user=getXmbUser(username); + if(user==null){ + user=getDeptUser(username); + } + return user; + + } + + + + /** + * 查询项目部用户信息 + * @param username + * @return + */ + /** + * 技术员编码 + */ + public static final String JSY_POST="2ca0d32b0f3611efa1940242ac130004"; + private SbdUser getXmbUser(String username) { + SbdUser user=mapper.getDcUserInfo(username); + Long bnsDeptId=mapper.getBonusDeptId(user); + if(bnsDeptId!=null){ + user.setBnsDeptId(bnsDeptId); + } + List roleList=new ArrayList<>(); + if(user!=null){ + //技术总工 、负责人、项目经理角色查询 + List role=mapper.getUserRoleByPro(user); + if(role!=null && !role.isEmpty()){ + List roleIds=mapper.getRoleIdByCode(role); + if(roleIds!=null && !roleIds.isEmpty()){ + roleList.addAll(roleIds); + } + } + user.setPostId(JSY_POST); + //是不是技术员 + int num=mapper.getJsyUserRole(user); + if(num>0){ + List roleIds=mapper.getRoleIdByCodeKey("jsy"); + if(roleIds!=null && !roleIds.isEmpty()){ + roleList.addAll(roleIds); + } + } + //查询 技术员、材料员 + if("材料员".equals(user.getPostName())){ + List roleIds=mapper.getRoleIdByCodeKey("cly"); + if(roleIds!=null && !roleIds.isEmpty()){ + roleList.addAll(roleIds); + } + } else if("科长".equals(user.getPostName())){ + if(user.getDeptName().contains("技术科")){ + List roleIds=mapper.getRoleIdByCodeKey("jskkz"); + if(roleIds!=null && !roleIds.isEmpty()){ + roleList.addAll(roleIds); + } + + }else if(user.getDeptName().contains("施工管理")){ + List roleIds=mapper.getRoleIdByCodeKey("sgglkkz"); + if(roleIds!=null && !roleIds.isEmpty()){ + roleList.addAll(roleIds); + } + }else if(user.getDeptName().contains("物资")){ + List roleIds=mapper.getRoleIdByCodeKey("wzkkz"); + if(roleIds!=null && !roleIds.isEmpty()){ + roleList.addAll(roleIds); + } + } + + + } else if("副科长".equals(user.getPostName())){ + if(user.getDeptName().contains("技术科")){ + List roleIds=mapper.getRoleIdByCodeKey("jskfkz"); + if(roleIds!=null && !roleIds.isEmpty()){ + roleList.addAll(roleIds); + } + + }else if(user.getDeptName().contains("施工管理")){ + List roleIds=mapper.getRoleIdByCodeKey("sgglkfkz"); + if(roleIds!=null && !roleIds.isEmpty()){ + roleList.addAll(roleIds); + } + }else if(user.getDeptName().contains("物资")){ + List roleIds=mapper.getRoleIdByCodeKey("wzkfkz"); + if(roleIds!=null && !roleIds.isEmpty()){ + roleList.addAll(roleIds); + } + } + } + user.setList(roleList); + return user; + } + + return null; + } + /** + * 查询分甘肃部门审核信息 + * @param username + * @return + */ + private SbdUser getDeptUser(String username) { + + SbdUser user=mapper.getSbdUser(username); + List roleList=new ArrayList<>(); + if(user!=null){ + SbdUser dept=mapper.getDeptById(user); + user.setUnitId(dept.getCompanyId()); + Long bnsDeptId=mapper.getBonusDeptId(user); + if(bnsDeptId!=null){ + user.setBnsDeptId(bnsDeptId); + } + + List roleName=mapper.getUserPost(user); + if(roleName!=null && !roleName.isEmpty()){ + for (String name:roleName){ + if("材料员".equals(name)){ + List roleIds=mapper.getRoleIdByCodeKey("cly"); + if(roleIds!=null && !roleIds.isEmpty()){ + roleList.addAll(roleIds); + } + }else if("科长".equals(name)){ + if(dept!=null){ + if(!StringUtil.isNullOrEmpty(dept.getName())){ + if(dept.getName().contains("技术科")){ + List roleIds=mapper.getRoleIdByCodeKey("jskkz"); + if(roleIds!=null && !roleIds.isEmpty()){ + roleList.addAll(roleIds); + } + + }else if(dept.getName().contains("施工管理")){ + List roleIds=mapper.getRoleIdByCodeKey("sgglkkz"); + if(roleIds!=null && !roleIds.isEmpty()){ + roleList.addAll(roleIds); + } + }else if(dept.getName().contains("物资")){ + List roleIds=mapper.getRoleIdByCodeKey("wzkkz"); + if(roleIds!=null && !roleIds.isEmpty()){ + roleList.addAll(roleIds); + } + } + + + } + + + + } + + }else if("副科长".equals(name)){ + if(dept!=null){ + if(!StringUtil.isNullOrEmpty(dept.getName())){ + if(dept.getName().contains("技术科")){ + List roleIds=mapper.getRoleIdByCodeKey("jskfkz"); + if(roleIds!=null && !roleIds.isEmpty()){ + roleList.addAll(roleIds); + } + + }else if(dept.getName().contains("施工管理")){ + List roleIds=mapper.getRoleIdByCodeKey("sgglkfkz"); + if(roleIds!=null && !roleIds.isEmpty()){ + roleList.addAll(roleIds); + } + }else if(dept.getName().contains("物资")){ + List roleIds=mapper.getRoleIdByCodeKey("wzkfkz"); + if(roleIds!=null && !roleIds.isEmpty()){ + roleList.addAll(roleIds); + } + } + + + } + + } + + } + + } + + + } + user.setList(roleList); + return user; + } + + return null; + } + +} diff --git a/bonus-modules/bonus-system/src/main/resources/mapper/system/SbdUserMapper.xml b/bonus-modules/bonus-system/src/main/resources/mapper/system/SbdUserMapper.xml new file mode 100644 index 0000000..d779467 --- /dev/null +++ b/bonus-modules/bonus-system/src/main/resources/mapper/system/SbdUserMapper.xml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + +