|
@@ -0,0 +1,869 @@
|
|
|
+package com.dragoninfo.dcuc.auth.admin.service.impl;
|
|
|
+
|
|
|
+import com.dragoninfo.dcuc.auth.admin.entity.AppMtAuth;
|
|
|
+import com.dragoninfo.dcuc.auth.admin.entity.ManageInfo;
|
|
|
+import com.dragoninfo.dcuc.auth.admin.entity.MgeLog;
|
|
|
+import com.dragoninfo.dcuc.auth.admin.entity.TempMtAuth;
|
|
|
+import com.dragoninfo.dcuc.auth.admin.enumresource.AdminObjectTypeEnum;
|
|
|
+import com.dragoninfo.dcuc.auth.admin.enumresource.AdminOperateTypeEnum;
|
|
|
+import com.dragoninfo.dcuc.auth.admin.repo.ManageInfoRepository;
|
|
|
+import com.dragoninfo.dcuc.auth.admin.service.IAppMtAuthService;
|
|
|
+import com.dragoninfo.dcuc.auth.admin.service.IManageInfoService;
|
|
|
+import com.dragoninfo.dcuc.auth.admin.service.IMgeLogService;
|
|
|
+import com.dragoninfo.dcuc.auth.admin.service.ITempMtAuthService;
|
|
|
+import com.dragoninfo.dcuc.auth.admin.vo.AdminAuditVO;
|
|
|
+import com.dragoninfo.dcuc.auth.sub.dto.AuthUserDTO;
|
|
|
+import com.dragoninfo.dcuc.auth.sub.entity.AuthUserInfo;
|
|
|
+import com.dragoninfo.dcuc.auth.sub.service.IAuthUserInfoService;
|
|
|
+import com.dragoninfo.dcuc.duceap.facade.ICodeListResourceFacade;
|
|
|
+import com.dragoninfo.dcuc.org.facade.IOrgInfoFacade;
|
|
|
+import com.dragoninfo.dcuc.org.vo.OrgTreeNode;
|
|
|
+import com.dragoninfo.duceap.commons.util.server.OrgInfoUtil;
|
|
|
+import com.dragonsoft.duceap.base.entity.http.ResponseStatus;
|
|
|
+import com.dragonsoft.duceap.base.entity.metadata.CodeRecord;
|
|
|
+import com.dragonsoft.duceap.base.entity.search.SearchDTO;
|
|
|
+import com.dragonsoft.duceap.base.entity.security.BaseSecurityUser;
|
|
|
+import com.dragonsoft.duceap.base.entity.security.SecurityUser;
|
|
|
+import com.dragonsoft.duceap.base.enums.BooleanEnum;
|
|
|
+import com.dragonsoft.duceap.base.utils.UserContextUtils;
|
|
|
+import com.dragonsoft.duceap.commons.util.collections.CollectionUtils;
|
|
|
+import com.dragonsoft.duceap.commons.util.string.StringUtils;
|
|
|
+import com.dragonsoft.duceap.core.entity.page.PageImpl;
|
|
|
+import com.dragonsoft.duceap.core.search.Searchable;
|
|
|
+import com.dragonsoft.duceap.core.search.enums.SearchOperator;
|
|
|
+import com.dragonsoft.duceap.core.search.filter.Condition;
|
|
|
+import org.slf4j.Logger;
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
+import org.springframework.beans.BeanUtils;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.data.domain.Example;
|
|
|
+import org.springframework.data.domain.Page;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
+
|
|
|
+import java.text.SimpleDateFormat;
|
|
|
+import java.util.*;
|
|
|
+import java.util.stream.Collectors;
|
|
|
+
|
|
|
+
|
|
|
+@Service
|
|
|
+@Transactional
|
|
|
+public class ManageInfoService implements IManageInfoService {
|
|
|
+
|
|
|
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private ManageInfoRepository manageInfoRepository;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private IAuthUserInfoService authUserInfoService;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private IAppMtAuthService appMtAuthService;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private ITempMtAuthService tempMtAuthService;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private IMgeLogService iMgeLogService;
|
|
|
+ @Autowired
|
|
|
+ private IOrgInfoFacade orgInfoFacade;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private ICodeListResourceFacade codeListResourceFacade;
|
|
|
+
|
|
|
+ private volatile boolean isEmpty = true;
|
|
|
+
|
|
|
+ private final Map<String, String> POLICE_CATEGORY_MAP = new HashMap<>();
|
|
|
+
|
|
|
+ private void checkPoliceTypeMap(){
|
|
|
+ if (!this.isEmpty){
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ synchronized (this.POLICE_CATEGORY_MAP){
|
|
|
+ if (this.isEmpty){
|
|
|
+ this.POLICE_CATEGORY_MAP.putAll(codeListResourceFacade.listCode("code", "T_MD_POLICE_TYPE").stream()
|
|
|
+ .collect(Collectors.toMap(CodeRecord::getValue,CodeRecord::getLabel)));
|
|
|
+ this.isEmpty = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ * 根据机构获取非管理员用户
|
|
|
+ *
|
|
|
+ * @param searchDTO
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public Page<AuthUserDTO> getUserNotInManage(SearchDTO searchDTO) {
|
|
|
+ checkPoliceTypeMap();
|
|
|
+ Searchable searchable = Searchable.toSearchable(searchDTO);
|
|
|
+ Condition orgIdEq = searchable.getSearchFilter("orgId", SearchOperator.eq);
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ if(null == orgIdEq) {
|
|
|
+ BaseSecurityUser currentUser = UserContextUtils.getCurrentUser();
|
|
|
+ boolean rootUser = authUserInfoService.isRootUser(currentUser.getId());
|
|
|
+ if(!rootUser) {
|
|
|
+ List<AppMtAuth> mtAuths = appMtAuthService.getByUserId(currentUser.getId());
|
|
|
+ if(CollectionUtils.isEmpty(mtAuths)) {
|
|
|
+ return new PageImpl<>(new ArrayList(), searchable.getPage(), 0L);
|
|
|
+ }
|
|
|
+ Set<String> allMtOrgIds = orgInfoFacade.getAllMtOrgIds(mtAuths.get(0).getOrgId());
|
|
|
+ searchable.addSearchFilter("orgId", SearchOperator.in, allMtOrgIds);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ Page<AuthUserInfo> userInfoPage = manageInfoRepository.getNotManagePolice(searchable);
|
|
|
+ List<AuthUserInfo> userInfos = userInfoPage.getContent();
|
|
|
+ List<AuthUserDTO> collect = userInfos.stream().map(e -> {
|
|
|
+ AuthUserDTO dto = new AuthUserDTO();
|
|
|
+ BeanUtils.copyProperties(e, dto);
|
|
|
+ final String code;
|
|
|
+ if (null == (code = e.getPoliceCategory())) {
|
|
|
+ return dto;
|
|
|
+ }
|
|
|
+ final String value = POLICE_CATEGORY_MAP.get(code);
|
|
|
+ dto.setPoliceCategory(value);
|
|
|
+ return dto;
|
|
|
+ }).collect(Collectors.toList());
|
|
|
+ return new PageImpl<>(collect, searchable.getPage(), userInfoPage.getTotalElements());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public ResponseStatus saveAdmin(String userIds) {
|
|
|
+ String[] idArray = userIds.split(",");
|
|
|
+ List<ManageInfo> manageInfos = new ArrayList<ManageInfo>();
|
|
|
+ Set<String> uids = new HashSet<String>();
|
|
|
+ for (String id : idArray) {
|
|
|
+ AuthUserInfo userInfo = authUserInfoService.findById(id);
|
|
|
+ if (userInfo == null) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ uids.add(id);
|
|
|
+ ManageInfo manageInfo = new ManageInfo();
|
|
|
+ manageInfo.setDeleted(BooleanEnum.FALSE.getValue());
|
|
|
+ manageInfo.setOrgId(userInfo.getOrgId());
|
|
|
+ manageInfo.setUserId(userInfo.getId());
|
|
|
+ manageInfo.setUserName(userInfo.getName());
|
|
|
+ manageInfo.setOrgName(userInfo.getOrgName());
|
|
|
+ manageInfo.setCreateUser(UserContextUtils.getCurrentUser().getId());
|
|
|
+ manageInfo.setCreateTime(new Date());
|
|
|
+ manageInfos.add(manageInfo);
|
|
|
+ }
|
|
|
+ manageInfoRepository.saveAll(manageInfos);
|
|
|
+
|
|
|
+ List<TempMtAuth> list = tempMtAuthService.findByUserIds(uids);
|
|
|
+ tempMtAuthService.deleteAll(list);
|
|
|
+ Map<String, String> authMap = list.stream()
|
|
|
+ .collect(Collectors.toMap(TempMtAuth::getUserId, TempMtAuth::getOrgId));
|
|
|
+ this.setMgeAuth(manageInfos, authMap);
|
|
|
+ return ResponseStatus.success();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public ResponseStatus deleteByUserId(String userId) {
|
|
|
+
|
|
|
+ appMtAuthService.delByUserId(userId);
|
|
|
+
|
|
|
+ manageInfoRepository.delByUserId(userId);
|
|
|
+ saveMgeLog(AdminOperateTypeEnum.QXGLY.getValue(),AdminObjectTypeEnum.SQGL.getValue(), userId, userId);
|
|
|
+ return ResponseStatus.success();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Page<AdminAuditVO> adminAuditPage(SearchDTO dto) {
|
|
|
+ Searchable searchable = Searchable.toSearchable(dto);
|
|
|
+ Page<AdminAuditVO> userInfoPage = manageInfoRepository.adminAuditPage(searchable);
|
|
|
+ List<AdminAuditVO> content = userInfoPage.getContent();
|
|
|
+ List<String> createUserIds = content.stream().map(AdminAuditVO::getCreateUser).collect(Collectors.toList());
|
|
|
+ List<AuthUserInfo> createUsers = authUserInfoService.findByIds(createUserIds);
|
|
|
+ Map<String, AuthUserInfo> userDTOMap = createUsers.stream().collect(Collectors.toMap(AuthUserInfo::getId, e -> e));
|
|
|
+ SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd");
|
|
|
+ content.forEach(e->{
|
|
|
+ String createUser = e.getCreateUser();
|
|
|
+ AuthUserInfo authUserInfo = userDTOMap.get(createUser);
|
|
|
+ if(null != authUserInfo) {
|
|
|
+ e.setCreateUserName(authUserInfo.getName());
|
|
|
+ }
|
|
|
+ Date createTime = e.getCreateTime();
|
|
|
+ if(null != createTime) {
|
|
|
+ e.setCreateTimeString(format.format(createTime));
|
|
|
+ }
|
|
|
+ });
|
|
|
+ return userInfoPage;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public ManageInfo manageInfoByUserId(String userId) {
|
|
|
+ ManageInfo manageInfo = new ManageInfo();
|
|
|
+ manageInfo.setUserId(userId);
|
|
|
+ Example<ManageInfo> example = Example.of(manageInfo);
|
|
|
+ return manageInfoRepository.findOne(example).orElse(null);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ * 广东地区管理员导入——新增管理员,新增理范围
|
|
|
+ *
|
|
|
+ * @param list 新增的管理员
|
|
|
+ * @param orgIdAuthMap 新的管理范围userId:orgIdAuth
|
|
|
+ */
|
|
|
+ private void setMgeAuth(Collection<ManageInfo> list, Map<String, String> orgIdAuthMap) {
|
|
|
+ List<AppMtAuth> appMtAuthList = new ArrayList<>();
|
|
|
+ List<MgeLog> mgeLogList = new ArrayList<>();
|
|
|
+ for (ManageInfo manageInfo : list) {
|
|
|
+ String userId = manageInfo.getUserId();
|
|
|
+ String orgIdAuth = orgIdAuthMap.get(userId);
|
|
|
+ mgeLogList.add(iMgeLogService.getMgeLog(AdminOperateTypeEnum.XZGLY.getValue(), userId, userId, AdminObjectTypeEnum.GLY.getValue()));
|
|
|
+ if (StringUtils.isNotEmpty(orgIdAuth)) {
|
|
|
+
|
|
|
+ AppMtAuth appMtAuth = new AppMtAuth(manageInfo.getId(), userId, orgIdAuth);
|
|
|
+ appMtAuthList.add(appMtAuth);
|
|
|
+
|
|
|
+ mgeLogList.add(iMgeLogService.getMgeLog(AdminOperateTypeEnum.XZJGGL.getValue(), userId, orgIdAuth, AdminObjectTypeEnum.JGGL.getValue()));
|
|
|
+ mgeLogList.add(iMgeLogService.getMgeLog(AdminOperateTypeEnum.XZSQGL.getValue(), userId, orgIdAuth, AdminObjectTypeEnum.SQGL.getValue()));
|
|
|
+ mgeLogList.add(iMgeLogService.getMgeLog(AdminOperateTypeEnum.XZRYGL.getValue(), userId, orgIdAuth, AdminObjectTypeEnum.RYGL.getValue()));
|
|
|
+ mgeLogList.add(iMgeLogService.getMgeLog(AdminOperateTypeEnum.XZGLYGL.getValue(), userId, orgIdAuth, AdminObjectTypeEnum.GLYGL.getValue()));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (CollectionUtils.isNotEmpty(appMtAuthList)) {
|
|
|
+ appMtAuthService.saveAll(appMtAuthList);
|
|
|
+ }
|
|
|
+ if (CollectionUtils.isNotEmpty(mgeLogList)) {
|
|
|
+ iMgeLogService.saveAll(mgeLogList);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ * 保存管理员操作日志
|
|
|
+ *
|
|
|
+ * @param type 操作类型
|
|
|
+ * @param userId 用户id
|
|
|
+ * @param ids 操作对象id
|
|
|
+ */
|
|
|
+ private void saveMgeLog(String type,String objectType, String userId, String ids) {
|
|
|
+ String objIds = ids.replace("[", "").replace("]", "");
|
|
|
+ if (StringUtils.isEmpty(objIds)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ MgeLog mgeLog = new MgeLog();
|
|
|
+ mgeLog.setMgeId(userId);
|
|
|
+ mgeLog.setOperateType(type);
|
|
|
+ mgeLog.setObjectType(objectType);
|
|
|
+ mgeLog.setObjectId(objIds);
|
|
|
+ SecurityUser userInfo = UserContextUtils.getCurrentUser();
|
|
|
+ if (userInfo == null) {
|
|
|
+ mgeLog.setOperateUserId("000000");
|
|
|
+ mgeLog.setOperateUserName("监控程序");
|
|
|
+ } else {
|
|
|
+ mgeLog.setOperateUserId(userInfo.getId());
|
|
|
+ mgeLog.setOperateUserName(userInfo.getName());
|
|
|
+ mgeLog.setOperateOrgId(userInfo.getDeptId());
|
|
|
+ mgeLog.setOperateOrgName(userInfo.getSecurityOrgName());
|
|
|
+ }
|
|
|
+ mgeLog.setOperateTime(new Date());
|
|
|
+ iMgeLogService.save(mgeLog);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean haveAuthWithManage(String userId) {
|
|
|
+ SecurityUser curUser = UserContextUtils.getCurrentUser();
|
|
|
+ if (authUserInfoService.isRootUser(curUser.getId())) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ String mgeMt = appMtAuthService.mgeAppRightRangeStr(curUser.getId());
|
|
|
+ AuthUserInfo userInfo = authUserInfoService.findById(userId);
|
|
|
+ String orgId = userInfo.getOrgId();
|
|
|
+ OrgTreeNode orgTreeNode = orgInfoFacade.getOrgTreeNode(orgId);
|
|
|
+
|
|
|
+ return OrgInfoUtil.isHaveAuth(userInfo.getOrgId(), orgTreeNode.getPath(), mgeMt);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Page<AuthUserDTO> rightRangeManagerList(SearchDTO searchDTO) {
|
|
|
+ checkPoliceTypeMap();
|
|
|
+ Searchable searchable = Searchable.toSearchable(searchDTO);
|
|
|
+ Condition orgIdEq = searchable.getSearchFilter("orgId", SearchOperator.eq);
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ if(null == orgIdEq) {
|
|
|
+ BaseSecurityUser currentUser = UserContextUtils.getCurrentUser();
|
|
|
+ boolean rootUser = authUserInfoService.isRootUser(currentUser.getId());
|
|
|
+ if(!rootUser) {
|
|
|
+ List<AppMtAuth> mtAuths = appMtAuthService.getByUserId(currentUser.getId());
|
|
|
+ if(CollectionUtils.isEmpty(mtAuths)) {
|
|
|
+ return new PageImpl<>(new ArrayList(), searchable.getPage(), 0L);
|
|
|
+ }
|
|
|
+ Set<String> allMtOrgIds = orgInfoFacade.getAllMtOrgIds(mtAuths.get(0).getOrgId());
|
|
|
+ searchable.addSearchFilter("orgId", SearchOperator.in, allMtOrgIds);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ Page<AuthUserInfo> userInfoPage = manageInfoRepository.getManageUserInfo(searchable);
|
|
|
+ List<AuthUserInfo> userInfos = userInfoPage.getContent();
|
|
|
+ List<AuthUserDTO> collect = userInfos.stream().map(e -> {
|
|
|
+
|
|
|
+ String name = e.getName();
|
|
|
+ if (org.springframework.util.StringUtils.hasText(name) && "ADMIN".equalsIgnoreCase(name)) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ AuthUserDTO dto = new AuthUserDTO();
|
|
|
+ BeanUtils.copyProperties(e, dto);
|
|
|
+ final String code;
|
|
|
+ if (null == (code = e.getPoliceCategory())) {
|
|
|
+ return dto;
|
|
|
+ }
|
|
|
+ dto.setPoliceCategory(POLICE_CATEGORY_MAP.get(code));
|
|
|
+ return dto;
|
|
|
+ }).filter(el-> el != null).collect(Collectors.toList());
|
|
|
+ return new PageImpl<>(collect, searchable.getPage(), userInfoPage.getTotalElements());
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+}
|