164
社区成员
发帖
与我相关
我的任务
分享姓名:陶炯 FZU学号:832301320
发布时间:2025-11-09
所属课程:EE308FZ[A] — Software Engineering
作业要求:需采用前后端分离架构实现联系人添加、修改、删除核心功能(数据存储于后端数据库),支持多可视化技术选型,代码分仓托管至 Github 并遵循规范,需完成博客撰写与云服务器部署。
本次作业目标:掌握前后端分离开发与数据交互逻辑,熟练运用版本控制工具与云部署流程,培养标准化编码与文档撰写能力。
目录
| 项目项 | 内容 |
|---|---|
| 课程名称 | EE308FZ[A] — Software Engineering |
| 作业名称 | 前后端分离通讯录编程 |
| 作业目标 | 掌握前后端分离架构开发流程,实现联系人增删改核心功能 |
| 技术栈 | 前端:HTML+CSS+JavaScript+Vue.js;后端:Python+Flask;数据库:SQLite |
开发前进行时间预估,开发后记录实际耗时,具体统计如下:
| 开发任务 | 预计时间(小时) | 实际时间(小时) |
|---|---|---|
| 需求分析与技术选型 | 1 | 0.5 |
| 前端页面布局与样式开发 | 3 | 4 |
| 前端Vue逻辑开发 | 1 | 1.5 |
| 后端Flask框架搭建 | 1 | 1 |
| 后端API与数据库开发 | 3 | 3 |
| 前后端联调测试 | 3 | 6 |
| 文档编写(README+博客) | 1.5 | 2 |
| 总计 | 13.5 | 18 |
本次开发的通讯录实现了添加、修改、删除核心功能,界面简洁直观,以下为功能演示截图(共10张,含完整操作流程):
启动后端后打开前端页面,默认显示空通讯录列表和添加表单,界面布局清晰

步骤1:在表单中输入姓名和电话号码(示例:姓名"Freddy",电话"12345678")



连续添加多个联系人数据,验证列表展示效果和数据存储稳定性。

步骤1:点击对应的"Edit"按钮,表单自动填充该联系人信息

步骤2:修改电话号码为"13900139000",点击"Update"按钮提交修改

步骤1:点击"mark"对应的"Delete"按钮,弹出确认提示框


测试删除最后一条联系人后,界面显示空状态提示

项目已部署至云服务器,通过公网可访问
访问链接:http://8.148.231.135/
采用经典前后端分离架构,职责划分清晰:
核心功能围绕联系人管理展开,结构如下:
通讯录系统
├── 前端功能
│ ├── 联系人表单(添加/编辑复用)
│ ├── 联系人列表展示
│ └── 联系人操作(编辑、删除、取消)
└── 后端功能
├── 数据库初始化(自动创建表)
├── API接口(5个核心接口)
│ ├── GET /contacts 获取所有联系人
│ ├── GET /contacts/{id} 获取单个联系人
│ ├── POST /contacts 添加联系人
│ ├── PUT /contacts/{id} 修改联系人
│ └── DELETE /contacts/{id} 删除联系人
└── 跨域支持(解决前后端端口不一致问题)
采用SQLite数据库,设计单表contacts存储联系人信息,表结构如下:
| 字段名 | 数据类型 | 主键/约束 | 说明 |
|---|---|---|---|
| id | INTEGER | 主键,自增 | 联系人唯一标识 |
| name | TEXT | NOT NULL | 联系人姓名(必填) |
| phone | TEXT | NOT NULL | 联系人电话(必填 |
数据库初始化由后端models.py中的init_db()函数自动完成,启动后端时会检查并创建表。
以"添加联系人"为例,完整流程如下:
前端核心逻辑在js/app.js中,实现数据绑定和API调用:
new Vue({
el: '#app',
data: {
contacts: [],
currentContact: {
id: null,
name: '',
phone: ''
},
isEditing: false
},
mounted() {
// Load contacts when the app starts
this.fetchContacts();
},
methods: {
// Fetch all contacts from the backend
fetchContacts() {
fetch('http://8.148.231.135:5000/contacts')
.then(response => response.json())
.then(data => {
this.contacts = data;
})
.catch(error => console.error('Error fetching contacts:', error));
},
// Save a new contact or update an existing one
saveContact() {
if (this.isEditing) {
// Update existing contact
fetch(`http://8.148.231.135:5000/contacts/${this.currentContact.id}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(this.currentContact)
})
.then(() => {
this.fetchContacts();
this.resetForm();
})
.catch(error => console.error('Error updating contact:', error));
} else {
// Add new contact
fetch('http://8.148.231.135:5000/contacts', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: this.currentContact.name,
phone: this.currentContact.phone
})
})
.then(() => {
this.fetchContacts();
this.resetForm();
})
.catch(error => console.error('Error adding contact:', error));
}
},
// Prepare form for editing a contact
editContact(contact) {
this.currentContact = { ...contact };
this.isEditing = true;
},
// Delete a contact
deleteContact(id) {
if (confirm('Are you sure you want to delete this contact?')) {
fetch(`http://8.148.231.135:5000/contacts/${id}`, {
method: 'DELETE'
})
.then(() => {
this.fetchContacts();
})
.catch(error => console.error('Error deleting contact:', error));
}
},
// Reset the form
resetForm() {
this.currentContact = {
id: null,
name: '',
phone: ''
};
this.isEditing = false;
}
}
});
关键解析:通过isEditing状态区分添加和编辑,复用同一个表单和保存方法,减少冗余代码;使用fetch实现前后端数据交互,成功后刷新列表确保数据同步。
(1)数据库操作(models.py)
import sqlite3
from config import Config
def get_db_connection():
"""Create and return a database connection"""
conn = sqlite3.connect(Config.DATABASE_URI)
conn.row_factory = sqlite3.Row
return conn
def init_db():
"""Initialize the database with the contacts table"""
conn = get_db_connection()
conn.execute('''
CREATE TABLE IF NOT EXISTS contacts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
phone TEXT NOT NULL
)
''')
conn.commit()
conn.close()
def get_all_contacts():
"""Get all contacts from the database"""
conn = get_db_connection()
contacts = conn.execute('SELECT * FROM contacts').fetchall()
conn.close()
return [dict(contact) for contact in contacts]
def get_contact_by_id(contact_id):
"""Get a single contact by ID"""
conn = get_db_connection()
contact = conn.execute('SELECT * FROM contacts WHERE id = ?', (contact_id,)).fetchone()
conn.close()
return dict(contact) if contact else None
def add_contact(name, phone):
"""Add a new contact to the database"""
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute('INSERT INTO contacts (name, phone) VALUES (?, ?)', (name, phone))
conn.commit()
contact_id = cursor.lastrowid
conn.close()
return contact_id
def update_contact(contact_id, name, phone):
"""Update an existing contact"""
conn = get_db_connection()
conn.execute('UPDATE contacts SET name = ?, phone = ? WHERE id = ?',
(name, phone, contact_id))
conn.commit()
conn.close()
return True
def delete_contact(contact_id):
"""Delete a contact from the database"""
conn = get_db_connection()
conn.execute('DELETE FROM contacts WHERE id = ?', (contact_id,))
conn.commit()
conn.close()
return True
关键解析:封装get_db_connection()方法复用数据库连接,避免重复代码;使用参数化查询,防止SQL注入,提升安全性;init_db()在后端启动时自动执行,无需手动建表。
(2)API接口(routes.py)
from flask import Blueprint, request, jsonify
import models
# Create a Blueprint for contact routes
contact_bp = Blueprint('contacts', __name__)
@contact_bp.route('/contacts', methods=['GET'])
def get_contacts():
"""Get all contacts"""
contacts = models.get_all_contacts()
return jsonify(contacts)
@contact_bp.route('/contacts/<int:contact_id>', methods=['GET'])
def get_contact(contact_id):
"""Get a single contact by ID"""
contact = models.get_contact_by_id(contact_id)
if contact:
return jsonify(contact)
return jsonify({'error': 'Contact not found'}), 404
@contact_bp.route('/contacts', methods=['POST'])
def add_contact():
"""Add a new contact"""
data = request.get_json()
if not data or 'name' not in data or 'phone' not in data:
return jsonify({'error': 'Name and phone number are required'}), 400
contact_id = models.add_contact(data['name'], data['phone'])
return jsonify({'id': contact_id, 'message': 'Contact added successfully'}), 201
@contact_bp.route('/contacts/<int:contact_id>', methods=['PUT'])
def update_contact(contact_id):
"""Update an existing contact"""
data = request.get_json()
if not data or 'name' not in data or 'phone' not in data:
return jsonify({'error': 'Name and phone number are required'}), 400
# Check if contact exists
contact = models.get_contact_by_id(contact_id)
if not contact:
return jsonify({'error': 'Contact not found'}), 404
models.update_contact(contact_id, data['name'], data['phone'])
return jsonify({'message': 'Contact updated successfully'})
@contact_bp.route('/contacts/<int:contact_id>', methods=['DELETE'])
def delete_contact(contact_id):
"""Delete a contact"""
# Check if contact exists
contact = models.get_contact_by_id(contact_id)
if not contact:
return jsonify({'error': 'Contact not found'}), 404
models.delete_contact(contact_id)
return jsonify({'message': 'Contact deleted successfully'})
(3)应用入口(app.py)
from flask import Flask
from flask_cors import CORS
import models
from routes import contact_bp
# Create Flask app
app = Flask(__name__)
# Enable CORS to allow cross-origin requests
CORS(app)
# Register blueprints
app.register_blueprint(contact_bp)
# Initialize database
models.init_db()
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=False)
关键解析:使用Flask-CORS解决前后端跨域问题(前端80端口,后端5000端口);通过蓝图(Blueprint)管理路由,使代码结构更清晰;启动时自动初始化数据库。
本次采用阿里云轻量应用服务器部署,步骤如下:
安装Python3:yum install python3 -y(CentOS系统)
进入后端src目录:cd /home/project/backend/src
安装Nginx:yum install nginx -y