移除 mvcar 目录,已独立为单独仓库

This commit is contained in:
halliday 2025-11-03 11:23:09 +08:00
parent e4e52db4f3
commit e50111aaad
18 changed files with 0 additions and 842 deletions

View File

@ -1,51 +0,0 @@
FROM python:3.9-slim
# 设置时区
ENV TZ=Asia/Shanghai
# 完全清空并重写所有源文件
RUN rm -f /etc/apt/sources.list /etc/apt/sources.list.d/* && \
echo "deb https://mirrors.tuna.tsinghua.edu.cn/debian/ trixie main" > /etc/apt/sources.list && \
echo "deb https://mirrors.tuna.tsinghua.edu.cn/debian/ trixie-updates main" >> /etc/apt/sources.list && \
echo "deb https://mirrors.tuna.tsinghua.edu.cn/debian-security trixie-security main" >> /etc/apt/sources.list
# 设置 ll 别名
RUN echo "alias ll='ls -alF'" >> /root/.bashrc
# 安装 nginx
RUN apt-get update && apt-get install -y --no-install-recommends \
nginx \
&& rm -rf /var/lib/apt/lists/*
# 设置 pip 清华源
RUN pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple && \
pip config set global.trusted-host pypi.tuna.tsinghua.edu.cn
# 创建应用目录
WORKDIR /app
# 复制后端文件
COPY backend/ ./backend/
COPY frontend/ /var/www/html/
# 安装Python依赖
RUN pip install --no-cache-dir -r backend/requirements.txt
# 创建数据和日志目录
RUN mkdir -p data logs
# 配置nginx
COPY nginx/nginx.conf /etc/nginx/sites-available/default
# 暴露端口
EXPOSE 80
# 启动脚本
COPY sbin/start.sh /start.sh
RUN chmod +x /start.sh
# 初始化数据库
RUN python backend/init_db.py
ENTRYPOINT ["/start.sh"]

View File

@ -1,11 +0,0 @@
[[source]]
url = "https://pypi.tuna.tsinghua.edu.cn/simple\n"
verify_ssl = true
name = "pip_conf_index_global"
[packages]
[dev-packages]
[requires]
python_version = "3.8"

View File

@ -1,113 +0,0 @@
# backend/app.py
import logging
from logging.handlers import RotatingFileHandler
from flask import Flask, jsonify, request
from flask_cors import CORS
import sqlite3
import os
# 获取当前文件所在目录
basedir = os.path.abspath(os.path.dirname(__file__))
db_path = os.path.join(basedir, '..', 'data', 'cars.db')
# 创建 logs 目录
logs_dir = os.path.join(basedir, '..', 'logs')
os.makedirs(logs_dir, exist_ok=True)
# 配置日志
log_formatter = logging.Formatter(
'%(asctime)s %(levelname)s %(name)s %(message)s'
)
log_file = os.path.join(logs_dir, 'app.log')
# 创建 rotating file handler (最大5MB保留5个备份)
file_handler = RotatingFileHandler(
log_file,
maxBytes=5*1024*1024, # 5MB
backupCount=5
)
file_handler.setFormatter(log_formatter)
file_handler.setLevel(logging.INFO)
# 创建 logger
app = Flask(__name__)
app.logger.addHandler(file_handler)
app.logger.setLevel(logging.INFO)
# 添加 CORS 支持
CORS(app)
# 数据库连接函数
def get_db_connection():
conn = sqlite3.connect(db_path)
conn.row_factory = sqlite3.Row
# 确保使用UTF-8编码
conn.execute('PRAGMA encoding = "UTF-8"')
return conn
@app.route('/api/cars', methods=['GET'])
def get_all_cars():
app.logger.info('获取所有车辆信息')
try:
conn = get_db_connection()
cars = conn.execute('SELECT * FROM cars').fetchall()
conn.close()
cars_list = [dict(car) for car in cars]
app.logger.info(f'成功获取 {len(cars_list)} 条车辆记录')
return jsonify(cars_list)
except Exception as e:
app.logger.error(f'获取所有车辆信息时出错: {str(e)}')
return jsonify({'error': '服务器内部错误'}), 500
@app.route('/api/cars/search', methods=['GET'])
def search_car():
plate = request.args.get('plate', '')
app.logger.info(f'搜索车牌号码: {plate}')
if not plate:
app.logger.warning('用户未提供车牌号码')
return jsonify({'error': '请输入车牌号码'}), 400
try:
conn = get_db_connection()
# 修改查询语句以正确处理中文字符
cars = conn.execute('SELECT * FROM cars WHERE plate LIKE ?', (f'%{plate}%',)).fetchall()
conn.close()
cars_list = [dict(car) for car in cars]
app.logger.info(f'搜索 "{plate}" 返回 {len(cars_list)} 条结果')
return jsonify(cars_list)
except Exception as e:
app.logger.error(f'搜索车牌号码时出错: {str(e)}')
return jsonify({'error': '服务器内部错误'}), 500
@app.route('/api/cars/<plate>', methods=['GET'])
def get_car_by_plate(plate):
app.logger.info(f'查询特定车牌号码: {plate}')
try:
conn = get_db_connection()
# 修改查询语句以正确处理中文字符
car = conn.execute('SELECT * FROM cars WHERE plate = ?', (plate,)).fetchone()
conn.close()
if car is None:
app.logger.warning(f'未找到车牌号码: {plate}')
return jsonify({'error': '未找到该车牌号码对应的车主信息'}), 404
app.logger.info(f'成功找到车牌号码: {plate}')
return jsonify(dict(car))
except Exception as e:
app.logger.error(f'查询特定车牌时出错: {str(e)}')
return jsonify({'error': '服务器内部错误'}), 500
# 添加一个根路径用于健康检查
@app.route('/', methods=['GET'])
def health_check():
app.logger.info('健康检查请求')
return jsonify({'status': 'ok', 'message': '服务正常运行'})
if __name__ == '__main__':
app.logger.info('启动 Flask 应用')
app.run(debug=True, host='0.0.0.0', port=5000)

View File

@ -1,41 +0,0 @@
# backend/init_db.py
import sqlite3
import os
# 获取当前文件所在目录
basedir = os.path.abspath(os.path.dirname(__file__))
db_path = os.path.join(basedir, '..', 'data', 'cars.db')
# 创建数据目录(如果不存在)
os.makedirs(os.path.dirname(db_path), exist_ok=True)
# 创建数据库连接
conn = sqlite3.connect(db_path)
# 确保使用UTF-8编码
conn.execute('PRAGMA encoding = "UTF-8"')
cursor = conn.cursor()
# 创建车辆表
cursor.execute('''
CREATE TABLE IF NOT EXISTS cars (
id INTEGER PRIMARY KEY AUTOINCREMENT,
plate TEXT UNIQUE NOT NULL,
phone TEXT NOT NULL,
brand TEXT NOT NULL
)
''')
# 插入示例数据
sample_cars = [
("京B67890", "131-2222-2222", "本田雅阁"),
("沪C11111", "132-3333-3333", "大众帕萨特"),
("浙D22222", "133-4444-4444", "奔驰E级")
]
cursor.executemany('INSERT OR IGNORE INTO cars (plate, phone, brand) VALUES (?, ?, ?)', sample_cars)
# 提交更改并关闭连接
conn.commit()
conn.close()
print("数据库初始化完成")

View File

@ -1,3 +0,0 @@
# backend/requirements.txt
Flask==2.3.2
flask-cors==4.0.0

View File

@ -1,24 +0,0 @@
# backend/view_logs.py
import os
# 获取当前文件所在目录
basedir = os.path.abspath(os.path.dirname(__file__))
log_file = os.path.join(basedir, '..', 'logs', 'app.log')
def view_logs(lines=20):
"""查看最新的日志条目"""
try:
with open(log_file, 'r', encoding='utf-8') as f:
all_lines = f.readlines()
# 获取最后几行
latest_lines = all_lines[-lines:] if len(all_lines) > lines else all_lines
print(f"最新 {len(latest_lines)} 条日志:")
for line in latest_lines:
print(line.strip())
except FileNotFoundError:
print("日志文件不存在,请先运行应用程序")
except Exception as e:
print(f"读取日志文件时出错: {e}")
if __name__ == '__main__':
view_logs()

Binary file not shown.

View File

@ -1,10 +0,0 @@
version: '3.8'
services:
mvcar:
build: .
ports:
- "80:80"
volumes:
- ./data:/app/data
- ./logs:/app/logs

View File

@ -1,180 +0,0 @@
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
}
body {
background-color: #f5f7fa;
color: #333;
line-height: 1.6;
padding: 20px;
display: flex;
flex-direction: column;
align-items: center;
min-height: 100vh;
}
.container {
max-width: 500px;
width: 100%;
background: white;
border-radius: 16px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
overflow: hidden;
margin-top: 20px;
}
.header {
background: linear-gradient(135deg, #4a6cf7, #6a82fb);
color: white;
padding: 25px 20px;
text-align: center;
}
.header h1 {
font-size: 24px;
margin-bottom: 8px;
}
.header p {
opacity: 0.9;
font-size: 15px;
}
.content {
padding: 25px;
}
.info-card {
background: #f8f9ff;
border-radius: 12px;
padding: 20px;
margin-bottom: 25px;
border-left: 4px solid #4a6cf7;
}
.info-card h2 {
color: #4a6cf7;
font-size: 18px;
margin-bottom: 10px;
display: flex;
align-items: center;
}
.info-card h2 i {
margin-right: 8px;
}
.phone-number {
font-size: 28px;
font-weight: bold;
color: #333;
margin: 15px 0;
text-align: center;
letter-spacing: 1px;
}
.call-button {
display: block;
width: 100%;
background: #4a6cf7;
color: white;
border: none;
border-radius: 50px;
padding: 16px;
font-size: 18px;
font-weight: bold;
cursor: pointer;
transition: all 0.3s ease;
margin-top: 20px;
display: flex;
justify-content: center;
align-items: center;
box-shadow: 0 4px 12px rgba(74, 108, 247, 0.3);
text-decoration: none;
}
.call-button i {
margin-right: 8px;
}
.call-button:hover {
background: #3a5ce5;
transform: translateY(-2px);
box-shadow: 0 6px 15px rgba(74, 108, 247, 0.4);
}
.car-info {
background: #f8f9ff;
border-radius: 12px;
padding: 20px;
margin-bottom: 25px;
}
.car-info h3 {
color: #4a6cf7;
margin-bottom: 15px;
font-size: 17px;
}
.car-info p {
margin-bottom: 10px;
}
.instructions {
margin-top: 30px;
background: #f8f9ff;
border-radius: 12px;
padding: 20px;
}
.instructions h3 {
color: #4a6cf7;
margin-bottom: 15px;
font-size: 17px;
}
.instructions ul {
list-style-type: none;
}
.instructions li {
margin-bottom: 12px;
padding-left: 24px;
position: relative;
}
.instructions li:before {
content: "•";
color: #4a6cf7;
font-weight: bold;
position: absolute;
left: 8px;
}
.footer {
text-align: center;
margin-top: 30px;
color: #666;
font-size: 14px;
}
@media (max-width: 480px) {
.container {
margin-top: 10px;
}
.header {
padding: 20px 15px;
}
.content {
padding: 20px;
}
.phone-number {
font-size: 24px;
}
}

View File

@ -1,128 +0,0 @@
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
}
body {
background-color: #f5f7fa;
color: #333;
line-height: 1.6;
padding: 20px;
display: flex;
flex-direction: column;
align-items: center;
min-height: 100vh;
}
.container {
max-width: 500px;
width: 100%;
background: white;
border-radius: 16px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
overflow: hidden;
margin-top: 20px;
}
.header {
background: linear-gradient(135deg, #4a6cf7, #6a82fb);
color: white;
padding: 25px 20px;
text-align: center;
}
.header h1 {
font-size: 24px;
margin-bottom: 8px;
}
.header p {
opacity: 0.9;
font-size: 15px;
}
.content {
padding: 25px;
}
.search-section {
text-align: center;
}
.search-section h2 {
color: #4a6cf7;
margin-bottom: 20px;
font-size: 20px;
}
.input-group {
margin-bottom: 20px;
}
.license-input {
width: 100%;
padding: 15px;
border: 2px solid #e1e5f0;
border-radius: 12px;
font-size: 16px;
text-align: center;
transition: border-color 0.3s;
}
.license-input:focus {
border-color: #4a6cf7;
outline: none;
}
.search-button {
width: 100%;
background: #4a6cf7;
color: white;
border: none;
border-radius: 50px;
padding: 16px;
font-size: 18px;
font-weight: bold;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 4px 12px rgba(74, 108, 247, 0.3);
}
.search-button:hover {
background: #3a5ce5;
transform: translateY(-2px);
box-shadow: 0 6px 15px rgba(74, 108, 247, 0.4);
}
.error-message {
color: #e74c3c;
text-align: center;
padding: 15px;
background-color: #fdf2f2;
border-radius: 8px;
margin-top: 20px;
display: none;
}
.footer {
text-align: center;
margin-top: 30px;
color: #666;
font-size: 14px;
}
@media (max-width: 480px) {
.container {
margin-top: 10px;
}
.header {
padding: 20px 15px;
}
.content {
padding: 20px;
}
}

View File

@ -1,34 +0,0 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>挪车电话查询</title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<div class="container">
<div class="header">
<h1>挪车电话查询</h1>
<p>请输入车牌号码查询车主联系方式</p>
</div>
<div class="content">
<div class="search-section">
<h2>车牌号码查询</h2>
<div class="input-group">
<input type="text" class="license-input" id="licensePlate" placeholder="请输入车牌号码例如粤A12345">
</div>
<button class="search-button" id="searchButton">查询</button>
<div class="error-message" id="errorMessage"></div>
</div>
</div>
</div>
<div class="footer">
<p>挪车电话服务 © kiki.kim</p>
</div>
<script src="js/script.js"></script>
</body>
</html>

View File

@ -1,31 +0,0 @@
// frontend/js/result.js
document.addEventListener('DOMContentLoaded', function() {
console.log('页面加载完成 - 挪车电话结果页面');
const phoneNumberElement = document.getElementById('phoneNumber');
const plateNumberElement = document.getElementById('plateNumber');
const carBrandElement = document.getElementById('carBrand');
const callButton = document.getElementById('callButton');
// 从 sessionStorage 获取车辆信息
const carInfoJson = sessionStorage.getItem('carInfo');
console.log('从 sessionStorage 获取车辆信息');
if (carInfoJson) {
const carInfo = JSON.parse(carInfoJson);
console.log(`车辆信息: ${JSON.stringify(carInfo)}`);
// 显示车辆信息
phoneNumberElement.textContent = carInfo.phone;
plateNumberElement.textContent = carInfo.plate;
carBrandElement.textContent = carInfo.brand;
// 设置拨号链接
callButton.href = 'tel:' + carInfo.phone.replace(/-/g, '');
console.log(`设置拨号链接: ${callButton.href}`);
} else {
console.warn('未找到车辆信息,重定向到搜索页面');
// 如果没有车辆信息,返回搜索页面
window.location.href = 'index.html';
}
});

View File

@ -1,90 +0,0 @@
// frontend/js/script.js
document.addEventListener('DOMContentLoaded', function() {
const searchButton = document.getElementById('searchButton');
const licensePlateInput = document.getElementById('licensePlate');
const errorMessage = document.getElementById('errorMessage');
console.log('页面加载完成 - 挪车电话查询页面');
// 添加搜索频率限制
let lastSearchTime = 0;
const SEARCH_DELAY = 1000; // 1秒内只能搜索一次
searchButton.addEventListener('click', function() {
const plate = licensePlateInput.value.trim();
console.log(`用户尝试搜索车牌号码: ${plate}`);
// 检查输入长度
if (plate.length < 3) {
console.warn('输入字符少于3个');
showError('请输入至少3个字符进行搜索');
return;
}
// 检查搜索频率
const currentTime = new Date().getTime();
if (currentTime - lastSearchTime < SEARCH_DELAY) {
console.warn('搜索过于频繁');
showError('搜索过于频繁,请稍后再试');
return;
}
lastSearchTime = currentTime;
if (!plate) {
console.warn('未输入车牌号码');
showError('请输入车牌号码');
return;
}
// 从后端API获取数据
console.log(`发送请求到: /api/cars/search?plate=${encodeURIComponent(plate)}`);
fetch(`/api/cars/search?plate=${encodeURIComponent(plate)}`)
.then(response => {
console.log(`收到响应状态: ${response.status}`);
if (!response.ok) {
throw new Error('网络响应错误');
}
return response.json();
})
.then(data => {
console.log(`收到数据,共 ${data.length} 条记录`);
if (data.length > 0) {
// 取第一个匹配的结果
const carInfo = data[0];
console.log(`找到车辆信息: ${JSON.stringify(carInfo)}`);
// 将车辆信息存储到 sessionStorage
sessionStorage.setItem('carInfo', JSON.stringify(carInfo));
console.log('跳转到结果页面');
// 跳转到结果页面
window.location.href = 'result.html';
} else {
console.warn('未找到匹配的车辆信息');
showError('没有找到该车牌号码对应的车主信息');
}
})
.catch(error => {
console.error('请求失败:', error);
showError('查询失败,请稍后再试');
});
});
// 回车键触发搜索
licensePlateInput.addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
console.log('用户按下回车键触发搜索');
searchButton.click();
}
});
function showError(message) {
console.log(`显示错误信息: ${message}`);
errorMessage.textContent = message;
errorMessage.style.display = 'block';
// 3秒后隐藏错误信息
setTimeout(() => {
console.log('隐藏错误信息');
errorMessage.style.display = 'none';
}, 3000);
}
});

View File

@ -1,48 +0,0 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>挪车电话</title>
<link rel="stylesheet" href="css/result.css">
</head>
<body>
<div class="container">
<div class="header">
<h1>挪车电话</h1>
<p>如需挪车,请直接拨打下方电话</p>
</div>
<div class="content">
<div class="info-card">
<h2><i>📞</i> 车主联系电话</h2>
<p>如需挪车,请拨打下方电话联系车主</p>
<div class="phone-number" id="phoneNumber">加载中...</div>
<a href="#" class="call-button" id="callButton">
<i></i> 一键拨号
</a>
</div>
<div class="car-info">
<h3>车辆信息</h3>
<p>车牌号码: <span id="plateNumber">加载中...</span></p>
<p>车辆品牌: <span id="carBrand">加载中...</span></p>
</div>
<div class="instructions">
<h3>使用说明</h3>
<ul>
<li>点击"一键拨号"按钮可直接拨打车主电话</li>
<li>如遇电话无法接通,请稍后再试</li>
</ul>
</div>
</div>
</div>
<div class="footer">
<p>挪车电话服务 © kiki.kim</p>
</div>
<script src="js/result.js"></script>
</body>
</html>

View File

@ -1,24 +0,0 @@
server {
listen 80;
server_name localhost;
# 前端静态文件
location / {
root /var/www/html;
index index.html;
try_files $uri $uri/ =404;
}
# API请求代理到后端Flask应用
location /api/ {
proxy_pass http://127.0.0.1:5000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
# 健康检查端点
location /health {
proxy_pass http://127.0.0.1:5000;
}
}

View File

@ -1,44 +0,0 @@
# 挪车电话查询系统
这是一个前后端分离的挪车电话查询系统使用Python Flask作为后端SQLite作为数据库HTML/CSS/JavaScript作为前端。
## 项目结构
- `backend/`: 后端代码Python Flask
- `frontend/`: 前端代码HTML/CSS/JavaScript
- `data/`: 数据库存放目录
## 安装与运行
1. 安装Python依赖
mvcar/
├── backend/
│ ├── app.py
│ ├── init_db.py
│ ├── view_logs.py
│ └── requirements.txt
├── frontend/
│ ├── index.html
│ ├── result.html
│ ├── css/
│ └── js/
├── data/
├── logs/
├── Dockerfile
├── nginx.conf
├── start.sh
└── docker-compose.yml (可选)
# 构建Docker镜像
docker build -t mvcar-app .
# 构建Docker镜像
docker build -t mvcar-app:v1.0.0 -t mvcar-app:latest .
# 运行容器
docker run -d -p 80:80 --name mvcar-container mvcar-app
# 或者使用docker-compose如果创建了docker-compose.yml
docker-compose up --build -d

View File

@ -1,8 +0,0 @@
#!/bin/bash
# 启动后端Flask应用
cd /app/backend
nohup python app.py > /app/logs/flask.log 2>&1 &
# 启动nginx
nginx -g "daemon off;"

View File

@ -1,2 +0,0 @@
@echo off
start "" code .