17.2 设计分析
17.2.1 功能分析
测试用户期望功能如下:
- 测试用户可查看测试XML文档全部原始数据。
- 测试用户可模糊查询图书信息。
- 测试用户可更新图书信息。
图17.1 功能用例建模
17.2.2 核心业务数据流分析
功能模块中的核心业务是:模糊查询图书并定位一条图书记录,更新图书信息并保存。
核心业务流程描述如下:
- 模糊查询图书信息并向用户输出表格化格式数据。
- 用户选定要更新的某行图书信息。
- 用户编辑选定的图书信息。
- 用户提交新数据及更新前书号到后台。
- 在内存根据更新前书号定位原图书位置并将新数据覆盖旧数据。
- 保存新数据覆盖XML源文件。
图17.2 核心业务数据流
17.2.3 文件包及文件设计分析
基于三层架构视角的文件包及文件设计如表17.1所述。
序号 |
包设计 |
文件设计 |
1 |
设计表示层包(包名:UI),抽象表示层文件容器。 |
(1) 设计界面主页文件main.js。
(2) 主页引入jquery框架(jquery.js),辅助表示层处理。
|
2 |
设计业务逻辑层包(包名:server),抽象业务层文件容器。 |
(1) 设计通用业务逻辑服务文件server.js。
(2) 设计当前模块专用业务逻辑服务文件server_17.js。
(3) 引入xmldom模块(xmldom.js,外部安装获得)。
(4) 引入xpath模块(xpath.js,外部安装获得)。
|
3 |
设计数据层包(包名:data),抽象数据层文件容器。 |
(1) 设计XML数据存储文件books.xml。
(2) 设计XML数据备份文件books_bak.xml
|
表17.1 文件包及文件设计
图17.3 文件包设计及文件关系
17.2.4 XML文档创新改进
为了减轻前端设计压力,可对现有XML文档进行创新设计,考虑到更新数据必须要有数据编辑接口,而编辑接口的数目与XML文档中数据条目数量有关,具体到以上文档,实际编辑数据接口数目与<td>标记数量应该一致。
基于以上分析,可事先在以上给定XML文档中增加一行(即增加一个特定<tr>标记),该特定<tr>标记可事先嵌入HTML代码,目的是让应用程序读取这个特定<tr>即可得到编辑数据界面,这样可避免前端设计人员再去设计数据编辑界面。
需增加的特定<tr>标记设计如下:
<tr id="add_update_XML">
<td>
<input class="edit_data" style="color:blue" type="text"/>
</td>
<td>
<input class="edit_data" style="color:blue" type="text"/>
</td>
<td>
<input class="edit_data" style="color:blue" type="text"/>
</td>
<td>
<input type="button" value="提交新数据" onclick="post_new_XMLdata()"/>
<input type="button" value="取消" onclick="$('tr').hide()"/>
</td>
</tr>
另外,为了新旧数据对比,可在这个<tr>标记之前再增加一个tr标记,用于临时存放待更新的旧数据。
17.4 代码实现
17.4.1 主页实现
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<h2>标记数据更新测试</h2>
<hr />
请输入书名(不输入可查全部)<input type="text" id="title_input" />
<input type="button" value="模糊查询图书" onclick="select_TagData()" />
<hr style="margin-top:0;margin-bottom:0" />
<input type="button" value="恢复数据为初始状态" onclick="restore_TagData()" />
<hr style="margin-top:0;margin-bottom:0" />
<div id="outer_div">
</div>
</body>
</html>
<script type="text/javascript" src="jquery-1.11.1.js"></script>
<script type="text/javascript">
//基于XPath的标记数据模糊查询
function select_TagData() {
var book_name = $("#title_input").val();
var xhttp = new XMLHttpRequest();
xhttp.open("post", "http://localhost:1017/mhFind_17?param=" + encodeURIComponent(book_name), true);
xhttp.onreadystatechange = function () {
if (xhttp.status == 200 && xhttp.readyState == 4) {
$("#outer_div").html("<table border='1'>" + xhttp.responseText + "</table>");
$("#old_data").hide();
$("#add_update_XML").hide();
var trs = $("tr");
for (var k = 3; k < trs.length; k++) {
trs[k].innerHTML = trs[k].innerHTML + '<td><input type="button" value="更新" onclick="init_update(this)" /></td>';
}
}
}
xhttp.send();
}
//初始化更新
function init_update(cur_obj) {
var update_inputs = $("#add_update_XML").find("input");
var cur_tr_obj = $(cur_obj).parent().parent();
var cur_tds = cur_tr_obj.find("td");
for (var k = 0; k < cur_tds.length - 1; k++) {
document.getElementById("old_data").innerHTML += cur_tds[k].outerHTML;
update_inputs[k].value = cur_tds[k].innerHTML;
}
document.getElementById("old_data").innerHTML += "原有图书信息";
$("tr").hide();
$("#table_head").show(); $("#old_data").show(); $("#add_update_XML").show();
}
//提交新数据
function post_new_XMLdata() {
if (edited()) {
var update_inputs = $("#add_update_XML").find("input.edit_data");
var count = update_inputs.length;
var new_dataTr = "\n<tr>\n";
for (var k = 0; k < count; k++) {
new_dataTr += "<td>" + $(update_inputs[k]).val() + "</td>\n";
}
new_dataTr += "</tr>";
//提交新数据到后台
var old_isbn = $("#old_data").find("td")[0].innerHTML;
alert(old_isbn);
var xhttp = new XMLHttpRequest();
xhttp.open("post", "http://localhost:1017/postUpdate_17?param1=" + encodeURIComponent(old_isbn) + "¶m2=" + encodeURIComponent(new_dataTr), true);
xhttp.onreadystatechange = function () {
if (xhttp.status == 200 && xhttp.readyState == 4) {
alert(xhttp.responseText);
}
}
xhttp.send();
}
else { alert("数据未做任何更新,无需提交到后台!"); }
}
//判断数据是否已编辑
function edited() {
var update_inputs = $("#add_update_XML").find("input.edit_data");
var old_td = $("#old_data").find("td");
var re_value = false;
for (var k = 0; k < update_inputs.length; k++) {
if ($(update_inputs[k]).val() != old_td[k].innerHTML) { re_value = true; }
}
return re_value;
}
//恢复数据
function restore_TagData() {
if (confirm("确定要将数据恢复到最初状态?")) {
var xhttp = new XMLHttpRequest();
xhttp.open("post", "http://localhost:1017/restoreData_17", true);
xhttp.onreadystatechange = function () {
if (xhttp.status == 200 && xhttp.readyState == 4) {
alert(xhttp.responseText);
}
}
xhttp.send();
}
}
</script>
17.4.2 后台服务主程序实现
服务主程序server.js实现代码如下:
const http_obj = require("http");//用于产生服务对象
const fs_obj = require("fs");//读写后台文件
const url_tran_obj = require("url");//解释URL
const server = http_obj.createServer(function (request, response) {
//请求路径处理
var req_str = decodeURI(request.url);
var req_head;
var POS = req_str.indexOf("?");
if (POS == -1) { req_head = req_str; }
else { req_head = req_str.substring(0, POS); }
//设置查询对象
var url_obj = url_tran_obj.parse(req_str, true);
var Q_obj = url_obj.query;
//响应头设置
response.setHeader("Content-Type", "text/plain;charset=utf-8");
response.setHeader("Access-Control-Allow-Origin", "*");//实现跨域访问
response.writeHead(200);
//%%%%%%%%%%%%%-----------begin your code
//初始化所有服务对象
require("./17/server/server_17.js")(request, response, req_head, Q_obj, fs_obj);
//%%%%%%%%%%%%%------------end your code
});
server.listen(1017);
console.log("Server is running at port 1017...");
17.4.3 后台服务子模块程序实现
子模块程序server_16.实现代码如下:
function server_17(request, response, req_head, Q_obj, fs_obj) {
//%%%%%%%%%%%%%-----------begin your code
if (req_head == "/mhFind_17") {
//模糊查询
var b_title = Q_obj["param"];
var b_xpath = "/table/tr[position()<4] | /table/tr[contains(td[2],'" + b_title +"')]";
var select = require("./xpath");//获取查询函数
var dom = require("./xmldom").DOMParser;//获取DOMParser类
fs_obj.readFile("./17/data/books.xml", "utf-8", function (err, data) {
if (err) { response.end(err); }
else {
var xmldom = new dom().parseFromString(data);
var nodes = select(xmldom, b_xpath);
if (nodes.length == 2) { response.end("未找到相关数据!"); }
else { response.end(nodes.toString()); }
}
});
return;
}
//以下更新XML文档
if (req_head == "/postUpdate_17") {
var old_isbn = Q_obj["param1"];
var new_data = Q_obj["param2"];
fs_obj.readFile("./17/data/books.xml", "utf-8", function (err, data) {
if (err) { response.end(err); }
else {
var dom = require("./xmldom").DOMParser;
var XMLdom = new dom().parseFromString(data);
var tr_doms = XMLdom.getElementsByTagName("tr");//找到所有tr
var rec_order=-1;
for (var k = 3; k < tr_doms.length; k++) {
var temp_isbn = tr_doms[k].getElementsByTagName("td")[0].textContent;
if (temp_isbn == old_isbn) { rec_order = k; break; }
}
if (rec_order > 2) {
tr_doms[rec_order] = new dom().parseFromString(new_data);//tr对象替换
var new_XMLtext = '<?xml version="1.0" encoding="utf-8"?>\n';
new_XMLtext += '<table border="1" id="books">\n';
new_XMLtext += tr_doms.toString() + "\n</table>";
fs_obj.writeFile("./17/data/books.xml", new_XMLtext ,"utf-8", function (err) {
if (err) { response.end(err); }
else { response.end("成功更新数据"); }
});
} else { response.end("当前数据可能已被其他用户修改,更新失败!"); }
}
});
return;
}
//恢复数据
if (req_head == "/restoreData_17") {
fs_obj.readFile("./17/data/books_bak.xml", "utf-8", function (err, data) {
if (err) { response.end(err.toString()); }
else {
fs_obj.writeFile("./17/data/books.xml", data, function (err) {
if (err) { response.end(err.toString()); }
else { response.end("成功恢复数据"); }
});
}
});
return;
}
//%%%%%%%%%%%%%------------end your code
}
module.exports=server_17
17.4.4 实现效果
数据更新前模糊查询如图17.5、17.6所示。
图17.5 数据更新前实现效果
数据更新进行中如图17.6所示。
图17.6 数据更新进行中实现效果