Easymall项目分布式拆分整合(七)
目录
1.创建一个quickstart的maven工程(src/main/resources)
一.Easymall分布式项目结构
二.用户搭建(最基础的六部)
1.创建一个quickstart的maven工程(src/main/resources)
2.pom继承parent,依赖common
3.后台user系统需要的其他依赖
<dependencies>
<dependency>
<groupId>cn..tedu</groupId>
<artifactId>springboot-common-easymall</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
4.application.properties文件
- 端口8093
- datasource
- mybatis
- redis
server.port=8093
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql:///easydb?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true
spring.datasource.password=root
spring.datasource.username=root
mybatis.configuration.mapUnderscoreToCamelCase=true
mybatis.mapperLocations=classpath:mapper/*.xml
mybatis.typeAliasesPackage=com.jt.common.pojo
redis.nodes=10.42.60.249:6379
redis.maxTotal=200
redis.maxIdle=8
redis.minIdle=3
5.启动类(MapperScan)
- 扫描接口mapper的包
- UserMapper(重新导入User的包名,common)
- UserMapper.xml(修改namespce对应新的接口全路径)
- 启动类
package com.jt;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.jt.user.mapper")
public class StarterUser {
public static void main(String[] args) {
SpringApplication.run(StarterUser.class, args);
}
}
6.nginx和hosts文件
1.nginx配置
#User服务器
server {
listen 80;
server_name user.easymall.com;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
location / {
proxy_pass http://127.0.0.1:8093;
proxy_connect_timeout 600;
proxy_read_timeout 600;
}
}
2.hoste文件配置
127.0.0.1 | user.easymall.com |
三.用户系统的功能
一.注册用户用户名校验
1.接口文件
请求地址 | user.easymall.com/user/checkUserName/{userName} |
请求方式 | GET |
请求参数 | 路径传参的String userName |
返回数据 | 存在返回1不可用,不存在返回0表示可用 |
2.UserController
public class UserController {
@Autowired
private UserService userService;
//接收前台的请求,返回数据库用户名可用还是不可用
@RequestMapping("checkUserName/{userName}")
public Integer checkUserName(@PathVariable String userName){
Integer exist=userService.checkUserName(userName);
return exist;
}
3.UserService
@Autowired
private UserMapper userMapper;
public Integer checkUserName(String userName) {
return userMapper.queryName(userName);
}
二.注册表单数据的提交
1.接口文件
请求地址 | user.easymall.com/user/save |
请求方式 | post |
请求参数 | User user对象接收,缺少的属性type,默认0,userId使用UUID解决 |
返回数据 | 1.SysResult的结构的数据,需要前端调用之后解析数据,可以满足返回数据的丰富性(既可以传递结果,也可以传递消息) 2.新增成功失败返回1/0 |
2.UserController
@RequestMapping("save")
public Integer saveUser(User user){
try{
userService.saveUser(user);
return 1;
}catch(Exception e){
e.printStackTrace();
return 0;
}
}
3.UserService
public void saveUser(User user) {
//user的id,type,password的加密
user.setUserId(UUID.randomUUID().toString());
user.setUserPassword(MD5Util.md5(user.getUserPassword()));
user.setUserType(0);
userMapper.insertUser(user);
}
三.实现注册方法与前台系统的整合
1.调用结构
2.前台的结构搭建
将easymall单体项目中的UserController(包含了与页面的交互逻辑),粘贴到web前端系统
3.导入一个UserController类
@Controller
public class UserController {
@Autowired
private UserService userService;
//ajax查询用户是否名称存在
@RequestMapping(value="user_ajax/checkUserName"
,method=RequestMethod.POST)
@ResponseBody
public SysResult checkUserName(String userName){
int exist=userService.queryName(userName);
//数据库如果存在,返回1,数据库如果不存在返回0
return SysResult.build(exist, "ok", null);
}
@RequestMapping(value="/user_ajax/regist"
,method=RequestMethod.POST)
@ResponseBody
public SysResult saveUser(User user){
try{
//业务层调用,插入表格数据
userService.saveUser(user);
return SysResult.build(1, null, null);
}catch(Exception e){
e.printStackTrace();
return SysResult.build(0, null, null);
}
}
3.创建一个UserService等待逻辑编写
1.用户名称校验
private static final String url="http://user.easymall.com/user/";
//注入httpClient
@Autowired
private HttpClientService client;
public int queryName(String userName) {
//按照接口文件进行调用和传参和数据的接收
String userUrl=url+"checkUserName/"+userName;
try{
String exist=client.doGet(userUrl);
return Integer.parseInt(exist);
}catch(Exception e){
e.printStackTrace();
return 0;
}
}
2.打开controller对应的交互逻辑代码注册user
编写service中saveUser方法
public void saveUser(User user) {
//按照注册接口调用user系统
String userUrl=url+"save";
try{
//生成参数
Map<String,Object> param=new HashMap<String,Object>();
param.put("userName", user.getUserName());
param.put("userPassword", user.getUserPassword());
param.put("userEmail", user.getUserEmail());
param.put("userNickname", user.getUserNickname());
client.doPost(userUrl,param);
}catch(Exception e){
e.printStackTrace();
}
}
四.用户的登录逻辑
用户登录逻辑,需要处理session共享问题,将用户信息,存储在redis中将key作为数据返回页面时,放到cookie中,只要cookie中key,后续访问逻辑www.easymall.com系统时,都会携带这个key,从而可以处理获取redis的数据使用.
一.接口方法,处理登录的校验
1.登录的用户系统校验逻辑
- 用户名密码的校验(where userName password是否存在数据)
- 如果不存在,按照接口返回不存在的数据
- 如果存在,将查询到的user作为整体数据存放到redis key值计算逻辑(公式:"EM_TICKET"+currentTime+userId),value值就是userJson字符串
- 将ticket的key值返回给前台
2.接口文件
请求地址 | user.easymall.com/user/login |
请求的参数 | User user对象接收,自动封装了userName和Password |
请求方式 | get/post |
返回数据 | 将存储在redis中的当前ticket的key值返回,存储到redis中的数据表示一个用户的登录状态(userJson),所以不能是永久数据,设置超时(做后续的逻辑,同一个用户最多登录一次) |
3.编写用户登录逻辑
1.UserController类
//验证登录的用户名密码是否正确
@RequestMapping("login")
public String doLogin(User user){
String ticket=userService.doLogin(user);
//成功登录返回redis的key,失败返回""
return ticket;
}
2.RedisCumUtils中添加一个超时方法
//封装一个带有超时逻辑的add方法
public void addOrUpdateExpire(String key,String value,Integer seconds){
ShardedJedis jedis = pool.getResource();
try{
jedis.setex(key, seconds, value);
}catch(Exception e){
//异常处理逻辑
}finally{
pool.returnBrokenResource(jedis);
}
}
3.UserService(redisUtil)类
@Autowired
private RedisCumUtils redis;
public String doLogin(User user) {
//查询一下数据库数据,是否存在userExist
//处理用户密码加密
user.setUserPassword(MD5Util.md5(user.getUserPassword()));
User exist=userMapper.selectExist(user);
try{
if(exist==null){//登录失败
return "";
}else{//表示成功,存储在redis返回key值
String ticket=MD5Util.md5("EM_TICKET"+System.currentTimeMillis()
+exist.getUserId());
//准备value值,mapper转化user为json字符串
String userJson=MapperUtils.MP.writeValueAsString(exist);
//set数据到redis供后续逻辑使用
redis.addOrUpdateExpire(ticket, userJson, 60*30);
//验证最多一个用户登录,顶替登录逻辑
//TODO
return ticket;
}
}catch(Exception e){
e.printStackTrace();
return "";
}
}
4.js页面发起ajax请求获取user登录的状态信息(携带cookie的值)
将jsp文件中head.jsp的js代码修改访问的域名,从原有的sso.jt.com换成user.jt.com
1.接口文件
请求地址 | user.easymall.com/user/query/{ticket} |
请求方式 | get |
请求参数 | redis中的key值,ticket路径接参 String callback |
返回数据 | 通过对callback的判断,实现返回数据的封装, if(callback==null)说明不是jsonp请求,返回syResult的json字符串 if(callback!=null)说明是jsonp请求,返回 callback(+sysResultJson+) sysResult的status==200表示成功,201表示失败 |
2.UserController
//校验登录状态,查询redis数据
@RequestMapping("query/{ticket}")
public String checkTicket(@PathVariable String ticket
,String callback){
try{
//走到redis校验数据
String userJson=userService.queryTicket(ticket);
//封装返回的json数据,SysResult
SysResult result=null;
if(StringUtils.isNotEmpty(userJson)){//登录状态正常
result=SysResult.build(200, "", userJson);
}else{
result=SysResult.build(201, "", null);
}
//将result解析成json等待返回使用
String resultJson=MapperUtils.MP.writeValueAsString(result);
//判断请求需要的数据格式,callback
if(callback==null){
return resultJson;//作为json字符串返回
}else{
return callback+"("+resultJson+")";//jsonp格式返回
}
}catch(Exception e){
return "";
}
}
3.UserService
public String queryTicket(String ticket) {
//TODO 超时时间延长--续租
return redis.query(ticket);
}