Tải bản đầy đủ (.pdf) (49 trang)

Báo Cáo Đồ Áncông Nghệ Phần Mềm Mớibooking App Mern Stack.pdf

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (2.95 MB, 49 trang )

<span class="text_page_counter">Trang 1</span><div class="page_container" data-page="1">

TRƯỜNG ĐẠI HỌC

<b>SƯ PHẠM KỸ THUẬT THÀNH PHỐ HỒ CHÍ MINH</b>

<b> </b>

--------HCMC University of Technology and Education KHOA CÔNG NGHỆ THÔNG TIN

<b>NGÀNH CÔNG NGHỆ THÔNG TIN</b>

BÁO CÁO ĐỒ ÁN

<b>CÔNG NGHỆ PHẦN MỀM MỚIBOOKING APP MERN STACK</b>

<b>GVHD: ThS. Nguyễn Minh Đạo Sinh viên thực hiện:</b>

<b> </b>

Thành phố Hồ Chí Minh, tháng 12 năm 2023

</div><span class="text_page_counter">Trang 2</span><div class="page_container" data-page="2">

<b>ĐIỂM SỐ</b>

<b>NHẬN XÉT CỦA GIÁO VIÊN HƯỚNG DẪN:</b>

<i><b> (Kí và ghi rõ họ tên)</b></i>

</div><span class="text_page_counter">Trang 3</span><div class="page_container" data-page="3">

<b>LỜI CẢM ƠN</b>

<b> Để 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</b>

đế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ể hồ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 q 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 hồ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.

</div><span class="text_page_counter">Trang 4</span><div class="page_container" data-page="4">

<b>Mục lục</b>

<small>Chương 1: Tổng quan chương trình...1</small>

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

<b><small>Chương 2. Nội dung...2</small></b>

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

</div><span class="text_page_counter">Trang 5</span><div class="page_container" data-page="5">

2.3.8 Giao diện trang My accommodations...18

2.3.9 Giao diện trang thêm nơi ở mới...19

<b><small>Chương 3. Cài đặt và kiểm thử...23</small></b>

</div><span class="text_page_counter">Trang 6</span><div class="page_container" data-page="6">

<i>Chương 1: Tổng quan chương trình</i>

<b>1.1. Giới thiệu về chương trình: </b>

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

<b>1.2. Các yêu cầu chức năng:</b>

- 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ẽ

<b>1.3. Các yêu cầu phi chức năng</b>

- 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…)

</div><span class="text_page_counter">Trang 7</span><div class="page_container" data-page="7">

<i>Chương 2. Nội dung</i>

<b>2.1. Database:</b>

<b><small>2.1.1 Class diagram</small></b>

</div><span class="text_page_counter">Trang 8</span><div class="page_container" data-page="8">

<b><small>2.1.2 Lược đồ Use Case</small></b>

<small>const bookingSchema = new mongoose.Schema({</small>

<small> place: {type:mongoose.Schema.Types.ObjectId, required:true, ref:'Place'}, user: {type:mongoose.Schema.Types.ObjectId, required:true},</small>

<small> checkIn: {type:Date, required:true}, checkOut: {type:Date, required:true}, name: {type:String, required:true}, phone: {type:String, required:true}, price:Number,</small>

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

<small>const placeSchema = new mongoose.Schema({</small>

<small> owner: {type:mongoose.Schema.Types.ObjectId, ref:'User'},</small>

</div><span class="text_page_counter">Trang 9</span><div class="page_container" data-page="9">

Tạ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

<b><small>2.1.4 Tóm tắt database</small></b>

Đâ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ề

<small>app.post('/api/register', async (req,res) => { mongoose.connect(process.env.MONGO_URL); const {name,email,password} = req.body;</small>

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

</div><span class="text_page_counter">Trang 10</span><div class="page_container" data-page="10">

<b><small>2.2.2 Login</small></b>

<small>app.post('/api/login', async (req,res) => {</small>

<small> mongoose.connect(process.env.MONGO_URL); const {email,password} = req.body;</small>

<small> const userDoc = await User.findOne({email}); if (err) throw err;</small>

<small> res.cookie('token', token).json(userDoc);</small>

Đầ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.

<b><small>2.2.3 Get profile</small></b>

<small>app.get('/api/profile', (req,res) => {</small>

<small> mongoose.connect(process.env.MONGO_URL); const {token} = req.cookies;</small>

<small> if (token) {</small>

<small> jwt.verify(token, jwtSecret, {}, async (err, userData) => { if (err) throw err;</small>

<small> const {name,email,_id} = await User.findById(userData.id); res.json({name,email,_id});</small>

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 đó.

</div><span class="text_page_counter">Trang 11</span><div class="page_container" data-page="11">

<b><small>2.2.4 Log out</small></b>

<small>app.post('/api/logout', (req,res) => { res.cookie('token', '').json(true);});</small>

Tiến hành xóa token trong cookie

<b><small>2.2.5 Upload ảnh bằng link</small></b>

<small>app.post('/api/upload-by-link', async (req,res) => { const {link} = req.body;</small>

<small> const newName = 'photo' + Date.now() + '.jpg'; await imageDownloader.image({</small>

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.

<b><small>2.2.6 Upload ảnh từ máy</small></b>

<small>const photosMiddleware = multer({dest:'uploads/'});</small>

<small>app.post('/api/upload', photosMiddleware.array('photos', 100), async (req,res) => { const uploadedFiles = [];</small>

<small> for (let = 0i< req.files.length; i++) {</small>

<small> const {path,originalname,mimetype} = req.files[i]; const parts = originalname.split('.')</small>

<small> const ext = parts[parts.length -1] const newPath = path + "." + ext fs.renameSync(path, newPath)</small>

<small> // const url = await uploadToS3(path, originalname, mimetype);</small>

<small> uploadedFiles.push(newPath);</small>

</div><span class="text_page_counter">Trang 12</span><div class="page_container" data-page="12">

<i>photosMiddleware.array('photos', 100) định nghĩa rằng middleware này sẽ xử</i>

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.

<b><small>2.2.7 Tạo place</small></b>

<small>app.post('/api/places', (req,res) => {</small>

<small> mongoose.connect(process.env.MONGO_URL); const {token} = req.cookies;</small>

<small> const {</small>

<small> title,address,addedPhotos,description,price, perks,extraInfo,checkIn,checkOut,maxGuests, } = req.body;</small>

<small> jwt.verify(token, jwtSecret, {}, async (err, userData) => { if (err) throw err;</small>

<small> const placeDoc = await Place.create({</small>

Đầ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.

<b><small>2.2.8 Get place của user</small></b>

<small>app.get('/api/user-places', (req,res) => {</small>

<small> mongoose.connect(process.env.MONGO_URL); const {token} = req.cookies;</small>

<small> jwt.verify(token, jwtSecret, {}, async (err, userData) => { const {id} = userData;</small>

<small> res.json( await Place.find({owner:id}) ); });</small>

Đầ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ọ.

</div><span class="text_page_counter">Trang 13</span><div class="page_container" data-page="13">

<b><small>2.2.9 Get place by id</small></b>

<small>app.get('/api/places/:id', async (req,res) => { mongoose.connect(process.env.MONGO_URL); const {id} = req.params;</small>

<small> res.json(await Place.findById(id));});</small>

Dùng findById để trả về nơi theo id được truyền từ client.

<b><small>2.2.10 Chỉnh sửa nơi ở</small></b>

<small>app.put('/api/places', async (req,res) => {</small>

<small> mongoose.connect(process.env.MONGO_URL); const {token} = req.cookies;</small>

<small> const {</small>

<small> id, title,address,addedPhotos,description,</small>

<small> perks,extraInfo,checkIn,checkOut,maxGuests,price, } = req.body;</small>

<small> jwt.verify(token, jwtSecret, {}, async (err, userData) => { if (err) throw err;</small>

<small> const placeDoc = await Place.findById(id); if (userData.id === placeDoc.owner.toString()) { placeDoc.set({</small>

<small> title,address,photos:addedPhotos,description, perks,extraInfo,checkIn,checkOut,maxGuests,price, });</small>

<small> await placeDoc.save(); res.json('ok');</small>

<small> } });});</small>

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.

<b><small>2.2.11 Get place</small></b>

<small>app.get('/api/places', async (req,res) => {</small>

<small> mongoose.connect(process.env.MONGO_URL); res.json( await Place.find() );</small>

Dùng find() để lấy ra được tất cả các nơi trong bảng Place

<b><small>2.2.12 Đặt chỗ </small></b>

<small>app.post('/api/bookings', async (req, res) => { mongoose.connect(process.env.MONGO_URL);</small>

</div><span class="text_page_counter">Trang 14</span><div class="page_container" data-page="14">

<small> const userData = await getUserDataFromReq(req);</small>

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.

<b><small>2.2.13 Get booking</small></b>

<small>app.get('/api/bookings', async (req,res) => { mongoose.connect(process.env.MONGO_URL); const userData = await getUserDataFromReq(req);</small>

<small> res.json( await Booking.find({user:userData.id}).populate('place') );});</small>

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.

<b><small>2.2.14 Hủy booking</small></b>

<small>app.delete('/api/bookings/:id', async (req, res) => { mongoose.connect(process.env.MONGO_URL); const bookingId = req.params.id;</small>

<small> const booking = await Booking.findById(bookingId);</small>

<small> await Booking.findByIdAndDelete(bookingId); res.json({ message: 'Booking deleted successfully' }); } catch (error) {</small>

<small> console.error(error);</small>

<small> res.status(500).json({ message: 'Internal server error' });</small>

</div><span class="text_page_counter">Trang 15</span><div class="page_container" data-page="15">

<small> }});</small>

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.

<b>2.3. FrontEnd</b>

<b><small>2.3.1. Giao diện Trang chủ: </small></b>

- 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

</div><span class="text_page_counter">Trang 16</span><div class="page_container" data-page="16">

<b><small>2.3.2 Giao diện trang Đăng nhập</small></b>

- 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

<small> async function handleLoginSubmit(ev) {</small>

</div><span class="text_page_counter">Trang 17</span><div class="page_container" data-page="17">

<small>  }</small>

<small>  if (redirect) {</small>

<small>    return <Navigate to='/'} />  }</small>

Đăng nhập thành cơng thì chuyển đến trang chủ

<b><small>2.3.3 Giao diện trang đăng ký</small></b>

- 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 <small>async function registerUser(ev) {</small>

</div><span class="text_page_counter">Trang 18</span><div class="page_container" data-page="18">

<small>      alert('Registration successful. Now you can log in');</small>

<b><small>2.3.4 Giao diện trang Tài khoản</small></b>

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 <small>const [redirect,setRedirect] =useState(null);</small>

<small>  const {ready,user,setUser} =useContext(UserContext);  let {subpage} = useParams();</small>

</div><span class="text_page_counter">Trang 19</span><div class="page_container" data-page="19">

<small>  if (subpage === undefined) {    subpage 'profile';</small>

<small>  }</small>

<small>  async function logout() {    await axios.post('/logout');</small>

<small>  if (ready && !user && redirect) {    return <Navigate to='/login'}/></small>

</div><span class="text_page_counter">Trang 20</span><div class="page_container" data-page="20">

<b><small>2.3.5 Giao diện trang chi tiết nơi ở</small></b>

- 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

</div><span class="text_page_counter">Trang 22</span><div class="page_container" data-page="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

<small>async function bookThisPlace() {</small>

<small>    const response =await axios.post('/bookings', {      checkIn,checkOut,numberOfGuests,name,phone,      place:place._id,</small>

<small>      price:numberOfNights *place.price,    });</small>

<small>    const bookingId response.data._id;</small>

<small>    setRedirect(`/account/bookings/${bookingId}`);  }</small>

</div><span class="text_page_counter">Trang 23</span><div class="page_container" data-page="23">

<b><small>2.3.6 Giao diện trang Booking</small></b>

- 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 đó

<small>useEffect(() => {    if (id) {</small>

<small>      axios.get('/bookings').then(response => {</small>

<small>        const foundBooking =response.data.find(({_id}) => _id === </small>

- Gọi api delete booking và truyền id vào để tiến hành xóa đơn hàng <small>const handleDeleteOrder async() => {</small>

<small>    const userConfirmed window.confirm(</small>

<small>      "Bạn có chắAc muốAn huCy đơn đặt phịng khống?"</small>

<small>    );</small>

<small>    // NếAu người dùng đã xác nhận, thực hiện xóa</small>

<small>    if (userConfirmed && id) {</small>

<small>      console.error("Error deleting order:", error);      alert("Đã xaCy ra lốTi khi xóa đơn đặt phịng.");        });</small>

<small>    }   }</small>

</div><span class="text_page_counter">Trang 24</span><div class="page_container" data-page="24">

<b><small>2.3.7 Giao diện trang My Booking</small></b>

- Gọi api get booking để lấy ra toàn bộ đơn hàng và hiển thị lên giao diện

<small>useEffect(() => {</small>

<small>    axios.get('/bookings').then(response => {      setBookings(response.data);</small>

</div><span class="text_page_counter">Trang 25</span><div class="page_container" data-page="25">

<small>    });  }, []);</small>

<b><small>2.3.8 Giao diện trang My accommodations</small></b>

- Gọi api get user-place để lấy được tất cả nơi mà user này đã đăng

</div><span class="text_page_counter">Trang 27</span><div class="page_container" data-page="27">

<b><small>2.3.9 Giao diện trang thêm nơi ở mới</small></b>

- Gọi api get place by id để tiến hành truyền dữ liệu vào các ô input để tiến

<small>    axios.get('/places/'+id).then(response => {       const {data} = response;</small>

<small>      title, address, addedPhotos,      description, perks, extraInfo,</small>

<small>      checkIn, checkOut, maxGuests, price,</small>

</div><span class="text_page_counter">Trang 28</span><div class="page_container" data-page="28">

Nế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ị

<small>async function addPhotoByLink(ev) {</small>

<small>function uploadPhoto(ev) {</small>

<small>    const files ev.target.files;    const data =new FormData();</small>

<small>    for (let i=0; i <files.length; i++) {      data.append('photos', files[]);</small>

<small>    }</small>

<small>    axios.post('/upload', data, {</small>

<small>      headers: {'Content-type':'multipart/form-data'}</small>

</div><span class="text_page_counter">Trang 30</span><div class="page_container" data-page="30">

<i>Chương 3. Cài đặt và kiểm thử</i>

<b>3.1. Cài đặt:</b>

<b><small>3.1.1 Back end:</small></b>

- 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 <small>MONGO_URL = mongodb+srv://booking:/?retryWrites=true&w=majority</small>

- 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

<b><small>3.1.2 Front end:</small></b>

- Bước 1: Mở terminal và cd đến folder client

</div><span class="text_page_counter">Trang 31</span><div class="page_container" data-page="31">

- Bước 2: Tạo file .evn với nội dung là url của backend <small>VITE_API_BASE_URL = http://localhost:4000/api</small>

- 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/

<b>3.2. Demo chương trình</b>

- 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”

</div>

×