Ch10-2 : Setting
[Setting]
這個小節,我們會根據上一個小節的資料表(Tables)設計,製做出對的網頁。為了畫面美觀,以及個人前端設計能力不足,無法設計出美觀畫面,我選擇別人免費上傳的HTML5的免費樣版與Form表單設計,再微調成我想要的畫面。若想要參閱其它相關網頁資料,可以參考Reference的網址查看。
這章節,需要大量引用到相關許多css與JavaScript,請參閱我GIT HUB上,Tag是Ch10的檔案內容,方便您參考與使用。
這段落會先介紹整個網站檔案相關擺放的目錄,後面會有幾個小段落,介紹我怎麼在組合這些東西,來完成註冊、認證、重發認證信的功能。
Resource
我們的免費樣版使用的是html5up.net上所提供的TXT樣式,下載回來的資源,裡面有css與js檔案,首先我們要把相關的資源,放到相關的目錄下,協助我們開發網頁。
參考 : https://github.com/loveu8/playMyBtaisMariaDB/tree/Ch10/public
public
└ stylesheets
└ font-awesome.min.css <-主要引用Google Fonts線上字元設定,若想要修改設定,可以參考Google Fonts
loginSignup.css <-註冊與表單的樣式檔
main.css <-全網共用的樣式檔,若是頁面引用進來後,畫面主要風格都會變成main.css所影響
normalize.css <-保持各種瀏覽器的一致性的樣式檔
└images
└highlight.png <-頁面上需要背景圖
overlay.png
└ javascripts
└jquery-3.1.0.min.js <-JQuery函式庫
jquery.dropotron.min.js <-下拉浮動選單
loginSignup.js <-註冊與登入JS檔案
main.js <-main.caa載入後,main.js協助畫面上的調整
skel-viewport.min.js <- Skel 模組
skel.min.js
util.js <-免費樣式有引用該JS函式,用途是前端呼叫後端程式(DWR),但之後不會使用到它
└ fonts
└ fontawesome-webfont.ttf <-字型檔
fontawesome-webfont.woff
fontawesome-webfont.woff2
Html
我會把檔案全部放在app/views/web/這個目錄下。至於網頁內容,會在後面三個小節才會詳細補充與說明。
參考 : https://github.com/loveu8/playMyBtaisMariaDB/tree/Ch10/app/views/web
app
└ views
└ web
headerLibs.scala.html <- Header區域,要引用的css與js檔案,我集中擺放成為一個頁面
headerNav.scala.html <- 上方Header的選單區域,因為會在許多頁面使用到,特別獨立出來
index.scala.html <- 我們網站的主要頁面
└ loginSignup
checkMemberAuth.scala.html <-檢查會員認證信的頁面
login.scala.html <-登入頁面(該章節尚未使用到)
loginSignupLibs.scala.html <-登入與注冊頁面,引用的資源
resendAuthEmail.scala.html <-重發認證信
signup.scala.html <-註冊頁面
signupOk.scala.html <-註冊成功頁面
app/controller,在這個目錄下新增WebController.java,來寫出我們網站需要的相關服務。
參考 : https://github.com/loveu8/playMyBtaisMariaDB/blob/Ch10/app/controllers/WebController.java
package controllers;
import java.util.HashMap;
import java.util.Map;
import javax.inject.Inject;
import play.Logger;
import play.data.FormFactory;
import play.libs.Json;
import play.mvc.Controller;
import play.mvc.Result;
import pojo.web.Member;
import pojo.web.MemberToken;
import pojo.web.MemberLoginStatus;
import pojo.web.MemberStatus;
import pojo.web.MemberTokenType;
import pojo.web.email.Email;
import pojo.web.signup.request.SignupRequest;
import pojo.web.signup.verific.VerificFormMessage;
import services.WebService;
import utils.signup.Utils_Signup;
import views.html.web.index;
import views.html.web.loginSignup.*;
import utils.mail.Utils_Email;
import utils.signup.*;
public class WebController extends Controller {
// 首頁
public Result index() {
return ok(index.render());
}
// 登入
public Result login() {
return ok(login.render());
}
// 註冊頁面
public Result signup() {
// 清除暫存錯誤訊息
// flash().clear();
return ok(signup.render());
}
@Inject
// 相依性注入Play的formFactory,可參考reference介紹
FormFactory formFactory;
@Inject
private WebService webService;
/**
* <pre>
* 進行註冊
*
* Step 1 : 取得表單註冊資料,若錯誤,回到註冊頁面,警告錯誤訊息。
* Step 2 : 進行表單驗證,是否正確。若錯誤,回到註冊頁面顯示錯誤訊息。
* Step 3 : 檢核通過,新增會員資料,且尚未認證。
* Step 4 : 註冊新增成功,新增認證連結資料。
* Step 5 : 新增會員記錄檔。
* Step 6 : 進行寄送認證信動作。
* Step 7 : 以上都順利完成,導入成功註冊頁面。
*
* </pre>
*/
public Result goToSignup() {
// 清除暫存錯誤訊息
flash().clear();
// Step 1
SignupRequest request = this.getSignupRequest();
if (request == null) {
flash().put("errorForm","註冊資料錯誤,請重新嘗試!!");
return ok(signup.render());
}
// Step 2
Map<String, VerificFormMessage> verificInfo = this.checkSingupRequest(request);
for (String key : verificInfo.keySet()) {
// 發現驗證沒過,放入錯誤訊息
if (!"200".equals(verificInfo.get(key).getStatus())) {
flash().put(key, verificInfo.get(key).getStatusDesc());
}
}
if(!flash().isEmpty()){
return ok(signup.render());
}
try {
// Step 3
int isSignupOk = webService.signupNewMember(request);
if(isSignupOk == 0){
flash().put("signupError", "註冊會員失敗,請重新註冊,謝謝。");
return ok(signup.render());
}
// Step 4
Utils_Signup utils_Signup = new Utils_Signup();
Member newMember = webService.findMemberByEmail(request.getEmail());
String signupAuthString = utils_Signup.genSignupAuthString(newMember.getEmail());
Map<String , String> memberToken = new HashMap<String , String>();
memberToken.put("memberNo", newMember.getMemberNo());
memberToken.put("tokenString", signupAuthString);
memberToken.put("type", MemberTokenType.Signup.toString());
int isSingAuthStringOk = webService.genSignupAuthData(memberToken);
// Step 5
Map<String , String> memberLoginData
= utils_Signup.genMemberLoginData(newMember.getMemberNo() ,
"PC" ,
request().remoteAddress() ,
MemberLoginStatus.S1.getStatus());
int isMemberLoginLogOk = webService.genMemberLoginLog(memberLoginData);
// Step 6
Utils_Email utils_Email = new Utils_Email();
Email email = utils_Email.genSinupAuthEmail(newMember, signupAuthString);
boolean isSeadMailOk = utils_Email.sendMail(email);
// Step 7
if(isSingAuthStringOk > 0 && isMemberLoginLogOk > 0 && isSeadMailOk ){
return ok(signupOk.render());
} else {
flash().put("signupError", "Opss...寄送認證信件發生錯誤,請使用重發認證信功能,完成認證動作,謝謝。");
return ok(signup.render());
}
} catch (Exception e) {
e.printStackTrace();
flash().put("signupError", "註冊會員失敗,請重新註冊,謝謝。");
return ok(signup.render());
}
}
// Step 1 : 取得註冊資訊請求
private SignupRequest getSignupRequest() {
SignupRequest request = null;
try {
request = formFactory.form(SignupRequest.class).bindFromRequest().get();
Logger.info("before , new member request data = " + Json.toJson(request));
} catch (Exception e) {
Logger.error("表單內容非註冊資訊,轉換類別錯誤,回傳空物件");
}
return request;
}
// Step 2 : 檢查註冊資訊
private Map<String, VerificFormMessage> checkSingupRequest(SignupRequest request) {
boolean isRegEmail = true;
boolean isUsedUsername = true;
try{
isRegEmail = webService.checkMemberByEmail(request.getEmail());
isUsedUsername = webService.checkMemberByUsername(request.getUsername());
} catch(Exception e){
e.printStackTrace();
}
Map<String, VerificFormMessage> verificInfo
= new Utils_Signup().checkSingupRequest(request , isRegEmail , isUsedUsername);
Logger.info("verificInfo = " + Json.toJson(verificInfo));
return verificInfo;
}
// test signupOk
public Result signupOk(){
return ok(signupOk.render());
}
/**
* <pre>
* 檢查註冊認證信連結
*
* Step1 : 檢查認證是否存在。
* Step2 : 檢查會員是否已經認證過。
* Step3 : 檢查連結是否使用過。
* Step4 : 檢查是否有逾期。
* Step5 : 檢查通過,開始更新與新增相關表單。
* Step5.1 : 更新會員認證表單
* Step5.2 : 更新會員表單
* Step5.3 : 新增會員紀錄表單
*
* </pre>
*/
public Result authMember(String auth){
// 清除暫存錯誤訊息
flash().clear();
// Step 1
MemberToken memberToken = null ;
try{
memberToken = webService.getMemberTokenData(auth , MemberTokenType.Signup.toString());
} catch(Exception e){
e.printStackTrace();
}
if(memberToken == null){
flash().put("authError", "認證連結有誤,請重新點選信中認證連結,或使用重發認證信,謝謝。");
play.Logger.warn("memberToken = " + Json.toJson(memberToken));
return ok(checkMemberAuth.render());
}
// Step 2
Member member = null;
try{
// 認證連結有資料,使用會員編號,查詢會員資料。
member = webService.findMemberByMemberNo(memberToken.getMemberNo());
} catch(Exception e){
e.printStackTrace();
flash().put("authError", "系統忙碌中,請稍後再次嘗試!");
return ok(checkMemberAuth.render());
}
if(!MemberStatus.S1.getStatus().equals(member.getStatus())){
flash().put("authError", "您的帳號,已經認證成功,不需再認證,謝謝。");
play.Logger.warn("member = " + Json.toJson(member));
return ok(checkMemberAuth.render());
}
// Step 3
boolean isUse = memberToken.getIsUse(); // 認證字串是否使用過
if (isUse){
flash().put("authError", "該連結已成功認證,不需要再次認證,謝謝。");
return ok(checkMemberAuth.render());
}
// Step 4
long dbTime = Long.parseLong(memberToken.getDbTime()); // 資料庫時間
long expiryDate = Long.parseLong(memberToken.getExpiryDate()); // 逾期時間
if(dbTime > expiryDate){
flash().put("authError", "認證時間已經逾期,請重新使用重發認證信功能謝謝。");
play.Logger.warn("dbTime = " + dbTime);
play.Logger.warn("expiryDate = " + expiryDate);
return ok(checkMemberAuth.render());
}
// Step 5
try{
int isUpdateMemberTokenOk = webService.updateMemberToken(member.getMemberNo());
int isUpdateMemberMainOk = webService.updateMemberToAuthOk(member.getMemberNo());
int isGenMemberChangeLogOk = webService.genMemberChangeLog(member);
play.Logger.info("isUpdateMemberAuthOk = " + isUpdateMemberTokenOk);
play.Logger.info("isUpdateMemberMainOk = " + isUpdateMemberMainOk);
play.Logger.info("isGenMemberChangeLogOk = " + isGenMemberChangeLogOk);
} catch(Exception e){
e.printStackTrace();
}
return ok(checkMemberAuth.render());
}
/**
* 重發認證信頁面
*/
public Result resendAuthEmail(){
return ok(resendAuthEmail.render());
}
/**
* <pre>
* 重發認證信檢查與寄送
*
* Step 1 : 檢查輸入的資料是否符合需要的格式
* Step 2 : 檢查輸入的信箱與使用名稱,是否有該會員資料
* Step 3 : 會員資料,檢查是否已認證
* Step 4 : 尚未認證,再次補寄送認證信
*
* </pre>
*/
public Result goToResendAuthEmail(){
// 清除暫存錯誤訊息
flash().clear();
SignupRequest request = this.getSignupRequest();
if(request==null){
flash().put("error", "輸入資料錯誤,請重新嘗試!!");
return ok(resendAuthEmail.render());
}
// Step 2
Member member = null;
String email = request.getEmail();
String username = request.getUsername();
try{
member = webService.findMemberByEmailAndUserName(email, username);
} catch(Exception e){
e.printStackTrace();
flash().put("error", "系統忙碌中,請稍候再嘗試,謝謝。");
return ok(resendAuthEmail.render());
}
// Step 2
if(member == null){
flash().put("error", "查無註冊資料,請確認資料是否填寫正確,謝謝。");
return ok(resendAuthEmail.render());
}
// Step 3
if(!MemberStatus.S1.getStatus().equals(member.getStatus())){
flash().put("error", "您的帳號,已經認證成功,不需再重新認證,謝謝。");
play.Logger.warn("member = " + Json.toJson(member));
return ok(resendAuthEmail.render());
}
// Step 4
try{
String signupAuthString = new Utils_Signup().genSignupAuthString(member.getEmail());
Map<String , String> memberToken = new HashMap<String , String>();
memberToken.put("memberNo", member.getMemberNo());
memberToken.put("tokenString", signupAuthString);
memberToken.put("type", MemberTokenType.Signup.toString());
int isSignupAuthStringOk = webService.genSignupAuthData(memberToken);
Utils_Email utils_Email = new Utils_Email();
Email authMail = utils_Email.genSinupAuthEmail(member, signupAuthString);
boolean isSeadMailOk = utils_Email.sendMail(authMail);
play.Logger.info("isSignupAuthStringOk = " + isSignupAuthStringOk +" , isSeadMailOk = " + isSeadMailOk);
} catch(Exception e){
e.printStackTrace();
}
flash().put("ok", "已重發認證信,請至註冊信箱收取認證信,謝謝。");
return ok(resendAuthEmail.render());
}
}
conf
寫好Webcontroller後,我們就需要在routes寫好對應的服務。
參考 : https://github.com/loveu8/playMyBtaisMariaDB/blob/Ch10/conf/routes
...
#------------------------------------
# chapter 10 - start
# http://127.0.0.1:9000/web/index (首頁)
GET /web/index controllers.WebController.index()
# http://127.0.0.1:9000/web/login (登入)
GET /web/login controllers.WebController.login()
# http://127.0.0.1:9000/web/signup (註冊申請)
GET /web/signup controllers.WebController.signup()
# http://127.0.0.1:9000/web/signupOk (註冊成功)
GET /web/signupOk controllers.WebController.signupOk()
# http://127.0.0.1:9000/web/signup (使用者註冊的檢查與申請)
POST /web/signup controllers.WebController.goToSignup()
# http://127.0.0.1:9000/web/authMember (認證信檢查)
GET /web/authMember controllers.WebController.authMember(auth : String)
# http://127.0.0.1:9000/web/resendAuthEmail (重發認證信頁面)
GET /web/resendAuthEmail controllers.WebController.resendAuthEmail()
# http://127.0.0.1:9000/web/resendAuthEmail (重發認證信檢查與寄送)
POST /web/resendAuthEmail controllers.WebController.goToResendAuthEmail()
# chapter 10 - end
#------------------------------------
[參考畫面]
電腦版畫面
手機版畫面
[Final]
以上是目前網站的基礎樣貌,接著三個小章節,註冊、認證、與重發認證信,才會詳細說明程式的怎麼運作的。讓我們繼續往前邁進吧!!
[Reference]
makes spiffy HTML5 site templates
https://html5up.net/
40+ Beautiful CSS Sign up & Registration Form
https://www.freshdesignweb.com/css-registration-form-templates/
來,讓我們談一談 Normalize.css
http://jerryzou.com/posts/aboutNormalizeCss/
skel
https://github.com/ajlkn/skel/blob/master/docs/skel-viewport.md
前端呼叫後端程式(DWR)
http://darwinfans.blogspot.tw/2009/04/dwr.html