337 lines
10 KiB
Markdown
337 lines
10 KiB
Markdown
|
|
# 软件开发工程师提示词(MySQL & Node.js 16.20.2 优化版)
|
|||
|
|
|
|||
|
|
## 项目概述
|
|||
|
|
开发一个前后端分离的 Web 应用,前端使用 Vue.js(3.x Composition API)、HTML5、JavaScript(ES6+)、CSS,后端使用 Node.js(版本 16.20.2,Express 框架),数据库为 MySQL。所有数据必须从 MySQL 动态获取,不允许硬编码或静态数据。前后端通过统一的 RESTful API 进行数据交互,使用 fetch 方法进行 HTTP 请求。接口格式需统一,筛选条件通过手动更新 filters 对象管理,确保绕过 v-model 可能存在的绑定问题,确保筛选条件正确更新和传递。
|
|||
|
|
|
|||
|
|
## 技术栈
|
|||
|
|
- **前端**:Vue.js (3.x Composition API)、HTML5、JavaScript (ES6+)、CSS
|
|||
|
|
- **后端**:Node.js (16.20.2, Express 框架)、Sequelize (6.x) 或 mysql2 (2.x)
|
|||
|
|
- **数据库**:MySQL(云服务如 AWS RDS,推荐 8.0.x)
|
|||
|
|
- **数据交互**:使用 fetch 方法,通过 RESTful API 进行前后端通信
|
|||
|
|
- **接口格式**:JSON,统一响应结构
|
|||
|
|
- **筛选条件管理**:手动更新 filters 对象,避免 v-model 绑定问题
|
|||
|
|
- **开发工具**:ESLint(前端)、StandardJS(后端)、Git
|
|||
|
|
|
|||
|
|
## 详细要求
|
|||
|
|
|
|||
|
|
### 1. 前端开发
|
|||
|
|
- **框架**:使用 Vue.js (3.x Composition API) 构建响应式界面。
|
|||
|
|
- **数据获取**:
|
|||
|
|
- 使用 fetch 方法从后端 API 获取数据。
|
|||
|
|
- 所有数据(如列表、筛选结果等)必须从 MySQL 动态加载,不允许硬编码或静态数据。
|
|||
|
|
- **筛选条件管理**:
|
|||
|
|
- 维护一个 reactive 的 filters 对象,用于存储筛选条件(如搜索关键词、分类、日期范围等)。
|
|||
|
|
- 通过事件处理程序手动更新 filters 对象(如 `filters.key = value`),避免直接使用 v-model 绑定可能导致的响应式问题。
|
|||
|
|
- 每次筛选条件更新后,触发 API 请求,将 filters 对象作为查询参数发送到后端。
|
|||
|
|
- **界面**:
|
|||
|
|
- 使用 HTML5 和 CSS 构建现代化、响应式布局。
|
|||
|
|
- 确保组件模块化,遵循 Vue 组件化开发规范。
|
|||
|
|
- **接口调用**:
|
|||
|
|
- 使用 fetch 方法发送 GET/POST 请求,统一处理 API 响应。
|
|||
|
|
- 示例 fetch 请求代码:
|
|||
|
|
```javascript
|
|||
|
|
async function fetchData(filters) {
|
|||
|
|
const query = new URLSearchParams(filters).toString();
|
|||
|
|
const response = await fetch(`/api/data?${query}`, {
|
|||
|
|
method: 'GET',
|
|||
|
|
headers: { 'Content-Type': 'application/json' }
|
|||
|
|
});
|
|||
|
|
const result = await response.json();
|
|||
|
|
return result;
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. 后端开发
|
|||
|
|
- **框架**:使用 Node.js 16.20.2 和 Express 框架(4.x,兼容 Node.js 16)构建 RESTful API。
|
|||
|
|
- **数据库交互**:
|
|||
|
|
- 连接 MySQL,所有数据从数据库动态查询。
|
|||
|
|
- 使用 mysql2 (2.x) 或 Sequelize (6.x) 管理 MySQL 交互,兼容 Node.js 16.20.2。
|
|||
|
|
- 示例(mysql2 连接池):
|
|||
|
|
```javascript
|
|||
|
|
const mysql = require('mysql2/promise');
|
|||
|
|
const pool = mysql.createPool({
|
|||
|
|
host: process.env.DB_HOST,
|
|||
|
|
user: process.env.DB_USER,
|
|||
|
|
password: process.env.DB_PASSWORD,
|
|||
|
|
database: 'project_db',
|
|||
|
|
waitForConnections: true,
|
|||
|
|
connectionLimit: 10,
|
|||
|
|
queueLimit: 0
|
|||
|
|
});
|
|||
|
|
```
|
|||
|
|
- 示例(Sequelize 模型):
|
|||
|
|
```javascript
|
|||
|
|
const { Sequelize, DataTypes } = require('sequelize');
|
|||
|
|
const sequelize = new Sequelize({
|
|||
|
|
dialect: 'mysql',
|
|||
|
|
host: process.env.DB_HOST,
|
|||
|
|
username: process.env.DB_USER,
|
|||
|
|
password: process.env.DB_PASSWORD,
|
|||
|
|
database: 'project_db'
|
|||
|
|
});
|
|||
|
|
const Data = sequelize.define('Data', {
|
|||
|
|
id: { type: DataTypes.INTEGER, autoIncrement: true, primaryKey: true },
|
|||
|
|
name: { type: DataTypes.STRING, allowNull: false },
|
|||
|
|
category: { type: DataTypes.STRING },
|
|||
|
|
date: { type: DataTypes.DATE }
|
|||
|
|
}, {
|
|||
|
|
indexes: [{ fields: ['name'] }, { fields: ['category'] }]
|
|||
|
|
});
|
|||
|
|
```
|
|||
|
|
- **API 设计**:
|
|||
|
|
- 统一接口路径,如 `/api/data`。
|
|||
|
|
- 支持查询 parameters(如 `?key=value`)处理筛选条件。
|
|||
|
|
- 统一响应格式:
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"status": "success",
|
|||
|
|
"data": [],
|
|||
|
|
"message": ""
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
- 示例 API 路由(使用 mysql2):
|
|||
|
|
```javascript
|
|||
|
|
const express = require('express');
|
|||
|
|
const mysql = require('mysql2/promise');
|
|||
|
|
const app = express();
|
|||
|
|
|
|||
|
|
const pool = mysql.createPool({
|
|||
|
|
host: process.env.DB_HOST,
|
|||
|
|
user: process.env.DB_USER,
|
|||
|
|
password: process.env.DB_PASSWORD,
|
|||
|
|
database: 'project_db'
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
app.use(express.json());
|
|||
|
|
app.use(require('cors')());
|
|||
|
|
|
|||
|
|
app.get('/api/data', async (req, res) => {
|
|||
|
|
try {
|
|||
|
|
const { name, category } = req.query;
|
|||
|
|
let query = 'SELECT * FROM data WHERE 1=1';
|
|||
|
|
const params = [];
|
|||
|
|
if (name) {
|
|||
|
|
query += ' AND name LIKE ?';
|
|||
|
|
params.push(`%${name}%`);
|
|||
|
|
}
|
|||
|
|
if (category) {
|
|||
|
|
query += ' AND category = ?';
|
|||
|
|
params.push(category);
|
|||
|
|
}
|
|||
|
|
const [rows] = await pool.query(query, params);
|
|||
|
|
res.json({ status: 'success', data: rows, message: '' });
|
|||
|
|
} catch (error) {
|
|||
|
|
res.status(500).json({ status: 'error', data: [], message: error.message });
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
app.listen(3000, () => console.log('Server running on port 3000'));
|
|||
|
|
```
|
|||
|
|
- **安全性**:
|
|||
|
|
- 使用参数化查询防止 SQL 注入。
|
|||
|
|
- 使用环境 variables(`dotenv` 包,兼容 Node.js 16.20.2)存储数据库连接信息。
|
|||
|
|
- 示例 `.env` 文件:
|
|||
|
|
```env
|
|||
|
|
DB_HOST=localhost
|
|||
|
|
DB_USER=root
|
|||
|
|
DB_PASSWORD=your_password
|
|||
|
|
DB_DATABASE=project_db
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3. 统一接口格式
|
|||
|
|
- **请求格式**:
|
|||
|
|
- GET 请求:通过 URL 查询参数传递 filters(如 `/api/data?name=example&category=test`)。
|
|||
|
|
- POST 请求:通过 JSON body 传递 filters。
|
|||
|
|
- **响应格式**:
|
|||
|
|
- 成功响应:
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"status": "success",
|
|||
|
|
"data": [/* 数据数组 */],
|
|||
|
|
"message": ""
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
- 错误响应:
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"status": "error",
|
|||
|
|
"data": [],
|
|||
|
|
"message": "错误描述"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 4. 筛选条件管理
|
|||
|
|
- **前端**:
|
|||
|
|
- 定义 filters 对象:
|
|||
|
|
```javascript
|
|||
|
|
import { reactive } from 'vue';
|
|||
|
|
const filters = reactive({
|
|||
|
|
name: '',
|
|||
|
|
category: '',
|
|||
|
|
dateRange: ''
|
|||
|
|
});
|
|||
|
|
```
|
|||
|
|
- 手动更新 filters:
|
|||
|
|
```javascript
|
|||
|
|
function updateFilter(key, value) {
|
|||
|
|
filters[key] = value;
|
|||
|
|
fetchData(filters); // 触发 API 请求
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
- 在 UI 中绑定事件(如 `@input`, `@change`)调用 updateFilter。
|
|||
|
|
- **后端**:
|
|||
|
|
- 解析查询参数或请求 body,转换为 MySQL 查询条件。
|
|||
|
|
- 示例 MySQL 查询:
|
|||
|
|
```javascript
|
|||
|
|
let query = 'SELECT * FROM data WHERE 1=1';
|
|||
|
|
const params = [];
|
|||
|
|
if (filters.name) {
|
|||
|
|
query += ' AND name LIKE ?';
|
|||
|
|
params.push(`%${filters.name}%`);
|
|||
|
|
}
|
|||
|
|
if (filters.category) {
|
|||
|
|
query += ' AND category = ?';
|
|||
|
|
params.push(filters.category);
|
|||
|
|
}
|
|||
|
|
const [rows] = await pool.query(query, params);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 5. 其他要求
|
|||
|
|
- **Node.js 16.20.2 兼容性**:
|
|||
|
|
- 使用兼容的依赖版本,如 `express@4.18.x`, `mysql2@2.3.x`, `sequelize@6.29.x`, `cors@2.8.x`.
|
|||
|
|
- 避免使用 Node.js 18+ 的新特性(如内置 `fetch`)。
|
|||
|
|
- **代码规范**:
|
|||
|
|
- 遵循 ESLint(前端,推荐 `@vue/eslint-config-standard`)和 StandardJS(后端)。
|
|||
|
|
- 组件和函数命名清晰,遵循 camelCase 或 PascalCase。
|
|||
|
|
- **错误处理**:
|
|||
|
|
- 前端:显示用户友好的错误提示(如 “无匹配数据”)。
|
|||
|
|
- 后端:返回明确的错误状态码和消息(如 400、500)。
|
|||
|
|
- **性能优化**:
|
|||
|
|
- 前端:使用防抖(debounce,300ms)或节流(throttle)优化频繁的筛选请求。
|
|||
|
|
- 后端:为 MySQL 表添加索引(如 `INDEX idx_name (name)`),使用连接池管理数据库连接。
|
|||
|
|
- **依赖管理**:
|
|||
|
|
- 示例 `package.json`:
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"dependencies": {
|
|||
|
|
"express": "^4.18.2",
|
|||
|
|
"mysql2": "^2.3.3",
|
|||
|
|
"sequelize": "^6.29.0",
|
|||
|
|
"cors": "^2.8.5",
|
|||
|
|
"dotenv": "^16.0.3"
|
|||
|
|
},
|
|||
|
|
"engines": {
|
|||
|
|
"node": "16.20.2"
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 6. MySQL 表结构
|
|||
|
|
- **示例表结构**:
|
|||
|
|
```sql
|
|||
|
|
CREATE TABLE data (
|
|||
|
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
|||
|
|
name VARCHAR(255) NOT NULL,
|
|||
|
|
category VARCHAR(100),
|
|||
|
|
date DATETIME,
|
|||
|
|
INDEX idx_name (name),
|
|||
|
|
INDEX idx_category (category)
|
|||
|
|
);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 示例代码
|
|||
|
|
|
|||
|
|
### 前端(Vue 组件)
|
|||
|
|
```vue
|
|||
|
|
<template>
|
|||
|
|
<div>
|
|||
|
|
<input type="text" placeholder="搜索名称" @input="updateFilter('name', $event.target.value)" />
|
|||
|
|
<select @change="updateFilter('category', $event.target.value)">
|
|||
|
|
<option value="">全部分类</option>
|
|||
|
|
<option value="category1">分类1</option>
|
|||
|
|
</select>
|
|||
|
|
<ul>
|
|||
|
|
<li v-for="item in dataList" :key="item.id">{{ item.name }}</li>
|
|||
|
|
</ul>
|
|||
|
|
</div>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<script setup>
|
|||
|
|
import { reactive, ref } from 'vue';
|
|||
|
|
|
|||
|
|
const filters = reactive({
|
|||
|
|
name: '',
|
|||
|
|
category: ''
|
|||
|
|
});
|
|||
|
|
const dataList = ref([]);
|
|||
|
|
|
|||
|
|
async function fetchData() {
|
|||
|
|
const query = new URLSearchParams(filters).toString();
|
|||
|
|
const response = await fetch(`/api/data?${query}`);
|
|||
|
|
const result = await response.json();
|
|||
|
|
if (result.status === 'success') {
|
|||
|
|
dataList.value = result.data;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function updateFilter(key, value) {
|
|||
|
|
filters[key] = value;
|
|||
|
|
fetchData();
|
|||
|
|
}
|
|||
|
|
</script>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 后端(Node.js 16.20.2 + Express + MySQL)
|
|||
|
|
```javascript
|
|||
|
|
const express = require('express');
|
|||
|
|
const mysql = require('mysql2/promise');
|
|||
|
|
const cors = require('cors');
|
|||
|
|
const app = express();
|
|||
|
|
|
|||
|
|
const pool = mysql.createPool({
|
|||
|
|
host: process.env.DB_HOST,
|
|||
|
|
user: process.env.DB_USER,
|
|||
|
|
password: process.env.DB_PASSWORD,
|
|||
|
|
database: 'project_db',
|
|||
|
|
waitForConnections: true,
|
|||
|
|
connectionLimit: 10,
|
|||
|
|
queueLimit: 0
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
app.use(express.json());
|
|||
|
|
app.use(cors());
|
|||
|
|
|
|||
|
|
app.get('/api/data', async (req, res) => {
|
|||
|
|
try {
|
|||
|
|
const { name, category } = req.query;
|
|||
|
|
let query = 'SELECT * FROM data WHERE 1=1';
|
|||
|
|
const params = [];
|
|||
|
|
if (name) {
|
|||
|
|
query += ' AND name LIKE ?';
|
|||
|
|
params.push(`%${name}%`);
|
|||
|
|
}
|
|||
|
|
if (category) {
|
|||
|
|
query += ' AND category = ?';
|
|||
|
|
params.push(category);
|
|||
|
|
}
|
|||
|
|
const [rows] = await pool.query(query, params);
|
|||
|
|
res.json({ status: 'success', data: rows, message: '' });
|
|||
|
|
} catch (error) {
|
|||
|
|
res.status(500).json({ status: 'error', data: [], message: error.message });
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
app.listen(3000, () => console.log('Server running on port 3000'));
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### MySQL 表结构
|
|||
|
|
```sql
|
|||
|
|
CREATE TABLE data (
|
|||
|
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
|||
|
|
name VARCHAR(255) NOT NULL,
|
|||
|
|
category VARCHAR(100),
|
|||
|
|
date DATETIME,
|
|||
|
|
INDEX idx_name (name),
|
|||
|
|
INDEX idx_category (category)
|
|||
|
|
);
|
|||
|
|
```
|