Đang tải... (xem toàn văn)
TRƯỜNG ĐẠI HỌC
SƯ PHẠM KỸ THUẬT THÀNH PHỐ HỒ CHÍ MINH
HCMC University of Technology and Education KHOA CÔNG NGHỆ THÔNG TIN
NGÀNH CÔNG NGHỆ THÔNG TIN
BÁO CÁO ĐỒ ÁN
CÔNG NGHỆ PHẦN MỀM MỚIBOOKING APP MERN STACK
GVHD: ThS Nguyễn Minh Đạo Sinh viên thực hiện:
Thành phố Hồ Chí Minh, tháng 12 năm 2023
Trang 2ĐIỂM SỐ
NHẬN XÉT CỦA GIÁO VIÊN HƯỚNG DẪN:
(Kí và ghi rõ họ tên)
Trang 3LỜI CẢM ƠN
Để hoàn thành tốt đề tài và bài báo cáo này, em xin gửi lời cảm ơn chân thành
đến giảng viên Nguyễn Minh Đạo, các quý thầy cô trong khoa Công Nghệ Thông Tin đã tận tình truyền đạt những kiến thức cần thiết giúp em có nền tảng để làm nên đề tài này, đã tạo điều kiện để em có thể tìm hiểu và thực hiện tốt đề tài Cùng với đó, em xin được gửi cảm ơn đến các bạn cùng khóa đã cung cấp nhiều thông tin và kiến thức hữu ích giúp em có thể hoàn thiện hơn đề tài của mình
Đề tài và bài báo cáo được em thực hiện trong khoảng thời gian ngắn, với những kiến thức còn hạn chế cùng nhiều hạn chế khác về mặt kĩ thuật và kinh nghiệm trong việc thực hiện một dự án phần mềm Do đó, trong quá trình làm nên đề tài có những thiếu sót là điều không thể tránh khỏi nên em rất mong nhận được những ý kiến đóng góp quý báu của các quý thầy cô để kiến thức của em được hoàn thiện hơn và em có thể làm tốt hơn nữa trong những lần sau Em xin chân thành cảm ơn
Cuối lời, em kính chúc quý thầy, quý cô luôn dồi dào sức khỏe và thành công hơn nữa trong sự nghiệp trồng người Một lần nữa em xin chân thành cảm ơn
Trang 4Mục lục
Chương 1: Tổng quan chương trình 1
1.1 Giới thiệu về chương trình: 1
1.2 Các yêu cầu chức năng: 1
1.3 Các yêu cầu phi chức năng 1
Chương 2 Nội dung 2
2.2.8 Get place của user 7
2.2.9 Get place by id 7
2.3.1 Giao diện Trang chủ: 10
2.3.2 Giao diện trang Đăng nhập 11
2.3.3 Giao diện trang đăng ký 12
2.3.4 Giao diện trang Tài khoản 13
2.3.5 Giao diện trang chi tiết nơi ở 14
2.3.6 Giao diện trang Booking 16
2.3.7 Giao diện trang My Booking 17
Trang 52.3.8 Giao diện trang My accommodations 18
2.3.9 Giao diện trang thêm nơi ở mới 19
Chương 3 Cài đặt và kiểm thử 23
Trang 6Chương 1: Tổng quan chương trình
1.1 Giới thiệu về chương trình:
Booking app Airbnb là một nơi giúp chúng ta có thể đặt được những chỗ yêu thích ở những nơi mà chúng ta muốn đến Chúng ta có thể lựa chọn được những nơi ở có giá vừa phải, có thời gian check in, check out và có giới hạn số người khi ở Bên cạnh đó nó còn cho phép chúng ta có thể chia sẻ chỗ ở của mình đến với mọi người
1.2 Các yêu cầu chức năng:
- Người dùng có thể tạo tài khoản với các thông tin cơ bản, đăng nhập vào hệ thống để có thể tiến hành lựa chọn, đặt chỗ hoặc chia sẽ chỗ ở của mình - Người dùng có thể xem chi tiết nơi ở và tiến hành đặt chỗ
- Người dùng có thể xem lại chi tiết đơn đã đặt hoặc hủy đơn - Người dùng có thể chia sẽ nơi ở của mình
- Người dùng có thể xem và chỉnh sửa nơi ở đã chia sẽ
1.3 Các yêu cầu phi chức năng
- Hệ thống đảm bảo tính dễ sử dụng cho người dùng - Thông tin mô tả, hình ảnh nơi ở đúng với thực tế
- Website dễ sử dụng cho các nền tảng khác nhau (desktop, mobile, tablet…)
Trang 7Chương 2 Nội dung
2.1 Database:
2.1.1 Class diagram
Trang 82.1.2 Lược đồ Use Case
const bookingSchema = new mongoose.Schema({
place: {type:mongoose.Schema.Types.ObjectId, required:true, ref:'Place'}, user: {type:mongoose.Schema.Types.ObjectId, required:true},
checkIn: {type:Date, required:true}, checkOut: {type:Date, required:true}, name: {type:String, required:true}, phone: {type:String, required:true}, price:Number,
Tạo bảng booking với các trường như place (khóa ngoại đến bảng Place), user (khóa ngoại đến bảng User), checkIn, checkout, name, phone, price.
- Place model
const placeSchema = new mongoose.Schema({
owner: {type:mongoose.Schema.Types.ObjectId, ref:'User'},
Trang 9Tạo bảng Place với các trường như owner (khóa ngoại đến bảng User), title, address, photos, description, perks, extraInfo, checkIn, checkout,
maxGuests, price
2.1.4 Tóm tắt database
Đây là một hệ thống dùng để đặt chỗ ở Người dùng có thể xem thông tin phòng, đặt chỗ, hủy chỗ đã đặt đồng thời người dùng cũng có thể chia sẽ nơi ở của mình đến người khác và có thể chỉnh sửa chỗ ở đã chia sẽ.
Mỗi người dùng có thể đặt nhiều chỗ , những mỗi chỗ đặt chỉ thuộc về
app.post('/api/register', async (req,res) => { mongoose.connect(process.env.MONGO_URL); const {name,email,password} = req.body;
Tiến hành tạo mới user với name, email, password từ request gửi đến (password được mã hóa) sau đó trả về thông tin user được tạo Nếu lỗi trả về lỗi 422 và thông tin lỗi
Trang 102.2.2 Login
app.post('/api/login', async (req,res) => {
mongoose.connect(process.env.MONGO_URL); const {email,password} = req.body;
const userDoc = await User.findOne({email}); if (err) throw err;
res.cookie('token', token).json(userDoc);
Đầu tiên sẽ dùng findOne để tìm email trong bảng user Nếu có thì sẽ tiến hành check password xem có đúng không Nếu password đúng thì dùng jwt.sign để chuỗi JWT với email và id của user, sau đó gửi token đã tạo đến client thông qua cookie Nếu password sai thì trả lỗi 422 với nội dung
password không đúng Nếu không tìm được email thì trả lỗi not found.
2.2.3 Get profile
app.get('/api/profile', (req,res) => {
mongoose.connect(process.env.MONGO_URL); const {token} = req.cookies;
if (token) {
jwt.verify(token, jwtSecret, {}, async (err, userData) => { if (err) throw err;
const {name,email,_id} = await User.findById(userData.id); res.json({name,email,_id});
Check token nếu có dùng jwt.verify để xác thực token và tiến hành giải mã token và trả name, email, và id của user đó.
Trang 112.2.4 Log out
app.post('/api/logout', (req,res) => { res.cookie('token', '').json(true);});
Tiến hành xóa token trong cookie
2.2.5 Upload ảnh bằng link
app.post('/api/upload-by-link', async (req,res) => { const {link} = req.body;
const newName = 'photo' + Date.now() + '.jpg'; await imageDownloader.image({
Dùng imageDownloader để tiến hành download ảnh và lưu lại với tên mới với đuôi file jpg, ảnh sẽ được lưu lại ở folder uploads ở phía backend, sau đó trả về ảnh được upload.
2.2.6 Upload ảnh từ máy
const photosMiddleware = multer({dest:'uploads/'});
app.post('/api/upload', photosMiddleware.array('photos', 100), async (req,res) => { const uploadedFiles = [];
for (let = 0i< req.files.length; i++) {
const {path,originalname,mimetype} = req.files[i]; const parts = originalname.split('.')
const ext = parts[parts.length -1] const newPath = path + "." + ext fs.renameSync(path, newPath)
// const url = await uploadToS3(path, originalname, mimetype);
uploadedFiles.push(newPath);
Trang 12photosMiddleware.array('photos', 100) định nghĩa rằng middleware này sẽ xử
lý tệp tin được gửi dưới dạng một mảng có tên là 'photos', và tối đa 100 tệp tin có thể được tải lên trong một lần yêu cầu.
Sau đó tiến hành xử lí các tệp ảnh được gửi đó bằng cách lấy ra tên file của từng ảnh đó và dùng fs.renameSync để tiến hành đổi tên cho các file đó và lưu lại vào mảng uploadedFile và trả về các ảnh đó về bên phía client.
2.2.7 Tạo place
app.post('/api/places', (req,res) => {
mongoose.connect(process.env.MONGO_URL); const {token} = req.cookies;
const {
title,address,addedPhotos,description,price, perks,extraInfo,checkIn,checkOut,maxGuests, } = req.body;
jwt.verify(token, jwtSecret, {}, async (err, userData) => { if (err) throw err;
const placeDoc = await Place.create({
Đầu tiên sẽ tiến hành xác thực token, sau đó thực hiện việc tạo nơi ở trong bản Place với các thông tin được gửi từ phía client và trả về thông tin đã tạo thành công.
2.2.8 Get place của user
app.get('/api/user-places', (req,res) => {
mongoose.connect(process.env.MONGO_URL); const {token} = req.cookies;
jwt.verify(token, jwtSecret, {}, async (err, userData) => { const {id} = userData;
res.json( await Place.find({owner:id}) ); });
Đầu tiên sẽ tiến hành xác thực token, nếu đúng thì sẽ trả về những nơi mà user đó đã tạo thông qua userId của họ.
Trang 132.2.9 Get place by id
app.get('/api/places/:id', async (req,res) => { mongoose.connect(process.env.MONGO_URL); const {id} = req.params;
res.json(await Place.findById(id));});
Dùng findById để trả về nơi theo id được truyền từ client.
2.2.10 Chỉnh sửa nơi ở
app.put('/api/places', async (req,res) => {
mongoose.connect(process.env.MONGO_URL); const {token} = req.cookies;
const {
id, title,address,addedPhotos,description,
perks,extraInfo,checkIn,checkOut,maxGuests,price, } = req.body;
jwt.verify(token, jwtSecret, {}, async (err, userData) => { if (err) throw err;
const placeDoc = await Place.findById(id); if (userData.id === placeDoc.owner.toString()) { placeDoc.set({
title,address,photos:addedPhotos,description, perks,extraInfo,checkIn,checkOut,maxGuests,price, });
await placeDoc.save(); res.json('ok');
} });});
Tiến hành xác thực token, nếu đúng thì sẽ dùng findById để tìm đến nơi ở đó theo id, sau đó kiểm tra thông tin id của user đang cần chỉnh sửa và id của người tạo xem đúng không, nếu đúng thì sẽ tiến hành lưu lại các thông tin cần chỉnh sửa và trả về kết quả cập nhật thành công.
2.2.11 Get place
app.get('/api/places', async (req,res) => {
mongoose.connect(process.env.MONGO_URL); res.json( await Place.find() );
Dùng find() để lấy ra được tất cả các nơi trong bảng Place
2.2.12 Đặt chỗ
app.post('/api/bookings', async (req, res) => { mongoose.connect(process.env.MONGO_URL);
Trang 14const userData = await getUserDataFromReq(req);
Tiến hành xác thực token để lấy user id, sau đó tiến hành tạo đơn trong bảng Booking với dữ liệu được truyền từ bên phía client và trả về thông tin đơn đã được đặt thành công.
2.2.13 Get booking
app.get('/api/bookings', async (req,res) => { mongoose.connect(process.env.MONGO_URL); const userData = await getUserDataFromReq(req);
res.json( await Booking.find({user:userData.id}).populate('place') );});
Tiến hành xác thực token và trả về thông tin booking trong bảng Booking thông qua id của user.
2.2.14 Hủy booking
app.delete('/api/bookings/:id', async (req, res) => { mongoose.connect(process.env.MONGO_URL); const bookingId = req.params.id;
const booking = await Booking.findById(bookingId);
await Booking.findByIdAndDelete(bookingId); res.json({ message: 'Booking deleted successfully' }); } catch (error) {
console.error(error);
res.status(500).json({ message: 'Internal server error' });
Trang 15}});
Tìm đơn hàng trong bảng Booking thông qua id được truyền từ client Sau đó tiến hành check xem có id đó không, nếu không thì thông báo lỗi booking not found, nếu có thì dùng findByIdAndDelete để tiến hành xóa dữ liệu trong bảng Booking thông qua id đó và trả về xóa thành công.
2.3 FrontEnd
2.3.1 Giao diện Trang chủ:
- Gọi api get places để lấy ra tất cả các phòng trong hệ thống sau đó hiển thị lên trang người dùng
Trang 162.3.2 Giao diện trang Đăng nhập
- Gọi api login và truyền vào thông tin đăng nhập để tiến hành đăng nhập, sau đó dùng UseContext để tiến hành lưu trữ thông tin đăng nhập và hiển thị tên người dùng trên trang web
async function handleLoginSubmit(ev) {
Trang 17}
if (redirect) {
return <Navigate to='/'} /> }
Đăng nhập thành công thì chuyển đến trang chủ
2.3.3 Giao diện trang đăng ký
- Gọi api register và truyền vào thông tin đăng kí để tiến hành đăng kí, nếu đăng kí thành công thì sẽ chuyển đến trang đăng nhập, sai thì sẽ thông báo lỗi async function registerUser(ev) {
Trang 18alert('Registration successful Now you can log in');
2.3.4 Giao diện trang Tài khoản
Dùng UserContext để hiển thị thông tin user và gọi api logout nếu muốn đăng xuất Nếu chưa đăng nhập thì sẽ chuyển đến trang login const [redirect,setRedirect] =useState(null);
const {ready,user,setUser} =useContext(UserContext); let {subpage} = useParams();
Trang 19if (subpage === undefined) { subpage 'profile';
}
async function logout() { await axios.post('/logout');
if (ready && !user && redirect) { return <Navigate to='/login'}/>
Trang 202.3.5 Giao diện trang chi tiết nơi ở
- Gọi api get place by id và truyền id của phòng được chọn để lấy được thông tin chi tiết của phòng đó và hiển thị lên trên giao diện
Trang 22- Gọi api booking và truyền thời gian, số lượng người, tên, số điện thoại để tiến hành đặt phòng
async function bookThisPlace() {
const response =await axios.post('/bookings', { checkIn,checkOut,numberOfGuests,name,phone, place:place._id,
price:numberOfNights *place.price, });
const bookingId response.data._id;
setRedirect(`/account/bookings/${bookingId}`); }
Trang 232.3.6 Giao diện trang Booking
- Gọi api get booking và tìm đến đơn có id bằng id trên url để lấy ra được thông tin chi tết của đơn hàng đó
useEffect(() => { if (id) {
axios.get('/bookings').then(response => {
const foundBooking =response.data.find(({_id}) => _id ===
- Gọi api delete booking và truyền id vào để tiến hành xóa đơn hàng const handleDeleteOrder async() => {
const userConfirmed window.confirm(
"Bạn có chắAc muốAn huCy đơn đặt phòng khống?"
);
// NếAu người dùng đã xác nhận, thực hiện xóa
if (userConfirmed && id) {
console.error("Error deleting order:", error); alert("Đã xaCy ra lốTi khi xóa đơn đặt phòng."); });
} }
Trang 242.3.7 Giao diện trang My Booking
- Gọi api get booking để lấy ra toàn bộ đơn hàng và hiển thị lên giao diện
useEffect(() => {
axios.get('/bookings').then(response => { setBookings(response.data);
Trang 25}); }, []);
2.3.8 Giao diện trang My accommodations
- Gọi api get user-place để lấy được tất cả nơi mà user này đã đăng
Trang 272.3.9 Giao diện trang thêm nơi ở mới
- Gọi api get place by id để tiến hành truyền dữ liệu vào các ô input để tiến
axios.get('/places/'+id).then(response => { const {data} = response;
title, address, addedPhotos, description, perks, extraInfo,
checkIn, checkOut, maxGuests, price,
Trang 28Nếu tạo mới thì sẽ gọi api post places để tạo nơi ở mới
- Ở phần hình ảnh: gọi api upload by link nếu tải ảnh bằng link và gọi api upload nếu tải ảnh từ thiết bị
async function addPhotoByLink(ev) {
function uploadPhoto(ev) {
const files ev.target.files; const data =new FormData();
for (let i=0; i <files.length; i++) { data.append('photos', files[]);
}
axios.post('/upload', data, {
headers: {'Content-type':'multipart/form-data'}
Trang 30Chương 3 Cài đặt và kiểm thử
3.1 Cài đặt:
3.1.1 Back end:
- Bước 1: Mở terminal và cd đến folder api
- Bước 2: Tạo một env với nội dung là đường link kết nối với mongoodb MONGO_URL = mongodb+srv://booking:gvF7bIzlGMh40ptv@cluster0.jhiwsro.mongodb.net/?retryWrites=true&w=majority
- Bước 3 : Tạo folder uploads trong folder api
- Bước 4: chạy câu lệnh npm install để download node modules - Bước 5: chạy câu lệnh npm start để khởi động
3.1.2 Front end:
- Bước 1: Mở terminal và cd đến folder client
Trang 31- Bước 2: Tạo file evn với nội dung là url của backend VITE_API_BASE_URL = http://localhost:4000/api
- Bước 3: Chạy câu lệnh npm install để download node modules - Bước 4: Chạy câu lệnh npm run dev để khởi động chương trình - Bước 5: Truy cập đường dẫn http://localhost:5173/
3.2 Demo chương trình
- Trang chủ:
- Đăng ký: Chọn vào avatar phía trên góc phải ở trang chủ để chuyển đến trang đăng nhập Sau đó chọn “Register now” để chuyển đến trang đăng ký và nhập thông tin đăng ký rồi nhấn “Register”