文章目錄
涉及技術
- 前端:以HTML+CSS+JS爲基礎,以LayUI爲前端框架
- 後端:JAVA爲語言基礎,Springboot爲後端框架,整合Mybatis替代了傳統的JDBC模式,數據庫是用的Mysql,模板引擎使用了Springboot推薦的Thymeleaf
數據庫設計
項目的數據庫設計改動過幾次,以下是最終版本
表與表的關聯性還是很強的,這裏提一點,就是當一開始的測試數據不完整或者添加了太多不必要的數據時,可能涉及數據庫的數據全部刪除的再重添加,你可以選擇使用觸發器,或者使用以下的方式:
如果你是使用的SQLyog,那麼你可以打開SQLyog點擊上方的數據庫,然後點擊備份與導出,然後點擊“以SQL轉儲文件備份數據庫”,然後你可以在彈出框中選擇“僅結構”,即可導出數據庫的SQL腳本,再刪除數據庫,再運行腳本創建數據庫(個人學習記錄,哈哈)
前端搭建
前端的搭建我主要採用的是LayUI框架(好用!),這裏就不多說,大家可以去官網學習學習,裏面的各類組件都是直接可以使用的,官網鏈接如下:
LayUI框架
除此之外,注意些常見問題
注意在你的js最外面加上如下代碼
其他相關問題 --> LayUI社區
後端實現
項目使用的是MVC設計模式(這裏盜圖了,具體在哪裏看到的,忘記了,對不住了,兄弟),用戶通過點擊,發送請求,再通過控制層實現對持久層的調用,再交還數據給控制層,通過Thymeleaf模板嵌套至前端中,進行實時渲染,同時用戶得到響應
通過maven創建JavaWeb項目,解決導入依賴的jar造成的不必要的衝突,在框架上,我們使用了SpringBoot,簡化了繁瑣的配置,我們整合了MyBatis,通過xml配置對數據庫的數據進行映射,從而實現對數據庫的操控,使用Druid數據源,可以有效監控併合理分配對數據庫的連接
項目重點實現
搜索欄(模糊查詢)
首頁發佈請求遍歷
分類跳轉
雖然都是概要圖,但是我在最後配了項目百度雲,你可以對比代碼閱讀,理解更加深刻
項目難點
圖片是使用base64的格式存入數據庫中(每個任務可以配多張圖片)
like this:
每個任務除了可以配圖以外,還有其他很多屬性,比如任務時間,任務地點等(都可以在上面的數據庫設計中看到,對應task表),當我們想將任務配圖像其他屬性一樣(比如任務時間,任務地點等)在前端通過thymeleaf中的th:each遍歷出來時,才發現,任務配圖和任務不是一張表(也就是 "pictures"表和 "task"表是分開的,只是通過task_id這個外鍵關聯),所以在控制層中寫了一個模型工具類(ModelUtil)
package com.kuang.utils;
import lombok.Data;
import java.util.List;
/**
* @author HP
*/
@Data
public class ModelUtil<T> {
/**模型對象*/
private T obj;
/**模型對象中圖片base64數據集合 指上傳多張圖片*/
private List<String> base64;
}
然後在控制層中的代碼如下:
/**
* @return 首頁
*/
@RequestMapping(value = "/index", method = RequestMethod.GET, produces = "application/json;charset=utf-8")
public String toMainPage(Model model){
//導航欄 大小分類
int parent_sort_id = 0;
Collection<Sorts> sorts = indexService.getAll(parent_sort_id);
model.addAttribute("sorts",sorts);
Collection<Sorts> smallsorts = indexService.getAllSmallSorts(parent_sort_id);
model.addAttribute("smallsorts",smallsorts);
//發佈的任務
List<ModelUtil> m = new ArrayList<>();
List<Task> tasks = indexService.getTaskAll();
tasks.forEach(t -> {
ModelUtil<Task> modelUtil = new ModelUtil<>();
List<TaskPictures> taskPictures = indexService.getAllTaskPictures(t.getTaskId());
List<String> bases = new ArrayList<>();
taskPictures.forEach(p -> bases.add(new String(p.getTaskPicture(), StandardCharsets.UTF_8)));
modelUtil.setObj(t);
modelUtil.setBase64(bases);
m.add(modelUtil);
});
model.addAttribute("tasks", m);
return "fore/index";
}
其次是在前端中的thymeleaf:
遍歷效果如圖:
接口調用(人臉識別+語音識別+百度地圖API)
人臉識別
這一方面我在博客裏多次提到過了,具體實現大家可以去看看我的java學生管理系統(百度人臉識別 + Swing + mysql + 郵件發送 )
裏面涉及到了百度AI從註冊到對單個參數的提取,可以幫助你實現人臉識別,在這次項目中唯一不同的是,我是通過H5打開的攝像頭,而鏈接中因爲用的是Swing,所以我當時採用的是opencv打開攝像頭來實時監測人臉(它自帶算法可以監測到人臉),下面附上H5打開攝像頭的代碼和一張比較重要的關係圖
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>人臉識別</title>
</head>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<style>
#video {
position: absolute;
right: 0;
top: 0;
margin: -100px -240px auto;
}
#img {
position: absolute;
left: 0;
top: 0;
}
.auto {
position: absolute;
left: 50%;
top: 50%;
height: 320px;
margin-top: -160px;
}
button {
cursor: pointer;
margin: 0 auto;
border: 1px solid #f0f0f0;
background: #5CACEE;
color: #FFF;
width: 100px;
height: 36px;
line-height: 36px;
border-radius: 8px;
text-align: center;
/*禁止選擇*/
-webkit-touch-callout: none;
/* iOS Safari */
-webkit-user-select: none;
/* Chrome/Safari/Opera */
-khtml-user-select: none;
/* Konqueror */
-moz-user-select: none;
/* Firefox */
-ms-user-select: none;
/* Internet Explorer/Edge */
user-select: none;
/* Non-prefixed version, currently not supported by any browser */
}
</style>
<body background="/static/img/face_bg.png">
<div class="auto">
<video id="video" width="480" height="320" autoplay></video>
<canvas id="canvas" width="480" height="320" style="display: none;"></canvas>
<div>
<button id="capture" onclick="alert('請將正臉置於攝像頭前');" style="display: none;"></button>
</div>
</div>
<script>
var file, stream;
//訪問用戶媒體設備的兼容方法
function getUserMedia(constraints, success, error) {
if (navigator.mediaDevices.getUserMedia) {
//最新的標準API
navigator.mediaDevices.getUserMedia(constraints).then(success).catch(error);
} else if (navigator.webkitGetUserMedia) {
//webkit核心瀏覽器
navigator.webkitGetUserMedia(constraints, success, error)
} else if (navigator.mozGetUserMedia) {
//firfox瀏覽器
navigator.mozGetUserMedia(constraints, success, error);
} else if (navigator.getUserMedia) {
//舊版API
navigator.getUserMedia(constraints, success, error);
}
}
let video = document.getElementById('video');
let canvas = document.getElementById('canvas');
let context = canvas.getContext('2d');
function success(stream) {
//兼容webkit核心瀏覽器
let CompatibleURL = window.URL || window.webkitURL;
//將視頻流設置爲video元素的源
console.log(stream);
stream = stream;
//video.src = CompatibleURL.createObjectURL(stream);
video.srcObject = stream;
video.play();
}
function error(error) {
console.log(`訪問用戶媒體設備失敗${error.name}, ${error.message}`);
}
if (navigator.mediaDevices.getUserMedia || navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia) {
//調用用戶媒體設備, 訪問攝像頭
getUserMedia({ video: { width: 480, height: 320 } }, success, error);
} else {
alert('不支持訪問用戶媒體');
}
// 獲取base64圖片鏈接
document.getElementById('capture').addEventListener('click', function () {
context.drawImage(video, 0, 0, 480, 320);
// 獲取圖片base64鏈接
var image_js = canvas.toDataURL('image/png');
console.log(image_js);
//ajax傳輸圖片至後端
$.ajax({
async: false,
type: "post",
url: "/login/receiveData",
data: { 'base64': JSON.stringify(image_js) },
dataType: "json",
mimeType: "multipart/form-data",
success: function (data) {
alert(data.msg);
location.reload();
if (data.success) {
location.href = "/index";
$(".msg").stop(true, true).animate({
opacity: 1
}, 550, function () {
$(".msg").animate({
opacity: 0
}, 1500, function () {
location.href = "/index";
});
});
} else {
location.href = "/user/toLogin";
}
},
error: function (data) {
}
})
});
// 兩秒後模擬點擊
setTimeout(function () {
// IE
if (document.all) {
document.getElementById("capture").click();
}
// 其它瀏覽器
else {
var e = document.createEvent("MouseEvents");
e.initEvent("click", true, true);
document.getElementById("capture").dispatchEvent(e);
}
}, 2000);
</script>
</body>
</html>
展示效果:這是人臉註冊的,和人臉登錄頁面基本一樣(註冊成功後跳轉登錄頁,人臉登錄後跳首頁)
語音識別
其實語音播報這塊,我之前也發過博客,主要是利用語音播報(文字轉語音)和實時錄音並上傳百度AI(語音轉文字),就這兩項即可實現簡單的人機對話,大家可以移步至:
java+百度語音識別(語音助手)
而這次項目只是在首頁加了一個小按鈕,供用戶點擊,再將點擊事件通過ajax傳到後端的控制層,控制層再和持久層交互,這樣實現語音操作數據庫,當然你也可以加個工具類,通過語音播報的形式,實現人機對話(此時就有小夥伴要問了,你這不智能啊!還要手點,辣雞!這個嘛,後面還在學習語音喚醒,敬請期待)
以下是上圖頁面中關於語音助手的js(“Tiko”是上圖黃色機器人按鈕button的id,回調函數中主要是寫的跳轉到的對應分類頁面,也就是我上面說的調用持久層的給予用戶的響應)
總覽關係圖:(上傳百度AI平臺這塊和人臉識別步驟基本一致,你可以多看看人臉識別是如何實現的)
控制層的代碼涉及語音的比較廣,我後面直接給項目代碼,就不單獨寫出來了
百度地圖API
這塊調用我也只是小有涉及,不多但確實對我的項目有幫助,提升了用戶體驗
這塊確實不難,我就不細說了,具體參看它的官網 --> 百度地圖開放平臺官網示例
這些示例都很好用,基本拿來即用,只是有一點很不好就是在定位方面,百度地圖沒法讓我們準確定位到自己的位置,只能定位到城市級別的水平,我參看了其他幾個地圖開發平臺好像都是這樣
項目後續跟進(盡全力跟進,但不一定成,哈哈)
- 會加入語音喚醒,這樣就不用點擊按鈕
- 加入消息通知(應該是通過消息隊列)
- 設置會員等級,加入支付功能(支付即可成爲會員,你懂的)
- 美化UI
項目百度雲鏈接
分享給小夥伴們,希望能幫到你,共同努力!一起進步!(安裝方式和相關配置都在鏈接中)
鏈接:https://pan.baidu.com/s/1sCaZIzTnGDJwgCrZeVog3Q
提取碼:wbpe