TRƯỜNG ĐẠI HỌC BÀ RỊA – VŨNG TÀU KHOA CÔNG NGHỆ KỸ THUẬT NÔNG NGHIỆP CÔNG NGHỆ CAO - - BÁO CÁO ĐỒ ÁN TỐT NGHIỆP ĐỀ TÀI: ỨNG DỤNG QUẢN LÝ CÁC THƠNG BÁO NSOCIAL Trình độ đào tạo : Đại học Ngành : Công nghệ thơng tin Chun ngành : Lập trình ứng dụng điện thoại di động game Khóa học : 2018-2021 Lớp : DH18LT Sinh viên thực : Lê Hoàng Nêu Mã số sinh viên : 18033883 GVHD : TS Phan Ngọc Hoàng BÀ RỊA - VŨNG TÀU, NĂM 2021 GVHD: PHAN NGỌC HOÀNG - SVTH: LÊ HOÀNG NÊU LỜI CẢM ƠN Để hoàn thành đề tài báo cáo đồ án trước hết, em xin gửi đến quý thầy, cô giáo khoa Công nghệ kỹ thuật – Nông nghiệp công nghệ cao trường Đại học Bà Rịa – Vũng Tàu lời cảm ơn chân thành Đặc biệt, em xin gửi đến thầy Phan Ngọc Hoàng, người tận tình hướng dẫn, giúp đỡ em hồn thành đề tài đồ án tốt nghiệp lời cảm ơn sâu sắc Em xin chân thành cảm ơn Ban Lãnh Đạo, phịng ban cơng ty cổ phần cơng nghệ W360S, tạo điều kiện thuận lợi cho em tìm hiểu thực tiễn tảng, kiến thức cần thiết giúp em hồn thành sản phẩm suốt q trình thực tập cơng ty Đồng thời xin chân thành cảm ơn Ban lãnh đạo trường Đại học Bà Rịa – Vũng Tàu cho môi trường học tập lành mạnh động Kính chúc Trường Đại học Bà Rịa – Vũng Tàu tiếp túc gặt hái nhiều thành công đường giáo dục hệ trẻ Và cuối xin cảm ơn ba mẹ hi sinh, chăm sóc, dành tất tâm huyết, sức lực có mơi trường học tập thực tập tốt Vì hi sinh công sức ba mẹ nên cố gắng đường tương lai sau Vì kiến thức thân cịn hạn chế trình độ lý luận kinh nghiệm thực tiễn cịn hạn chế nên báo cáo khơng thể tránh khỏi thiếu sót, em mong nhận ý kiến đóng góp thầy, để em học thêm nhiều kinh nghiệm hoàn thiện thân đáp ứng với nhu cầu công việc thực tế tương lai Em xin chân thành cảm ơn! GVHD: PHAN NGỌC HOÀNG - SVTH: LÊ HOÀNG NÊU LỜI CAM ĐOAN Tôi xin cam đoan kết đạt đồ án sản phẩm riêng cá nhân, khơng chép lại người khác Trong tồn nội dung đồ án, điều trình bày cá nhân tổng hợp từ nhiều nguồn tài liệu Tất tài liệu tham khảo có xuất xứ rõ ràng trích dẫn hợp pháp Tơi xin hồn tồn chịu trách nhiệm chịu hình thức kỷ luật theo quy định cho lời cam đoan Vũng Tàu, ngày 01 tháng 12 năm 2021 Sinh viên thực Lê Hồng Nêu GVHD: PHAN NGỌC HỒNG - SVTH: LÊ HỒNG NÊU NHẬN XÉT CỦA GIẢNG VIÊN HƯỚNG DẪN Vũng Tàu, ngày … tháng … năm 20… Người hướng dẫn TS Phan Ngọc Hồng GVHD: PHAN NGỌC HỒNG - SVTH: LÊ HOÀNG NÊU NHẬN XÉT CỦA GIẢNG VIÊN PHẢN BIỆN GVHD: PHAN NGỌC HOÀNG - SVTH: LÊ HOÀNG NÊU MỤC LỤC DANH MỤC BẢNG _ DANH MỤC SƠ ĐỒ 10 DANH MỤC HÌNH _ 11 LỜI NÓI ĐẦU _ 13 CHƯƠNG TỔNG QUAN 14 1.1 Lý chọn đề tài 14 1.2 Mục tiêu chức hệ thống _ 14 1.2.1 Mục tiêu _ 14 1.2.2 Chức 15 1.3 Mơ tả tốn 15 CHƯƠNG CƠ SỞ LÝ THUYẾT _ 16 2.1 Cấu trúc chương trình _ 16 2.2 Giới thiệu Dart Flutter 16 2.2.1 Dart _ 16 2.2.2 Flutter _ 17 2.3 Giới thiệu Firebase _ 17 2.3.1 Tổng quát 17 2.3.2 Giới thiệu Firebase Authentication _ 18 2.3.3 Giới thiệu Firebase Firestore _ 19 2.3.4 Giới thiệu Firebase Cloud Storage _ 19 2.4 Giới thiệu Visual Studio Code 20 2.5 Giới thiệu GitHub _ 20 GVHD: PHAN NGỌC HOÀNG - SVTH: LÊ HOÀNG NÊU CHƯƠNG PHÂN TÍCH HỆ THỐNG _ 22 3.1 Mô tả hệ thống 22 3.1.1 Quản lý thông báo 22 3.1.2 Quản lý tin nhắn _ 22 3.1.3 Quản lý Tài khoản 22 3.2 Xây dựng chức hệ thống _ 23 3.2.1 Mô tả chức 23 3.2.2 Đặc tả chức ứng dụng _ 26 3.3 Thiết kế hệ thống 38 3.3.1 Sơ đồ sở liệu _ 38 3.3.2 Chi tiết sở liệu 42 CHƯƠNG XÂY DỰNG ỨNG DỤNG 46 4.1 Giao diện đăng nhập _ 46 4.2 Giao diện hình _ 46 4.3 Giao diện hình Home _ 47 4.3.1 Giao diện thông báo 47 4.3.2 Giao diện đăng thông báo _ 49 4.3.3 Giao diện lượt thích bình luận 51 4.4 Giao diện hình Chatroom 55 4.4.1 Giao diện nhóm chat tạo nhóm chat 55 4.4.2 Giao diện chi tiết nhóm chat 56 4.5 Giao diện tài khoản người dùng _ 59 4.6 Giao diện hình khác 63 4.6.1 Giao diện hình thơng tin cá nhân người dùng khác _ 63 GVHD: PHAN NGỌC HOÀNG - SVTH: LÊ HOÀNG NÊU CHƯƠNG KẾT LUẬN 65 5.1 Kết đạt 65 5.2 Hướng phát triển 65 TÀI LIỆU THAM KHẢO 67 PHỤ LỤC 68 5.3 Sơ đồ cấu trúc 68 5.4 Code xử lý _ 69 5.4.1 File main.dart _ 69 5.4.2 Authentication.dart _ 70 5.4.3 File FirebaseOperations.dart 72 5.4.4 File PostOptions.dart _ 74 5.4.5 File GroupMessageHelper.dart 86 GVHD: PHAN NGỌC HOÀNG - SVTH: LÊ HOÀNG NÊU DANH MỤC BẢNG Bảng 3-4 Bảng mô tả chứa quản lý tài khoản Admin _ 25 Bảng 3-5 Bảng mô tả chức thông tin cá nhân _ 25 Bảng 3-6 Bảng mô tả chức thông báo _ 25 Bảng 3-7 Bảng mô tả chức thích thơng báo _ 25 Bảng 3-7 Bảng mơ tả chức bình luận thơng báo 25 Bảng 3-8 Bảng mơ tả chức nhóm chat 26 Bảng 3-8 Bảng mơ tả chức nhắn tin nhóm chat 26 Bảng 3-8 Bảng mô tả chức thành viên nhóm chat _ 26 Bảng 3-12 Cơ sở liệu bảng tài khoản người dùng _ 42 Bảng 3-13 Cơ sở liệu bảng following _ 42 Bảng 3-14 Cơ sở liệu bảng followers _ 43 Bảng 3-15 Cơ sở liệu bảng thông tin cá nhân 43 Bảng 3-16 Cơ sở liệu bảng thông báo 44 Bảng 3-17 Cơ sở liệu bảng lượt thích _ 44 Bảng 3-18 Cơ sở liệu bảng bình luận _ 44 Bảng 3-19 Cơ sở liệu bảng phòng chat 44 Bảng 3-20 Cơ sở liệu bảng thành viên phòng chat 45 Bảng 3-21 Cơ sở liệu bảng hóa đơn 45 GVHD: PHAN NGỌC HOÀNG - SVTH: LÊ HOÀNG NÊU DANH MỤC SƠ ĐỒ Sơ đồ Sơ đồ user-case tài khoản User 23 Sơ đồ Sơ đồ user-case tài khoản Manager 24 Sơ đồ Sơ đồ user-case tài khoản Admin 24 Sơ đồ Sơ đồ trình đăng nhập 27 Sơ đồ Sơ đồ trình sửa thông tin cá nhân 28 Sơ đồ Sơ đồ trình bình luận thơng báo 29 Sơ đồ Sơ đồ q trình nhắn tin nhóm chat 30 Sơ đồ Sơ đồ trình tìm kiếm thơng tin người dùng 31 Sơ đồ Sơ đồ trình theo dõi người dùng khác (following) 32 Sơ đồ 10 Sơ đồ trình đăng thơng báo khơng có hình ảnh 33 Sơ đồ 11 Sơ đồ trình đăng thơng báo có hình ảnh 34 Sơ đồ 12 Sơ đồ trình tạo nhóm chat 35 Sơ đồ 13 Sơ đồ trình thêm thành viên vào nhóm chat 36 Sơ đồ 14 Sơ đồ trình xem xóa tài khoản 37 Sơ đồ 15 Sơ đồ trình tạo tài khoản 38 Sơ đồ 16 Sơ đồ sở liệu người dùng (User) 39 Sơ đồ 17 Sơ đồ sở liệu thông báo (Post) 40 Sơ đồ 18 Sơ đồ sở liệu phòng chat (Chatroom) 41 Sơ đồ 19 Sơ đồ sở liệu tổng quát 42 GVHD: PHAN NGỌC HOÀNG - SVTH: LÊ HOÀNG NÊU 10 Navigator.push( context, PageTransition( child: AltProfile( userUid: documentSnapshot['useruid'], ), type: PageTransitionType bottomToTop)); } }, child: CircleAvatar( backgroundImage: NetworkImage( documentSnapshot['userimage']), ), ), title: Text( documentSnapshot['username'], style: TextStyle( color: ConstantColors.blueColor, fontWeight: FontWeight.bold, fontSize: 16), ), subtitle: Text( documentSnapshot['useremail'], style: TextStyle( color: ConstantColors.whiteColor, fontWeight: FontWeight.bold, fontSize: 12), ), trailing: Provider.of(context, listen: false) getUserUid == documentSnapshot['useruid'] ? Container( width: 0, height: 0, ) : MaterialButton( onPressed: () {}, child: Text( 'Follow', style: TextStyle( color: ConstantColors.whiteColor, fontWeight: FontWeight.bold, GVHD: PHAN NGỌC HOÀNG - SVTH: LÊ HOÀNG NÊU 85 fontSize: 14), ), color: ConstantColors.blueColor, ), ); }).toList(), ); } }, ), ) ], ), height: MediaQuery.of(context).size.height * 0.5, width: MediaQuery.of(context).size.width, decoration: BoxDecoration( color: ConstantColors.blueGreyColor, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12))), ); }); } } 5.4.5 File GroupMessageHelper.dart File xử lý chức nhóm chat như: thời gian, rời nhóm, thêm thành viên, chuyển quyền sở hữu, nhắn tin, stickers, import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:nsocial/common/Constantcolors.dart'; import 'package:nsocial/screens/AltProfile/alt_profile.dart'; import 'package:nsocial/services/Authentication.dart'; import 'package:nsocial/services/FirebaseOperations.dart'; import 'package:page_transition/page_transition.dart'; import 'package:provider/provider.dart'; import 'package:timeago/timeago.dart' as timeago; class PostFunctions with ChangeNotifier { TextEditingController commentController = TextEditingController(); TextEditingController editCaptionController = TextEditingController(); late String imageTimePosted; String get getimageTimeposted => imageTimePosted; GVHD: PHAN NGỌC HOÀNG - SVTH: LÊ HOÀNG NÊU 86 Future checkLike(BuildContext context, String postId) async { bool check = false; QuerySnapshot data = await FirebaseFirestore.instance collection('posts') doc(postId) collection('likes') get(); List readlData = []; data.docs.forEach((element) { readlData.add(element); // ignore: unnecessary_statements }); readlData.forEach((element) { if (element.id == Provider.of(context, listen: false).getUserUid) { print("CHeckTrue"); check = true; } }); print(''); return check; } showTimeAgo(dynamic timedata) { Timestamp time = timedata; DateTime dateTime = time.toDate(); imageTimePosted = timeago.format(dateTime); print(imageTimePosted); notifyListeners(); } showPostOptions(BuildContext context, String postId) { return showModalBottomSheet( isScrollControlled: true, context: context, builder: (context) { return Padding( padding: EdgeInsets.only( bottom: MediaQuery.of(context).viewInsets.bottom), child: Container( child: Column( children: [ Padding( GVHD: PHAN NGỌC HOÀNG - SVTH: LÊ HOÀNG NÊU 87 padding: const EdgeInsets.symmetric(horizontal: 150), child: Divider( thickness: 4, color: ConstantColors.whiteColor, ), ), Container( child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ MaterialButton( onPressed: () { showModalBottomSheet( context: context, builder: (context) { return Container( child: Center( child: Row( children: [ Container( width: 300, height: 50, child: TextField( decoration: InputDecoration( hintText: 'Add New Caption', hintStyle: TextStyle( color: ConstantColors whiteColor, fontWeight: FontWeight.bold, fontSize: 16), ), style: TextStyle( color: ConstantColors.whiteColor, fontWeight: FontWeight.bold, fontSize: 16), controller: editCaptionController, ), ), FloatingActionButton( onPressed: () { Provider.of( context, listen: false) updateCation(postId, { GVHD: PHAN NGỌC HOÀNG - SVTH: LÊ HOÀNG NÊU 88 'caption': editCaptionController.text }); }, child: Icon( FontAwesomeIcons.fileUpload, color: ConstantColors.whiteColor, ), backgroundColor: ConstantColors.redColor, ) ], ), ), ); }); }, color: ConstantColors.blueColor, child: Text( 'Edit Caption', style: TextStyle( color: ConstantColors.whiteColor, fontWeight: FontWeight.bold, fontSize: 16), ), ), MaterialButton( onPressed: () { Navigator.pop(context); showDialog( context: context, builder: (context) { return AlertDialog( backgroundColor: ConstantColors.darkColor, title: Text( 'Delete this post?', style: TextStyle( color: ConstantColors.whiteColor, fontWeight: FontWeight.bold, fontSize: 16), ), actions: [ MaterialButton( onPressed: () { Navigator.pop(context); GVHD: PHAN NGỌC HOÀNG - SVTH: LÊ HOÀNG NÊU 89 }, child: Text( 'No', style: TextStyle( decoration: TextDecoration.underline, decorationColor: ConstantColors.whiteColor, color: ConstantColors.whiteColor, fontWeight: FontWeight.bold, fontSize: 16), ), ), MaterialButton( onPressed: () { Provider.of( context, listen: false) deleteUserPost(postId, 'posts') whenComplete(() { Navigator.pop(context); }); }, color: ConstantColors.redColor, child: Text( 'Yes', style: TextStyle( color: ConstantColors.whiteColor, fontWeight: FontWeight.bold, fontSize: 16), ), ), ], ); }); }, color: ConstantColors.blueColor, child: Text( 'Delete Post', style: TextStyle( color: ConstantColors.whiteColor, fontWeight: FontWeight.bold, fontSize: 16), ), ) GVHD: PHAN NGỌC HOÀNG - SVTH: LÊ HOÀNG NÊU 90 ], ), ) ], ), height: MediaQuery.of(context).size.height * 0.1, width: MediaQuery.of(context).size.width, decoration: BoxDecoration( color: ConstantColors.blueGreyColor, borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12))), ), ); }); } Future addLike(BuildContext context, String postId, String subDocId) async { return FirebaseFirestore.instance collection('posts') doc(postId) collection('likes') doc(subDocId) set({ 'likes': FieldValue.increment(1), 'username': Provider.of(context, listen: false) getInitUserName, 'useruid': Provider.of(context, listen: false).getUserUid, 'userimage': Provider.of(context, listen: false) getInitUserImage, 'useremail': Provider.of(context, listen: false) getInitUserEmail, 'time': Timestamp.now() }); } Future addComment(BuildContext context, String postId, String comment) async { await FirebaseFirestore.instance collection('posts') doc(postId) collection('comments') doc(comment) set({ 'comment': comment, 'username': Provider.of(context, listen: false) GVHD: PHAN NGỌC HOÀNG - SVTH: LÊ HOÀNG NÊU 91 .getInitUserName, 'useruid': Provider.of(context, listen: false).getUserUid, 'userimage': Provider.of(context, listen: false) getInitUserImage, 'useremail': Provider.of(context, listen: false) getInitUserEmail, 'time': Timestamp.now() }); } showCommentsSheet( BuildContext context, DocumentSnapshot snapshot, String docId) { return Container( child: Padding( padding: EdgeInsets.only(bottom: 0), child: Container( // height: MediaQuery.of(context).size.height * 0.7, width: MediaQuery.of(context).size.width, child: Column( children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 150), child: Divider( thickness: 4, color: ConstantColors.whiteColor, ), ), // Container( // decoration: BoxDecoration( // border: Border.all(color: ConstantColors.whiteColor), // borderRadius: BorderRadius.circular(5)), // child: Center( // child: Text( // 'Comments', // style: TextStyle( // color: ConstantColors.blueColor, // fontSize: 16, // fontWeight: FontWeight.bold), // // ), ), // ), Container( width: MediaQuery.of(context).size.width, height: MediaQuery.of(context).size.height * 0.35, child: StreamBuilder( GVHD: PHAN NGỌC HOÀNG - SVTH: LÊ HOÀNG NÊU 92 stream: FirebaseFirestore.instance collection('posts') doc(docId) collection('comments') orderBy('time') snapshots(), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return Center( child: CircularProgressIndicator(), ); } else { return new ListView( physics: AlwaysScrollableScrollPhysics(), children: snapshot.data!.docs map((DocumentSnapshot documentSnapshot) { return Container( height: MediaQuery.of(context).size.height * 0.15, width: MediaQuery.of(context).size.width, child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Padding( padding: const EdgeInsets.only(top: 8, left: 8), child: GestureDetector( onTap: () { if (documentSnapshot['useruid'] != Provider.of(context, listen: false) getUserUid) { Navigator.push( context, PageTransition( child: AltProfile( userUid: documentSnapshot[ 'useruid'], ), type: PageTransitionType bottomToTop)); } }, GVHD: PHAN NGỌC HOÀNG - SVTH: LÊ HOÀNG NÊU 93 child: CircleAvatar( backgroundColor: ConstantColors.darkColor, radius: 15, backgroundImage: NetworkImage( documentSnapshot['userimage']), ), ), ), Column( children: [ Container( child: Row( children: [ Text( documentSnapshot['username'], style: TextStyle( color: ConstantColors.whiteColor, fontWeight: FontWeight.bold, fontSize: 14), ) ], ), ) ], ), Container( child: Row( children: [ IconButton( onPressed: () {}, icon: Icon( FontAwesomeIcons.arrowUp, color: ConstantColors.blueColor, size: 14, )), Text( '0', style: TextStyle( color: ConstantColors.whiteColor, fontWeight: FontWeight.bold, fontSize: 14), ), IconButton( GVHD: PHAN NGỌC HOÀNG - SVTH: LÊ HOÀNG NÊU 94 onPressed: () {}, icon: Icon( FontAwesomeIcons.reply, color: ConstantColors.yellowColor, size: 14, )), ], ), ) ], ), Container( width: MediaQuery.of(context).size.width, child: Row( mainAxisAlignment: MainAxisAlignment.start, children: [ IconButton( onPressed: () {}, icon: Icon( Icons.arrow_forward_ios_outlined, color: ConstantColors.blueColor, size: 12, )), Container( width: MediaQuery.of(context).size.width * 0.7, child: Text( documentSnapshot['comment'], style: TextStyle( color: ConstantColors.whiteColor, fontSize: 14), ), ), IconButton( onPressed: () {}, icon: Icon( FontAwesomeIcons.trashAlt, color: ConstantColors.redColor, size: 14, )), ], ), ), // Divider( // color: ConstantColors.darkColor.withOpacity(0.2), GVHD: PHAN NGỌC HOÀNG - SVTH: LÊ HOÀNG NÊU 95 // ) ], ), ); }).toList(), ); } }, ), ), ], ), decoration: BoxDecoration( color: ConstantColors.blueGreyColor, borderRadius: BorderRadius.only( topLeft: Radius.circular(0), topRight: Radius.circular(0))), ), )); } showLikes(BuildContext context, String postId) { return showModalBottomSheet( context: context, builder: (context) { return Container( child: Column( children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 150), child: Divider( thickness: 4, color: ConstantColors.whiteColor, ), ), Container( decoration: BoxDecoration( border: Border.all(color: ConstantColors.whiteColor), borderRadius: BorderRadius.circular(5)), child: Center( child: Text( 'Likes', style: TextStyle( color: ConstantColors.blueColor, fontSize: 16, fontWeight: FontWeight.bold), GVHD: PHAN NGỌC HOÀNG - SVTH: LÊ HOÀNG NÊU 96 ), ), ), Container( height: MediaQuery.of(context).size.height * 0.2, width: MediaQuery.of(context).size.width, child: StreamBuilder( stream: FirebaseFirestore.instance collection('posts') doc(postId) collection('likes') snapshots(), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return Center( child: CircularProgressIndicator(), ); } else { return new ListView( children: snapshot.data!.docs map((DocumentSnapshot documentSnapshot) { return ListTile( leading: GestureDetector( onTap: () { if (documentSnapshot['useruid'] != Provider.of(context, listen: false) getUserUid) { Navigator.push( context, PageTransition( child: AltProfile( userUid: documentSnapshot['useruid'], ), type: PageTransitionType bottomToTop)); } }, child: CircleAvatar( backgroundImage: NetworkImage( documentSnapshot['userimage']), ), ), title: Text( GVHD: PHAN NGỌC HOÀNG - SVTH: LÊ HOÀNG NÊU 97 documentSnapshot['username'], style: TextStyle( color: ConstantColors.blueColor, fontWeight: FontWeight.bold, fontSize: 16), ), subtitle: Text( documentSnapshot['useremail'], style: TextStyle( color: ConstantColors.whiteColor, fontWeight: FontWeight.bold, fontSize: 12), ), trailing: Provider.of(context, listen: false) getUserUid == documentSnapshot['useruid'] ? Container( width: 0, height: 0, ) : MaterialButton( onPressed: () {}, child: Text( 'Follow', style: TextStyle( color: ConstantColors.whiteColor, fontWeight: FontWeight.bold, fontSize: 14), ), color: ConstantColors.blueColor, ), ); }).toList(), ); } }, ), ) ], ), height: MediaQuery.of(context).size.height * 0.5, width: MediaQuery.of(context).size.width, decoration: BoxDecoration( color: ConstantColors.blueGreyColor, GVHD: PHAN NGỌC HOÀNG - SVTH: LÊ HOÀNG NÊU 98 borderRadius: BorderRadius.only( topLeft: Radius.circular(12), topRight: Radius.circular(12))), ); }); } } GVHD: PHAN NGỌC HOÀNG - SVTH: LÊ HOÀNG NÊU 99 ... ✓ Quản lý thông báo: o Quản lý danh mục thông báo o Quản lý thông tin phản hồi thông báo ✓ Quản lý nhân viên: o Quản lý thông tin cá nhân o Quản lý tài khoản ✓ Quản lý thông tin trao đổi: o Quản. .. thơng tin cho nhau, xử lý thông tin định 1.2.2 Chức ✓ Quản lý thông báo ✓ Quản lý nhân viên ✓ Quản lý thông tin trao đổi 1.3 Mơ tả tốn ➢ Mục đích ứng dụng đưa thông báo, thông tin đến nhân viên... Từ lí ứng dụng ? ?NSocial? ?? - ứng dụng dùng để quản lí thơng báo cho nhân viên - giải pháp tiên tiến, hiệu việc gửi thông báo đến nhân viên 1.2 Mục tiêu chức hệ thống 1.2.1 Mục tiêu Gửi thông báo