## GDSC課程評價網站小組 ### 期末專案發表 ![](https://i.imgur.com/46kXfXj.png) ---- ## 我們做了什麼(ㄧ) #### 政大課程評價網 主要的功能包含 1. 瀏覽課程評價 2. 上傳課程評價 ---- <!-- ## 我們做了什麼(二) 與一般課程評價網站不同的是多了以下幾個功能: 1. 留言板 2. 貼文版 3. 收藏課程 4. 課程尋找過濾器(提供親切的圖形介面供使用者過濾出自己想找的課程) ---- --> ## 為什麼想開發課程評價網站 1. 降低同學發修課評價的成本,不需要再到論壇上發文,而是可直接填表上傳 2. 集合所有政大課程到一個網站,找課程資料不需要再到處google ---- ### 專案成品 https://course-nccu.com ![](https://i.imgur.com/RNuppJK.png =400x400) ---- ### 掃我填評價 ![](https://i.imgur.com/MnuOthI.png =400x400) --- #### 目錄 1. #### 專案規劃 2. #### 資料庫規劃 2-a. 課程 2-b. 使用者帳號 2-c. 課程評價 3. #### 前後端開發 3-a. MVC開發架構 3-b. 前端頁面開發 3-c. 後端api 3-d. 網站上線 4. #### 未來規劃 --- ## 專案規劃 ---- ## 規劃 我們一開始有很多想法 樹莓派、自走車、 咖啡豆辨識機、 車輛違規辨識、 政大二手書交易網 ---- ### 最後我們決定走自己最擅長的領域-前後端App ![](https://i.imgur.com/lHNcfQG.png =800x500) ---- ### 我們開始找網頁技術能夠解決的問題 我們在政大的課程查詢網站上找到了這份excel檔, ![](https://i.imgur.com/HlKEFAp.jpg =500x300) 我們覺得要做出政大的課程評價網只需要這個就夠了 (true) ---- 所以我們簡單規劃了一下這個專案的資料有哪幾種 1. 課程 <- 由政大提供 2. 使用者 <- 自己做登入登出系統 3. 使用者對課程進行的評價 <- 自己做評價上傳系統 確認是做的出來的之後 就開始做課程評價系統了 --- # 規劃資料庫 --- ## 規劃資料庫(1)-課程 ![](https://i.imgur.com/TIiGR2A.jpg) ---- ### 處理課程資料 我們觀察這份政大課程查詢下載的excel檔 ![](https://i.imgur.com/DAYvtHI.png =800x150) 我們發現科目代碼得編碼是有規則的 從前3碼可以看出這堂課是哪一大分類的 中間3碼則表是子分類 ---- 科目代碼編碼規則整理 | 課程類別 | 前3碼 | | ---- | ----- | | 整開 | 000 | | 中文通識 | 031 | | 英文通識 | 032 | | 書院 | 045 | | 資訊 | 046 | | 人文 | 041 | | 社會 | 042 | | 自然 | 043 | | 跨領域 | 044 / 090 / 993 | | 體育 | 002 | | 備註: | 子類別看中間3碼 | ---- ### 資料庫中一堂課的識別代碼製造方式: 一堂課的識別代碼(Primary Key)= 學年(3碼)+ 學期(1碼) + 科目代碼(9碼) 共13碼 ---- ### 以 110 上學期的微積分為例 他的識別碼是 1101000713001 拆解如下: | 學年(3碼) | 學期(1碼) | 科目代碼(9碼) | ---- | ----- | ----- | | 110 | 1 | 000713001 | ---- ### 處理課程資料 我們將excel檔轉成csv檔, 並按照科目代碼幫課程進行分類後, 直接import到我們的資料庫, 然後對每堂課加上type跟subtype 還有一些必要的attribute, 就這樣把課程資料搞定了 ---- ## 課程資料結構 ``` javascript [2|3|4|5|6-7|8-9|10-11|12-13|14-19|20|21-24|25-26|27-28] const courseSchema = mongoose.Schema({ id: { type: String ,unique:true //primary key() semester: String //學期(共四碼:)e.g. "1101"代表110學年上學期 前三碼代表學年 第四碼代表上或下學期 code: String //由政大編碼的九碼課程id (取自課程查詢下載的excel檔) point: Number, //學分 (取自課程查詢下載的excel檔) courseNameZH_TW: String //課程名稱(中文) (取自課程查詢下載的excel檔) courseName: String //課程名稱 (取自課程查詢下載的excel檔) instructorZH_TW: String //教師名稱 (取自課程查詢下載的excel檔) instructor: String //教師名稱 (取自課程查詢下載的excel檔) departmentZH_TW: String //開課單位 (取自課程查詢下載的excel檔) department: String //開課單位 (取自課程查詢下載的excel檔) sessionZH_TW: String //上課時段 (取自課程查詢下載的excel檔) session: String ////上課時段 (取自課程查詢下載的excel檔) classroom: String //上課教室 (取自課程查詢下載的excel檔) typeOfCredit: String // (取自課程查詢下載的excel檔) lectureLanguage: String //教學使用語言 (取自課程查詢下載的excel檔) isCoreGeneral: String //是否為核心通識 (取自課程查詢下載的excel檔) information: String //詳細資訊 (取自課程查詢下載的excel檔) note: String //課程註解 (取自課程查詢下載的excel檔) comments: [String], default: [] //此課程的評價陣列 avg_rate:Number, //此課程的平均評價 avg_sweet:Number, //此課程的平均甜度 avg_loading:Number, //此課程的平均涼度 avg_gain:Number, //此課程的平均收穫 num_of_feedback:Number, //此課程的評價數 num_of_like:Number, //此課程的評價數 type: String //此課程的種類:有以下幾種["DEPARTMENT","GENERAL","INTEGRATED","PE","ND"] subType: String //此課程的子種類 列舉如下 // type為"DEPARTMENT" 的有以下幾種subtype:["CHINESE","COMPUTER_SCIENCE"...] // type為"GENERAL" 有以下幾種subtype:["SOCIAL","HUMANITY","ENGLISH"...] // type為"INTEGRATED" 有以下幾種subtype:["CALCULUS","CIVIL_LAW"...] // type為"PE"(體育) 目前沒有subtype subtype值為"" // type為"ND"(國防) 目前沒有subtype subtype值為"" short_code: String //此課程的短id code的前三碼 }) ``` --- ## 規劃資料庫(2)-政大學生帳號 ![](https://i.imgur.com/1SZ14ZL.jpg) ---- ## 處理使用者登入/登出 為了確認使用者是否是真的政大的學生, 我們的使用者登入登出系統使用了google auth 第三方登入驗證 ---- ## 登入架構流程圖 ![](https://i.imgur.com/wHFuFzi.jpg) ---- ## 登入架構 使用者在跟google登入驗證成功後,再將拿到的token傳到我們的server, 然後我們server再拿此token跟google確認, 確認成功後,google就會回傳這個使用者的資料供我們寫入我們資料庫(使用upsert) ---- ## 登入demo ![](https://i.imgur.com/pEwaTD4.gif) ---- ## 驗證使用者是否為政大學生 done 使用google第三方登入驗證系統得好處就是 我們可以驗證使用者是否使用@g.nccu.edu.tw登入我們的系統 因為我們希望只有政大的學生能上傳評價 ---- ## 使用者資料結構 ``` javascript [3-6] const userSchema = mongoose.Schema({ id: { type: String ,unique:true}, //primary key token: { type: String ,unique:true}, //google api 回傳的此使用者的代碼(驗證用) name: { type: String, required: true }, // (取自google api) email: { type: String, required: true }, // (取自google api) picture: { type: String }, // 人物頭像圖(取自google api) nickname: { type: String },// 暱稱 loginChannel: { type: String }, //使用者的登入管道 isValid: { type: String }, //是否通過信箱驗證 isBanned: { type: String }, //是否被ban likes:[String], //使用者收藏的課程id陣列 thumbups:[String], //使用者按讚的評價(feedback)id陣列 },{timestamps: true}); ``` --- ## 規劃資料庫(3)-課程評價 因為課程跟使用者都有了,所以課程評價也可以做了 ![](https://i.imgur.com/CWJDFnP.png) ---- ## 評價資料關係圖 ![](https://i.imgur.com/ejWaPMd.jpg =800x600) ---- ## 評價資料結構圖 |評價id| 課程 | 學生 | 總分 | 甜度 | 涼度 | 評語 | |--------| -------- | -------- | -------- | -------- | -------- | -------- | |1| 1101000713001 | 108703051 | 4 | 3 | 4 | 老師人很好! | |2| 1101000314061 | 108703060 | 5 | 2 | 5 | 很涼~ | ---- ## 評價資料結構 ``` javascript [2|3-6|7-8|9|11] const courseFeedBackSchema = mongoose.Schema({ id:String, //(PK) rate: Number, //評價 sweet: Number, //甜度 loading: Number, //涼度 gain: String, //收獲 introduction: String, //懶人包 description: String, //評價內容 user: String, //使用者id user_nickname: String, //使用者暱稱 course: String, //課程id (FK) course_semester: String, //e.g. "1101" course_code: String, //由政大編碼的九碼課程id num_of_thumbsup: Number, //按讚數 }) ``` ---- ## 評價資料關係 ![](https://i.imgur.com/7jUGfR8.jpg =800x600) --- ## 資料庫ok了,開始處理前端跟後端 <!-- 在那之前,得先確認開發架構跟工具 ![](https://i.imgur.com/zBD4BGD.jpg) --> --- # 前端、後端架構 ![](https://i.imgur.com/tUKhKAO.png) ---- # 前端、後端架構 ![](https://i.imgur.com/lV9tPt3.png) ---- ### 前端、後端架構圖 ![](https://i.imgur.com/16zPj11.jpg) ---- ### 另一種理解:MVC架構 ![](https://i.imgur.com/uWIO4aU.png) ---- ### 專案架構圖 ![](https://i.imgur.com/vKSFY5p.jpg) --- <!-- ## 工具 ![](https://i.imgur.com/ypabrLx.jpg =500x200) ---- --> ### 前端 #### 負責跟使用者互動的App、User Interface <!-- ![](https://i.imgur.com/uhc58Ej.jpg =800x400) --> ![](https://i.imgur.com/sTYwzHg.jpg) ---- ### 前端使用工具(1) #### React.js ![](https://i.imgur.com/BDworWa.png) ---- #### 為什麼我們前端要用React.js ![](https://i.imgur.com/t9frONY.jpg) ---- ### 前端使用工具(2) #### material UI ![](https://i.imgur.com/CHCitqP.png) ---- ### 為什麼我們前端要用Material UI github 星星數第三的React library Next.js ( ⭐ 79K) Ant Design ( ⭐ 77K) Material-UI(⭐ 74K) 開發快/好看/客製化 Tailewind CSS(⭐ 52K) 開發慢/好看/客製化 Bootstrap React(⭐ 20K) 開發快/不好看/制式化 --- <!-- ### 前端使用工具 #### Material UI ---- --> ### 後端 ![](https://i.imgur.com/GYEsr4N.png) ---- ### 後端架構圖 ![](https://i.imgur.com/R8QYt3G.jpg) ---- ### server #### node.js server ![](https://i.imgur.com/QoEASDy.jpg) ---- #### node.js server結構 ![](https://i.imgur.com/8gE4baf.png) ---- ### 資料庫 ![](https://i.imgur.com/38NzIBL.jpg) ---- ### mongoDB ![](https://i.imgur.com/teDL2OV.png) ---- ### mongoDB資料結構 ## 評價資料結構 ``` javascript [1|19|20-31] const course = { course:"1101000211011", semester:"1101", code:"000211011", point:3, courseNameZH_TW:"政治學", courseName:"Political science", instructorZH_TW:"蔡中民", instructor:"TSAI CHUNG-MIN", departmentZH_TW:"政治系", department:"Department of Political Science", sessionZH_TW:"一D56", session:"mon13-16", classroom:"綜合270751", typeOfCredit:"必/Required", lectureLanguage:"中文/Mandarin", isCoreGeneral:"否/No", information:"@異動資訊Information of alteration:N/A", note:"@備註Note:政治系優先。本課程於第二階段初選登記開放選課。", comments:["小明:good","派大星:"老師人很好"], feedbacks:[ { id:1, user:"11", rate:4 }, { id:2, user:"12", rate:4 } ], avg_rate:3.3333333333333335, avg_sweet:2.5, avg_loading:2.1666666666666665, avg_gain:4.166666666666667, num_of_feedback:3 } ``` ---- ## 總結:全部工具的程式語言都是javascript ![](https://i.imgur.com/ypabrLx.jpg =500x300) ~~謎之音:javascript真香~~ ---- ### 專案架構圖 ![](https://i.imgur.com/W657cGn.png) ---- ### 專案架構圖 ![](https://i.imgur.com/yhgDxLT.jpg) ---- ### 完成品 [DEMO](https://hackmd.io/@_YdT2NPxQtGdnDBoiC3yQg/HkxZ7DehF) <!-- ### api ``` javascript [1|5|8-10] export const fetchPost = (id) => API.get(`/posts/${id}`); export const fetchPosts = (page) => API.get(`/posts?page=${page}`); export const fetchPostsByCreator = (name) => API.get(`/posts/creator?name=${name}`); export const fetchPostsBySearch = (searchQuery) => API.get(`/posts/search?searchQuery=${searchQuery.search || 'none'}&tags=${searchQuery.tags}`); export const createPost = (newPost) => API.post('/posts', newPost); export const likePost = (id) => API.patch(`/posts/${id}/likePost`); export const comment = (value, id) => API.post(`/posts/${id}/commentPost`, { value }); export const updatePost = (id, updatedPost) => API.patch(`/posts/${id}`, updatedPost); export const deletePost = (id) => API.delete(`/posts/${id}`); export const fetchCourse = (course) => API.post(`/course`,course); export const fetchCourseWithCheck = (course) => API.post(`/course/check`,course); export const fetchCourseDetail = (course) => API.post(`/course/detail`,course); export const fetchCourseResource = (course) => API.post(`/resource/get`,course); export const fetchCourses = (page) => API.get(`/courses?page=${page}`); export const fetchCoursesBySearch = (department) => API.get(`/courses/department?department=${department}`); export const fetchCoursesByDepartment = (department) => API.post(`/course/department`, { department }); export const fetchCoursesByType = (data) => API.post(`/course/type`, data); export const fetchCoursesByUserLike = (data) => API.post(`/course/userlike`, data); export const createCourse = (newBook) => API.post('/books', newBook); export const fetchFeedBack = (feedBack) => API.post('/feedback/get', feedBack); export const createFeedBack = (newFeedBack) => API.post('/feedback', newFeedBack); export const updateFeedBack = (feedBack) => API.post('/feedback/update', feedBack); export const deleteFeedBack = (feedBack) => API.post('/feedback/delete', feedBack); export const createResource = (newResource) => API.post('/resource', newResource); export const createCourseComment = (value, id) => API.post(`/course/${id}/commentPost`, { value }); export const updateLike = (userData) => API.post('/user/like', userData); export const updateNickname = (userData) => API.post('/user/nickname', userData); export const signIn = (formData) => API.post('/user/signin', formData); export const signUp = (formData) => API.post('/user/signup', formData); export const googleSignIn = (formData) => API.post('/user/googlesignin', formData); ``` --> --- ### 課程評價網站初版完成了 deploy到heroku上 ~~因為免費的最香~~ ![](https://i.imgur.com/8X2zIe5.png) ---- ### 買網域 ![](https://i.imgur.com/bFvYKO1.png) ---- ### 使用cloud flare增加安全 ![](https://i.imgur.com/UWTxl2I.png) 使用者 -> https://course-nccu.com -> 真實網域 ---- ### 休息一下 https://course-nccu.com --- ### 開發願景(接下來要做的事) ![](https://i.imgur.com/uQnBt92.jpg) ~~願景:vison~~ ---- ### 蒐集評價 ![](https://i.imgur.com/LDe0oVk.png =800x300) <!-- ![](https://i.imgur.com/AD1WjxB.png =800x300) --> ---- <!-- ### 版本控制 因為我們希望這個專案能有其他夥伴加入 | 理想 |vs| 實際 | | -------- | -------- | -------- | | 有commit訊息、不同功能不同branch | | 只有一條很長的master ---- --> <!-- ### 提升UI/UX 讓使用者更好操作本網站 ---- --> ### 做dark mode功能 為了使用者(自己)的眼睛,必須要做。 ---- ### 整合DCARD上的修課心得 ![](https://i.imgur.com/VPuOH4R.png) ---- ### SEO ![](https://i.imgur.com/TBuROa8.png =800x600) ---- ### SEO ![](https://i.imgur.com/HIeJejq.jpg) ---- <!-- ### 跟政大選課系統re整合 開評價Api出來給他們call --- --> ### 做考古題跟筆記上傳功能 <!-- 交大有做這個 --> ![](https://i.imgur.com/EqwrPPg.jpg) ---- ### 做二手書交易 ![](https://i.imgur.com/VYCUG7s.jpg) --- ### 最終目標: 流量/評價 永動機 ![](https://i.imgur.com/QtunKwg.png) --- ### 特別感謝 選課系統組長的超強視覺宣傳圖 ![](https://i.imgur.com/YWYqlBJ.png ) --- ### 特別感謝 法律系 何傑恩同學的近2000筆Dcard選課紀錄 --- ### 特別感謝 GDSC core team 的幫忙宣傳 GDSC的資源跟開發氣氛 --- ## 謝謝大家撥空觀賞 https://course-nccu.com
{"metaMigratedAt":"2023-06-16T17:11:05.452Z","metaMigratedFrom":"YAML","title":"規劃資料庫","breaks":true,"slideOptions":"{\"transition\":\"slide\",\"slideOptions\":null,\"spotlight\":{\"enabled\":true}}","contributors":"[{\"id\":\"fd8753d8-d3f1-42d1-9d9c-3068882df242\",\"add\":24455,\"del\":11771}]"}
    235 views