淘东电商项目(16) -会员注册功能

引言

在上一节《淘东电商项目(15) -项目配置信息分类(Apollo Namespace命名空间)》,主要讲解如何在Apollo配置中心里分类配置信息,对项目的配置信息进一步优化。

代码已提交至Github(版本号:681c6a3d22861931f8d2958f04a27da27ccf2391),有兴趣的同学可以下载来看看:https://github.com/ylw-github/taodong-shop

本文进入主题,主要实现会员注册功能。通过本文的讲解,可以让大家熟练的使用Feign客户端。

本文目录结构:
l____引言
l____ 1. 会员注册流程图
l____ 2. 会员服务模块
l________ 2.1 会员数据库部分
l____________ 2.1.1 数据库表设计
l____________ 2.1.2 项目数据库配置
l________ 2.2 会员接口部分
l____________ 2.2.1 验证手机号接口(供微信服务使用)
l____________ 2.2.2 Feign远程调用注册码验证接口
l____________ 2.2.3 会员注册接口
l____ 3.微信服务模块
l________ 3.1 Feign远程调用手机号验证接口
l________ 3.2 微信获取注册码并验证手机号
l________ 3.3 验证注册码接口
l____ 4.测试
l________ 4.1 验证Swagger接口是否可用?
l________ 4.2 验证微信获取注册码功能
l________ 4.3 验证会员注册功能
l____总结

1. 会员注册流程图

在这里插入图片描述

2. 会员服务模块

2.1 会员数据库部分

微服务项目中,一般一个微服务对应一个数据库。所以会员服务要有会员数据库,微信要有微信数据库,现在在MySQL里面建立会员数据库。

直接在Navicat里选择连接,新建数据库“taodong-member”:
在这里插入图片描述
新建成功:
在这里插入图片描述

2.1.1 数据库表设计

数据库脚本:

DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `USER_ID` int(12) NOT NULL AUTO_INCREMENT COMMENT 'user_id',
  `MOBILE` varchar(11) NOT NULL COMMENT '手机号',
  `EMAIL` varchar(50) NOT NULL COMMENT '邮箱号',
  `PASSWORD` varchar(64) NOT NULL COMMENT '密码',
  `USER_NAME` varchar(50) DEFAULT NULL COMMENT '用户名',
  `SEX` tinyint(1) DEFAULT '0' COMMENT '性别  1男  2女',
  `AGE` tinyint(3) DEFAULT '0' COMMENT '年龄',
  `CREATE_TIME` timestamp NULL DEFAULT NULL COMMENT '注册时间',
  `IS_AVALIBLE` tinyint(1) DEFAULT '1' COMMENT '是否可用 1正常  2冻结',
  `PIC_IMG` varchar(255) DEFAULT NULL COMMENT '用户头像',
  `QQ_OPENID` varchar(50) DEFAULT NULL COMMENT 'QQ联合登陆id',
  `WX_OPENID` varchar(50) DEFAULT NULL COMMENT '微信公众号关注id',
  PRIMARY KEY (`USER_ID`),
  UNIQUE KEY `MOBILE_UNIQUE` (`MOBILE`),
  UNIQUE KEY `EMAIL_UNIQUE` (`EMAIL`)
) ENGINE=InnoDB AUTO_INCREMENT=25 DEFAULT CHARSET=utf8 COMMENT='用户会员表';

新建成功:
在这里插入图片描述

2.1.2 项目数据库配置

step1:定义实体类(含Swagger注释):

@Data
@ApiModel(value = "用户中注册")
public class UserEntity {

	/**
	 * userid
	 */
	@ApiModelProperty(value = "用户id")
	private Long userid;
	/**
	 * 手机号码
	 */
	@ApiModelProperty(value = "手机号码")
	private String mobile;
	/**
	 * 邮箱
	 */
	@ApiModelProperty(value = "邮箱")
	private String email;
	/**
	 * 密码
	 */
	@ApiModelProperty(value = "密码")
	private String password;
	/**
	 * 用户名称
	 */
	@ApiModelProperty(value = "用户名称")
	private String userName;
	/**
	 * 性别 0 男 1女
	 */
	@ApiModelProperty(value = "用户性别")
	private char sex;
	/**
	 * 年龄
	 */
	@ApiModelProperty(value = "用户年龄")
	private Long age;
	/**
	 * 注册时间
	 */
	@ApiModelProperty(value = "创建时间")
	private Date createTime;
	/**
	 * 修改时间
	 *
	 */
	@ApiModelProperty(value = "修改时间")
	private Date updateTime;
	/**
	 * 账号是否可以用 1 正常 0冻结
	 */
	@ApiModelProperty(value = "账号是否可以用 1 正常 0冻结")
	private char is_avalible;
	/**
	 * 用户头像
	 */
	@ApiModelProperty(value = " 用户头像")
	private String pic_img;
	/**
	 * 用户关联 QQ 开放ID
	 */
	@ApiModelProperty(value = "用户关联 QQ 开放ID")
	private Date qq_openid;
	/**
	 * 用户关联 微信 开放ID
	 */
	@ApiModelProperty(value = "用户关联 微信 开放ID")
	private Date WX_OPENID;
}

step2:会员项目定义Mapper,启动类记得添加@MapperScan(basePackages = "com.ylw.service.member.mapper")

/**
 * description: 用户mapper
 * create by: YangLinWei
 * create time: 2020/2/21 3:21 下午
 */
public interface UserMapper {

	@Insert("INSERT INTO `user` VALUES (null,#{mobile}, #{email}, #{password}, #{userName}, null, null, null, '1', null, null, null);")
	int register(UserEntity userEntity);

	@Select("SELECT * FROM user WHERE MOBILE=#{mobile};")
	UserEntity existMobile(@Param("mobile") String mobile);
}

step3:配置数据库相关信息:

spring:
  application:
    name: taodong-shop-service-member
  datasource:
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/taodong-member

2.2 会员接口部分

2.2.1 验证手机号接口(供微信服务使用)

step1:定义接口(注意Feign调用接口,使用的是POST请求

@Api(tags = "会员服务接口")
public interface MemberService {
    /**
     * 根据手机号码查询是否已经存在,如果存在返回当前用户信息
     *
     * @param mobile
     * @return
     */
    @ApiOperation(value = "根据手机号码查询是否已经存在")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "query", name = "mobile", dataType = "String", required = true, value = "用户手机号码"), })
    @PostMapping("/existMobile")
    BaseResponse<UserEntity> existMobile(@RequestParam("mobile") String mobile);
}

step2:接口实现:

@RestController
public class MemberServiceImpl extends BaseApiService<UserEntity> implements MemberService {

	@Autowired
	private UserMapper userMapper;

	@Override
	public BaseResponse<UserEntity> existMobile(String mobile) {
		// 1.验证参数
		if (StringUtils.isEmpty(mobile)) {
			return setResultError("手机号码不能为空!");
		}
		// 2.根据手机号码查询用户信息 单独定义code 表示是用户信息不存在把
		UserEntity userEntity = userMapper.existMobile(mobile);
		if (userEntity == null) {
			return setResultError(Constants.HTTP_RES_CODE_EXISTMOBILE_203, "用户信息不存在!");
		}
		// 对特殊铭感字段需要做脱敏
		userEntity.setPassword(null);
		return setResultSuccess(userEntity);
	}
}

2.2.2 Feign远程调用注册码验证接口

定义Feign接口:

@FeignClient("taodong-shop-service-weixin")
public interface VerificaCodeServiceFeign extends VerificaCodeService {

}

2.2.3 会员注册接口

step1:定义接口(注意请求参数RequestBody、RequestParam,接口需要填,Swagger用到

@Api(tags = "会员注册接口")
public interface MemberRegisterService {
    /**
     * 用户注册接口
     *
     * @param userEntity
     * @return
     */
    @PostMapping("/register")
    @ApiOperation(value = "会员用户注册信息接口")
    BaseResponse<JSONObject> register(@RequestBody UserEntity userEntity,
                                      @RequestParam("registCode") String registCode);

}

step2:接口实现(这里写了Feign远程调用微信服务“注册码验证”接口,先贴上代码,下面会讲到)

@RestController
public class MemberRegisterServiceImpl extends BaseApiService<JSONObject> implements MemberRegisterService {
	@Autowired
	private UserMapper userMapper;
	@Autowired
	private VerificaCodeServiceFeign verificaCodeServiceFeign;

	@Transactional
	public BaseResponse<JSONObject> register(@RequestBody UserEntity userEntity, String registCode) {
		// 1.参数验证
		String userName = userEntity.getUserName();
		if (StringUtils.isEmpty(userName)) {
			return setResultError("用户名称不能为空!");
		}
		String mobile = userEntity.getMobile();
		if (StringUtils.isEmpty(mobile)) {
			return setResultError("手机号码不能为空!");
		}
		String password = userEntity.getPassword();
		if (StringUtils.isEmpty(password)) {
			return setResultError("密码不能为空!");
		}
		// 2.验证码注册码是否正确 暂时省略 会员调用微信接口实现注册码验证
		BaseResponse<JSONObject> verificaWeixinCode = verificaCodeServiceFeign.verificaWeixinCode(mobile, registCode);
		if (!verificaWeixinCode.getCode().equals(Constants.HTTP_RES_CODE_200)) {
			return setResultError(verificaWeixinCode.getMsg());
		}
		// 3.对用户的密码进行加密 // MD5 可以解密 暴力破解
		String newPassword = MD5Util.MD5(password);
		userEntity.setPassword(newPassword);
		// 4.调用数据库插入数据
		return userMapper.register(userEntity) > 0 ? setResultSuccess("注册成功") : setResultError("注册失败!");
	}

}

3.微信服务模块

3.1 Feign远程调用手机号验证接口

定义会员Feign接口:

@FeignClient("taodong-shop-service-member")
public interface MemberServiceFeign extends MemberService {

}

3.2 微信获取注册码并验证手机号

@Component
public class MsgHandler extends AbstractHandler {

    /**
     * 发送验证码消息
     */
    @Value("${taodong.weixin.registration.code.message}")
    private String registrationCodeMessage;
    /**
     * 默认回复消息
     */
    @Value("${taodong.weixin.default.registration.code.message}")
    private String defaultRegistrationCodeMessage;
    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private MemberServiceFeign memberServiceFeign;

    @Override
    public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context, WxMpService weixinService,
                                    WxSessionManager sessionManager) {

        if (!wxMessage.getMsgType().equals(XmlMsgType.EVENT)) {
            // TODO 可以选择将消息保存到本地
        }

        // 当用户输入关键词如“你好”,“客服”等,并且有客服在线时,把消息转发给在线客服
        try {
            if (StringUtils.startsWithAny(wxMessage.getContent(), "你好", "客服")
                    && weixinService.getKefuService().kfOnlineList().getKfOnlineList().size() > 0) {
                return WxMpXmlOutMessage.TRANSFER_CUSTOMER_SERVICE().fromUser(wxMessage.getToUser())
                        .toUser(wxMessage.getFromUser()).build();
            }
        } catch (WxErrorException e) {
            e.printStackTrace();
        }
        // 1. 获取微信客户端发送的消息
        String fromContent = wxMessage.getContent();
        // 2.使用正则表达式验证消息是否为手机号码格式
        if (RegexUtils.checkMobile(fromContent)) {
            // 1.根据手机号码调用会员服务接口查询用户信息是否存在
            BaseResponse<UserEntity> reusltUserInfo = memberServiceFeign.existMobile(fromContent);
            if (reusltUserInfo.getCode().equals(Constants.HTTP_RES_CODE_200)) {
                return new TextBuilder().build("该手机号码" + fromContent + "已经存在!", wxMessage, weixinService);
            }
            if (!reusltUserInfo.getCode().equals(Constants.HTTP_RES_CODE_EXISTMOBILE_203)) {
                return new TextBuilder().build(reusltUserInfo.getMsg(), wxMessage, weixinService);
            }
            // 3.如果是手机号码格式的话,随机生产4位数字注册码
            int registCode = registCode();
            String content = registrationCodeMessage.format(registrationCodeMessage, registCode);
            // 将注册码存入在redis中 key为手机号码
            redisUtil.setString(Constants.WEIXINCODE_KEY + fromContent, registCode + "", Constants.WEIXINCODE_TIMEOUT);
            return new TextBuilder().build(content, wxMessage, weixinService);
        }
        // 否则情况下返回默认消息 调用第三方机器人接口
        return new TextBuilder().build(defaultRegistrationCodeMessage, wxMessage, weixinService);

    }

    // 获取注册码
    private int registCode() {
        int registCode = (int) (Math.random() * 9000 + 1000);
        return registCode;
    }

}

3.3 验证注册码接口

step1:定义接口

@Api(tags = "微信注册码验证码接口")
public interface VerificaCodeService {

    /**
     * 功能说明:根据手机号码验证码token是否正确
     *
     * @return
     */
    @ApiOperation(value = "根据手机号码验证码token是否正确")
    @PostMapping("/verificaWeixinCode")
    @ApiImplicitParams({
            // @ApiImplicitParam(paramType="header",name="name",dataType="String",required=true,value="用户的姓名",defaultValue="zhaojigang"),
            @ApiImplicitParam(paramType = "query", name = "phone", dataType = "String", required = true, value = "用户手机号码"),
            @ApiImplicitParam(paramType = "query", name = "weixinCode", dataType = "String", required = true, value = "微信注册码") })
    public BaseResponse<JSONObject> verificaWeixinCode(@RequestParam("phone") String phone,
                                                       @RequestParam("weixinCode") String weixinCode);
}

step2:实现接口

@RestController
public class VerificaCodeServiceImpl extends BaseApiService<JSONObject> implements VerificaCodeService {

	@Autowired
	private RedisUtil redisUtil;

	@Override
	public BaseResponse<JSONObject> verificaWeixinCode(String phone, String weixinCode) {
		// 1.验证码参数是否为空
		if (StringUtils.isEmpty(phone)) {
			return setResultError("手机号码不能为空!");
		}
		if (StringUtils.isEmpty(weixinCode)) {
			return setResultError("注册码不能为空!");
		}
		// 2.根据手机号码查询redis返回对应的注册码
		String weixinCodeKey = Constants.WEIXINCODE_KEY + phone;
		String redisCode = redisUtil.getString(weixinCodeKey);
		if (StringUtils.isEmpty(redisCode)) {
			return setResultError("注册码可能已经过期!!");
		}
		// 3.redis中的注册码与传递参数的weixinCode进行比对
		if (!redisCode.equals(weixinCode)) {
			return setResultError("注册码不正确");
		}
		// 移出对应验证码
		redisUtil.delKey(weixinCodeKey);
		return setResultSuccess("验证码比对正确");
	}

}

4.测试

测试前,依次启动「微信服务」、「会员服务」,启动成功后,打开注册中心,可以看到注册成功:
在这里插入图片描述
注意:启动前记得在AppWeiXin启动类添加“@EnableFeignClients”注解。

4.1 验证Swagger接口是否可用?

浏览器输入会员服务Swagger地址:http://localhost:8300/swagger-ui.html#,可以看到:
在这里插入图片描述
同时可以在里面接口进行请求:
在这里插入图片描述

同时微信服务的Swagger界面也可以进行操作,此处不再详述。

4.2 验证微信获取注册码功能

在这里插入图片描述

4.3 验证会员注册功能

Swagger请求:
在这里插入图片描述
注册成功:
在这里插入图片描述
数据库已经插入数据库:
在这里插入图片描述

总结

在这里插入图片描述

发布了2642 篇原创文章 · 获赞 5008 · 访问量 44万+

猜你喜欢

转载自blog.csdn.net/qq_20042935/article/details/104423787