Biể đồ lớp
Biểu đồ trạng thái
Biểu đồ trạng thái lớp bệnh nhân
Hình 3.17. Biểu đồ trạng thái lớp bệnh nhân
Biểu đồ trạng thái lớp tài khoản
Trạng thái lớp bệnh nhân
Hình 3.19. Trạng thái lớp bệnh nhân
Trạng thái cuộc gọi điện
Biểu đồ thành phần
Hình 3.21. Biểu đồ thành phần Biểu đồ triển khai Biểu đồ triển khai
Hình 3.22. Biểu đồ triển khai
3.4.3. Thiết kế hệ thống:
Giao diện đăng nhập hệ thống
Hình 3.23. Giao diện đăng nhập hệ thống Giao diện bác sĩ Giao diện bác sĩ
Giao diện quản lý bệnh nhân
Giao diện quản lý hồ sơ bệnh án
Hình 3.25. Giao diện quản lý hồ sơ bệnh án
Giao diện hiển thị thông tin bệnh nhân
Giao diện thêm bệnh nhân
Hình 3.27. Giao diện thêm bệnh nhân Giao diện bệnh nhân Giao diện bệnh nhân
Giao diện chính
Giao diện hiển thị thơng tin cá nhân
Hình 3.29. Giao diện hiển thị thông tin cá nhân
Giao diện tư vấn từ xa
Giao diện giám đốc
Hình 3.31. Giao diện giám đốc
b) Thiết kế cơ sở dữ liệu
Cơ sở dữ liệu của hệ thống được thiết kế gồm có 5 Collection, gồm: Bac_si, Benh_nhan, Benh_an, Tin_nhan, Xet_nghiem. Mỗi Collection được nhúng thêm các Document nhỏ để tăng thêm khả năng lưu trữ dữ liệu. Do Giám đốc và quản lý có thơng tin trùng với bác sĩ nên sẽ được lưu chung tại Colletion Bac_si và được phân biệt qua Field ChucDanh. Dưới đây là thiết kế của các Colletion.
Colletion BacSi
Colletion BenhNhan
Hình 3.33. Colletion BenhNhan
Colletion BenhAn
Colletion TinNhan
Hình 3.35. Colletion TinNhan
Colletion XetNghiem
Hình 3.36. Colletion XetNghiem
c) Thiết kế các ràng buộc
Ràng buộc trong thiết kế hệ thống
Quy trình:
Định hướng, nghiên cứu tính khả thi Bản kế hoạch dự án Bản đặc tả yêu cầu hệ thống Phân tích, thiết kế hệ thống Xây dựng và kiểm thử hệ thống Công cụ phát triển: Chrome DevTools MongoDB Compass
d) Thiết kế kiến trúc hệ thống
Sử dụng mơ hình kiến trúc Model-View-Controller. Mơ hình này tách riêng phần biểu diễn và phần tương tác ra khỏi dữ liệu hệ thống. Hệ thống được cấu trúc hóa thành ba component logic tương tác với nhau:
+Model component: quản lý dữ liệu hệ thống và các thao tác trên dữ trên dữ liệu đó.
+View component: định nghĩa và quản lý cách dữ liệu được biểu diễn tới người dùng như thế nào.
+Controller component: Quản lý tương tác người dùng và chuyển các tương tác này tới View và Model.
3.5. Cài đặt (Implementation)
3.5.1. Cài đặt Modul:
Hệ thống hỗ trợ tương tác trong quá trình điều trị bệnh nhân từ xa được xây dựng dựa trên mơ hình MVC. Trong chương trình sử dụng các lớp sau:
Controller dùng để tiếp nhận những yêu cầu xử lý từ phía người dùng, nó gồm các function xử lý nhiều nghiệp vụ logic giúp lấy đúng dữ liệu thông tin cần thiết nhờ các nghiệp nghiệp vụ lớp Model cung cấp và hiển thị dữ liệu đó ra cho người dùng ở lớp View. Lớp controller là lớp trung gian để lớp Model và lớp View tương tác với nhau qua lớp Controller. Ví dụ như lớp “patientController”
const express = require('express'); const mongoose = require('mongoose'); const multer = require('multer'); const path = require('path'); const fs = require('fs');
const Patient = mongoose.model('Patient'); //
exports.procedureRequest = function(req, res, next){ res.render("patients/procedurerequest", {
title: 'procedurerequest' });
} //
exports.listPatients = function(req, res) { return Patient.find((err, docs) => { if (!err) {
res.render("patients/index", { title: 'List patients', list: docs,
patientSlideBarActive:true, });
} else {
console.log('Error in retrieving patients list :' + err); }
}); }
//
exports.searchPatients = function (req, res, next) { var q = req.query.q
Patient.find({ name: { $regex: q, $options: 'i' } }, { _id: 0, __v: 0 }, (err, docs) => {
if (!err) {
res.json(docs) } else {
}
}).limit(5); }
exports.searchResults = function (req, res, next) { var q = req.query.q
Patient.find({ name: { $regex: q, $options: 'i' } }, (err, docs) => { if (!err) { console.log(docs) res.render("patients/index", { title: 'patients', patientSlideBarActive: true, list: docs, }); } else {
console.log('Error in retrieving patients list :' + err); }
}); }
exports.editPatient = function(req, res, next) { Patient.findById(req.params.id, (err, doc) => { if (!err) {
console.log(doc)
res.render("patients/create_edit", { title: "Edit patients",
isEdit: true, patient: doc }); } }); }
exports.detailsPatient = function(req, res, next) { Patient.findById(req.params.id, (err, doc) => { if (!err) {
var date = new Date(doc.birthDate); var date1 = new Date(Date.now())
var age = parseInt(date1.getFullYear()) - parseInt(date.getFullYear());
console.log(age);
res.render("patients/details", { title: "Details Patient", isEdit: true,
patient: doc,
birthDate: date.getDate() + '/' + date.getMonth() + '/' + date.getFullYear(),
Age: age, });
}); }
const storageEngine = multer.diskStorage({ destination: './public/files',
filename: function(req, file, fn) {
fn(null, new Date().getTime().toString() + '- ' + file.fieldname + path.extname(file.originalname)); // console.log(file)
} });
const uploadPhoto = multer({ storage: storageEngine, limits: {
fileSize: 2000000 },
fileFilter: function(req, file, callback) { validateFile(file, callback);
}
}).single('photo');
var validateFile = function(file, cb) { allowedFileTypes = /jpeg|jpg|png|gif/;
const extension = allowedFileTypes.test(path.extname(file.originaln ame).toLowerCase());
const mimeType = allowedFileTypes.test(file.mimetype); if (extension && mimeType) {
return cb(null, true); } else {
cb("Invalid file type. Only JPEG, PNG and GIF file are allowed. ")
} }
exports.insertRecord = function(req, res, next) { uploadPhoto(req, res, (error) => {
if (error) {
console.log("Error 1"); } else {
var patient = new Patient(); patient.id = req.body.id; patient.name = req.body.name; patient.telecom = req.body.telecom; patient.birthDate = req.body.birthDate; patient.sex = req.body.sex; patient.job = req.body.job; patient.address = req.body.address; patient.account = [{ useruser: req.body.user, password: req.body.password, }]; if (req.file == undefined) { patient.photo = "files/donganh.png"
var fullPath = "files/" + req.file.filename; patient.photo = fullPath; } console.log(req.body) patient.save((err, doc) => { if (!err) { return res.redirect('/patients/list'); } else { if (err.name == 'ValidationError') { handleValidationError(err, req.body); console.log(req.body) res.render("patients/create_edit", { viewTitle: "Insert Patient", isEdit: false,
patient: req.body });
} else
console.log('Error during record insertion : ' + err); } }); } }); } //
exports.updateRecord = function updateRecord(req, res) { uploadPhoto(req, res, (error) => {
if (error) { console.log('Error 1'); } else { Patient.findOneAndUpdate({ _id: req.body._id }, req.body, { runValidators: true }, (err, doc) => { if (!err) { console.log(doc)
if (req.file == undefined) {} else {
var fullPath = "./public/" + doc.photo
if (fullPath != "./public/files/donganh.png") { fs.unlink(fullPath, (error) => { if (error) { console.error(error) return } })
var fullPath1 = "files/" + req.file.filename; doc.photo = fullPath1; } console.log(req.file) doc.communication[0].language =req.body.language doc.save(); res.redirect('/patients/list'); } else { if (err.name == 'ValidationError') { handleValidationError(err, req.body); res.render("/patients/create_edit", { viewTitle: 'Update Patient', isEdit: true,
patient: req.body });
} else
console.log('Error during record update : ' + e rr); } }); } }); }
exports.deleteRecord = function(req, res) {
Patient.findByIdAndRemove(req.params.id, (err, doc) => { if (!err) {
res.redirect('/patients/list');
var fullPath = "./public/" + doc.photo
if (fullPath != "./public/files/donganh.png") { fs.unlink(fullPath, (err) => { if (err) { console.error(err) return } }) } } else {
console.log('Error in patients delete :' + err); }
}); }
//
function handleValidationError(err, body) { for (field in err.errors) {
switch (err.errors[field].path) { case 'name':
body['nameError'] = err.errors[field].message; break;
break; }
} }
Lớp Model: Là nơi chứa những nghiệp vụ tương tác với hệ quản trị cơ sở dữ liệu (MongoDB ); Lớp model được định nghĩa thông qua giao diện Schema. Schema sẽ định nghĩa các trường được lưu trong mỗi document đi kèm với điều kiện xác thực, giá trị mặc định của nó. Schema sau đó được "biên dịch" thành mơ hình qua phương thức mongoose.model(). Một khi đã có mơ hình thì ta có thể dùng nó để tìm, thêm, sửa, và xố các đối tượng cùng kiểu. Dưới đây là ví dụ file patient.model.js
const mongoose = require('mongoose'); var patientSchema = new mongoose.Schema({ id: {
type: String },
name: {
type: String,
required: 'This field is required.' }, account:[{ user:{ type: String }, password:{ type: Boolean, }, }], telecom:{ type: Number }, sex:{ type: Boolean }, birthDate:{ type: Date }, job:{ type: String }, address:{ type: String } , }); patientSchema.path('telecom').validate((val) => { return /[0-9]{10,12}/.test(val); }, 'Invalid telecom'); mongoose.model('Patient', patientSchema);
Lớp Views: Là lớp chứa các file giao diện phía người dùng, nó là nơi chứa những giao diện như một nút bấm, khung nhập, menu, hình ảnh… nó đảm nhiệm nhiệm vụ hiển thị dữ liệu và giúp người dùng tương tác với hệ thống. Trong Views gồm 2 flile là file create_edit và file index. File create để thiết kế giao diện người dùng, gọi đến AIP lấy
dự liệu từ CSDL, file index là file giao diện người dùng sau khi sử dụng nút bấm. Sau đây là ví dụ lớp viewer của đối tượng bệnh nhân:
File “create_edit.hbs”
<div class="row">
<div class="col-md-12">
<div class="card card-primary">
<div class="card-header no-border">
<h3 class="card-title">Thêm bệnh nhân</h3> <div class="d-flex justify-content-between"> </div>
</div>
<form action="{{#if isEdit}}/patients/update{{else}}/patients/sto re{{/if}}" method="POST" autocomplete="off" enctype="multipart/form-data"> <div class="card-body">
<input type="hidden" name="_id" value="{{patient._id}}"> <div class="row">
<label class="col-md-2">Mã bệnh nhân</label> <div class="col-md-4">
<input name="id" class="form- control" value="{{patient.id}}"/>
</div>
<label class="col-md-2">Họ và tên</label> <div class="col-md-4">
<input name="name" class="form- control" value="{{patient.name}}"/>
</div> </div> <br>
<div class="row">
<label class="col-md-2">Tài khoản</label> </div>
<br>
<div class="row">
<label class="col-md-2">User</label> <div class="col-md-4">
<input type="textarea" name="user" class="form- control" value="{{patient.account.user}}" />
</div>
<label class="col-md-2">Password</label> <div class="col-md-4">
<input type="text" name="password" class="form- control" value="{{patient.account.password}}" />
</div> </div> <br>
<div class="row">
<label class="col-md-2">Số điện thoại</label> <div class="col-md-4">
<input name="telecom" class="form- control" value="{{patient.telecom}}"/>
</div>
<label class="col-md-2">Giới tính</label> <div class="col-md-4">
<select name="sex" class="form-control"> <option value="1">Male</option> <option value="2">Female</option> </select>
<br>
<div class="row">
<label class="col-md-2">Ngày sinh</label> <div class="col-md-4">
<input name="birthDate" class="form- control datepicker" value="{{patient.birthDate}}"/>
</div>
<label class="col-md-2">Nghề nghiệp</label> <div class="col-md-4">
<input name="job" class="form- control" value="{{patient.job}}"/>
</div> </div> <br>
<div class="row">
<label class="col-md-2">Địa chỉ</label> <div class="col-md-4">
<input name="address" class="form- control" value="{{patient.address}}"/> </div> </div> <br> <div class="card-footer"> <center><button class="btn btn- default">Submit</button></center> </div> </form> </div> </div> </div> <script> $(document).ready(function(){ $('.datepicker').datepicker(); }); </script> File “index.hbs” <div class="row"> <div class="col-md-12"> <div class="card"> <div class="card-header"> <div class="row"> <h3 class="card-title">Danh sách bệnh nhân</h3>
<a href="/patients/create" class="btn btn-primary btn- sm" style="margin-left: 10px;">Thêm mới</a>
</div>
<div class="card-tools"> <div class="row">
<form class="form-inline ml- 3" action="/patients/list/result" method="get">
<div class="input-group input-group-sm"> <input class="form-control float- right" style="background-
color:white;" id="search" name="q" type="search" placeholder="Search" aria- label="Search" autocomplete="off">
<div class="input-group-append"> <button class="btn btn-
navbar" type="submit">
<i class="fa fa-search"> </i>
</div>
<div class="input-group input-group-sm" id="search- results" style="position: absolute;width:203px;top:32px;">
</div> </form> </div> </div> </div> <div class="card-body">
<table class="table table-hover"> <thead> <tr> <th>#</th> <th>Tên</th> <th>Tuổi</th> <th>Địa chỉ</th> <th>Action</th> </tr> </thead> <tbody id="tbody_table_user"></tbody> </table> <div id="paginate"> </div> </div> </div> </div> </div> <script>
var sources = function () { var result = [];
{{#each list}}
var date = new Date("{{this.birthDate}}"); var date1 = new Date(Date.now())
var age = parseInt(date1.getFullYear()) - parseInt(date.getFullYear()); result.push({name:"{{this.name}}",age:age,id:"{{this._id}}",address :"{{this.address}}"}); {{/each}} //console.log(result) return result; }(); </script> <script src="/js/Search/searchPatients.js"></script> <script src = /js/pagination/paginationPatients.js></script>
Vì hệ thống phải quản lý nhiều thông tin của các đối tượng. Nên cần rất nhiều dòng lệnh route cho mỗi đối tượng. Trong bài xây dựng hệ thống thông tin này em phân chia các route này theo từng đối tượng. Mỗi đối tượng có một file route riêng để việc bảo trì sẽ dễ dàng hơn.file routes dùng để định tuyến, các client gửi yêu cầu lên server qua đường link được định tuyến ở routes, server phản hồi lại tới client. Sau đây là ví dụ về file “patient.js”
var express = require('express'); var router = express.Router();
var patient_controller = require('../controllers/patientController'); const auth = require('../middlewares/auth');
});
router.get('/create', auth, function(req, res, next){
res.render('patients/create_edit', { title: 'Create new patient', isEdit: false });
});
router.post('/store', function(req, res, next) { patient_controller.insertRecord(req, res, next); });
router.get('/delete/:id',function(req,res,next){ patient_controller.deleteRecord(req,res)
})
router.get('/details/:id', function(req, res, next){ patient_controller.detailsPatient(req,res,next); });
router.get('/edit/:id', auth, function(req, res, next){ patient_controller.editPatient(req, res, next);
});
router.post('/update', function(req, res, next) { patient_controller.updateRecord(req, res, next); });
router.get('/delete/:id', auth, function(req, res, next){ patient_controller.deleteRecord(req, res, next);
});
router.get('/procedurerequest', auth, function(req, res, next){ patient_controller.procedureRequest(req, res, next);
});
module.exports = router;
Public : ngoài ra hệ thống còn sử dụng folder Public chứa các file ảnh, video, để phía clinet nhìn được.
File “www” để tạo và thiết lập các thông số của server, Server tạo Socket, gán số hiệu cổng và lắng nghe yêu cầu nối kết.
File “app.js” để thiết lập cho folder “public” ở dạng static để phía clinet có thể truy cập đc, tạo ra link api truy cập tới các modle tương ứng
link truy cập tới api
File db: Để kết nối tới CSDL
Source code chi tiết xem tại đĩa CD đính kèm.
3.5.2. Chuyển giao cơ sở dữ liệu
Hệ cơ sở dữ liệu hiện tại của phòng khám nha khoa Như Ngọc hỗ trợ lưu trữ từ các phiếu chỉ định cho đến các phiếu thu chi của phòng khám. Trong quá trình xây dựng hệ thống hỗ trợ điều trị từ xa, nhận thấy một số Collection khơng cịn phù hợp hệ thống mới nên em đã tiến hành một số sự điều chỉnh sau:
Đầu tiên, ta xác định các Collection phù hợp có thể sử dụng lại để bảo tồn thơng tin dữ liệu của phòng khám, các Collection cần thay thế và điều chỉnh cấu trúc, loại bỏ các Collection dư thừa không phù hợp. Một số Collection bị loại bỏ là : Collection DichVu, Collection HoaDon, Collection ThuThuat.
Tiếp theo, đối với các Collection cần thay thế và điều chỉnh cấu trúc, ta tiến hành tạo mới Collection có cấu trúc ổn định và phù hợp hơn so với hệ thống.
Cuối cùng, chuyển dữ liệu của các Collection phù hợp từ hệ cơ sở dữ liệu hiện tại của phòng khám nha khoa Như Ngọc sang hệ cơ sở dữ liệu mới của hệ thống.
3.5.3. Kiếm thử hệ thống
Kiểm thử phần mềm là quá trình thực thi 1 chương trình với mục đích tìm ra lỗi. Kiểm thử phần mềm đảm bảo sản phẩm phần mềm đáp ứng chính xác, đầy đủ và đúng theo yêu cầu của khách hàng, yêu cầu của sản phẩm đề đã đặt ra. Kiểm thử phần mềm cũng cung cấp mục tiêu, cái nhìn độc lập về phần mềm, điều này cho phép việc đánh giá và hiểu rõ các rủi ro khi thực thi phần mềm.
Trong bài khóa luận này, em sử dụng kỹ thuật kiểm thử hộp đen để kiểm thử hệ thống. Kiểm thử hộp đen là một phương pháp kiểm thử phần mềm được thực hiện mà không biết được cấu tạo bên trong của phần mềm, là cách mà các tester kiểm tra xem hệ thống như một chiếc hộp đen, khơng có cách nào nhìn thấy bên trong của cái hộp.
Do giới hạn về thời gian và khóa luận nên em trình bày kiểm thử 1 chức năng chi tiết:
Chức năng thêm bệnh nhân được thực hiện bởi đối tượng bác sĩ.
STT Tên ca kiểm thử Dữ liệu đầu vào Thao tác NSD
Kết quả đầu ra Kết quả thực tế Đánh giá 1 Kiểm tra giao diện thêm mới bệnh nhân -Chọn chức năng quản lý bệnh nhân -Chọn thêm mới -Hiển thị danh sách bệnh nhân
-Sau khi chọn thêm mới hiển thị các trường nhập thông tin bệnh nhân -Cuối trang có nút “submit” để xác nhận thêm bệnh nhân -Hiển thị danh sách bệnh nhân -Sau khi chọn thêm mới hiển thị các trường nhập thông tin bệnh nhân -Cuối trang có nút “submit” để xác nhận thêm bệnh nhân Ca kiểm thử thành công 2 Kiểm tra tính -Khơng điền thơng
-Giữ ngun trang thêm bệnh nhân
-Chuyển trang danh sách bệnh
Ca kiểm
bắt buộc của thêm mới bénh nhân tin -Chọn “submit” -Thêm bệnh nhân không thành công nhân -Thêm bệnh nhân không thành công thử thất bại 3 Kiểm tra định dạng số điện thoại -Mã bệnh nhân: M01 -Họ và tên: Lê Thị Huyền -User: HuyenLT -Password: 12345 -Số điện thoại: abcdef -Giới tính: Nữ -Ngày sinh: 09/12/1998 -Nghề nghiệp: Sinh viên -Địa chỉ: Thanh Thủy, Phú Thọ -Chọn chức năng quản lý bệnh nhân -Chọn chức thêm mới -Điền thông tin dữ liệu đầu vào như cột bên -Chọn “submit”
-Giữ nguyên trang thêm bệnh nhân -Thêm bệnh nhân không thành công -Giữ nguyên trang thêm bệnh nhân -Thêm bệnh nhân không thành công Ca kiểm thử thành công 4 Kiểm tra độ dài số điện thoại -Mã bệnh nhân: M02 -Họ và tên: Nguyễn Thị Huyền -User: HuyenNT -Password: 12345 -Số điện thoại: 111111 -Giới tính: Nữ -Ngày sinh: 09/12/1998 -Nghề nghiệp: Sinh viên -Địa chỉ: