package com.uas.account.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.uas.account.AccountConfig;
import com.uas.account.core.Const;
import com.uas.account.core.ICallable;
import com.uas.account.core.Logger;
import com.uas.account.core.Page;
import com.uas.account.core.util.ExecuteUtils;
import com.uas.account.dao.AppDao;
import com.uas.account.dao.UserDao;
import com.uas.account.dao.UserSpaceDao;
import com.uas.account.entity.*;
import com.uas.account.exception.AccountException;
import com.uas.account.exception.VisibleError;
import com.uas.account.util.BeanUtil;
import com.uas.sso.common.encrypt.MD5;
import com.uas.sso.common.util.HttpUtil;
import com.uas.sso.common.util.HttpUtil.ResponseWrap;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.ui.ModelMap;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
@Transactional
public class UserServiceImpl implements UserService {

	@Autowired
	private UserDao userDao;
	@Autowired
	private UserSpaceDao userSpaceDao;
	@Autowired
	private AppService appService;
	@Autowired
	private TokenService tokenService;
	@Autowired
	private SysDataToMallService sysService;
	@Autowired
	private AppDao appDao;
	private Logger logger = Logger.getLogger();

	/**
	 * email标记，包含标记的字符串视为邮箱
	 */
	private static final String EMAIL_SIGN = "@";

	@Override
	@CacheEvict(value = { "users", "userView" }, allEntries = true)
	public List<User> save(User user, UserDetail userDetail) {
		String UID = userDetail == null ? (user == null ? null : user.getUid()) : userDetail.getMobile();
		if (UID == null) {
			throw new AccountException("参数有误");
		}
		if (userDetail.getMobile() != null && !userDetail.getMobile().matches(Const.REGEXP_MOBILE)) {
			userDetail.setValid(Const.NO);
			// 这里不能简单置空，或抛出异常；考虑接入的业务系统的复杂情况
		}
		if (user.getUid() != null && !user.getUid().matches(Const.REGEXP_MOBILE) && userDetail.getMobile() != null) {
			UID = StringUtils.trimAllWhitespace(userDetail.getMobile());
			user.setUid(UID);
		}
		if (userDetail.getEmail() != null && !userDetail.getEmail().matches(Const.REGEXP_EMAIL)) {
			// 不合法的邮箱地址设置为空
			userDetail.setEmail(null);
		}
		if (user.getSecondUID() != null && !user.getSecondUID().matches(Const.REGEXP_EMAIL)) {
			user.setSecondUID(userDetail.getEmail());
		}
		// 详细资料
		UserDetail oldUserDetail = userDao.findOne(UID);
		if (oldUserDetail != null) {
			BeanUtil.copyProperties(userDetail, oldUserDetail, true);
		} else {
			oldUserDetail = userDetail;
		}
		oldUserDetail = userDao.save(oldUserDetail);

		// 应用相关资料
		App app = appService.findOne(user.getAppId());
		UserSpace userSpace = getUserSpaceByUser(user, true);
		if (userSpace == null) {
			if (!AccountConfig.ACCOUNT_CENTER.equals(app.getUid())
					&& (user.getSpaceId() != null || user.getSpaceUID() != null || user.getSpaceDialectUID() != null)) {
				logger.error(
						"修改用户信息",
						String.format("根据{appId: %s, dialectUID: %s, spaceUID: %s, spaceId: %s}没有找到企业信息", user.getAppId(),
								user.getSpaceDialectUID(), user.getSpaceUID(), user.getSpaceId()));
			}
		} else {
			user.setSpaceId(userSpace.getId());
			user.setSpaceUID(StringUtils.trimAllWhitespace(userSpace.getUid()));
			user.setAppId(userSpace.getAppId());
			if (user.getSpaceDialectUID() != null && userSpace.getDialectUID() == null) {
				userSpace.setDialectUID(user.getSpaceDialectUID());
				userSpaceDao.save(userSpace);
			}
		}

		if (!AccountConfig.ACCOUNT_CENTER.equals(app.getUid())) {
			User oldUser = getExistUser(user, userSpace);
			// encry password
			String noEncryPwd = user.getPassword();
			BeanUtil.copyProperties(user, oldUser, true, "id", "password");
			// 存在修改个人信息的时候，直接修改密码的情况
			if (noEncryPwd != null && noEncryPwd.length() < 32) {
				oldUser.setPassword(getEncryPassword(app.getEncryFormat(), noEncryPwd, user.getSalt()));
				userDao.changePasswordByAppAndUser(app.getUid(), oldUser, oldUser.getPassword());
			} else {
				String password = setPasswordBySameApp(app, oldUser, userDetail);
				oldUser.setPassword(password);
				noEncryPwd = null;
			}
			// 存在直接修改已绑定邮箱的情况
			String newSecondUID = user.getSecondUID();
			if (newSecondUID != null && !newSecondUID.equals(oldUser.getSecondUID())) {
				userDao.changeSecondUIDByUser(oldUser, user.getSecondUID());
			} else {
				newSecondUID = null;
			}
			oldUser = userDao.save(oldUser);
			logger.info(this.getClass(), oldUser.getUid(), String.format("修改用户信息，%s From: %s, To: sso", oldUser.getName(), app.getUid()),
					oldUser);
			// 自动同步到其他App
			syncUser(oldUser, oldUserDetail, noEncryPwd, newSecondUID);
		} else {
			// 自动同步到其他App
			syncUser(user, oldUserDetail, user.getPassword(), null);
		}
		return findByUID(UID);
	}

	@Autowired
	private DataSourceTransactionManager transactionManager;

	@Override
    @CacheEvict(value = { "users", "userView" }, allEntries = true)
//	@Transactional
    public List<User> saveAllInfo(User user, UserDetail userDetail) {
		DefaultTransactionDefinition def = new DefaultTransactionDefinition();
		def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); // 事物隔离级别，开启新事务，这样会比较安全些。
		TransactionStatus status = transactionManager.getTransaction(def); // 获得事务状态
		try {
        // UU号不为空，则找到旧数据
        List<User> oldUsers = null;
        UserDetail oldDetail = null;
        String oldMobile = null;
		if (StringUtils.isEmpty(user.getDialectUID())) {
			throw new AccountException("该用户没有UU号");
		}
		oldUsers = userDao.findByDialectUID(user.getDialectUID());
		if (CollectionUtils.isEmpty(oldUsers)) {
			throw new AccountException("该用户未注册");
		}
		oldDetail = userDao.findOne(oldUsers.get(0).getUid());
		if (oldDetail == null) {
			throw new AccountException("没有找到用户信息");
		}
		oldMobile = oldDetail.getMobile();
		if (oldMobile == null) {
			throw new AccountException("用户信息已失效");
		}

        // 判断是否有人使用过修改后的账号
		if (user.getUid() != null && !user.getUid().equals(oldUsers.get(0).getUid())) {
			List<User> users = userDao.findByUID(user.getUid());
			if (!CollectionUtils.isEmpty(users)) {
//				throw new AccountException("改手机号已被使用，请输入自己的手机号");
			}
		}
		if (userDetail.getMobile() != null && !userDetail.getMobile().equals(oldDetail.getMobile())) {
			UserDetail temp = userDao.findOne(userDetail.getMobile());
			if (temp != null) {
//				throw new AccountException("改手机号已被使用，请输入自己的手机号");
			}
		}

		if (userDetail.getMobile() != null && !userDetail.getMobile().matches(Const.REGEXP_MOBILE)) {
            userDetail.setValid(Const.NO);
            // 这里不能简单置空，或抛出异常；考虑接入的业务系统的复杂情况
        }
        // 如果user中的手机号不合法，则用userDetail的手机号
		if (user.getUid() != null && !user.getUid().matches(Const.REGEXP_MOBILE) && userDetail.getMobile() != null) {
            String UID = StringUtils.trimAllWhitespace(userDetail.getMobile());
            user.setUid(UID);
        }
		// 不合法的邮箱地址设置为空
		if (userDetail.getEmail() != null && !userDetail.getEmail().matches(Const.REGEXP_EMAIL)) {
            userDetail.setEmail(null);
        }
		// 如果user中的邮箱不合法，则用userDetail的邮箱
        if (user.getSecondUID() != null && !user.getSecondUID().matches(Const.REGEXP_EMAIL)) {
            user.setSecondUID(userDetail.getEmail());
        }

		// 修改userDetail数据
		BeanUtil.copyProperties(userDetail, oldDetail, true);
		userDao.save(oldDetail, oldMobile);

		// 修改user数据
        for (User oldUser : oldUsers) {
            BeanUtil.copyProperties(user, oldUser, true, "id", "password", "appId", "spaceDialectUID", "spaceId", "spaceUID", "dialectUID");
            userDao.save(oldUser);
        }

		App app = appService.findOne(user.getAppId());
		if (app != null) {
			app = app.getUserControl() == null ? app : appService.findOne(app.getUserControl());
		}

		// 修改密码
		String noEncryPwd = user.getPassword();
		String newPwd = null;
		if (noEncryPwd != null && noEncryPwd.length() < 32) {
			newPwd = getEncryPassword(app.getEncryFormat(), noEncryPwd, user.getSalt());
		} else {
			newPwd = oldUsers.get(0).getPassword();
		}
		userDao.changePasswordByAppAndUser(app.getUid(), user, newPwd);

		logger.info(this.getClass(), oldDetail.getMobile(), String.format("修改用户信息，%s From: %s, To: sso", oldDetail.getName(), app.getUid()),
				JSON.toJSON(userDetail));
		// 自动同步到其他App
		oldUsers.get(0).setAppId(user.getAppId());

			//逻辑代码，可以写上你的逻辑处理代码
			transactionManager.commit(status);

		syncUser(oldUsers.get(0), oldDetail, noEncryPwd, oldUsers.get(0).getSecondUID());
		} catch (Exception e) {
			transactionManager.rollback(status);
		}
        return findByUID(userDetail.getMobile());
    }

	private User getExistUser(User newOne, UserSpace userSpace) {
		User oldOne = null;
		if (newOne.getUid() != null) {
			oldOne = findOne(newOne.getAppId(), userSpace == null ? null : userSpace.getId(), newOne.getUid());
		}
		if (oldOne == null && newOne.getDialectUID() != null) {
			oldOne = findDialectOne(newOne.getAppId(), userSpace == null ? null : userSpace.getId(), newOne.getDialectUID());
		}
		if (oldOne == null) {
			oldOne = newOne;
		}
		return oldOne;
	}

	/**
	 * 设置密码
	 *
	 * @param app
	 * @param user
	 * @param detail
	 */
	@Override
	public String setPasswordBySameApp(App app, User user, UserDetail detail) {
		if (null == user.getId() && StringUtils.isEmpty(user.getPassword())) {
			List<UserView> othUsers = findByAppAndUID(app.getUid(), detail.getMobile());
			user = new User(app, detail);
			if (!CollectionUtils.isEmpty(othUsers)) {
				// 同应用下，密码密文是一致的，可以直接设置
				user.setPassword(othUsers.get(0).getPassword());
				user.setSalt(othUsers.get(0).getSalt());
				if (StringUtils.isEmpty(user.getDialectUID())) {
					user.setDialectUID(othUsers.get(0).getDialectUID());
				}
			}
		}
		return user.getPassword();
	}

	/**
	 * 获取用户绑定的企业
	 *
	 * @param user
	 * @return
	 */
	private UserSpace getUserSpaceByUser(User user, boolean strict) {
		UserSpace userSpace = null;
		if (user != null) {
			if (user.getSpaceUID() != null) {
				userSpace = userSpaceDao.findOne(user.getAppId(), user.getSpaceUID());
			}
			if (userSpace == null && user.getSpaceDialectUID() != null) {
				userSpace = userSpaceDao.findDialectOne(user.getAppId(), user.getSpaceDialectUID());
			}
			if (userSpace == null && user.getSpaceId() != null) {
				userSpace = userSpaceDao.findOne(user.getSpaceId());
			}
			if (userSpace == null && strict) {
				// 查找全部，然后任意匹配一个
				if (!StringUtils.isEmpty(user.getUid())) {
					List<UserView> users = findByAppAndUID(user.getAppId(), user.getUid());
					if (!CollectionUtils.isEmpty(users)) {
						user.setSpaceId(users.get(0).getSpaceId());
						if (user.getSpaceId() != null) {
							userSpace = userSpaceDao.findOne(user.getSpaceId());
						}
					}
				}
			}
		}
		return userSpace;
	}

	/**
	 * 获取密码密文
	 *
	 * @return
	 */
	@Override
	public String getEncryPassword(String format, String noEncryPwd, String salt) {
		if (StringUtils.isEmpty(format)) {
			return noEncryPwd;
		}
		// 超过32认为是已加密过的密文
		if (noEncryPwd.length() >= 32) {
			logger.error("用户密码加密", String.format("传递过来的密码(%s)必须是未加密的明文", noEncryPwd));
			throw new AccountException("invalid password");
		}
		// $password{$salt}
		String password = format.replace(App.ENCRY_PARAM_PASSWORD, noEncryPwd);
		password = password.replace(App.ENCRY_PARAM_SALT, salt == null ? "" : salt);
		return MD5.toMD5(password);
	}

	private void syncUser(final User user, final UserDetail userDetail, final String noEncryPwd, final String newSecondUID) {
		final App app = appService.findOne(user.getAppId());
		String copyApps = app.getCopyApps();
		List<String> apps = null;
		final List<User> users = userDao.findByUID(user.getUid());
		final Map<String, String> dialectUIDs = new HashMap<>(users.size());
		for (User u : users) {
			dialectUIDs.put(u.getAppId(), u.getDialectUID());
		}
		// 同时拷贝到其他应用（app用逗号分隔），默认all表示全部
		if (StringUtils.isEmpty(copyApps) || App.COPY_ALL.equals(copyApps)) {
			apps = appService.findAllIDs();
		} else {
			apps = Arrays.asList(copyApps.split(App.COPY_APP_SPLIT));
		}
		ExecuteUtils.execute(new ICallable<Void, String>() {

			@Override
			public Void call(String appId) throws Exception {
				App tempApp = appService.findOne(appId);
				// 不是级联应用（如商城，级联于平台）+ 配置了回调接口 + 属于PUBLIC作用域
				if (tempApp != null && !tempApp.getUid().equals(app.getUid()) && StringUtils.isEmpty(tempApp.getUserControl())
						&& !StringUtils.isEmpty(tempApp.getBackUserUrl()) && "PUBLIC".equals(tempApp.getScope())) {
					User tempUser = new User();
					BeanUtil.copyProperties(user, tempUser, true, "id", "appId", "dialectUID", "spaceId");
					tempUser.setAppId(tempApp.getUid());
					// 不用考虑tempS是否空，即允许独立用户存在
					UserSpace tempS = getUserSpaceByUser(tempUser, true);
					if (null == tempS && Const.YES != tempApp.getDefaultUse()) {
						return null;
					}
					tempUser.setDialectUID(dialectUIDs.get(appId));
					tempUser.setSpaceId(tempS == null ? null : tempS.getId());
					tempUser.setSpaceUID(tempS == null ? null : tempS.getUid());
					tempUser.setSpaceDialectUID(tempS == null ? null : tempS.getDialectUID());
					User oldUser = getExistUser(tempUser, tempS);
					BeanUtil.copyProperties(tempUser, oldUser, true, "id", "password");
					if (noEncryPwd != null) {
						oldUser.setPassword(getEncryPassword(tempApp.getEncryFormat(), noEncryPwd, oldUser.getSalt()));
					}
					try {
						User backUser = syncUserToApp(tempApp, oldUser, userDetail);
						if (backUser.getDialectUID() != null) {
							oldUser.setDialectUID(backUser.getDialectUID());
						}
						if (backUser.getSalt() != null) {
							oldUser.setSalt(backUser.getSalt());
						}
						if (noEncryPwd != null) {
							oldUser.setPassword(getEncryPassword(tempApp.getEncryFormat(), noEncryPwd, oldUser.getSalt()));
							userDao.changePasswordByAppAndUser(tempApp.getUid(), oldUser, oldUser.getPassword());
						}
						if (newSecondUID != null) {
							userDao.changeSecondUIDByUser(oldUser, oldUser.getSecondUID());
						}
						oldUser = userDao.save(oldUser);
						logger.info(this.getClass(), oldUser.getUid(),
								String.format("修改用户信息，%s From: sso, To: %s", oldUser.getName(), tempApp.getUid()), oldUser);
					} catch (Exception e) {
						logger.error(this.getClass(), oldUser.getUid(),
								String.format("修改用户信息，%s From: sso, To: %s", oldUser.getName(), tempApp.getUid()), e);
						userDao.saveFailed(new UserFailed(user, oldUser, e));
					}
				}
				return null;
			}
		}, apps);
	}

	@Override
	public User syncUserToApp(App app, User user, UserDetail userDetail) throws Exception {
		String url = app.getBackUserUrl();
		if (!StringUtils.isEmpty(url)) {
			JSONObject formData = JSON.parseObject(JSON.toJSONString(user));
			formData.putAll(JSON.parseObject(JSON.toJSONString(userDetail)));
			ResponseWrap res = HttpUtil.doPost(url, formData, 30000);
			if (!res.isSuccess()) {
				throw new Exception(res.getContent());
			} else {
				return JSON.parseObject(res.getContent(), User.class);
			}
		}
		return null;
	}

	/**
	 * 同步到优软众创
	 *
	 * @param dialectUID
	 * @param noEncryPwd
	 */
	private void sysncUserPwdToUuzc(String dialectUID, String noEncryPwd) {
		UuzcUserDeatil user = new UuzcUserDeatil();
		user.setUid(dialectUID);
		user.setPassword(noEncryPwd);
		JSONObject formData = new JSONObject();
		if (user != null) {
			formData = JSON.parseObject(JSON.toJSONString(user));
		}
		try {
			ResponseWrap res = HttpUtil.doPost("http://192.168.253.163/uuzclm/uu_login/index/ubtob/updpassword",
					formData);
			System.out.println(res.isSuccess());
			System.out.println(res.getContent());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public User syncUserPwdToApp(App app, User user, String noEncryPwd) throws Exception {
		String url = StringUtils.isEmpty(app.getBackUserPwdUrl()) ? app.getBackUserUrl() : app.getBackUserPwdUrl();
		if (!StringUtils.isEmpty(url)) {
			ModelMap formData = new ModelMap();
			formData.addAttribute("uid", user.getUid());
			formData.addAttribute("secondUID", user.getSecondUID());
			formData.addAttribute("dialectUID", user.getDialectUID());
			formData.addAttribute("password", noEncryPwd);
			ResponseWrap res = HttpUtil.doPost(url, formData);
			if (!res.isSuccess()) {
				throw new Exception(res.getContent());
			} else {
				return JSON.parseObject(res.getContent(), User.class);
			}
		}
		return null;
	}

	@Override
	public User findOne(String appId, String spaceUID, String uid) {
		return userDao.findOne(appId, spaceUID, uid);
	}

	@Override
	@Cacheable(value = "users", key = "'appId:'+#appId+'spaceId:'+#spaceId+'uid:'+#uid")
	public User findOne(String appId, Long spaceId, String uid) {
		return userDao.findOne(appId, spaceId, uid);
	}

	@Override
	@Cacheable(value = "users", key = "'appId:'+#appId+'spaceId:'+#spaceId+'dialectUID:'+#dialectUID")
	public User findDialectOne(String appId, Long spaceId, String dialectUID) {
		return userDao.findDialectOne(appId, spaceId, dialectUID);
	}

	@Override
	@Cacheable(value = "userView")
	public UserView findOneView(String appId, Long spaceId, String uid) {
		return userDao.findOneView(appId, spaceId, uid);
	}

	@Override
	@Cacheable(value = "userView")
	public UserView findOneViewBySecondUID(String appId, Long spaceId, String secondUID) {
		return userDao.findOneViewBySecondUID(appId, spaceId, secondUID);
	}

	@Override
	@Cacheable(value = "userView")
	public UserView findOneViewByDialectUID(String appId, Long spaceId, String dialectUID) {
		return userDao.findOneViewByDialectUID(appId, spaceId, dialectUID);
	}

	@Override
	public List<User> findByUID(String uid) {
		return userDao.findByUID(uid);
	}

	@Override
	public List<User> findBySecondUID(String secondUID) {
		return userDao.findBySecondUID(secondUID);
	}

	@Override
	public List<UserView> findViewByUID(String uid) {
		return userDao.findViewByUID(uid);
	}

	@Override
	public List<UserView> findViewBySecondUID(String secondUID) {
		return userDao.findViewBySecondUID(secondUID);
	}

	@Override
	public Page<User> findAll(String appId, String spaceId, int pageNumber, int pageSize) {
		return userDao.findAll(appId, spaceId, pageNumber, pageSize);
	}

	@Override
	@Cacheable(value = "userView", key = "'appId:'+#appId+'uid:'+#uid")
	public List<UserView> findByAppAndUID(String appId, String uid) {
		return userDao.findByAppAndUID(appId, uid);
	}

	@Override
	@Cacheable(value = "userView", key = "'appId:'+#appId+'secondUID:'+#secondUID")
	public List<UserView> findByAppAndSecondUID(String appId, String secondUID) {
		return userDao.findByAppAndSecondUID(appId, secondUID);
	}

	@Override
	@Cacheable(value = "userView", key = "'appId:'+#appId+'dialectUID:'+#dialectUID")
	public List<UserView> findByAppAndDialectUID(String appId, String dialectUID) {
		return userDao.findByAppAndDialectUID(appId, dialectUID);
	}

	@Override
	public List<User> findByDialectUID(String dialectUID) {
		return userDao.findByDialectUID(dialectUID);
	}

	@Override
	@CacheEvict(value = { "users", "userView" }, allEntries = true)
	public void changePassword(App app, User user, String noEncryPwd) {
		if (noEncryPwd.length() < 32) {
			user.setPassword(getEncryPassword(app.getEncryFormat(), noEncryPwd, user.getSalt()));
		} else {
			user.setPassword(noEncryPwd);
		}
		userDao.changePasswordByAppAndUser(app.getUid(), user, user.getPassword());
		logger.info(String.format("修改用户密码，%s From: %s, To: sso", user.getName(), app.getUid()), user);
		// 自动同步到其他App
		syncUserPassword(user, noEncryPwd);
	}

	@Override
	@CacheEvict(value = { "users", "userView" }, allEntries = true)
	public void changePasswordAlone(App app, User user, String noEncryPwd) {
		if (noEncryPwd.length() < 32) {
			user.setPassword(getEncryPassword(app.getEncryFormat(), noEncryPwd, user.getSalt()));
		} else {
			user.setPassword(noEncryPwd);
		}
		userDao.changePasswordByAppAndUser(app.getUid(), user, user.getPassword());
		logger.info(String.format("修改用户密码(standalone)，%s From: %s, To: sso", user.getName(), app.getUid()), user);
	}

	@Override
	@CacheEvict(value = { "users", "userView" }, allEntries = true, beforeInvocation = true)
	public void syncUserPassword(final User user, final String noEncryPwd) {
		final App app = appService.findOne(user.getAppId());
		String copyApps = app.getCopyApps();
		if (user.getUid() == null) {
			User user1 = userDao.findByDialectUID(user.getDialectUID()).get(0);
			user.setUid(user1.getUid());
			user.setSecondUID(user1.getSecondUID());
		}
		App app2 = appService.findOne("b2b");
//		sysncUserPwdToUuzc(user.getDialectUID(), getEncryPassword(app2.getEncryFormat(), noEncryPwd, user.getSalt()));
		user.setDialectUID(null);
		user.setSpaceDialectUID(null);
		List<String> apps = null;
		// 同时拷贝到其他应用（app用逗号分隔），默认all表示全部
		if (StringUtils.isEmpty(copyApps) || App.COPY_ALL.equals(copyApps)) {
			apps = appService.findAllIDs();
		} else {
			apps = Arrays.asList(copyApps.split(App.COPY_APP_SPLIT));
		}
		ExecuteUtils.execute(new ICallable<Void, String>() {

			@Override
			public Void call(String appId) throws Exception {
				App tempApp = appService.findOne(appId);
				// 不是级联应用（如商城，级联于平台）+ 配置了回调接口 + 属于PUBLIC作用域
				if (tempApp != null && !tempApp.getUid().equals(app.getUid()) && StringUtils.isEmpty(tempApp.getUserControl())
						&& !StringUtils.isEmpty(tempApp.getBackUserUrl()) && "PUBLIC".equals(tempApp.getScope())) {
					User tempUser = new User();
					BeanUtil.copyProperties(user, tempUser, true, "id", "appId", "dialectUID", "spaceId");
					tempUser.setAppId(tempApp.getUid());
					// 不用考虑tempS是否空，即允许独立用户存在
					UserSpace tempS = getUserSpaceByUser(tempUser, true);
					if (null == tempS && Const.YES != tempApp.getDefaultUse()) {
						return null;
					}
					tempUser.setSpaceId(tempS == null ? null : tempS.getId());
					tempUser.setSpaceDialectUID(tempS == null ? null : tempS.getDialectUID());
					User oldUser = getExistUser(tempUser, tempS);
					BeanUtil.copyProperties(tempUser, oldUser, true);
					oldUser.setPassword(getEncryPassword(tempApp.getEncryFormat(), noEncryPwd, oldUser.getSalt()));
					try {
						syncUserPwdToApp(tempApp, oldUser, noEncryPwd);
						userDao.changePasswordByAppAndUser(appId, oldUser, oldUser.getPassword());
						logger.info(String.format("修改用户密码，%s From: sso, To: %s", oldUser.getName(), tempApp.getUid()), oldUser);
					} catch (Exception e) {
						logger.error(String.format("修改用户密码，%s From: sso, To: %s", oldUser.getName(), tempApp.getUid()), e);
						userDao.saveFailed(new UserFailed(user, oldUser, e));
					}
				}
				return null;
			}

		}, apps);
	}

	@Override
	@CacheEvict(value = { "users", "userView" }, allEntries = true)
	public void delete(User user) {
		UserSpace userSpace = getUserSpaceByUser(user, false);
		if (null != userSpace) {
			user = getExistUser(user, userSpace);
			sysToMall(user);
		}
		if (null != user.getId()) {
			userDao.delete(user);
		}
	}

    /**
     * 通知商城同步删除
     *
     * @param user
     */
    private void sysToMall(final User user) {
        ExecuteUtils.asyncExecute(new Runnable() {
            @Override
            public void run() {
                try {
                    sysService.reMoveUser(user);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    @Override
	public User findOne(User user) {
		UserSpace userSpace = getUserSpaceByUser(user, false);
		return getExistUser(user, userSpace);
	}

	@Override
	public UserDetail findOne(String mobile) {
		return userDao.findOne(mobile);
	}

	@Override
	public List<UserDetail> findByEmail(String email) {
		return userDao.findByEmail(email);
	}

	@CacheEvict(value = { "users", "userView" }, allEntries = true)
	@Override
	public void changeSecondUID(String uid, String newSecondUID) {
		UserDetail detail = findOne(uid);
		if (null != detail) {
			detail.setEmail(newSecondUID);
			userDao.save(detail);
		}
		List<User> users = findByUID(uid);
		if (!CollectionUtils.isEmpty(users)) {
			syncUser(users.get(0), detail, null, newSecondUID);
		} else {
			throw new VisibleError("未找到用户");
		}
	}

	@Override
	public Page<UserDetail> findAllDetail(String name, String mobile, int pageNumber, int pageSize) {
		return userDao.findAllDetail(name, mobile, pageNumber, pageSize);
	}

	@CacheEvict(value = { "users", "userView" }, allEntries = true)
	@Override
	public void applyApp(User user) {
		App app = appService.findOne(user.getAppId());
		// 不是级联应用（如商城，级联于平台）+ 配置了回调接口 + 属于PUBLIC作用域
		if (app != null && StringUtils.isEmpty(app.getUserControl()) && !StringUtils.isEmpty(app.getBackUserUrl())
				&& "PUBLIC".equals(app.getScope())) {
			// 不用考虑userSpace是否空，即允许独立用户存在
			UserSpace userSpace = getUserSpaceByUser(user, true);
			if (null == userSpace && Const.YES != app.getDefaultUse()) {
				return;
			}
			user.setSpaceId(userSpace == null ? null : userSpace.getId());
			user.setSpaceUID(userSpace == null ? null : userSpace.getUid());
			user.setSpaceDialectUID(userSpace == null ? null : userSpace.getDialectUID());
			User oldUser = getExistUser(user, userSpace);
			UserDetail userDetail = findOne(user.getUid());
			BeanUtil.copyProperties(user, oldUser, true, "id", "password");
			try {
				User backUser = syncUserToApp(app, oldUser, userDetail);
				if (backUser.getDialectUID() != null) {
					oldUser.setDialectUID(backUser.getDialectUID());
				}
				if (backUser.getSalt() != null) {
					oldUser.setSalt(backUser.getSalt());
				}
				oldUser = userDao.save(oldUser);
				logger.info(this.getClass(), oldUser.getUid(),
						String.format("修改用户信息，%s From: sso, To: %s", oldUser.getName(), app.getUid()), oldUser);
			} catch (Exception e) {
				throw new VisibleError(e.getMessage());
			}
		}
	}

	@Override
	public ModelMap getUserAppCount() {
		ModelMap map = new ModelMap("全部", userDao.getUserDetailCount());
		map.addAllAttributes(userDao.getUserAppCount());
		return map;
	}

	@Override
	public List<User> findUserAppByUID(String uid_) {
		return userDao.findByUID(uid_);
	}

	@Override
	public UserDetail register(UserDetail user) {
		String checkcodeToken = user.getCheckcodeToken();
		String checkcode = user.getCheckcode();
		if (StringUtils.isEmpty(checkcodeToken)) {
			throw new VisibleError("请先获取验证码！");
		}
		if (StringUtils.isEmpty(checkcode)) {
			throw new VisibleError("请先输入验证码！");
		}
		Token existToken = tokenService.findOne(checkcodeToken);
		if (existToken == null || existToken.isExpired()) {
			throw new VisibleError("验证码已经失效，请重新获取");
		}
		String existCode = existToken.getBind().toString();
		if (!checkcode.equals(existCode)) {
			throw new VisibleError("验证码错误");
		}

		UserDetail existUserDetail = userDao.findOne(user.getMobile());
		if(existUserDetail != null) {
			// 判断数据是否有问题，或者未生成
			if(existUserDetail.getValid() != 1) {
				existUserDetail.setValid(1);
				userDao.save(existUserDetail);
			}

			Boolean registed = false;
			List<App> apps = appService.findAll();
			for(App app : apps) {
				if( !StringUtils.isEmpty(app.getPersonalEnable()) && (Const.YES == app.getPersonalEnable()) ) {
					App controlApp = StringUtils.isEmpty(app.getUserControl()) ? app : appService.findOne(app.getUserControl());
					if( !StringUtils.isEmpty(controlApp.getDefaultUse()) && (Const.YES == controlApp.getDefaultUse()) ) {
						List<UserView> existUsers = userDao.findByAppAndUID(controlApp.getUid(), user.getMobile());
						if (!CollectionUtils.isEmpty(existUsers)) {
							registed = true;// 注册过
						}
					}
				}
			}

			if (registed) {
				throw new VisibleError("registed");
			}
		} else {
			user.setValid(1);
			user = userDao.save(user);
		}
		return user;
	}

	@Override
	public void initUser(String uid_, final String password) {
		final UserDetail userDetail = userDao.findOne(uid_);


		List<App> apps = appService.findAll();
		ExecuteUtils.execute(new ICallable<Void, App>() {
			@Override
			public Void call(App app) throws Exception {
				if(!StringUtils.isEmpty(app.getPersonalEnable()) && (Const.YES == app.getPersonalEnable())) {
					App controlApp = StringUtils.isEmpty(app.getUserControl()) ? app : appService.findOne(app.getUserControl());
					if(!StringUtils.isEmpty(controlApp.getDefaultUse()) && (Const.YES == controlApp.getDefaultUse())) {
						// 应用本身无需绑定企业
						User user = new User(controlApp, userDetail);
						user.setPassword(password);
						userDao.save(user);// save or update
						try {
							User backUser = syncUserToApp(controlApp, user, userDetail);
							if (backUser != null) {
								user.setPassword(getEncryPassword(controlApp.getEncryFormat(), password, backUser.getSalt()));
								user.setDialectUID(backUser.getDialectUID());
								user.setSalt(backUser.getSalt());
								// 不能调用userService的save()
								userDao.save(user);
								logger.info(this.getClass(), userDetail.getMobile(), "创建用户: " + user.getUid(), user);
							}
						} catch (Exception e) {
							e.printStackTrace();
							userDao.saveFailed(new UserFailed(user, e));
						}
					}
				}
				return null;
			}

		}, apps);
	}

	@Override
	public void deleteByAppAndSpace(String appId, long spaceId) {
		userDao.deleteByAppAndSpace(appId, spaceId);
	}

	@Override
	public List<UserSpaceDetailInfo> findByEnName(String name, Integer pageNumber) {
		return userDao.findByEnNameAndPageNumber(name, pageNumber);
	}

	@Override
	public List<User> findByBusinessCode(String businessCode) {
		return userDao.findByBusinessCode(businessCode);
	}

	@Override
	public Integer getEnterpriseCount() {
		return userDao.getEnterpriseCount();
	}

	@Override
	public Integer getPersonalUserCount() {
		return userDao.getPersonalUserCount();
	}

	@Override
	public Integer getUuUserCount() {
		return userDao.getUuUserCount();
	}

	@Override
	public UuzcUserDeatil initUserForUUzc(final UserDetail userDetail, final String password) {
		List<App> apps = appService.findAll();
		UuzcUserDeatil uuzcUser = new UuzcUserDeatil();
		ExecuteUtils.execute(new ICallable<Void, App>() {
			@Override
			public Void call(App app) throws Exception {
				if (!StringUtils.isEmpty(app.getPersonalEnable()) && (Const.YES == app.getPersonalEnable())) {
					App controlApp = StringUtils.isEmpty(app.getUserControl()) ? app
							: appService.findOne(app.getUserControl());
					if (!StringUtils.isEmpty(controlApp.getDefaultUse()) && (Const.YES == controlApp.getDefaultUse())) {
						// 应用本身无需绑定企业
						User user = new User(controlApp, userDetail);
						user.setPassword(password);
						userDao.save(user);// save or update
						try {
							User backUser = syncUserToApp(controlApp, user, userDetail);
							if (backUser != null) {
								user.setPassword(
										getEncryPassword(controlApp.getEncryFormat(), password, backUser.getSalt()));
								user.setDialectUID(backUser.getDialectUID());
								user.setSalt(backUser.getSalt());
								// 不能调用userService的save()
								userDao.save(user);
								logger.info(this.getClass(), userDetail.getMobile(), "创建用户: " + user.getUid(), user);
							}
						} catch (Exception e) {
							e.printStackTrace();
							userDao.saveFailed(new UserFailed(user, e));
						}
					}
				}
				return null;
			}

		}, apps);
		List<User> users = userDao.findByUID(userDetail.getMobile());
		if (!CollectionUtils.isEmpty(users)) {
			uuzcUser = users.get(0).covert();
		}
		return uuzcUser;
	}

	@Override
	public UserDetail save(UserDetail detail) {
		return userDao.save(detail);
	}

	@Override
	public Long findUserUUByMobile(String tel) {
		List<UserView> users = userDao.findByAppAndUID("b2b", tel);
		for (UserView userView : users){
			if (userView.getDialectUID() != null) {
				return Long.valueOf(userView.getDialectUID());
			}
		}
		return null;
	}

	@Override
	public void lock(String account) {
		if (account.contains(EMAIL_SIGN)) {
			userDao.lockEmail(account);
			logger.info("账号锁定-邮箱", "邮箱为"+account+"的账号被锁定");
		} else if (account.matches(Const.REGEXP_MOBILE)) {
			userDao.lockTel(account);
			logger.info("账号锁定-手机号", "手机号为"+account+"的账号被锁定");
		} else {
			userDao.lockUU(account);
			logger.info("账号锁定-uu号", "uu号为"+account+"的账号被锁定");
		}
	}

	@Override
	public boolean isLock(String account) {
		if (account.contains(EMAIL_SIGN)) {
//			userDao.isLockEmail(account);
			return false;
		} else if (account.matches(Const.REGEXP_MOBILE)) {
			return userDao.isLockTel(account);
		} else {
			return userDao.isLockUU(account);
		}
	}

    @Override
    public User findHrInfo(String businessCode) {
        return userDao.findHrInfo(businessCode);
    }

    @Override
    public void updateHrInfo(User hrInfo, boolean flag) {
        userSpaceDao.updateInfo(hrInfo, flag);
    }

	@Override
	public User syncUserToB2B(UserDetail userDetail, String password) {
		App app = appDao.findOne("b2b");
		// 应用本身无需绑定企业
		User user = new User(app, userDetail);
		user.setPassword(password);
		userDao.save(user);// save or update
		try {
			User backUser = syncUserToApp(app, user, userDetail);
			if (backUser != null) {
				user.setPassword(getEncryPassword(app.getEncryFormat(), password, backUser.getSalt()));
				user.setDialectUID(backUser.getDialectUID());
				user.setSalt(backUser.getSalt());
				// 不能调用userService的save()
				user = userDao.save(user);
				logger.info(this.getClass(), userDetail.getMobile(), "创建用户: " + user.getUid(), user);
				return user;
			}
		} catch (Exception e) {
			e.printStackTrace();
			userDao.saveFailed(new UserFailed(user, e));
		}
		return null;
	}

	@Override
	public String getUserUUByImId(Long imId) {
		return userDao.getUserUUByImId(imId);
	}

	@Override
	public User findByAppId(User user) {
		UserSpace userSpace = getUserSpaceByUser(user, true);
		return getExistUser(user, userSpace);
	}

	@Override
	public Integer findHrAccount(String businessCode) {
		return userSpaceDao.findHrAccount(businessCode);
	}

	@Override
	public void setHrAccount(UserDetail detail, UuzcUserSpaceDetail uuzcUserSpaceDetail) throws Exception {
		UserSpace userSpace = userSpaceDao.findOne("b2b", uuzcUserSpaceDetail.getBusinessCode());
		App app = appDao.findOne("b2b");
		if (userSpace != null) {
			userSpace.setDialectUID(userSpace.getDialectUID());
			userSpace = userSpaceDao.save(userSpace);
			logger.info(this.getClass(), uuzcUserSpaceDetail.getBusinessCode(), "开通应用: b2b", userSpace);
			UserSpaceDetail userSpaceDetail = userSpaceDao.findByBusinessCode(uuzcUserSpaceDetail.getBusinessCode());
			sysService.UserSpace(userSpace, userSpaceDetail);
			User user = new User(app, userSpace, detail);
			user.setPassword("111111");
			user = userDao.save(user);
			try {
				User backUser = syncUserToApp(app, user, detail);
				if (backUser != null) {
					user.setDialectUID(backUser.getDialectUID());
					user.setHr((short) Const.YES);
					if (backUser.getAppId().equals("b2b")) {
						user.setSalt(backUser.getSalt());
						user.setPassword(getEncryPassword(app.getEncryFormat(), "111111", backUser.getSalt()));
					} else if (backUser.getAppId().equals("im")) {
						user.setPassword(getEncryPassword(app.getEncryFormat(), "111111", null));
					}
					// 不能调用userService的save()
					user = userDao.save(user);
					logger.info(this.getClass(), uuzcUserSpaceDetail.getBusinessCode(), "创建用户: " + user.getUid(), user);
					sysService.user(user);
				}
			} catch (Exception e) {
				e.printStackTrace();
				userDao.saveFailed(new UserFailed(user, e));
			}
		}
	}

}
