Browse Source

登录时返回微信信息

master
zhangye 6 years ago
parent
commit
3647a9e9dd
  1. 2
      ht/src/main/java/com/ccsens/ht/service/DoctorService.java
  2. 23
      tall/src/main/java/com/ccsens/tall/bean/dto/UserDto.java
  3. 183
      tall/src/main/java/com/ccsens/tall/bean/po/SysWx.java
  4. 1191
      tall/src/main/java/com/ccsens/tall/bean/po/SysWxExample.java
  5. 24
      tall/src/main/java/com/ccsens/tall/bean/vo/UserVo.java
  6. 9
      tall/src/main/java/com/ccsens/tall/persist/dao/SysWxDao.java
  7. 30
      tall/src/main/java/com/ccsens/tall/persist/mapper/SysWxMapper.java
  8. 5
      tall/src/main/java/com/ccsens/tall/service/IUserService.java
  9. 307
      tall/src/main/java/com/ccsens/tall/service/UserService.java
  10. 42
      tall/src/main/java/com/ccsens/tall/web/UserController.java
  11. 370
      tall/src/main/resources/mapper_raw/SysWxMapper.xml
  12. 4
      util/src/main/java/com/ccsens/util/CodeEnum.java
  13. 7
      util/src/main/java/com/ccsens/util/WebConstant.java
  14. 37
      util/src/main/java/com/ccsens/util/wx/WxGzhUtil.java

2
ht/src/main/java/com/ccsens/ht/service/DoctorService.java

@ -70,7 +70,7 @@ public class DoctorService implements IDoctorService {
oldDoctor = doctor;
}
}
//查找职务是否存在
//查找科室是否存在
HtPosition position = htPositionDao.selectByPrimaryKey(submit.getPositionId());
if (position == null) {
log.info("{}未找到对应职务。", submit.getPositionId());

23
tall/src/main/java/com/ccsens/tall/bean/dto/UserDto.java

@ -27,7 +27,7 @@ public class UserDto {
@ApiModelProperty("登录客户端:0-wxmp,1-H5,2-Android,3-IOS")
@NotNull(message = "client is required.")
private Integer client;
@ApiModelProperty("登录类型:0-wxmp,1-phone,2-email,3-accounts")
@ApiModelProperty("登录类型:0-wxmp,1-phone,2-email,3-accounts,4-OAUTH2_Wx,5-Wx_H5,6-OAUTH2_WeiBo")
@NotNull(message = "type is required.")
private Integer type;
@ApiModelProperty("登录信息")
@ -62,7 +62,7 @@ public class UserDto {
public static class UpdatePassword{
@ApiModelProperty("手机号")
@NotEmpty(message = "手机号不能为空")
@Pattern(regexp="^1[34578]\\d{9}$",message="请输入正确的手机号")
@Pattern(regexp="^[1]([3-9])[0-9]{9}$",message="请输入正确的手机号")
private String phone;
@ApiModelProperty("验证码")
@NotEmpty(message = "验证码不能为空.")
@ -79,7 +79,7 @@ public class UserDto {
public static class UserSignup{
@ApiModelProperty("手机号")
@NotEmpty(message = "手机号不能为空")
@Pattern(regexp="^1[34578]\\d{9}$",message="请输入正确的手机号")
@Pattern(regexp="^[1]([3-9])[0-9]{9}$",message="请输入正确的手机号")
private String phone;
@ApiModelProperty("验证码")
@NotEmpty(message = "验证码不能为空.")
@ -98,7 +98,7 @@ public class UserDto {
@Data
@ApiModel
public static class WxPhone{
public static class WxMergePhone{
@ApiModelProperty("手机号")
private String phone;
@ApiModelProperty("手机验证码")
@ -106,4 +106,19 @@ public class UserDto {
@ApiModelProperty("微信code")
private String wxCode;
}
@Data
@ApiModel
public static class WxBindingPhone{
@ApiModelProperty("手机号")
private String phone;
@ApiModelProperty("手机验证码")
private String smsCode;
@ApiModelProperty("用户名")
private String username;
@ApiModelProperty("密码")
private String password;
@ApiModelProperty("微信code")
private String wxCode;
}
}

183
tall/src/main/java/com/ccsens/tall/bean/po/SysWx.java

@ -0,0 +1,183 @@
package com.ccsens.tall.bean.po;
import java.io.Serializable;
import java.util.Date;
public class SysWx implements Serializable {
private Long id;
private Long userId;
private String openId;
private String unionId;
private String nickname;
private String headImgUrl;
private Byte sex;
private String province;
private String city;
private String country;
private String language;
private String privilege;
private Date createdAt;
private Date updatedAt;
private Byte recStatus;
private static final long serialVersionUID = 1L;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public String getOpenId() {
return openId;
}
public void setOpenId(String openId) {
this.openId = openId == null ? null : openId.trim();
}
public String getUnionId() {
return unionId;
}
public void setUnionId(String unionId) {
this.unionId = unionId == null ? null : unionId.trim();
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname == null ? null : nickname.trim();
}
public String getHeadImgUrl() {
return headImgUrl;
}
public void setHeadImgUrl(String headImgUrl) {
this.headImgUrl = headImgUrl == null ? null : headImgUrl.trim();
}
public Byte getSex() {
return sex;
}
public void setSex(Byte sex) {
this.sex = sex;
}
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province == null ? null : province.trim();
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city == null ? null : city.trim();
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country == null ? null : country.trim();
}
public String getLanguage() {
return language;
}
public void setLanguage(String language) {
this.language = language == null ? null : language.trim();
}
public String getPrivilege() {
return privilege;
}
public void setPrivilege(String privilege) {
this.privilege = privilege == null ? null : privilege.trim();
}
public Date getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
}
public Date getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(Date updatedAt) {
this.updatedAt = updatedAt;
}
public Byte getRecStatus() {
return recStatus;
}
public void setRecStatus(Byte recStatus) {
this.recStatus = recStatus;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(getClass().getSimpleName());
sb.append(" [");
sb.append("Hash = ").append(hashCode());
sb.append(", id=").append(id);
sb.append(", userId=").append(userId);
sb.append(", openId=").append(openId);
sb.append(", unionId=").append(unionId);
sb.append(", nickname=").append(nickname);
sb.append(", headImgUrl=").append(headImgUrl);
sb.append(", sex=").append(sex);
sb.append(", province=").append(province);
sb.append(", city=").append(city);
sb.append(", country=").append(country);
sb.append(", language=").append(language);
sb.append(", privilege=").append(privilege);
sb.append(", createdAt=").append(createdAt);
sb.append(", updatedAt=").append(updatedAt);
sb.append(", recStatus=").append(recStatus);
sb.append("]");
return sb.toString();
}
}

1191
tall/src/main/java/com/ccsens/tall/bean/po/SysWxExample.java

File diff suppressed because it is too large

24
tall/src/main/java/com/ccsens/tall/bean/vo/UserVo.java

@ -30,6 +30,8 @@ public class UserVo {
private String token;
@ApiModelProperty("刷新token")
private String refresh_token;
@ApiModelProperty("微信信息")
private WxInfo wxInfo;
}
@Data
@ -61,4 +63,26 @@ public class UserVo {
private Long id;
}
@Data
@ApiModel
public static class WxInfo{
@ApiModelProperty("openId")
private String openId;
@ApiModelProperty("unionId")
private String unionId;
@ApiModelProperty("微信名")
private String nickname;
@ApiModelProperty("微信头像")
private String headImgUrl;
@ApiModelProperty("性别")
private Byte sex;
@ApiModelProperty("省")
private String province;
@ApiModelProperty("市")
private String city;
@ApiModelProperty("国家")
private String country;
@ApiModelProperty("语言")
private String language;
}
}

9
tall/src/main/java/com/ccsens/tall/persist/dao/SysWxDao.java

@ -0,0 +1,9 @@
package com.ccsens.tall.persist.dao;
import com.ccsens.tall.persist.mapper.SysWxMapper;
import org.springframework.stereotype.Repository;
@Repository
public interface SysWxDao extends SysWxMapper {
}

30
tall/src/main/java/com/ccsens/tall/persist/mapper/SysWxMapper.java

@ -0,0 +1,30 @@
package com.ccsens.tall.persist.mapper;
import com.ccsens.tall.bean.po.SysWx;
import com.ccsens.tall.bean.po.SysWxExample;
import java.util.List;
import org.apache.ibatis.annotations.Param;
public interface SysWxMapper {
long countByExample(SysWxExample example);
int deleteByExample(SysWxExample example);
int deleteByPrimaryKey(Long id);
int insert(SysWx record);
int insertSelective(SysWx record);
List<SysWx> selectByExample(SysWxExample example);
SysWx selectByPrimaryKey(Long id);
int updateByExampleSelective(@Param("record") SysWx record, @Param("example") SysWxExample example);
int updateByExample(@Param("record") SysWx record, @Param("example") SysWxExample example);
int updateByPrimaryKeySelective(SysWx record);
int updateByPrimaryKey(SysWx record);
}

5
tall/src/main/java/com/ccsens/tall/service/IUserService.java

@ -17,16 +17,17 @@ public interface IUserService {
UserVo.TokenBean generateToken(WebConstant.CLIENT_TYPE client_type, Object subject, Map<String, Object> payLoads) throws Exception;
UserVo.TokenBean getUserInfoAndToken(WebConstant.CLIENT_TYPE client_type, UserVo.UserSign userSignVo, Map<String, Object> theMap) throws Exception;
boolean tokenNotExistInCache(Long authId) throws Exception;
UserVo.SmsCode getSignInSmsCode(String phone) throws Exception;
UserVo.SmsCode getSignInSmsCode(String phone,Integer client) throws Exception;
SysUser getUserById(Long aLong) throws Exception;
UserVo.UserSign registerUser(UserDto.UserSignup userSignup)throws Exception;
String bindingPhone(Long currentUserId,UserDto.WxPhone wxPhone)throws Exception;
String bindingNewPhone(Long currentUserId,UserDto.WxBindingPhone wxPhone)throws Exception;
String getPhone(Long userId);

307
tall/src/main/java/com/ccsens/tall/service/UserService.java

@ -1,5 +1,7 @@
package com.ccsens.tall.service;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.ObjectUtil;
@ -9,10 +11,7 @@ import com.ccsens.tall.bean.dto.UserDto;
import com.ccsens.tall.bean.po.*;
import com.ccsens.tall.bean.vo.UserVo;
import com.ccsens.tall.exception.SmsException;
import com.ccsens.tall.persist.dao.ProMemberDao;
import com.ccsens.tall.persist.dao.ProMemberRoleDao;
import com.ccsens.tall.persist.dao.SysAuthDao;
import com.ccsens.tall.persist.dao.SysUserDao;
import com.ccsens.tall.persist.dao.*;
import com.ccsens.util.*;
import com.ccsens.util.bean.wx.po.WxOauth2UserInfo;
import com.ccsens.util.exception.BaseException;
@ -26,6 +25,8 @@ import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Slf4j
@Service
@ -43,6 +44,8 @@ public class UserService implements IUserService {
private Snowflake snowflake;
@Autowired
private RedisUtil redisUtil;
@Autowired
private SysWxDao sysWxDao;
/**
* 登录
@ -66,7 +69,9 @@ public class UserService implements IUserService {
case Wxmp:
return wxmplogin(identifier);
case OAUTH2_Wx:
return wxLogin(identifier);
return wxLogin(identifyType,identifier);
case Wx_H5:
return wxH5Login(identifyType,identifier);
case OAUTH2_WeiBo:
//Fix Me.
break;
@ -83,47 +88,123 @@ public class UserService implements IUserService {
}
/**
* 微信登陆
* 微信网页登陆
*
* @param code
* @return
* @throws Exception
*/
private UserVo.UserSign wxLogin(String code) throws Exception {
private UserVo.UserSign wxH5Login(WebConstant.IDENTIFY_TYPE identifyType,String code) {
UserVo.UserSign userSignVo = null;
//0.获取微信信息openId和unionId
String openid = null;
String unionId = null;
WxOauth2UserInfo wxOauth2UserInfo = WxGzhUtil.getOauth2UserInfo(code);
//获取微信信息并保存
WxOauth2UserInfo wxOauth2UserInfo = WxGzhUtil.getOauth2UserInfo(identifyType,code);
SysAuth theAuth = null;
if (ObjectUtil.isNotNull(wxOauth2UserInfo)) {
openid = wxOauth2UserInfo.getOpenId();
unionId = wxOauth2UserInfo.getUnionId();
//查找有无保存的信息
SysWxExample sysWxExample = new SysWxExample();
sysWxExample.createCriteria().andUnionIdEqualTo(wxOauth2UserInfo.getUnionId());
List<SysWx> sysWxList = sysWxDao.selectByExample(sysWxExample);
SysWx sysWx = null;
if (CollUtil.isNotEmpty(sysWxList)) {
//如果有信息,则更新
sysWx = sysWxList.get(0);
BeanUtil.copyProperties(wxOauth2UserInfo, sysWx);
sysWxDao.updateByPrimaryKeySelective(sysWx);
} else {
//没有则保存微信信息
sysWx = new SysWx();
BeanUtil.copyProperties(wxOauth2UserInfo, sysWx);
sysWx.setId(snowflake.nextId());
sysWxDao.insertSelective(sysWx);
}
//查找认证信息
SysAuthExample authExample = new SysAuthExample();
authExample.createCriteria().andIdentifyTypeEqualTo((byte) WebConstant.IDENTIFY_TYPE.Wx_H5.value)
.andIdentifierEqualTo(sysWx.getUnionId()).andCredentialEqualTo(sysWx.getOpenId());
List<SysAuth> authList = authDao.selectByExample(authExample);
if (CollectionUtil.isNotEmpty(authList)) {
theAuth = authList.get(0);
} else {
//1.添加user
SysUser user = new SysUser();
user.setId(snowflake.nextId());
userDao.insertSelective(user);
//2.添加auth
theAuth = new SysAuth();
theAuth.setId(snowflake.nextId());
theAuth.setUserId(user.getId());
theAuth.setIdentifyType((byte) WebConstant.IDENTIFY_TYPE.Wx_H5.value);
theAuth.setIdentifier(sysWx.getUnionId());
theAuth.setCredential(sysWx.getOpenId());
authDao.insertSelective(theAuth);
}
}else{
throw new BaseException(CodeEnum.NOT_SELECT_WX);
}
log.info("openId,{}");
//1.查找对应账户,不存在则注册
List<SysAuth> authList = null;
//2.返回
userSignVo = new UserVo.UserSign();
userSignVo.setUserId(theAuth.getUserId());
userSignVo.setAuthId(theAuth.getId());
return userSignVo;
}
/**
* 微信公众号登陆
*
* @param code
* @return
* @throws Exception
*/
private UserVo.UserSign wxLogin(WebConstant.IDENTIFY_TYPE identifyType,String code) throws Exception {
UserVo.UserSign userSignVo = null;
//获取微信信息并保存
WxOauth2UserInfo wxOauth2UserInfo = WxGzhUtil.getOauth2UserInfo(identifyType,code);
SysAuth theAuth = null;
SysAuthExample authExample = new SysAuthExample();
authExample.createCriteria().andIdentifyTypeEqualTo((byte) WebConstant.IDENTIFY_TYPE.OAUTH2_Wx.value)
.andIdentifierEqualTo(openid).andCredentialEqualTo(unionId);
authList = authDao.selectByExample(authExample);
if (CollectionUtil.isNotEmpty(authList)) {
theAuth = authList.get(0);
} else {
// throw new BaseException(CodeEnum.NOT_BINDING);
//1.添加user
SysUser user = new SysUser();
user.setId(snowflake.nextId());
userDao.insertSelective(user);
//2.添加auth
SysAuth auth = new SysAuth();
auth.setId(snowflake.nextId());
auth.setUserId(user.getId());
auth.setIdentifyType((byte) WebConstant.IDENTIFY_TYPE.OAUTH2_Wx.value);
auth.setIdentifier(openid);
auth.setCredential(unionId);
authDao.insertSelective(auth);
if (ObjectUtil.isNotNull(wxOauth2UserInfo)) {
//查找有无保存的信息
SysWxExample sysWxExample = new SysWxExample();
sysWxExample.createCriteria().andUnionIdEqualTo(wxOauth2UserInfo.getUnionId());
List<SysWx> sysWxList = sysWxDao.selectByExample(sysWxExample);
SysWx sysWx = null;
if (CollUtil.isNotEmpty(sysWxList)) {
//如果有信息,则更新
sysWx = sysWxList.get(0);
BeanUtil.copyProperties(wxOauth2UserInfo, sysWx);
sysWxDao.updateByPrimaryKeySelective(sysWx);
} else {
//没有则保存微信信息
sysWx = new SysWx();
BeanUtil.copyProperties(wxOauth2UserInfo, sysWx);
sysWx.setId(snowflake.nextId());
sysWxDao.insertSelective(sysWx);
}
//查找认证信息
SysAuthExample authExample = new SysAuthExample();
authExample.createCriteria().andIdentifyTypeEqualTo((byte) WebConstant.IDENTIFY_TYPE.OAUTH2_Wx.value)
.andIdentifierEqualTo(sysWx.getUnionId()).andCredentialEqualTo(sysWx.getOpenId());
List<SysAuth> authList = authDao.selectByExample(authExample);
if (CollectionUtil.isNotEmpty(authList)) {
theAuth = authList.get(0);
} else {
//1.添加user
SysUser user = new SysUser();
user.setId(snowflake.nextId());
userDao.insertSelective(user);
//2.添加auth
theAuth = new SysAuth();
theAuth.setId(snowflake.nextId());
theAuth.setUserId(user.getId());
theAuth.setIdentifyType((byte) WebConstant.IDENTIFY_TYPE.OAUTH2_Wx.value);
theAuth.setIdentifier(sysWx.getUnionId());
theAuth.setCredential(sysWx.getOpenId());
authDao.insertSelective(theAuth);
}
}else{
throw new BaseException(CodeEnum.NOT_SELECT_WX);
}
//2.返回
@ -322,7 +403,23 @@ public class UserService implements IUserService {
* 发送验证码
*/
@Override
public UserVo.SmsCode getSignInSmsCode(String phone) throws Exception {
public UserVo.SmsCode getSignInSmsCode(String phone,Integer client) throws Exception {
//获取登陆客户端类型
WebConstant.CLIENT_TYPE client_type = null;
if(ObjectUtil.isNotNull(client)) {
client_type = WebConstant.CLIENT_TYPE.valueOf(client);
}else {
client_type = WebConstant.CLIENT_TYPE.valueOf(1);
}
//验证手机号的正确性
String regex = "[1]([3-9])[0-9]{9}$";
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(phone);
boolean isMatch = m.matches();
if (!isMatch) {
throw new BaseException(CodeEnum.PHONE_ERR);
}
UserVo.SmsCode smsCodeVo = null;
//1.验证发送间隔是否大于指定间隔
if (redisUtil.hasKey(RedisKeyManager.getSigninSmsExistKey(phone))) {
@ -340,7 +437,7 @@ public class UserService implements IUserService {
redisUtil.set(RedisKeyManager.getSigninSmsExistKey(phone), verifyCode, codeExistINSeconds);
//5.发送验证码
SmsUtil.sendSms(phone,verifyCode,"微信小程序",codeValidInSeconds);
SmsUtil.sendSms(phone,verifyCode,client_type.phase,codeValidInSeconds);
//6.返回
smsCodeVo = new UserVo.SmsCode();
@ -467,6 +564,15 @@ public class UserService implements IUserService {
*/
@Override
public Boolean findPhone(String phone) {
//验证手机号的正确性
String regex = "[1]([3-9])[0-9]{9}$";
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(phone);
boolean isMatch = m.matches();
if (!isMatch) {
throw new BaseException(CodeEnum.PHONE_ERR);
}
Boolean flag = false;
SysAuthExample authExample = new SysAuthExample();
authExample.createCriteria().andIdentifyTypeEqualTo((byte) WebConstant.IDENTIFY_TYPE.Phone.value)
@ -502,56 +608,60 @@ public class UserService implements IUserService {
}
/**
* 微信绑定手机号
* 微信绑定新手机号没有账号注册
*
* @param currentUserId
* @param wxPhone
* @return
* @throws Exception
*/
@Override
public String bindingPhone(Long currentUserId, UserDto.WxPhone wxPhone) throws Exception {
if (isSmsCodeCorrect(wxPhone.getPhone(), wxPhone.getSmsCode())) {
//查找该用户以前绑定的手机
SysAuthExample authExample = new SysAuthExample();
authExample.createCriteria().andIdentifyTypeEqualTo((byte) WebConstant.IDENTIFY_TYPE.Phone.value)
.andUserIdEqualTo(currentUserId);
List<SysAuth> authList = authDao.selectByExample(authExample);
if (CollectionUtil.isNotEmpty(authList)) {
for (SysAuth auth : authList) {
if (auth.getIdentifier().equals(wxPhone.getPhone())) {
throw new BaseException(-1, "您的账号已经绑定过此手机,请勿重复绑定");
} else {
throw new BaseException(-1, "您的账号已绑定手机" + auth.getIdentifier());
}
}
} else {
//改手机对应账户,如果有,提示
List<SysAuth> phoneList = null;
SysAuth theAuth = null;
SysAuthExample phoneExample = new SysAuthExample();
authExample.createCriteria().andIdentifyTypeEqualTo((byte) WebConstant.IDENTIFY_TYPE.Phone.value)
.andIdentifierEqualTo(wxPhone.getPhone());
phoneList = authDao.selectByExample(phoneExample);
if (CollectionUtil.isNotEmpty(phoneList)) {//已被注册 提示
//合并
} else {
throw new BaseException(CodeEnum.NOT_REGISTER);
// //绑定 添加auth
// SysAuth auth = new SysAuth();
// auth.setId(snowflake.nextId());
// auth.setUserId(currentUserId);
// auth.setIdentifyType((byte) WebCo nstant.IDENTIFY_TYPE.Phone.value);
// auth.setIdentifier(phone);
// auth.setCredential(code);
// authDao.insertSelective(auth);
}
}
}else {
throw new BaseException(CodeEnum.SMS_CODE_CORRECT);
}
public String bindingNewPhone(Long currentUserId, UserDto.WxBindingPhone wxPhone) throws Exception {
// if (isSmsCodeCorrect(wxPhone.getPhone(), wxPhone.getSmsCode())) {
// //查找该用户以前绑定的手机
// SysAuthExample authExample = new SysAuthExample();
// authExample.createCriteria().andIdentifyTypeEqualTo((byte) WebConstant.IDENTIFY_TYPE.Phone.value)
// .andUserIdEqualTo(currentUserId);
// List<SysAuth> authList = authDao.selectByExample(authExample);
// if (CollectionUtil.isNotEmpty(authList)) {
// for (SysAuth auth : authList) {
// if (auth.getIdentifier().equals(wxPhone.getPhone())) {
// throw new BaseException(-1, "您的账号已经绑定过此手机,请勿重复绑定");
// } else {
// throw new BaseException(-1, "您的账号已绑定手机" + auth.getIdentifier());
// }
// }
// } else {
// //改手机对应账户,如果有,提示
// List<SysAuth> phoneList = null;
// SysAuth theAuth = null;
// SysAuthExample phoneExample = new SysAuthExample();
// authExample.createCriteria().andIdentifyTypeEqualTo((byte) WebConstant.IDENTIFY_TYPE.Phone.value)
// .andIdentifierEqualTo(wxPhone.getPhone());
// phoneList = authDao.selectByExample(phoneExample);
// if (CollectionUtil.isNotEmpty(phoneList)) {//已被注册 提示
// //合并
// } else {
// throw new BaseException(CodeEnum.NOT_REGISTER);
//// //绑定 添加auth
//// SysAuth auth = new SysAuth();
//// auth.setId(snowflake.nextId());
//// auth.setUserId(currentUserId);
//// auth.setIdentifyType((byte) WebCo nstant.IDENTIFY_TYPE.Phone.value);
//// auth.setIdentifier(phone);
//// auth.setCredential(code);
//// authDao.insertSelective(auth);
// }
// }
// }else {
// throw new BaseException(CodeEnum.SMS_CODE_CORRECT);
// }
return null;
}
/**
* 更改绑定手机号
*/
@Override
public void updatePhone(Long currentUserId, UserDto.UpdatePhone updatePhone) throws Exception {
if (isSmsCodeCorrect(updatePhone.getNewPhone(), updatePhone.getCode())) {
@ -754,4 +864,39 @@ public class UserService implements IUserService {
UserVo.UserSign userSign = saveAuth(up);
return userSign.getUserId();
}
@Override
public UserVo.TokenBean getUserInfoAndToken(WebConstant.CLIENT_TYPE client_type, UserVo.UserSign userSignVo, Map<String, Object> theMap) throws Exception {
UserVo.TokenBean tokenBean = generateToken(client_type, userSignVo.getUserId(), theMap);
//获取手机号
String phone = getPhone(userSignVo.getUserId());
//获取账号
String account = selectAccountByPhone(phone);
//获取用户的微信信息
SysAuth sysAuth = authDao.selectByPrimaryKey(userSignVo.getAuthId());
UserVo.WxInfo wxInfo = new UserVo.WxInfo();
SysWx sysWx = getSysWx(sysAuth.getIdentifier());
if(ObjectUtil.isNotNull(sysAuth)){
BeanUtil.copyProperties(sysWx,wxInfo);
}
tokenBean.setId(userSignVo.getUserId());
tokenBean.setPhone(phone);
tokenBean.setAccount(account);
tokenBean.setWxInfo(wxInfo);
return tokenBean;
}
private SysWx getSysWx(String unionId){
SysWx sysWx = null;
SysWxExample sysWxExample = new SysWxExample();
sysWxExample.createCriteria().andUnionIdEqualTo(unionId);
List<SysWx> sysWxList = sysWxDao.selectByExample(sysWxExample);
if(CollectionUtil.isNotEmpty(sysWxList)){
sysWx = sysWxList.get(0);
}
return sysWx;
}
}

42
tall/src/main/java/com/ccsens/tall/web/UserController.java

@ -91,14 +91,8 @@ public class UserController {
if (ObjectUtil.isNotNull(userSignVo)) {
Map<String, Object> theMap = CollectionUtil.newHashMap();
theMap.put("authId", String.valueOf(userSignVo.getAuthId()));
UserVo.TokenBean tokenBean = userService.generateToken(client_type, userSignVo.getUserId(), theMap);
//获取手机号
String phone = userService.getPhone(userSignVo.getUserId());
//获取账号
String account = userService.selectAccountByPhone(phone);
tokenBean.setId(userSignVo.getUserId());
tokenBean.setPhone(phone);
tokenBean.setAccount(account);
UserVo.TokenBean tokenBean = userService.getUserInfoAndToken(client_type, userSignVo, theMap);
return JsonResponse.newInstance().ok(tokenBean);
} else {
return JsonResponse.newInstance().fail("登陆信息不正确.");
@ -110,8 +104,9 @@ public class UserController {
})
@RequestMapping(value = "/smscode", method = RequestMethod.GET, produces = {"application/json;charset=UTF-8"})
public JsonResponse<UserVo.SmsCode> getSmsCode(HttpServletRequest request,
@ApiParam @RequestParam String phone) throws Exception {
UserVo.SmsCode smsCodeVo = userService.getSignInSmsCode(phone);
@ApiParam @RequestParam String phone,
@ApiParam @RequestParam Integer client) throws Exception {
UserVo.SmsCode smsCodeVo = userService.getSignInSmsCode(phone,client);
return JsonResponse.newInstance().ok(smsCodeVo);
}
@ -148,19 +143,19 @@ public class UserController {
@RequestMapping(value = "/account", method = RequestMethod.GET, produces = {"application/json;charset=UTF-8"})
public JsonResponse accounts(@ApiParam @RequestParam String account) throws Exception {
userService.findAccount(account);
return JsonResponse.newInstance().ok();
Boolean flag = userService.findAccount(account);
return JsonResponse.newInstance().ok(flag);
}
@ApiOperation(value = "/检查号是否被注册", notes = "")
@ApiOperation(value = "/检查手机号是否被注册", notes = "")
@ApiImplicitParams({
})
@RequestMapping(value = "/phone", method = RequestMethod.GET, produces = {"application/json;charset=UTF-8"})
public JsonResponse findPhone(@ApiParam @RequestParam String phone) throws Exception {
userService.findPhone(phone);
return JsonResponse.newInstance().ok();
Boolean flag = userService.findPhone(phone);
return JsonResponse.newInstance().ok(flag);
}
// @ApiOperation(value = "/修改账号信息",notes = "")
@ -173,14 +168,25 @@ public class UserController {
// }
@ApiOperation(value = "/微信绑定手机",notes = "")
@ApiOperation(value = "/微信合并已有账号",notes = "")
@ApiImplicitParams({
})
@RequestMapping(value="/merge",method = RequestMethod.POST,produces = {"application/json;charset=UTF-8"})
public JsonResponse bindingPhone(HttpServletRequest request,
@ApiParam @RequestBody UserDto.WxMergePhone wxPhone) throws Exception {
Long currentUserId = Long.valueOf(((Claims) request.getAttribute(WebConstant.REQUEST_KEY_CLAIMS)).getSubject());
// String returnPhone = userService.bindingPhone(currentUserId,wxPhone);
return JsonResponse.newInstance().ok();
}
@ApiOperation(value = "/微信绑定账号(没有账号,注册)",notes = "")
@ApiImplicitParams({
})
@RequestMapping(value="/phone",method = RequestMethod.POST,produces = {"application/json;charset=UTF-8"})
public JsonResponse bindingPhone(HttpServletRequest request,
@ApiParam @RequestBody UserDto.WxPhone wxPhone) throws Exception {
@ApiParam @RequestBody UserDto.WxBindingPhone wxPhone) throws Exception {
Long currentUserId = Long.valueOf(((Claims) request.getAttribute(WebConstant.REQUEST_KEY_CLAIMS)).getSubject());
String returnPhone = userService.bindingPhone(currentUserId,wxPhone);
String returnPhone = userService.bindingNewPhone(currentUserId,wxPhone);
return JsonResponse.newInstance().ok(returnPhone);
}

370
tall/src/main/resources/mapper_raw/SysWxMapper.xml

@ -0,0 +1,370 @@
<?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.ccsens.tall.persist.mapper.SysWxMapper">
<resultMap id="BaseResultMap" type="com.ccsens.tall.bean.po.SysWx">
<id column="id" jdbcType="BIGINT" property="id" />
<result column="user_id" jdbcType="BIGINT" property="userId" />
<result column="open_id" jdbcType="VARCHAR" property="openId" />
<result column="union_id" jdbcType="VARCHAR" property="unionId" />
<result column="nickname" jdbcType="VARCHAR" property="nickname" />
<result column="head_img_url" jdbcType="VARCHAR" property="headImgUrl" />
<result column="sex" jdbcType="TINYINT" property="sex" />
<result column="province" jdbcType="VARCHAR" property="province" />
<result column="city" jdbcType="VARCHAR" property="city" />
<result column="country" jdbcType="VARCHAR" property="country" />
<result column="language" jdbcType="VARCHAR" property="language" />
<result column="privilege" jdbcType="VARCHAR" property="privilege" />
<result column="created_at" jdbcType="TIMESTAMP" property="createdAt" />
<result column="updated_at" jdbcType="TIMESTAMP" property="updatedAt" />
<result column="rec_status" jdbcType="TINYINT" property="recStatus" />
</resultMap>
<sql id="Example_Where_Clause">
<where>
<foreach collection="oredCriteria" item="criteria" separator="or">
<if test="criteria.valid">
<trim prefix="(" prefixOverrides="and" suffix=")">
<foreach collection="criteria.criteria" item="criterion">
<choose>
<when test="criterion.noValue">
and ${criterion.condition}
</when>
<when test="criterion.singleValue">
and ${criterion.condition} #{criterion.value}
</when>
<when test="criterion.betweenValue">
and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
</when>
<when test="criterion.listValue">
and ${criterion.condition}
<foreach close=")" collection="criterion.value" item="listItem" open="(" separator=",">
#{listItem}
</foreach>
</when>
</choose>
</foreach>
</trim>
</if>
</foreach>
</where>
</sql>
<sql id="Update_By_Example_Where_Clause">
<where>
<foreach collection="example.oredCriteria" item="criteria" separator="or">
<if test="criteria.valid">
<trim prefix="(" prefixOverrides="and" suffix=")">
<foreach collection="criteria.criteria" item="criterion">
<choose>
<when test="criterion.noValue">
and ${criterion.condition}
</when>
<when test="criterion.singleValue">
and ${criterion.condition} #{criterion.value}
</when>
<when test="criterion.betweenValue">
and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
</when>
<when test="criterion.listValue">
and ${criterion.condition}
<foreach close=")" collection="criterion.value" item="listItem" open="(" separator=",">
#{listItem}
</foreach>
</when>
</choose>
</foreach>
</trim>
</if>
</foreach>
</where>
</sql>
<sql id="Base_Column_List">
id, user_id, open_id, union_id, nickname, head_img_url, sex, province, city, country,
language, privilege, created_at, updated_at, rec_status
</sql>
<select id="selectByExample" parameterType="com.ccsens.tall.bean.po.SysWxExample" resultMap="BaseResultMap">
select
<if test="distinct">
distinct
</if>
<include refid="Base_Column_List" />
from t_sys_wx
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
<if test="orderByClause != null">
order by ${orderByClause}
</if>
</select>
<select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from t_sys_wx
where id = #{id,jdbcType=BIGINT}
</select>
<delete id="deleteByPrimaryKey" parameterType="java.lang.Long">
delete from t_sys_wx
where id = #{id,jdbcType=BIGINT}
</delete>
<delete id="deleteByExample" parameterType="com.ccsens.tall.bean.po.SysWxExample">
delete from t_sys_wx
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
</delete>
<insert id="insert" parameterType="com.ccsens.tall.bean.po.SysWx">
insert into t_sys_wx (id, user_id, open_id,
union_id, nickname, head_img_url,
sex, province, city,
country, language, privilege,
created_at, updated_at, rec_status
)
values (#{id,jdbcType=BIGINT}, #{userId,jdbcType=BIGINT}, #{openId,jdbcType=VARCHAR},
#{unionId,jdbcType=VARCHAR}, #{nickname,jdbcType=VARCHAR}, #{headImgUrl,jdbcType=VARCHAR},
#{sex,jdbcType=TINYINT}, #{province,jdbcType=VARCHAR}, #{city,jdbcType=VARCHAR},
#{country,jdbcType=VARCHAR}, #{language,jdbcType=VARCHAR}, #{privilege,jdbcType=VARCHAR},
#{createdAt,jdbcType=TIMESTAMP}, #{updatedAt,jdbcType=TIMESTAMP}, #{recStatus,jdbcType=TINYINT}
)
</insert>
<insert id="insertSelective" parameterType="com.ccsens.tall.bean.po.SysWx">
insert into t_sys_wx
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id != null">
id,
</if>
<if test="userId != null">
user_id,
</if>
<if test="openId != null">
open_id,
</if>
<if test="unionId != null">
union_id,
</if>
<if test="nickname != null">
nickname,
</if>
<if test="headImgUrl != null">
head_img_url,
</if>
<if test="sex != null">
sex,
</if>
<if test="province != null">
province,
</if>
<if test="city != null">
city,
</if>
<if test="country != null">
country,
</if>
<if test="language != null">
language,
</if>
<if test="privilege != null">
privilege,
</if>
<if test="createdAt != null">
created_at,
</if>
<if test="updatedAt != null">
updated_at,
</if>
<if test="recStatus != null">
rec_status,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">
#{id,jdbcType=BIGINT},
</if>
<if test="userId != null">
#{userId,jdbcType=BIGINT},
</if>
<if test="openId != null">
#{openId,jdbcType=VARCHAR},
</if>
<if test="unionId != null">
#{unionId,jdbcType=VARCHAR},
</if>
<if test="nickname != null">
#{nickname,jdbcType=VARCHAR},
</if>
<if test="headImgUrl != null">
#{headImgUrl,jdbcType=VARCHAR},
</if>
<if test="sex != null">
#{sex,jdbcType=TINYINT},
</if>
<if test="province != null">
#{province,jdbcType=VARCHAR},
</if>
<if test="city != null">
#{city,jdbcType=VARCHAR},
</if>
<if test="country != null">
#{country,jdbcType=VARCHAR},
</if>
<if test="language != null">
#{language,jdbcType=VARCHAR},
</if>
<if test="privilege != null">
#{privilege,jdbcType=VARCHAR},
</if>
<if test="createdAt != null">
#{createdAt,jdbcType=TIMESTAMP},
</if>
<if test="updatedAt != null">
#{updatedAt,jdbcType=TIMESTAMP},
</if>
<if test="recStatus != null">
#{recStatus,jdbcType=TINYINT},
</if>
</trim>
</insert>
<select id="countByExample" parameterType="com.ccsens.tall.bean.po.SysWxExample" resultType="java.lang.Long">
select count(*) from t_sys_wx
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
</select>
<update id="updateByExampleSelective" parameterType="map">
update t_sys_wx
<set>
<if test="record.id != null">
id = #{record.id,jdbcType=BIGINT},
</if>
<if test="record.userId != null">
user_id = #{record.userId,jdbcType=BIGINT},
</if>
<if test="record.openId != null">
open_id = #{record.openId,jdbcType=VARCHAR},
</if>
<if test="record.unionId != null">
union_id = #{record.unionId,jdbcType=VARCHAR},
</if>
<if test="record.nickname != null">
nickname = #{record.nickname,jdbcType=VARCHAR},
</if>
<if test="record.headImgUrl != null">
head_img_url = #{record.headImgUrl,jdbcType=VARCHAR},
</if>
<if test="record.sex != null">
sex = #{record.sex,jdbcType=TINYINT},
</if>
<if test="record.province != null">
province = #{record.province,jdbcType=VARCHAR},
</if>
<if test="record.city != null">
city = #{record.city,jdbcType=VARCHAR},
</if>
<if test="record.country != null">
country = #{record.country,jdbcType=VARCHAR},
</if>
<if test="record.language != null">
language = #{record.language,jdbcType=VARCHAR},
</if>
<if test="record.privilege != null">
privilege = #{record.privilege,jdbcType=VARCHAR},
</if>
<if test="record.createdAt != null">
created_at = #{record.createdAt,jdbcType=TIMESTAMP},
</if>
<if test="record.updatedAt != null">
updated_at = #{record.updatedAt,jdbcType=TIMESTAMP},
</if>
<if test="record.recStatus != null">
rec_status = #{record.recStatus,jdbcType=TINYINT},
</if>
</set>
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
</if>
</update>
<update id="updateByExample" parameterType="map">
update t_sys_wx
set id = #{record.id,jdbcType=BIGINT},
user_id = #{record.userId,jdbcType=BIGINT},
open_id = #{record.openId,jdbcType=VARCHAR},
union_id = #{record.unionId,jdbcType=VARCHAR},
nickname = #{record.nickname,jdbcType=VARCHAR},
head_img_url = #{record.headImgUrl,jdbcType=VARCHAR},
sex = #{record.sex,jdbcType=TINYINT},
province = #{record.province,jdbcType=VARCHAR},
city = #{record.city,jdbcType=VARCHAR},
country = #{record.country,jdbcType=VARCHAR},
language = #{record.language,jdbcType=VARCHAR},
privilege = #{record.privilege,jdbcType=VARCHAR},
created_at = #{record.createdAt,jdbcType=TIMESTAMP},
updated_at = #{record.updatedAt,jdbcType=TIMESTAMP},
rec_status = #{record.recStatus,jdbcType=TINYINT}
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
</if>
</update>
<update id="updateByPrimaryKeySelective" parameterType="com.ccsens.tall.bean.po.SysWx">
update t_sys_wx
<set>
<if test="userId != null">
user_id = #{userId,jdbcType=BIGINT},
</if>
<if test="openId != null">
open_id = #{openId,jdbcType=VARCHAR},
</if>
<if test="unionId != null">
union_id = #{unionId,jdbcType=VARCHAR},
</if>
<if test="nickname != null">
nickname = #{nickname,jdbcType=VARCHAR},
</if>
<if test="headImgUrl != null">
head_img_url = #{headImgUrl,jdbcType=VARCHAR},
</if>
<if test="sex != null">
sex = #{sex,jdbcType=TINYINT},
</if>
<if test="province != null">
province = #{province,jdbcType=VARCHAR},
</if>
<if test="city != null">
city = #{city,jdbcType=VARCHAR},
</if>
<if test="country != null">
country = #{country,jdbcType=VARCHAR},
</if>
<if test="language != null">
language = #{language,jdbcType=VARCHAR},
</if>
<if test="privilege != null">
privilege = #{privilege,jdbcType=VARCHAR},
</if>
<if test="createdAt != null">
created_at = #{createdAt,jdbcType=TIMESTAMP},
</if>
<if test="updatedAt != null">
updated_at = #{updatedAt,jdbcType=TIMESTAMP},
</if>
<if test="recStatus != null">
rec_status = #{recStatus,jdbcType=TINYINT},
</if>
</set>
where id = #{id,jdbcType=BIGINT}
</update>
<update id="updateByPrimaryKey" parameterType="com.ccsens.tall.bean.po.SysWx">
update t_sys_wx
set user_id = #{userId,jdbcType=BIGINT},
open_id = #{openId,jdbcType=VARCHAR},
union_id = #{unionId,jdbcType=VARCHAR},
nickname = #{nickname,jdbcType=VARCHAR},
head_img_url = #{headImgUrl,jdbcType=VARCHAR},
sex = #{sex,jdbcType=TINYINT},
province = #{province,jdbcType=VARCHAR},
city = #{city,jdbcType=VARCHAR},
country = #{country,jdbcType=VARCHAR},
language = #{language,jdbcType=VARCHAR},
privilege = #{privilege,jdbcType=VARCHAR},
created_at = #{createdAt,jdbcType=TIMESTAMP},
updated_at = #{updatedAt,jdbcType=TIMESTAMP},
rec_status = #{recStatus,jdbcType=TINYINT}
where id = #{id,jdbcType=BIGINT}
</update>
</mapper>

4
util/src/main/java/com/ccsens/util/CodeEnum.java

@ -84,7 +84,9 @@ public enum CodeEnum {
GAME_PREPARATION(67, "游戏已经开始倒计时了", true),
GAME_PROCESSING (68, "游戏已经在火热进行啦", true),
GAME_COMPLETED(69, "抱歉,您来晚了,游戏已结束", true),
NOT_REGISTER(70,"该手机号尚未注册账号,请填写用户名与密码进行注册同时绑定微信",true)
NOT_REGISTER(70,"该手机号尚未注册账号,请填写用户名与密码进行注册同时绑定微信",true),
PHONE_ERR(71,"请输入正确的手机号",true),
NOT_SELECT_WX(72,"未查到对应的微信信息",true)
;
public CodeEnum addMsg(String msg){

7
util/src/main/java/com/ccsens/util/WebConstant.java

@ -133,8 +133,8 @@ public class WebConstant {
}
public enum CLIENT_TYPE {
Wxmp(0,"微信小程序"), H5(1,"Web H5"),Android(2
,"Android App"),IOS(3,"IOS App");
Wxmp(0,"微信小程序"), H5(1,"网页"),Android(2
,"安卓客户端"),IOS(3,"苹果客户端");
public int value;
public String phase;
@ -174,7 +174,8 @@ public class WebConstant {
case 2: return Email;
case 3: return Account;
case 4: return OAUTH2_Wx;
case 5: return OAUTH2_WeiBo;
case 5: return Wx_H5;
case 6: return OAUTH2_WeiBo;
default: return null;
}
}

37
util/src/main/java/com/ccsens/util/wx/WxGzhUtil.java

@ -7,6 +7,7 @@ import cn.hutool.http.HttpRequest;
import com.ccsens.util.DateUtil;
import com.ccsens.util.JacksonUtil;
import com.ccsens.util.WebConstant;
import com.ccsens.util.bean.wx.po.*;
import com.ccsens.util.exception.BaseException;
import com.ccsens.util.exception.BusinessException;
@ -62,11 +63,37 @@ public class WxGzhUtil {
= "https://api.weixin.qq.com/sns/userinfo?access_token=%1$s&openid=%2$s";
private static final String APPID = "wx7af1bf1e14facf82";
private static final String SECRET = "a6613fae11b497639c0224b820aaf6d9";
private static final String APPID_H5 = "wxd1842e073e0e6d91";
private static final String SECRET_H5 = "96d69b79039caf92a2abafa999880cad";
private static final String TOKEN = "nNzkL9KkZUOIS8uU";
private static final String ENCODING_AES_KEY = "MQEXG7grhRNsARbUzem6OwnGr2ZW9o5jsauNqaQWOuu";
private static final String MCHID = "";
private static final String KEY = "";
private static String appId(WebConstant.IDENTIFY_TYPE identifyType){
switch (identifyType){
case Wx_H5:
return APPID_H5;
case OAUTH2_Wx:
default:
return APPID;
}
}
private static String secret(WebConstant.IDENTIFY_TYPE identifyType){
switch (identifyType){
case Wx_H5:
return SECRET_H5;
case OAUTH2_Wx:
default:
return SECRET;
}
}
/**
* 数组转字符串
*/
@ -136,7 +163,7 @@ public class WxGzhUtil {
globalWxAccessToken.getCreatedAt() + globalWxAccessToken.getExpiresIn()
>= DateUtil.currentSeconds() - ACCESS_TOKEN_RESERVED_SECONDS){
WxAccessToken wxAccessToken = null;
String url = String.format(URL_GET_ACCESS_TOKEN,"client_credential",APPID,SECRET);
String url = String.format(URL_GET_ACCESS_TOKEN,"client_credential",SECRET);
String response = HttpRequest.get(url).execute().body();
Console.log("getAccessToken: {}",response);
try {
@ -208,9 +235,9 @@ public class WxGzhUtil {
* @param code OAuth2授权码
* @return WxOauth2AccessToken
*/
public static WxOauth2AccessToken getOauth2AccessToken(String code) throws BaseException {
public static WxOauth2AccessToken getOauth2AccessToken(WebConstant.IDENTIFY_TYPE identifyType,String code) throws BaseException {
WxOauth2AccessToken wxOauth2AccessToken = null;
String url = String.format(URL_GET_OAUTH2_ACCESS_TOKEN,APPID,SECRET,code);
String url = String.format(URL_GET_OAUTH2_ACCESS_TOKEN,appId(identifyType),secret(identifyType),code);
String response = HttpRequest.get(url).execute().body();
Console.log("url: {}\nresponse: {}",url,response);
try {
@ -266,8 +293,8 @@ public class WxGzhUtil {
* @return WxOauth2UserInfo
* @throws BaseException 异常
*/
public static WxOauth2UserInfo getOauth2UserInfo(String code) throws BaseException {
WxOauth2AccessToken wxOauth2AccessToken = getOauth2AccessToken(code);
public static WxOauth2UserInfo getOauth2UserInfo(WebConstant.IDENTIFY_TYPE identifyType,String code) throws BaseException {
WxOauth2AccessToken wxOauth2AccessToken = getOauth2AccessToken(identifyType,code);
return getOauth2UserInfo(wxOauth2AccessToken.getAccessToken(),wxOauth2AccessToken.getOpenId());
}
}

Loading…
Cancel
Save