Easymall項目分佈式拆分整合(七)
一.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);
}