Commit 0a564129 authored by 徐小光's avatar 徐小光

Merge branch 'feature_modify_phone_no' into 'master'

Feature modify phone no

See merge request !37
parents 93163ede 2e6600ea
......@@ -330,6 +330,11 @@
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
<dependency>
<groupId>com.qiniu</groupId>
<artifactId>qiniu-java-sdk</artifactId>
<version>7.2.27</version>
</dependency>
</dependencies>
<build>
......
package cn.quantgroup.xyqb.aspect.limit;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 禁止接口频繁访问
* <p>
* 使用该组件,方法入参,首参数应为userId
* </p>
* Date: 2019/11/6
* Time: 下午5:07
*
* @author: yangrui
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AccessLimit {
String redisKey();
}
package cn.quantgroup.xyqb.aspect.limit;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
@Aspect
@Component
public class AccessLimitAspect {
@Resource
private StringRedisTemplate stringRedisTemplate;
/**
* 要求不高时 可使用。
* setnx + expire 问题,lua解决。
* expire问题,严谨可使用Redission。
*
* @param pjp
* @throws Throwable
*/
@Around("@annotation(cn.quantgroup.xyqb.aspect.limit.AccessLimit)")
public Object accessLimit(ProceedingJoinPoint pjp) throws Throwable {
Object o = null;
AccessLimit accessLimit = ((MethodSignature) pjp.getSignature()).getMethod().getAnnotation(AccessLimit.class);
String redisKey = accessLimit.redisKey() + "_" + pjp.getArgs()[0];
boolean lock = stringRedisTemplate.opsForValue().setIfAbsent(redisKey, "");
if (lock) {
try {
stringRedisTemplate.expire(redisKey, 1, TimeUnit.MINUTES);
o = pjp.proceed();
} finally {
stringRedisTemplate.delete(redisKey);
}
}
return o;
}
}
......@@ -83,7 +83,6 @@ public class HttpConfig {
.defaultViewInclusion(false)
.serializationInclusion(JsonInclude.Include.NON_NULL)
.featuresToEnable(SerializationFeature.WRITE_ENUMS_USING_INDEX,
DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS,
DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
return builder;
}
......
package cn.quantgroup.xyqb.config.http;
import cn.quantgroup.xyqb.service.http.IHttpService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
/**
* rest
* <p>
* Date: 2019/11/5
* Time: 上午11:37
*
* @author: yangrui
*
* @deprecated Use
* {@link IHttpService}}
* instead. 统一.
*/
@Deprecated
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate() {
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setConnectTimeout(60 * 1000);
requestFactory.setReadTimeout(60 * 1000);
RestTemplate restTemplate = new RestTemplate(requestFactory);
return restTemplate;
}
}
......@@ -7,15 +7,18 @@ import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
/**
......@@ -27,7 +30,7 @@ import java.util.Set;
@RestController
public class ExceptionHandlingController implements IBaseController {
private static final JsonResult EXCEPTION_RESULT = new JsonResult("internal error", (long)HttpStatus.INTERNAL_SERVER_ERROR.value(), "");
private static final JsonResult EXCEPTION_RESULT = new JsonResult("internal error", (long) HttpStatus.INTERNAL_SERVER_ERROR.value(), "");
/**
* 密码错误次数达到上限异常
......@@ -62,8 +65,8 @@ public class ExceptionHandlingController implements IBaseController {
@ExceptionHandler(UserNotExistException.class)
@ResponseStatus(HttpStatus.UNAUTHORIZED)
public JsonResult userNotExistException(UserNotExistException unee) {
log.info("throw UserNotExistException,msg={},businessCode={},code={}", unee.getMessage(), 1L, (long)HttpStatus.UNAUTHORIZED.value());
return new JsonResult(unee.getMessage(), (long)HttpStatus.UNAUTHORIZED.value(), null);
log.info("throw UserNotExistException,msg={},businessCode={},code={}", unee.getMessage(), 1L, (long) HttpStatus.UNAUTHORIZED.value());
return new JsonResult(unee.getMessage(), (long) HttpStatus.UNAUTHORIZED.value(), null);
}
/**
......@@ -87,8 +90,8 @@ public class ExceptionHandlingController implements IBaseController {
@ExceptionHandler(WechatRelateUserException.class)
@ResponseStatus(HttpStatus.UNAUTHORIZED)
public JsonResult wechatRelateUserException(WechatRelateUserException wrue) {
log.info("throw WechatRelateUserException,msg={},businessCode={},code={}", wrue.getMessage(), 1L, (long)HttpStatus.UNAUTHORIZED.value());
return new JsonResult(wrue.getMessage(), (long)HttpStatus.UNAUTHORIZED.value(), null);
log.info("throw WechatRelateUserException,msg={},businessCode={},code={}", wrue.getMessage(), 1L, (long) HttpStatus.UNAUTHORIZED.value());
return new JsonResult(wrue.getMessage(), (long) HttpStatus.UNAUTHORIZED.value(), null);
}
/**
......@@ -117,4 +120,42 @@ public class ExceptionHandlingController implements IBaseController {
String message = constraintViolations.iterator().next().getMessage();
return JsonResult.buildErrorStateResult(message, null);
}
@ExceptionHandler({MethodArgumentNotValidException.class, BindException.class})
@ResponseBody
@ResponseStatus(HttpStatus.OK)
public JsonResult handleBindException(Exception e) {
BindingResult result = null;
if (e instanceof MethodArgumentNotValidException) {
result = ((MethodArgumentNotValidException) e).getBindingResult();
} else if (e instanceof BindException) {
result = ((BindException) e).getBindingResult();
}
if (result != null) {
List<ObjectError> errors = result.getAllErrors();
StringBuilder sb = new StringBuilder();
if (errors != null && errors.size() > 0) {
Iterator<ObjectError> iterator = errors.iterator();
ObjectError err = null;
while ((err = iterator.next()) != null) {
sb.append(err.getDefaultMessage());
if (iterator.hasNext()) {
sb.append("; ");
} else {
sb.append("。");
break;
}
}
}
return JsonResult.buildErrorStateResult(sb.toString(), null);
}
return null;
}
@ExceptionHandler(DataException.class)
@ResponseBody
@ResponseStatus(HttpStatus.OK)
public JsonResult handleDataException(DataException e) {
return JsonResult.buildErrorStateResult(e.getMessage(), null);
}
}
......@@ -15,6 +15,7 @@ import cn.quantgroup.xyqb.exception.VerificationCodeErrorException;
import cn.quantgroup.xyqb.model.JsonResult;
import cn.quantgroup.xyqb.model.LoginProperties;
import cn.quantgroup.xyqb.model.UserModel;
import cn.quantgroup.xyqb.model.session.SessionStruct;
import cn.quantgroup.xyqb.service.merchant.IMerchantService;
import cn.quantgroup.xyqb.service.register.IUserRegisterService;
import cn.quantgroup.xyqb.service.session.ISessionService;
......@@ -24,6 +25,7 @@ import cn.quantgroup.xyqb.service.user.IUserDetailService;
import cn.quantgroup.xyqb.service.user.IUserService;
import cn.quantgroup.xyqb.service.user.UserCenterService;
import cn.quantgroup.xyqb.service.wechat.IWechatService;
import cn.quantgroup.xyqb.session.XyqbSessionContextHolder;
import cn.quantgroup.xyqb.util.IpUtil;
import cn.quantgroup.xyqb.util.MqUtils;
import cn.quantgroup.xyqb.util.PasswordUtil;
......@@ -414,6 +416,28 @@ public class UserController implements IBaseController {
return JsonResult.buildSuccessResult("token valid", exist);
}
@RequestMapping("/token")
public JsonResult token(@RequestParam String token) {
Map<String, Object> result = new HashMap<>();
result.put("exist", false);
if (StringUtils.isEmpty(token)) {
return JsonResult.buildSuccessResult(null, result);
}
if (token.contains(Constants.TOKEN_MASTER)) {
return JsonResult.buildSuccessResult(null, result);
}
SessionStruct sessionStruct = XyqbSessionContextHolder.getXSessionFromRedis(token);
if (sessionStruct == null || sessionStruct.getValues() == null) {
return JsonResult.buildSuccessResult(null, result);
}
User user = sessionStruct.getValues().getUser();
String phoneNo = user.getPhoneNo();
result.put("phoneNo", phoneNo);
result.put("userId", user.getId());
result.put("exist", true);
return JsonResult.buildSuccessResult(null, result);
}
/**
* 用户中心首页,显示用户头像、昵称、姓名
*
......
package cn.quantgroup.xyqb.controller.modifyphoneno;
import cn.quantgroup.xyqb.controller.IBaseController;
import cn.quantgroup.xyqb.controller.modifyphoneno.req.AuditReq;
import cn.quantgroup.xyqb.controller.modifyphoneno.req.ModifyPhoneNoQueryReq;
import cn.quantgroup.xyqb.controller.modifyphoneno.req.Step1Req;
import cn.quantgroup.xyqb.controller.modifyphoneno.req.Step2Req;
import cn.quantgroup.xyqb.controller.modifyphoneno.resp.ProgressResp;
import cn.quantgroup.xyqb.entity.ModifyPhoneNo;
import cn.quantgroup.xyqb.entity.User;
import cn.quantgroup.xyqb.model.JsonResult;
import cn.quantgroup.xyqb.service.user.IModifyPhoneNoService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.data.domain.Page;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
/**
* 用户手机号修改相关api
* <p>
* Date: 2019/11/4
* Time: 上午11:23
*
* @author: yangrui
*/
@Api(tags = "用户手机号修改相关api")
@RestController
@RequestMapping("/v1/user/modify/phone_no")
public class ModifyPhoneNoController implements IBaseController {
@Resource
private IModifyPhoneNoService modifyPhoneNoService;
@ApiOperation("app - 查询用户手机号修改进度")
@GetMapping("/progress")
public JsonResult<ProgressResp> progress() {
User user = getCurrentUserFromRedis();
if (user == null) {
return JsonResult.buildErrorStateResult("系统错误", null);
}
return JsonResult.buildSuccessResultGeneric(modifyPhoneNoService.progress(user.getId()));
}
/**
* step_1
*
* @param step1Req
* @return
*/
@ApiOperation("app - 申请修改手机号Step_1")
@PostMapping("/step_1")
public JsonResult step1(@Valid @RequestBody Step1Req step1Req) {
User user = getCurrentUserFromRedis();
if (user == null) {
return JsonResult.buildErrorStateResult("系统错误", null);
}
return JsonResult.buildSuccessResultGeneric(modifyPhoneNoService.saveStep1(user.getId(), step1Req));
}
/**
* step_2
*
* @param step2Req
* @return
*/
@ApiOperation("app - 申请修改手机号Step_2")
@PostMapping("/step_2")
public JsonResult step2(@Valid @RequestBody Step2Req step2Req) {
modifyPhoneNoService.saveStep2(step2Req);
return JsonResult.buildSuccessResult();
}
/*------------------------------------------------------------------------------------------------------------*/
/**
* 后台客服处理功能 - 查询列表
*/
@ApiOperation("后台客服处理功能 - 查询列表")
@GetMapping
public JsonResult<Page<ModifyPhoneNo>> list(@Valid ModifyPhoneNoQueryReq modifyPhoneNoQueryReq) {
return JsonResult.buildSuccessResultGeneric(modifyPhoneNoService.list(modifyPhoneNoQueryReq));
}
/**
* 后台客服处理功能 - 人工审核
*/
@ApiOperation("后台客服处理功能 - 人工审核")
@PostMapping("/audit")
public JsonResult audit(@Valid @RequestBody AuditReq auditReq) {
modifyPhoneNoService.audit(auditReq);
return JsonResult.buildSuccessResult();
}
/**
* 后台客服处理功能 - 反馈跟进
*/
@ApiOperation("后台客服处理功能 - 反馈跟进")
@PostMapping("/{id}/feedback")
public JsonResult feedback(@PathVariable Long id) {
modifyPhoneNoService.feedback(id);
return JsonResult.buildSuccessResult();
}
/*------------------------------------------------------------------------------------------------------------*/
}
/**
* modify phone_no api
*/
package cn.quantgroup.xyqb.controller.modifyphoneno;
package cn.quantgroup.xyqb.controller.modifyphoneno.req;
import cn.quantgroup.xyqb.model.ModifyPhoneNoApplyStatusEnum;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
/**
* Date: 2019/11/4
* Time: 上午11:53
*
* @author: yangrui
*/
@Data
public class AuditReq implements Serializable {
private static final long serialVersionUID = 9183603336718659105L;
@NotNull(message = "id不能为空")
private Long id;
@ApiModelProperty("申请状态 1 通过; 2 不通过;")
@NotNull(message = "申请状态不能为空")
private ModifyPhoneNoApplyStatusEnum applyStatus;
@ApiModelProperty("申请状态补充原因")
@Length(max = 256, message = "原因最长256字符")
private String applyStatusReason;
}
package cn.quantgroup.xyqb.controller.modifyphoneno.req;
import cn.quantgroup.xyqb.model.ModifyPhoneNoApplyStatusEnum;
import cn.quantgroup.xyqb.model.ModifyPhoneNoProcessingStatusEnum;
import cn.quantgroup.xyqb.controller.req.Page;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
* Date: 2019/11/5
* Time: 下午4:02
*
* @author: yangrui
*/
@Data
public class ModifyPhoneNoQueryReq extends Page implements Serializable {
private static final long serialVersionUID = -5578450647474952863L;
/**
* 申请人旧手机号
*/
@ApiModelProperty("申请人旧手机号")
private String phoneNo;
/**
* 开始时间
*/
@ApiModelProperty("开始时间")
private String startAt;
/**
* 结束时间
*/
@ApiModelProperty("结束时间")
private String endAt;
/**
* 申请状态 0处理中; 1修改完成; 2不允许修改;
*/
@ApiModelProperty("申请状态 0 处理中; 1 修改完成; 2 不允许修改;")
private ModifyPhoneNoApplyStatusEnum applyStatus;
/**
* 处理状态 0待人工处理 1待用户反馈结果 2已反馈
*/
@ApiModelProperty("处理状态 0 待人工处理; 1 待用户反馈结果; 2 已反馈;")
private ModifyPhoneNoProcessingStatusEnum processingStatus;
}
package cn.quantgroup.xyqb.controller.modifyphoneno.req;
import cn.quantgroup.xyqb.entity.ModifyPhoneNo;
import cn.quantgroup.xyqb.util.ValidationUtil;
import cn.quantgroup.xyqb.validator.IdCard;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.NotBlank;
import org.springframework.beans.BeanUtils;
import javax.validation.constraints.Pattern;
import java.io.Serializable;
import java.util.Date;
/**
* Date: 2019/11/4
* Time: 上午11:29
*
* @author: yangrui
*/
@Data
public class Step1Req implements Serializable {
private static final long serialVersionUID = 3072182423041383540L;
/**
* 注册人真实姓名
*/
@ApiModelProperty("注册人真实姓名")
@NotBlank(message = "注册人真实姓名不能为空")
@Length(max = 32, message = "注册人真实姓名最长32字符")
private String name;
/**
* 注册人身份证件号
*/
@ApiModelProperty("注册人身份证件号")
@NotBlank(message = "注册人身份证件号不能为空")
@IdCard
private String idCard;
/**
* 当前手机号码
*/
@ApiModelProperty("当前手机号码")
@NotBlank(message = "当前手机号码不能为空")
@Pattern(regexp = ValidationUtil.phoneRegExp, message = "当前手机号码格式错误")
private String prevPhoneNo;
/**
* 新手机号码
*/
@ApiModelProperty("新手机号码")
@NotBlank(message = "新手机号码不能为空")
@Pattern(regexp = ValidationUtil.phoneRegExp, message = "新手机号码格式错误")
private String curPhoneNo;
/**
* 新手机号码短信验证码
*/
@ApiModelProperty("新手机号码短信验证码")
@NotBlank(message = "新手机号码短信验证码不能为空")
private String smsCode;
public static ModifyPhoneNo adapt(Step1Req step1Req) {
ModifyPhoneNo modifyPhoneNo = new ModifyPhoneNo();
BeanUtils.copyProperties(step1Req, modifyPhoneNo);
Date date = new Date();
modifyPhoneNo.setCreatedAt(date);
modifyPhoneNo.setUpdatedAt(date);
return modifyPhoneNo ;
}
}
package cn.quantgroup.xyqb.controller.modifyphoneno.req;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
/**
* Date: 2019/11/4
* Time: 上午11:37
*
* @author: yangrui
*/
@Data
public class Step2Req implements Serializable {
private static final long serialVersionUID = 3199682428349201451L;
/**
*
*/
@NotNull(message = "id不能为空")
private Long id;
/**
* 身份证正面
*/
@ApiModelProperty("身份证正面")
@NotBlank(message = "身份证正面不能为空")
@Length(max = 256, message = "身份证正面图片地址最长256字符")
private String idCardFaceUrl;
/**
* 身份证背面
*/
@ApiModelProperty("身份证背面")
@NotBlank(message = "身份证背面不能为空")
@Length(max = 256, message = "身份证背面图片地址最长256字符")
private String idCardRearUrl;
/**
* 本人手持身份证照片
*/
@ApiModelProperty("本人手持身份证照片")
@NotBlank(message = "本人手持身份证照片不能为空")
@Length(max = 256, message = "本人手持身份证照片地址最长256字符")
private String idCardHoldUrl;
}
/**
* modify phone_no req
*/
package cn.quantgroup.xyqb.controller.modifyphoneno.req;
package cn.quantgroup.xyqb.controller.modifyphoneno.resp;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
/**
* Date: 2019/11/4
* Time: 上午11:46
*
* @author: yangrui
*/
@Data
public class ModifyPhoneNoResp {
private Long id;
@ApiModelProperty("申请编号")
private String applyNo;
@ApiModelProperty("申请人姓名")
private String name;
@ApiModelProperty("申请人身份证号")
private String idCard;
@ApiModelProperty("申请人原手机号")
private String prevPhoneNo;
@ApiModelProperty("申请人现手机号")
private String curPhoneNo;
@ApiModelProperty("申请时间")
private Date createdAt;
@ApiModelProperty("申请结果")
private Integer applyStatus;
@ApiModelProperty("拒绝原因")
private String applyStatusReason;
@ApiModelProperty("处理状态")
private Integer processStatus;
@ApiModelProperty("最近更新时间")
private Date updatedAt;
@ApiModelProperty("身份证正面")
private String idCardFaceUrl;
@ApiModelProperty("身份证背面")
private String idCardRearUrl;
@ApiModelProperty("本人手持身份证照片")
private String idCardHoldUrl;
}
package cn.quantgroup.xyqb.controller.modifyphoneno.resp;
import lombok.Data;
/**
* 用户手机号修改进度
* <p>
* Date: 2019/11/11
* Time: 下午4:34
*
* @author: yangrui
*/
@Data
public class ProgressResp {
/**
* 进度
* 0初始化(用户未填写过申请单或提交过的申请单均已处理完成且反馈);
* 1已填写基本信息,未上传身份证图片;
* 2已填写基本信息,且已上传身份证图片
*/
private Integer progress;
/**
* 当progress = 1时,该值不为空,为api step2需要的id。
*/
private Long id;
}
/**
* modify phone_no resp
*/
package cn.quantgroup.xyqb.controller.modifyphoneno.resp;
/**
* base api && global exception handler
*/
package cn.quantgroup.xyqb.controller;
package cn.quantgroup.xyqb.controller.req;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.hibernate.validator.constraints.Range;
/**
* common page
* <p>
* Date: 2019/11/5
* Time: 下午4:06
*
* @author: yangrui
*/
@Data
public class Page {
/**
* page
*/
@ApiModelProperty("one-based page index")
private Integer page = 1;
/**
* size
*/
@ApiModelProperty("the size of the page to be returned")
@Range(min = 1, max = 1000, message = "size参数必须在1到1000之间")
private Integer size = 30;
}
package cn.quantgroup.xyqb.entity;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import javax.persistence.*;
import java.io.Serializable;
import java.util.Date;
/**
* Date: 2019/11/4
* Time: 下午2:15
*
* @author: yangrui
*/
@Data
@Entity
@Table(name = "user_modify_phone_no")
public class ModifyPhoneNo implements Serializable {
private static final long serialVersionUID = -7797532159380593454L;
/**
* id
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
/**
* user.id
*/
@Column(name = "user_id")
private Long userId;
/**
* 注册人真实姓名
*/
private String name;
/**
* 注册人身份证件号
*/
@Column(name = "id_card")
private String idCard;
/**
* 原手机号码
*/
@Column(name = "prev_phone_no")
private String prevPhoneNo;
/**
* 新手机号码
*/
@Column(name = "cur_phone_no")
private String curPhoneNo;
/**
* 身份证正面
*/
@Column(name = "id_card_face_url")
private String idCardFaceUrl;
/**
* 身份证背面
*/
@Column(name = "id_card_rear_url")
private String idCardRearUrl;
/**
* 本人手持身份证照片
*/
@Column(name = "id_card_hold_url")
private String idCardHoldUrl;
/**
* 申请状态 0处理中; 1修改完成; 2不允许修改;
*/
@Column(name = "apply_status")
private Integer applyStatus;
/**
* 申请状态补充原因
*/
@Column(name = "apply_status_reason")
private String applyStatusReason;
/**
* 处理状态 0待人工处理 1待用户反馈结果 2已反馈
*/
@Column(name = "processing_status")
private Integer processingStatus;
/**
*
*/
@Column(name = "created_at")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date createdAt;
/**
*
*/
@Column(name = "updated_at")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date updatedAt;
}
/**
* <p>@Entity</p>
*/
package cn.quantgroup.xyqb.entity;
package cn.quantgroup.xyqb.event;
import cn.quantgroup.xyqb.entity.User;
import cn.quantgroup.xyqb.model.session.SessionStruct;
import cn.quantgroup.xyqb.model.session.SessionValue;
import cn.quantgroup.xyqb.service.session.ISessionService;
import cn.quantgroup.xyqb.service.user.IUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
import java.util.List;
@Slf4j
@Component
public class CacheEvictPhoneNoUpdateEventListener implements ApplicationListener<PhoneNoUpdateEvent> {
@Autowired
private IUserService userService;
@Autowired
private ISessionService sessionService;
@Override
public void onApplicationEvent(PhoneNoUpdateEvent event) {
String oldPhoneNo = event.getOldPhoneNo();
User user = event.getUser();
List<SessionStruct> sessionStructList = sessionService.findByUserId(user.getId());
for (SessionStruct sessionStruct : sessionStructList) {
SessionValue values = sessionStruct.getValues();
User sessionUser = values.getUser();
//修改 session 中的手机号
sessionUser.setPhoneNo(user.getPhoneNo());
values.setUser(sessionUser);
}
sessionService.persistSession(sessionStructList);
userService.userCacheEvict(user.getUuid(), oldPhoneNo);
}
}
package cn.quantgroup.xyqb.event;
import cn.quantgroup.xyqb.entity.User;
import lombok.Getter;
import org.springframework.context.ApplicationEvent;
/**
* @author 徐小光
*/
@Getter
public class PhoneNoUpdateEvent extends ApplicationEvent {
private User user;
private String oldPhoneNo;
/**
* Create a new ApplicationEvent.
*
* @param source the object on which the event initially occurred (never {@code null})
*/
public PhoneNoUpdateEvent(Object source) {
super(source);
}
public PhoneNoUpdateEvent(Object source, User user, String oldPhoneNo) {
super(source);
this.user = user;
this.oldPhoneNo = oldPhoneNo;
}
}
......@@ -5,7 +5,6 @@ import cn.quantgroup.xyqb.entity.UserHashMapping;
import cn.quantgroup.xyqb.repository.IUserHashMappingRepository;
import cn.quantgroup.xyqb.util.encrypt.Md5Util;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
......@@ -16,14 +15,13 @@ import javax.persistence.PersistenceException;
/**
* userDetail 更新时, 保存 hashMapping 数据
*/
@Component
@Slf4j
@Component
public class UserDetailHashEventListener implements ApplicationListener<UserDetailUpdateEvent> {
@Autowired
private IUserHashMappingRepository userHashMappingRepository;
@Override
public void onApplicationEvent(UserDetailUpdateEvent event) {
UserDetail userDetail = event.getUserDetail();
......@@ -33,27 +31,19 @@ public class UserDetailHashEventListener implements ApplicationListener<UserDeta
//如果不存在, 我先保存一下
if (userHashMapping == null) {
userHashMapping = new UserHashMapping(userDetail.getUserId());
String phoneNo = userDetail.getPhoneNo();
userHashMapping.setPhoneNoMd5(Md5Util.build(phoneNo));
}
String idNoMd5 = userHashMapping.getIdNoMd5();
//可能存在手机号修改. 搞一下.
String phoneNo = userDetail.getPhoneNo();
userHashMapping.setPhoneNoMd5(Md5Util.build(phoneNo));
//可能存在身份证号修改. 搞一下.
String idNo = userDetail.getIdNo().toUpperCase();
String idNoMd5New = Md5Util.build(idNo);
//新旧相同, 直接跳过
if (StringUtils.equals(idNoMd5, idNoMd5New)) {
return;
}
userHashMapping.setIdNoMd5(idNoMd5New);
userHashMapping.setIdNoMd5(Md5Util.build(idNo));
try {
userHashMappingRepository.save(userHashMapping);
} catch (PersistenceException e){
} catch (PersistenceException e) {
log.error("保存userHashMapping重复, 无需再保存, userId:{}", userDetail.getUserId());
}
}
}
package cn.quantgroup.xyqb.event;
import cn.quantgroup.xyqb.entity.User;
import cn.quantgroup.xyqb.entity.WechatUserInfo;
import cn.quantgroup.xyqb.service.wechat.IWechatService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
import java.util.Objects;
@Component
public class WechatPhoneNoUpdateEventListener implements ApplicationListener<PhoneNoUpdateEvent> {
@Autowired
private IWechatService wechatService;
@Override
public void onApplicationEvent(PhoneNoUpdateEvent event) {
String oldPhoneNo = event.getOldPhoneNo();
User user = event.getUser();
WechatUserInfo userInfo = wechatService.findWechatUserInfoByPhoneNo(oldPhoneNo);
if (Objects.isNull(userInfo)) {
return;
}
if (Objects.isNull(userInfo.getPhoneNo())) {
return;
}
userInfo.setPhoneNo(user.getPhoneNo());
wechatService.saveWechatUserInfo(userInfo);
}
}
package cn.quantgroup.xyqb.exception;
/**
* 数据异常
* <p>
* Date: 2019/11/5
* Time: 上午11:01
*
* @author: yangrui
*/
public class DataException extends RuntimeException {
public DataException(String message) {
super(message);
}
}
......@@ -76,6 +76,14 @@ public class JsonResult<T> implements Serializable {
return new JsonResult(msg, SUCCESS_CODE, null, SUCCESS_BUSSINESS_CODE);
}
public static<T> JsonResult<T> buildSuccessResult() {
return new JsonResult<>(null, SUCCESS_CODE, null, SUCCESS_BUSSINESS_CODE);
}
public static<T> JsonResult<T> buildSuccessResultGeneric(T data) {
return new JsonResult<>(null, SUCCESS_CODE, data, SUCCESS_BUSSINESS_CODE);
}
/**
* 构造状态不正确的JsonResult
*
......
package cn.quantgroup.xyqb.model;
import lombok.Getter;
/**
* 用户手机号修改申请状态
* <p>
* Date: 2019/11/5
* Time: 下午2:11
*
* @author: yangrui
*/
@Getter
public enum ModifyPhoneNoApplyStatusEnum {
/**
* 处理中
*/
INIT("处理中"),
/**
* 修改完成
*/
DONE("修改完成"),
/**
* 不允许修改
*/
NO_ALLOW("不允许修改");
/**
* 描述
*/
private String desc;
ModifyPhoneNoApplyStatusEnum(String desc) {
this.desc = desc;
}
}
package cn.quantgroup.xyqb.model;
import lombok.Getter;
/**
* 用户手机号修改处理状态
* <p>
* Date: 2019/11/5
* Time: 下午2:11
*
* @author: yangrui
*/
@Getter
public enum ModifyPhoneNoProcessingStatusEnum {
/**
* 待人工处理
*/
INIT("待人工处理"),
/**
* 待用户反馈
*/
WAIT_4_USER_FEEDBACK("待用户反馈"),
/**
* 已反馈
*/
DONE("已反馈");
/**
* 描述
*/
private String desc;
ModifyPhoneNoProcessingStatusEnum(String desc) {
this.desc = desc;
}
}
/**
* Base package for model.
*/
package cn.quantgroup.xyqb.model;
package cn.quantgroup.xyqb.repository;
import cn.quantgroup.xyqb.entity.ModifyPhoneNo;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
public interface IModifyPhoneNoRepository extends JpaRepository<ModifyPhoneNo, Long>, JpaSpecificationExecutor<ModifyPhoneNo> {
@Query(nativeQuery = true, value = "select * from user_modify_phone_no " +
"where user_id = :userId and (apply_status is null or apply_status = :applyStatus)" +
"limit 1")
ModifyPhoneNo findFirstByUserIdAndApplyStatus(@Param("userId") Long userId,
@Param("applyStatus") Integer applyStatus);
}
......@@ -52,8 +52,9 @@ public interface IUserDetailRepository extends JpaRepository<UserDetail, Long>,
/**
* 按照身份证号和手机号 - 模糊查询
*
* @param phoneNo - 手机号
* @param idNo - 身份证号
* @param idNo - 身份证号
* @return List<UserDetail>
*/
@Query(value = "select * from user_detail where phone_no like ?1 and id_no like ?2", nativeQuery = true)
......@@ -65,4 +66,5 @@ public interface IUserDetailRepository extends JpaRepository<UserDetail, Long>,
List<UserDetail> findByIdBetween(Long id, Long endId);
UserDetail findByUserIdAndPhoneNoAndNameAndIdNo(Long userId, String phoneNo, String name, String idNo);
}
/**
* Base package for repository.
*/
package cn.quantgroup.xyqb.repository;
/**
* Base package for service.
*/
package cn.quantgroup.xyqb.service;
......@@ -6,6 +6,8 @@ import cn.quantgroup.xyqb.model.LoginProperties;
import cn.quantgroup.xyqb.model.session.SessionStruct;
import cn.quantgroup.xyqb.model.session.SessionValue;
import java.util.List;
/**
* Created by 11 on 2016/12/28.
*/
......@@ -29,4 +31,9 @@ public interface ISessionService {
void deleteByUserId(long userId);
void deleteUserCatch(User user);
List<SessionStruct> findByUserId(long userId);
void persistSession(List<SessionStruct> sessionStructList);
}
......@@ -9,14 +9,12 @@ import cn.quantgroup.xyqb.model.session.SessionStruct;
import cn.quantgroup.xyqb.model.session.SessionValue;
import cn.quantgroup.xyqb.service.session.ISessionService;
import cn.quantgroup.xyqb.service.session.aspect.UserBtRegisterFill;
import cn.quantgroup.xyqb.service.user.IUserService;
import cn.quantgroup.xyqb.util.MqUtils;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Caching;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
......@@ -35,10 +33,6 @@ public class SessionServiceImpl implements ISessionService {
@Resource
private RedisTemplate<String, String> stringRedisTemplate;
@Resource
private IUserService userService;
@Resource
private ApplicationEventPublisher applicationEventPublisher;
/**
* 更新session
......@@ -101,7 +95,8 @@ public class SessionServiceImpl implements ISessionService {
/**
* 每个渠道都有不同的 session
* @param userId 用户 ID
*
* @param userId 用户 ID
* @param properties baitiao/xyqb/vcc ... + 用户注册来源
* @return redisKey. 用来标识这个渠道的用户 Session 是否存在
*/
......@@ -227,6 +222,31 @@ public class SessionServiceImpl implements ISessionService {
}
@Override
public List<SessionStruct> findByUserId(long userId) {
List<SessionStruct> sessionStructList = new ArrayList<>();
String setKey = getUserSessionSetKey(userId);
Set<String> userIdKeys = stringRedisTemplate.opsForSet().members(setKey);
if (CollectionUtils.isEmpty(userIdKeys)) {
return sessionStructList;
}
for (String userIdKey : userIdKeys) {
String sessionId = stringRedisTemplate.opsForValue().get(userIdKey);
SessionStruct sessionStruct = findSessionBySessionId(sessionId);
sessionStructList.add(sessionStruct);
}
return sessionStructList;
}
@Override
public void persistSession(List<SessionStruct> sessionStructList) {
for (SessionStruct sessionStruct : sessionStructList) {
String sid = sessionStruct.getSid();
SessionValue values = sessionStruct.getValues();
persistSession(sid, values);
}
}
/**
* 获取用户的会话缓存Set的Redis-Key
*
......
package cn.quantgroup.xyqb.service.user;
import cn.quantgroup.xyqb.controller.modifyphoneno.req.AuditReq;
import cn.quantgroup.xyqb.controller.modifyphoneno.req.ModifyPhoneNoQueryReq;
import cn.quantgroup.xyqb.controller.modifyphoneno.req.Step1Req;
import cn.quantgroup.xyqb.controller.modifyphoneno.req.Step2Req;
import cn.quantgroup.xyqb.controller.modifyphoneno.resp.ProgressResp;
import cn.quantgroup.xyqb.entity.ModifyPhoneNo;
import org.springframework.data.domain.Page;
/**
* Date: 2019/11/4
* Time: 下午2:56
*
* @author: yangrui
*/
public interface IModifyPhoneNoService {
/**
*
*/
Long saveStep1(Long id, Step1Req step1Req);
/**
*
*/
void saveStep2(Step2Req step2Req);
ProgressResp progress(Long id);
Page<ModifyPhoneNo> list(ModifyPhoneNoQueryReq modifyPhoneNoQueryReq);
void feedback(Long id);
void audit(AuditReq auditReq);
}
......@@ -52,4 +52,19 @@ public interface IUserService {
List<UserInfo> findUserInfosByPhones(List<String> phones);
User findByMd5(Integer type, String md5Value);
/**
* 修改手机号码.
* @param oldPhoneNo 旧手机号
* @param newPhoneNo 新手机号
* @return 返回用户信息
*/
User modifyPhoneNo(String oldPhoneNo, String newPhoneNo);
/**
* 删除这个用户相关缓存
* @param uuid 根据 uuid
* @param phoneNo 根据手机号
*/
void userCacheEvict(String uuid, String phoneNo);
}
......@@ -4,6 +4,8 @@ import cn.quantgroup.xyqb.Constants;
import cn.quantgroup.xyqb.entity.User;
import cn.quantgroup.xyqb.entity.UserDetail;
import cn.quantgroup.xyqb.entity.UserHashMapping;
import cn.quantgroup.xyqb.event.PhoneNoUpdateEvent;
import cn.quantgroup.xyqb.exception.DataException;
import cn.quantgroup.xyqb.model.UserInfo;
import cn.quantgroup.xyqb.repository.IUserHashMappingRepository;
import cn.quantgroup.xyqb.repository.IUserRepository;
......@@ -19,14 +21,18 @@ import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import java.sql.Timestamp;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
/**
......@@ -47,6 +53,9 @@ public class UserServiceImpl implements IUserService {
@Autowired
private IUserDetailService userDetailService;
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
@Override
@Cacheable(value = "usercache", key = "'xyqbuser' + #phone", unless = "#result == null", cacheManager = "cacheManager")
......@@ -139,7 +148,6 @@ public class UserServiceImpl implements IUserService {
user = userRepository.save(user);
stringRedisTemplate.expire("usercache:xyqbuser" + phoneNo, 1L, TimeUnit.MILLISECONDS);
return PasswordUtil.validatePassword(password, user.getPassword());
}
@Override
......@@ -200,4 +208,43 @@ public class UserServiceImpl implements IUserService {
return userRepository.findById(userId);
}
@Override
@Transactional(rollbackFor = Exception.class)
public User modifyPhoneNo(String oldPhoneNo, String newPhoneNo) {
//1. 判断新手机号是否存在
User newPhoneUser = userRepository.findByPhoneNo(newPhoneNo);
if (Objects.nonNull(newPhoneUser)) {
//新手机号已存在
throw new DataException("信息填写有误,请重新填写。");
}
User oldPhoneUser = userRepository.findByPhoneNo(oldPhoneNo);
if (Objects.isNull(oldPhoneUser)) {
//这不是扯了.旧手机号不存在.
throw new DataException("信息填写有误,请重新填写。");
}
//2. 执行修改
//2.1 修改 user 表
oldPhoneUser.setPhoneNo(newPhoneNo);
User user = userRepository.saveAndFlush(oldPhoneUser);
//2.2 修改 user_detail 表
UserDetail oldPhoneUserDetail = userDetailService.findByPhoneNo(oldPhoneNo);
oldPhoneUserDetail.setPhoneNo(newPhoneNo);
userDetailService.saveUserDetail(oldPhoneUserDetail);
//3. 发送事件
PhoneNoUpdateEvent phoneNoUpdateEvent = new PhoneNoUpdateEvent(this, user, oldPhoneNo);
applicationEventPublisher.publishEvent(phoneNoUpdateEvent);
return oldPhoneUser;
}
@Override
@Caching(evict = {
@CacheEvict(value = "usercache", key = "'xyqbuser' + #phoneNo", cacheManager = "cacheManager"),
@CacheEvict(value = "usercache", key = "'xyqbuser' + #uuid", cacheManager = "cacheManager")
})
public void userCacheEvict(String uuid, String phoneNo) {
log.info("清理用户缓存成功,uuid:{},phoneNo:{}", uuid, phoneNo);
}
}
......@@ -22,7 +22,7 @@ public class XyqbSessionContextHolder {
private static final ThreadLocal<SessionStruct> threadSession = new ThreadLocal<>();
private static RedisTemplate<String, String> redisTemplate = null;
public static void setRedisTemplate(RedisTemplate<String, String> redisTemplate){
public static void setRedisTemplate(RedisTemplate<String, String> redisTemplate) {
XyqbSessionContextHolder.redisTemplate = redisTemplate;
}
......@@ -40,6 +40,7 @@ public class XyqbSessionContextHolder {
public static SessionStruct getXSessionFromRedis() {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String token = request.getHeader(Constants.X_AUTH_TOKEN);
log.info("getXSessionFromRedis token = 【{}】", token);
return getXSessionFromRedis(token);
}
......
package cn.quantgroup.xyqb.util;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.joda.time.Days;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import java.util.Calendar;
import java.util.Date;
/**
* Created by xuran on 2017/7/4.
* joda-time
* <p>
* Date: 2019/11/6
* Time: 上午11:18
*
* @author: yangrui
*/
public class DateUtils {
public static final String YMD_FORMAT = "yyyy-MM-dd";
public static final String YMD_HMS_FORMAT = "yyyy-MM-dd HH:mm:ss";
/**
* @param dateTimeStr
* @param formatStr
* @return
*/
public static Date strToDate(String dateTimeStr, String formatStr) {
DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(formatStr);
DateTime dateTime = dateTimeFormatter.parseDateTime(dateTimeStr);
return dateTime.toDate();
}
/**
* 计算当前时间到当天 23:59:59.999 时间差,
* 返回时间差(单位秒)
......
......@@ -17,7 +17,7 @@ import java.util.regex.Pattern;
@Slf4j
public class ValidationUtil {
private static final String phoneRegExp = "^1[3456789][0-9]{9}$";
public static final String phoneRegExp = "^1[3456789][0-9]{9}$";
private static final String chineseNameRegExp = "^[\u4e00-\u9fff]+((\\.|·)[\u4e00-\u9fff]+){0,2}$";
private static final String chineseNameExtendRegExp = "^[\u4dae\u4e00-\u9fff]+((\\.|·)[\u4dae\u4e00-\u9fff]+){0,2}$";
private static final String ipv4RegExp = "^((2[0-4][0-9]|25[0-5]|[01]?[0-9][0-9]?)\\.){3}(2[0-4][0-9]|25[0-5]|[01]?[0-9][0-9]?)$";
......
/**
* Base package for util.
*/
package cn.quantgroup.xyqb.util;
package cn.quantgroup.xyqb.validator;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author 徐小光
*/
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = IdCardValidator.class)
public @interface IdCard {
/**
* 出错提示
*
* @return
*/
String message() default "身份证号码错误";
/**
* 啥玩意?
*
* @return
*/
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
package cn.quantgroup.xyqb.validator;
import cn.quantgroup.xyqb.util.IdcardValidator;
import org.apache.commons.lang3.StringUtils;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
/**
* 身份证验证器, <IdCard,String> String 对应的是可以对哪些类型校验
*
* @author 徐小光
*/
public class IdCardValidator implements ConstraintValidator<IdCard, String> {
/**
* 这个方法在初始化的时候调用. 如果配置错了, 你可以抛一个异常. 启动就报错了
*
* @param constraint
*/
@Override
public void initialize(IdCard constraint) {
}
/**
* 这....true = 校验通过. false = 校验失败
*
* @param idCard 就是那个String
* @param context
* @return
*/
@Override
public boolean isValid(String idCard, ConstraintValidatorContext context) {
if (StringUtils.isNotBlank(idCard)) {
return IdcardValidator.isValidate18Idcard(idCard);
}
return true;
}
}
/**
* custom validator
*/
package cn.quantgroup.xyqb.validator;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment