引言
在上一节《淘东电商项目(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请求:
注册成功:
数据库已经插入数据库: