团队项目技术规格说明书
简述
概述
- 根据网站的需求,我们团队在经过网上调研比较之后,决定使用bootstrap作为前端开发框架,Django作为后端服务器开发框架。选用MySQL作为数据库。在开发阶段使用sqlite作为临时的数据库。
前端技术
- 我们团队选择了bootstrap作为前端框架。一方面由于其文档教程较为丰富,另一方面在于其社区活跃,使用的人多。遇到困难能够更轻松地解决。我们还选择了pingendo的IDE,帮助我们可视化开发前端,现有模板帮助我们省去不必要的工作。
后端技术
- 我们团队比较了基于Java的spring,基于python的web.py和Django,以及php的后端开发框架。调研发现spring框架学习周期长,而php这一门语言我们并不了解。Python语言团队成员都较为熟悉。Web.py一方面功能并不强大,且不支持Python3,所以我们最终选择了Django。这一框架学习周期较短,且功能完善,社区活跃,开发文档也很丰富。
前端技术规格
页面之间联系
- 前端分为主页,搜索结果页面,课程详细信息页面,添加评分评论页面四个部分。关系如下图:
各页面布局
- 此处只列出简略的页面布局,更加精细的布局原型在功能规格说明书中展示:
后端技术规格
数据库结构规格
- 数据库中需要存储学校,院系,课程,教师,用户,评价记录,评分记录7个实体,且实体之间存在联系,实体也包含其属性。联系属性如下图所示:
关于一些数据格式的说明
- 用户的用户名不能出现重复。一旦确定不能修改。邮箱不能重复,邮箱验证在beta阶段实现。用户权限暂分为完全权限和禁言两种。
- 评价记录呈树状结构生长。标签可以为空,其他的必须完整。
- 课程中的课程简介和课程编号可以为空。
- 教师信息中教师姓名,所属院系不能为空。
- 院系中院系名和所属学校不能为空。
- 课程信息的获取途径
- 在教务网站中爬取。经测试,可以使用JavaScript在网页上爬取信息。
- 在前端提供用户补充课程信息的接口。
前后端接口规格
POST和GET规格
- GET:
-
搜索:
/search/?school=[schoolName]&keyword=[input words]
返回搜索结果页面 -
课程详细:
/course/[course id]
返回课程详细页面 -
主页:
/index
或/
返回主页
-
- POST:
- 登录:
/signIn
传入 | username | string | |
password | string | ||
传出 | statCode | int | 0表示成功,-1表示用户名不存在,-2表示密码错误,-3表示未知错误 |
username | string | ||
neckname | string |
- 注册:
/signUp
传入 | username | string | |
string | |||
password | string | ||
传出 | statCode | int | 0表示成功,-1表示用户名重复,-2表示邮箱重复,-3表示未知错误 |
- 发表评论:
/course_addComment
传入 | username | string | |
content | string | ||
parentId | int | 如果为空表示没有,id就是隐藏起来的。现阶段会一直为空。 | |
courseId | int | ||
传出 | statCode | int | 0表示成功,-1表示用户名不存在,-2表示父评论不存在,-3表示未知错误 |
- 发表评分
/course_addRate
传入 | username | string | |
rate | int[] | 表示评分数组。 | |
courseId | int | ||
传出 | statCode | int | 0表示成功,-1表示用户名不存在,-2表示评分格式不合法,-3表示未知错误 |
网页模板使用的字典规格
- 需要填写模板的网页只有主页,搜索结果页面和课程详细信息页面
-
主页
{ "schoolNameList": [xxx, xxx, xxx] // a list of school name // 在确定搜索范围时会用到 }
-
搜索结果页面
{ "courseList": [ { "courseName": // string type "courseNumber": // string type "courseIndex": // int type "teachers": [...] // teacher names, string type "departmentName": // string type "description": // string type, can be null "overAllRate": // float type "commentCount": // number of comments, int }, ... ] // 这里xxxIndex是用来填到跳转页面部分的。例如: // 《a href="/course/{{ courseIndex }}"》courseName《/ a》 }
-
课程详细页
{ "courseName": // string type "courseNumber": // string type "courseIndex": // int type "description": // string type "credit": // int type "courseType": // string type, 例如专业课,核心通识之类的分类 "department": { "departmentName": // string type "departmentIndex": // int type } "teachers": [ { "teacherName": // string type }, ... ] "comments": [ { "userName": // string type "time": // string type "content": // string type "commentIndex": // int type } ] "rates": [ { "aspect": // rating aspect, string "value": // value of rate, float "count": // how many peaple rate it, int }, ... ] }
接口规格更新说明
-
/ or /index
得到网站首页 -
/search
method: get data: { keywords: school:(可选) department:(可选) } response: 搜索结果页面searchResult.html
-
/course/<course_number>
response: 课程详细信息页面CoursePage.html templates: { 'course_name': 'course_credit': 'course_profession': 'course_type': 'course_scores': 'detail_names': // name of detail rate subject 'detail_scores': // list of scores 'course_website': 'profession_website': }
-
course/<course_number>/rate
response: 课程评分页面ratePage.html templates: { 'course': { 'name': 'school': 'department': }, 'detail_names': }
-
use/<usename>
method: post data: { password: (可选) } response: 用户信息页面userPage.html templates: { 'userName': 'userimgurl': 'assessments': [ { 'courseName': 'course_id': 'content': 'time': 'likeCount': 'commentCount': } ... ] 'discussions': [ { 'userName': 'userimgurl': 'course_id': 'content': 'time': 'title': 'originalContent': 'newmsg': } ... ] }
-
/signIn
method: post data: { 'username': 'password': } response: { 'statCode': 'username': }
-
/signUp
method: post data: { 'username': 'mail': 'password': } response: { 'statCode': 'username': }
-
/active/<active_code>
response: { 'statCode': }
-
/submitComment
method: post data: { 'username': 'comment': 'rate':[] 'course_number': 'anonymous': 'term': 'thacher':[] } response: { 'statCode': }
-
/submitDiscuss
method: post data: { 'username': 'discuss': 'comment_id': } response: { 'statCode': }
-
/getSchool
method: get response: { 'school': }
-
/getDepartment
method: get data: { 'school': } response: { 'department': }
-
/getComment
method: get data: { 'username': 'course_number': } response: { 'comments': [ { 'userName': 'text': 'iTerm': 'iTotal': 'iId': 'isSelf': 'snum': 'cnum': 'support': } ... ] }
-
/getDiscuss
method: get data: { 'iId': } response: { 'discusses':[ { 'userName': 'text': 'time': 'discuss_id': } ... ] }
-
/getTeachers
method: get data: { course_number: } response: { 'teachers': }
-
/addComment/<comment_id>
method: get response: 追加评论页面addRatePage.html templates: { 'course': { 'name': 'school': 'department': 'term': 'teacher': } 'originalRate': }
-
/changeComment
method: post data: { 'comment_id': 'comment_add': 'password': } response: { 'statCode': 'course_number': }
-
/delComment
method: post data: { 'comment_id': 'passowrd': } response: { 'statCode': }
-
/changeSupport
method: post data: { 'comment_id': 'username': 'passowrd': } response: { 'statCode': }
-
/delDiscuss
method: post data: { 'discuss_id': 'password': } response: { 'statCode': }