Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2fb3c70f87 | |||
| 5c9f4c1a4f | |||
| b2ad942b2c | |||
| 0d20bc09bc | |||
| 8d1612b956 | |||
| 3893065393 | |||
| bafbc8344b | |||
| b4ab3c625a | |||
| 678c79c5a7 | |||
| 016a5bf6b9 | |||
| 1a196f968e | |||
| 6a43cec22e | |||
| 93dfc4e5cd | |||
| 537a95989e | |||
| 88346ca1ec | |||
| 5d320e0ccf | |||
| 5bbb0877d5 |
@@ -0,0 +1,7 @@
|
||||
<html>
|
||||
<head><title>404 Not Found</title></head>
|
||||
<body>
|
||||
<center><h1>404 Not Found</h1></center>
|
||||
<hr><center>nginx</center>
|
||||
</body>
|
||||
</html>
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
# 洗车预约系统安装指南
|
||||
|
||||
## 1. 环境要求
|
||||
|
||||
- PHP 5.6 或更高版本
|
||||
- MySQL 5.5 或更高版本
|
||||
- Web服务器(如Apache、Nginx等)
|
||||
|
||||
## 2. 安装步骤
|
||||
|
||||
### 2.1 克隆项目
|
||||
|
||||
首先,将项目克隆到您的本地或服务器上:
|
||||
|
||||
```bash
|
||||
git clone [项目仓库地址]
|
||||
cd carwash_order
|
||||
```
|
||||
|
||||
### 2.2 配置数据库
|
||||
|
||||
1. 创建数据库:
|
||||
- 使用MySQL客户端(如phpMyAdmin、Navicat等)创建一个新的数据库
|
||||
- 数据库名称建议使用 `carwash_booking`
|
||||
|
||||
2. 导入数据库结构:
|
||||
- 执行 `carwash_db.sql` 文件,创建所需的表结构和初始数据
|
||||
|
||||
### 2.3 配置数据库连接
|
||||
|
||||
1. 复制配置文件模板:
|
||||
- 将 `config.php.env` 重命名为 `config.php`
|
||||
|
||||
2. 修改数据库连接信息:
|
||||
- 使用文本编辑器打开 `config.php` 文件
|
||||
- 根据您的数据库配置修改以下参数:
|
||||
|
||||
```php
|
||||
// 数据库主机地址
|
||||
$host = 'localhost'; // 通常为localhost,如使用远程数据库请填写IP地址
|
||||
|
||||
// 数据库用户名
|
||||
$username = 'your_username'; // 替换为您的MySQL用户名
|
||||
|
||||
// 数据库密码
|
||||
$password = 'your_password'; // 替换为您的MySQL密码
|
||||
|
||||
// 数据库名称
|
||||
$database = 'carwash_booking'; // 替换为您创建的数据库名称
|
||||
```
|
||||
|
||||
### 2.4 部署到Web服务器
|
||||
|
||||
- 将项目文件上传到Web服务器的根目录或虚拟主机的文档根目录
|
||||
- 确保Web服务器具有对项目文件的读取权限
|
||||
- 确保PHP可以执行项目中的PHP文件
|
||||
|
||||
## 3. 访问系统
|
||||
|
||||
在浏览器中输入您的域名或IP地址,即可访问洗车预约系统。
|
||||
|
||||
例如:
|
||||
```
|
||||
http://localhost/carwash_order/
|
||||
```
|
||||
|
||||
## 4. 常见问题
|
||||
|
||||
### 4.1 数据库连接失败
|
||||
|
||||
- 检查 `config.php` 文件中的数据库连接信息是否正确
|
||||
- 确保MySQL服务正在运行
|
||||
- 确保数据库用户具有足够的权限
|
||||
|
||||
### 4.2 页面显示错误
|
||||
|
||||
- 检查PHP版本是否符合要求
|
||||
- 检查Web服务器的错误日志
|
||||
- 确保所有项目文件都已正确上传
|
||||
|
||||
## 5. 其他配置
|
||||
|
||||
- 如果您需要修改时区设置,可以在PHP文件中添加:`date_default_timezone_set('Asia/Shanghai');`
|
||||
- 如果您需要修改会话超时时间,可以在PHP文件中添加:`ini_set('session.gc_maxlifetime', 3600);`
|
||||
|
||||
## 6. 注意事项
|
||||
|
||||
- 不要将 `config.php` 文件提交到Git仓库,该文件包含敏感的数据库连接信息
|
||||
- 定期备份数据库和项目文件
|
||||
- 确保使用强密码保护数据库和服务器
|
||||
|
||||
如有其他问题,请参考项目的README.md文件或联系技术支持。
|
||||
@@ -1,7 +0,0 @@
|
||||
<?php
|
||||
// config.php - 数据库配置文件
|
||||
$host = 'localhost';
|
||||
$username = 'root';
|
||||
$password = '';
|
||||
$database = 'carwash_booking';
|
||||
?>
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
// config.php.env - 数据库配置文件模板
|
||||
// 请将此文件重命名为 config.php 并根据实际情况修改以下配置
|
||||
|
||||
// 数据库主机地址
|
||||
$host = 'localhost';
|
||||
|
||||
// 数据库用户名
|
||||
$username = 'your_username';
|
||||
|
||||
// 数据库密码
|
||||
$password = 'your_password';
|
||||
|
||||
// 数据库名称
|
||||
$database = 'carwash_booking';
|
||||
?>
|
||||
@@ -0,0 +1,30 @@
|
||||
-- 创建WPS表单数据表
|
||||
CREATE TABLE IF NOT EXISTS wps_form_submissions (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
rid VARCHAR(50) NOT NULL COMMENT '表单提交ID',
|
||||
form_id VARCHAR(50) NOT NULL COMMENT '表单ID',
|
||||
form_title VARCHAR(255) NOT NULL COMMENT '表单标题',
|
||||
creator_id VARCHAR(50) NOT NULL COMMENT '创建者ID',
|
||||
create_time DATETIME NOT NULL COMMENT '创建时间',
|
||||
update_time DATETIME NOT NULL COMMENT '更新时间',
|
||||
mobile VARCHAR(20) COMMENT '请输入手机号',
|
||||
name VARCHAR(255) COMMENT '怎么称呼您',
|
||||
license_plate VARCHAR(20) COMMENT '车牌号',
|
||||
date DATE COMMENT '日期',
|
||||
time_slot VARCHAR(20) COMMENT '时间段',
|
||||
car_type VARCHAR(50) COMMENT '车型',
|
||||
has_car_coat VARCHAR(10) COMMENT '是否有车衣',
|
||||
car_wash_habit VARCHAR(10) COMMENT '有无自己撸车习惯',
|
||||
car_wash_experience VARCHAR(50) COMMENT '撸车经验',
|
||||
wash_frequency VARCHAR(50) COMMENT '洗车频率',
|
||||
age_group VARCHAR(20) COMMENT '请选择年龄段',
|
||||
remarks TEXT COMMENT '备注内容',
|
||||
auto_number VARCHAR(50) COMMENT '自动编号',
|
||||
status VARCHAR(20) DEFAULT 'pending' COMMENT '状态',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',
|
||||
updated_at DATETIME NOT NULL COMMENT '记录更新时间',
|
||||
INDEX idx_rid (rid),
|
||||
INDEX idx_license_plate (license_plate),
|
||||
INDEX idx_date (date),
|
||||
INDEX idx_mobile (mobile)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
-- carwash_db.sql - 数据库创建脚本
|
||||
CREATE DATABASE IF NOT EXISTS carwash_booking;
|
||||
USE carwash_booking;
|
||||
|
||||
-- 创建VIP客户表
|
||||
CREATE TABLE IF NOT EXISTS vip_customers (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
customer_name VARCHAR(100) NOT NULL COMMENT '客户姓名',
|
||||
phone VARCHAR(20) NOT NULL COMMENT '联系电话',
|
||||
car_model VARCHAR(50) COMMENT '车型',
|
||||
car_number VARCHAR(20) COMMENT '车牌号',
|
||||
email VARCHAR(100) COMMENT '邮箱地址',
|
||||
birthday DATE COMMENT '生日',
|
||||
notes TEXT COMMENT '备注信息',
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NULL
|
||||
);
|
||||
|
||||
-- 更新VIP客户表的索引结构
|
||||
-- 1. 移除phone字段的UNIQUE约束(如果存在)
|
||||
ALTER TABLE vip_customers DROP INDEX IF EXISTS phone;
|
||||
|
||||
-- 2. 添加phone和car_number的复合唯一索引
|
||||
ALTER TABLE vip_customers ADD UNIQUE INDEX idx_phone_car_number (phone, car_number);
|
||||
|
||||
-- 3. 验证索引添加成功
|
||||
SHOW INDEX FROM vip_customers;
|
||||
|
||||
-- 创建套餐表
|
||||
CREATE TABLE IF NOT EXISTS packages (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
package_name VARCHAR(100) NOT NULL,
|
||||
description TEXT,
|
||||
base_duration INT NOT NULL COMMENT '基础服务时长(分钟)',
|
||||
price DECIMAL(10,2) NOT NULL,
|
||||
services TEXT NOT NULL COMMENT '包含的服务项目(用逗号分隔)',
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NULL
|
||||
);
|
||||
|
||||
-- 修改预约表支持时间段、会员类型和来源
|
||||
DROP TABLE IF EXISTS bookings;
|
||||
CREATE TABLE IF NOT EXISTS bookings (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
customer_name VARCHAR(100) NOT NULL,
|
||||
phone VARCHAR(20) NOT NULL,
|
||||
car_model VARCHAR(50) NOT NULL,
|
||||
car_number VARCHAR(20) NOT NULL,
|
||||
package_id INT,
|
||||
custom_services TEXT COMMENT '自定义服务内容',
|
||||
start_time DATETIME NOT NULL,
|
||||
end_time DATETIME NOT NULL,
|
||||
duration INT NOT NULL COMMENT '实际服务时长(分钟)',
|
||||
total_price DECIMAL(10,2) NOT NULL,
|
||||
notes TEXT,
|
||||
status ENUM('待确认', '已确认', '进行中', '已完成', '已取消') DEFAULT '待确认',
|
||||
member_type ENUM('普通客户', 'VIP会员') DEFAULT '普通客户' COMMENT '会员类型',
|
||||
source ENUM('抖音', '微信', '快手', '朋友介绍', '其他') DEFAULT '其他' COMMENT '客户来源',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NULL,
|
||||
FOREIGN KEY (package_id) REFERENCES packages(id) ON DELETE SET NULL
|
||||
);
|
||||
|
||||
-- 创建WPS表单数据表
|
||||
CREATE TABLE IF NOT EXISTS wps_form_submissions (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
rid VARCHAR(50) NOT NULL COMMENT '表单提交ID',
|
||||
form_id VARCHAR(50) NOT NULL COMMENT '表单ID',
|
||||
form_title VARCHAR(255) NOT NULL COMMENT '表单标题',
|
||||
creator_id VARCHAR(50) NOT NULL COMMENT '创建者ID',
|
||||
create_time DATETIME NOT NULL COMMENT '创建时间',
|
||||
update_time DATETIME NOT NULL COMMENT '更新时间',
|
||||
mobile VARCHAR(20) COMMENT '请输入手机号',
|
||||
name VARCHAR(255) COMMENT '怎么称呼您',
|
||||
license_plate VARCHAR(20) COMMENT '车牌号',
|
||||
date DATE COMMENT '日期',
|
||||
time_slot VARCHAR(20) COMMENT '时间段',
|
||||
car_type VARCHAR(50) COMMENT '车型',
|
||||
has_car_coat VARCHAR(10) COMMENT '是否有车衣',
|
||||
car_wash_habit VARCHAR(10) COMMENT '有无自己撸车习惯',
|
||||
car_wash_experience VARCHAR(50) COMMENT '撸车经验',
|
||||
wash_frequency VARCHAR(50) COMMENT '洗车频率',
|
||||
age_group VARCHAR(20) COMMENT '请选择年龄段',
|
||||
remarks TEXT COMMENT '备注内容',
|
||||
auto_number VARCHAR(50) COMMENT '自动编号',
|
||||
status VARCHAR(20) DEFAULT 'pending' COMMENT '状态',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',
|
||||
updated_at DATETIME NOT NULL COMMENT '记录更新时间',
|
||||
INDEX idx_rid (rid),
|
||||
INDEX idx_license_plate (license_plate),
|
||||
INDEX idx_date (date),
|
||||
INDEX idx_mobile (mobile)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
-- 插入示例套餐数据
|
||||
INSERT INTO packages (package_name, description, base_duration, price, services) VALUES
|
||||
('基础洗车', '基础外观清洗', 30, 50.00, '外观冲洗,泡沫清洁,内饰吸尘'),
|
||||
('精洗套餐', '全面深度清洗', 90, 150.00, '外观精洗,内饰深度清洁,轮胎清洁,打蜡'),
|
||||
('VIP套餐', '顶级豪华洗护', 180, 300.00, '全套精洗,抛光打蜡,内饰护理,发动机清洁,真皮护理');
|
||||
|
||||
-- 插入示例预约数据(包含会员类型和来源)
|
||||
INSERT INTO bookings (customer_name, phone, car_model, car_number, package_id, start_time, end_time, duration, total_price, notes, member_type, source) VALUES
|
||||
('张三', '13800138001', '大众朗逸', '京A12345', 1, '2024-12-20 09:00:00', '2024-12-20 09:30:00', 30, 50.00, '第一次来', '普通客户', '抖音'),
|
||||
('李四', '13800138002', '丰田凯美瑞', '京B67890', 2, '2024-12-20 10:30:00', '2024-12-20 12:00:00', 90, 150.00, '需要特别清洗内饰', 'VIP会员', '朋友介绍'),
|
||||
('王五', '13800138003', '宝马X3', '沪C88888', 3, '2024-12-21 14:00:00', '2024-12-21 17:00:00', 180, 300.00, 'VIP客户,定期保养', 'VIP会员', '微信'),
|
||||
('赵六', '13800138004', '本田雅阁', '粤A66666', 1, '2024-12-21 09:30:00', '2024-12-21 10:00:00', 30, 50.00, '快手看到广告来的', '普通客户', '快手');
|
||||
|
||||
-- 插入示例VIP客户数据
|
||||
INSERT INTO vip_customers (customer_name, phone, car_model, car_number, email, birthday, notes) VALUES
|
||||
('张总', '13900139001', '奔驰S500', '京V88888', 'zhang@example.com', '1980-05-15', '长期VIP客户,商务人士'),
|
||||
('王女士', '13900139002', '奥迪A6L', '京A66666', 'wang@example.com', '1985-03-20', '女企业家,每月定期保养'),
|
||||
('李先生', '13900139003', 'BMW X5', '沪B99999', 'li@example.com', '1978-11-10', '房地产公司老板,对服务要求较高');
|
||||
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
// 测试数据库连接和表创建功能
|
||||
require_once 'config.php';
|
||||
|
||||
// 记录日志的函数
|
||||
function log_test_message($message, $type = 'info') {
|
||||
$log_file = 'test_db.log';
|
||||
$timestamp = date('Y-m-d H:i:s');
|
||||
$log_entry = "[$timestamp] [$type] $message\n";
|
||||
file_put_contents($log_file, $log_entry, FILE_APPEND);
|
||||
echo $log_entry;
|
||||
}
|
||||
|
||||
echo "=== 测试数据库连接和表创建功能 ===\n";
|
||||
|
||||
// 测试数据库连接
|
||||
try {
|
||||
$pdo = new PDO("mysql:host=$host;dbname=$database;charset=utf8mb4", $username, $password);
|
||||
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
|
||||
log_test_message("数据库连接成功");
|
||||
} catch(PDOException $e) {
|
||||
log_test_message("数据库连接失败: " . $e->getMessage(), 'error');
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// 测试创建表
|
||||
try {
|
||||
$sql = "CREATE TABLE IF NOT EXISTS wps_form_submissions (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
rid VARCHAR(50) NOT NULL COMMENT '表单提交ID',
|
||||
form_id VARCHAR(50) NOT NULL COMMENT '表单ID',
|
||||
form_title VARCHAR(255) NOT NULL COMMENT '表单标题',
|
||||
creator_id VARCHAR(50) NOT NULL COMMENT '创建者ID',
|
||||
create_time DATETIME NOT NULL COMMENT '创建时间',
|
||||
update_time DATETIME NOT NULL COMMENT '更新时间',
|
||||
mobile VARCHAR(20) COMMENT '请输入手机号',
|
||||
name VARCHAR(255) COMMENT '怎么称呼您',
|
||||
license_plate VARCHAR(20) COMMENT '车牌号',
|
||||
date DATE COMMENT '日期',
|
||||
time_slot VARCHAR(20) COMMENT '时间段',
|
||||
car_type VARCHAR(50) COMMENT '车型',
|
||||
wash_type VARCHAR(100) COMMENT '洗车类型',
|
||||
wash_packages VARCHAR(255) COMMENT '洗车套餐',
|
||||
wash_services TEXT COMMENT '洗车服务',
|
||||
additional_services TEXT COMMENT '额外服务',
|
||||
need_wax VARCHAR(10) COMMENT '是否需要打蜡',
|
||||
wax_type VARCHAR(50) COMMENT '蜡的类型',
|
||||
cleaning_needs TEXT COMMENT '清洁需求',
|
||||
interior_cleaning VARCHAR(10) COMMENT '是否需要内饰清洁',
|
||||
exterior_cleaning VARCHAR(10) COMMENT '是否需要外观清洁',
|
||||
vip_member VARCHAR(10) COMMENT '是否为VIP会员',
|
||||
vip_number VARCHAR(50) COMMENT 'VIP卡号',
|
||||
payment_method VARCHAR(50) COMMENT '支付方式',
|
||||
special_requests TEXT COMMENT '特殊要求',
|
||||
technician VARCHAR(50) COMMENT '指定技师',
|
||||
status VARCHAR(20) DEFAULT 'pending' COMMENT '状态',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间',
|
||||
INDEX idx_rid (rid),
|
||||
INDEX idx_license_plate (license_plate),
|
||||
INDEX idx_date (date),
|
||||
INDEX idx_mobile (mobile)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;";
|
||||
|
||||
$pdo->exec($sql);
|
||||
log_test_message("表创建成功");
|
||||
} catch(PDOException $e) {
|
||||
log_test_message("表创建失败: " . $e->getMessage(), 'error');
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// 测试插入数据
|
||||
try {
|
||||
$sql = "INSERT INTO wps_form_submissions (
|
||||
rid, form_id, form_title, creator_id, create_time, update_time,
|
||||
mobile, name, license_plate, date, time_slot, car_type
|
||||
) VALUES (
|
||||
:rid, :form_id, :form_title, :creator_id, :create_time, :update_time,
|
||||
:mobile, :name, :license_plate, :date, :time_slot, :car_type
|
||||
)";
|
||||
|
||||
$stmt = $pdo->prepare($sql);
|
||||
|
||||
$stmt->bindValue(':rid', 'test_rid_' . time());
|
||||
$stmt->bindValue(':form_id', 'test_form_id');
|
||||
$stmt->bindValue(':form_title', '测试表单');
|
||||
$stmt->bindValue(':creator_id', 'test_creator_id');
|
||||
$stmt->bindValue(':create_time', date('Y-m-d H:i:s'));
|
||||
$stmt->bindValue(':update_time', date('Y-m-d H:i:s'));
|
||||
$stmt->bindValue(':mobile', '13800138000');
|
||||
$stmt->bindValue(':name', '测试用户');
|
||||
$stmt->bindValue(':license_plate', '京A12345');
|
||||
$stmt->bindValue(':date', date('Y-m-d'));
|
||||
$stmt->bindValue(':time_slot', '14:00-15:00');
|
||||
$stmt->bindValue(':car_type', '轿车');
|
||||
|
||||
$stmt->execute();
|
||||
|
||||
$insert_id = $pdo->lastInsertId();
|
||||
log_test_message("测试数据插入成功,ID: " . $insert_id);
|
||||
} catch(PDOException $e) {
|
||||
log_test_message("测试数据插入失败: " . $e->getMessage(), 'error');
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// 测试查询数据
|
||||
try {
|
||||
$sql = "SELECT * FROM wps_form_submissions ORDER BY id DESC LIMIT 1";
|
||||
$stmt = $pdo->query($sql);
|
||||
$row = $stmt->fetch();
|
||||
|
||||
if ($row) {
|
||||
log_test_message("测试数据查询成功");
|
||||
log_test_message("查询结果: " . print_r($row, true));
|
||||
} else {
|
||||
log_test_message("测试数据查询失败: 没有找到数据", 'error');
|
||||
}
|
||||
} catch(PDOException $e) {
|
||||
log_test_message("测试数据查询失败: " . $e->getMessage(), 'error');
|
||||
exit(1);
|
||||
}
|
||||
|
||||
echo "\n=== 所有测试完成 ===\n";
|
||||
@@ -0,0 +1,263 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>WPS表单Webhook测试</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
.container {
|
||||
background-color: #f5f5f5;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
h1 {
|
||||
color: #333;
|
||||
text-align: center;
|
||||
}
|
||||
.test-section {
|
||||
margin: 20px 0;
|
||||
padding: 15px;
|
||||
background-color: white;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
button {
|
||||
background-color: #4CAF50;
|
||||
color: white;
|
||||
padding: 10px 20px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
margin: 5px;
|
||||
}
|
||||
button:hover {
|
||||
background-color: #45a049;
|
||||
}
|
||||
.response {
|
||||
margin-top: 10px;
|
||||
padding: 10px;
|
||||
background-color: #f0f0f0;
|
||||
border-radius: 4px;
|
||||
white-space: pre-wrap;
|
||||
font-family: monospace;
|
||||
}
|
||||
.log-section {
|
||||
margin-top: 30px;
|
||||
}
|
||||
#log-content {
|
||||
height: 300px;
|
||||
overflow-y: auto;
|
||||
background-color: #2d2d2d;
|
||||
color: #f0f0f0;
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
font-family: monospace;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>WPS表单Webhook测试工具</h1>
|
||||
|
||||
<div class="test-section">
|
||||
<h2>1. 验证请求测试</h2>
|
||||
<p>模拟WPS表单的验证请求,Webhook应返回绑定码</p>
|
||||
<button onclick="testVerification()">发送验证请求</button>
|
||||
<div class="response" id="verification-response"></div>
|
||||
</div>
|
||||
|
||||
<div class="test-section">
|
||||
<h2>2. 表单数据测试</h2>
|
||||
<p>模拟WPS表单发送数据,Webhook应接收并处理数据</p>
|
||||
<button onclick="testFormData()">发送表单数据</button>
|
||||
<div class="response" id="form-data-response"></div>
|
||||
</div>
|
||||
|
||||
<div class="log-section">
|
||||
<h2>3. Webhook日志</h2>
|
||||
<button onclick="loadLog()">加载日志</button>
|
||||
<button onclick="clearLog()">清空日志</button>
|
||||
<div id="log-content"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Webhook URL
|
||||
const webhookUrl = 'http://carwash.com/webhook.php';
|
||||
|
||||
// 发送验证请求
|
||||
async function testVerification() {
|
||||
const responseDiv = document.getElementById('verification-response');
|
||||
responseDiv.innerHTML = '发送中...';
|
||||
|
||||
try {
|
||||
const response = await fetch(webhookUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: '' // 空请求体,模拟验证请求
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
responseDiv.innerHTML = '响应: ' + JSON.stringify(data, null, 2);
|
||||
} catch (error) {
|
||||
responseDiv.innerHTML = '错误: ' + error.message;
|
||||
}
|
||||
}
|
||||
|
||||
// 发送表单数据
|
||||
async function testFormData() {
|
||||
const responseDiv = document.getElementById('form-data-response');
|
||||
responseDiv.innerHTML = '发送中...';
|
||||
|
||||
// 模拟WPS实际表单数据格式
|
||||
const formData = {
|
||||
"rid": "lAFP6N1Tap",
|
||||
"formId": "20251205123619412736007",
|
||||
"formTitle": "张老师撸车(私家车库)预约撸车表单",
|
||||
"aid": "20251205131620432762589",
|
||||
"eventTs": Date.now(),
|
||||
"messageTs": Date.now(),
|
||||
"creatorId": "344573037",
|
||||
"creatorName": "吴展鹏",
|
||||
"event": "create_answer",
|
||||
"version": 2,
|
||||
"answerContents": [
|
||||
{
|
||||
"qid": "eo513g",
|
||||
"type": "input",
|
||||
"title": "怎么称呼您",
|
||||
"value": "吴展鹏"
|
||||
},
|
||||
{
|
||||
"qid": "xKFUcp",
|
||||
"type": "input",
|
||||
"title": "车型",
|
||||
"value": "比亚迪宋PRODMi"
|
||||
},
|
||||
{
|
||||
"qid": "NFJDDT",
|
||||
"type": "input",
|
||||
"title": "车牌号",
|
||||
"value": "新AF10365-普通输入"
|
||||
},
|
||||
{
|
||||
"qid": "t2u2i4",
|
||||
"type": "licensePlate",
|
||||
"title": "车牌号",
|
||||
"value": "新AF10365-车牌专用"
|
||||
},
|
||||
{
|
||||
"qid": "7jgfmh",
|
||||
"type": "telphone",
|
||||
"title": "请输入手机号",
|
||||
"value": "18699627661"
|
||||
},
|
||||
{
|
||||
"qid": "3j6opi",
|
||||
"type": "select",
|
||||
"title": "是否有车衣",
|
||||
"value": ["否,无车衣"]
|
||||
},
|
||||
{
|
||||
"qid": "ej48lk",
|
||||
"type": "select",
|
||||
"title": "有无自己撸车习惯",
|
||||
"value": ["有"]
|
||||
},
|
||||
{
|
||||
"qid": "1tnljp",
|
||||
"type": "select",
|
||||
"title": "撸车经验",
|
||||
"value": ["我是老司机啦"]
|
||||
},
|
||||
{
|
||||
"qid": "x02g35",
|
||||
"type": "select",
|
||||
"title": "洗车频率",
|
||||
"value": ["每2周一次(每月1-2次)"]
|
||||
},
|
||||
{
|
||||
"qid": "54dbo7",
|
||||
"type": "select",
|
||||
"title": "请选择年龄段",
|
||||
"value": ["26~30"]
|
||||
},
|
||||
{
|
||||
"qid": "rwxgkc",
|
||||
"type": "autoNum",
|
||||
"title": "自动编号",
|
||||
"value": "zlslc202512002"
|
||||
},
|
||||
{
|
||||
"qid": "r3ft9n",
|
||||
"type": "input",
|
||||
"title": "备注内容",
|
||||
"value": "谢谢"
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await fetch(webhookUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(formData)
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
responseDiv.innerHTML = '响应: ' + JSON.stringify(data, null, 2);
|
||||
} catch (error) {
|
||||
responseDiv.innerHTML = '错误: ' + error.message;
|
||||
}
|
||||
}
|
||||
|
||||
// 加载日志
|
||||
async function loadLog() {
|
||||
const logContent = document.getElementById('log-content');
|
||||
logContent.innerHTML = '加载中...';
|
||||
|
||||
try {
|
||||
const response = await fetch('/log/wps_form_webhook.log');
|
||||
if (response.ok) {
|
||||
const text = await response.text();
|
||||
logContent.innerHTML = text;
|
||||
} else {
|
||||
logContent.innerHTML = '无法加载日志文件: ' + response.statusText;
|
||||
}
|
||||
} catch (error) {
|
||||
logContent.innerHTML = '错误: ' + error.message;
|
||||
}
|
||||
}
|
||||
|
||||
// 清空日志
|
||||
async function clearLog() {
|
||||
const logContent = document.getElementById('log-content');
|
||||
|
||||
try {
|
||||
const response = await fetch('webhook.php?action=clear_log', {
|
||||
method: 'POST'
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
logContent.innerHTML = '日志已清空';
|
||||
} else {
|
||||
logContent.innerHTML = '清空日志失败';
|
||||
}
|
||||
} catch (error) {
|
||||
logContent.innerHTML = '错误: ' + error.message;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
+314
@@ -0,0 +1,314 @@
|
||||
<?php
|
||||
// WPS表单Webhook接收端点
|
||||
|
||||
// 设置响应头
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
// 绑定码(根据WPS表单要求提供)
|
||||
$bind_code = '20251205123619412736007';
|
||||
|
||||
// 日志文件路径
|
||||
$log_file = 'log/wps_form_webhook.log';
|
||||
|
||||
// 加载数据库配置
|
||||
require_once 'config.php';
|
||||
|
||||
// 数据库连接函数
|
||||
function get_db_connection() {
|
||||
global $host, $username, $password, $database;
|
||||
try {
|
||||
$pdo = new PDO("mysql:host=$host;dbname=$database;charset=utf8mb4", $username, $password);
|
||||
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
|
||||
return $pdo;
|
||||
} catch(PDOException $e) {
|
||||
log_message("数据库连接失败: " . $e->getMessage(), 'error');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// 字段对照表(根据WPS表单实际字段)
|
||||
$field_mapping = array(
|
||||
// 题目标题 => array(qid, type)
|
||||
'日期' => array('0AmAeI', 'date'),
|
||||
'单选项' => array('y0Rvqm', 'select'),
|
||||
'图片和附件' => array('FOS5GT', 'file'),
|
||||
'等级' => array('0wJSrH', 'star'),
|
||||
'填写ID' => array('vwS7ci', 'input'),
|
||||
'提交时间' => array('yjbQYS', 'date'),
|
||||
'答题时间(秒)' => array('AA2kq8', 'input'),
|
||||
'车牌号' => array('t2u2i4', 'licensePlate'), // 只使用这个车牌号字段(qid: t2u2i4, type: licensePlate)
|
||||
'车牌号2' => array('7nAOz7', 'input'), // 不使用
|
||||
'车牌号(普通)' => array('NFJDDT', 'input'), // 不使用
|
||||
'怎么称呼您' => array('eo513g', 'input'),
|
||||
'车型' => array('xKFUcp', 'input'),
|
||||
'请输入手机号' => array('7jgfmh', 'input'),
|
||||
'是否有车衣' => array('3j6opi', 'select'),
|
||||
'有无自己撸车习惯' => array('ej48lk', 'select'),
|
||||
'撸车经验' => array('1tnljp', 'select'),
|
||||
'洗车频率' => array('x02g35', 'select'),
|
||||
'请选择年龄段' => array('54dbo7', 'select'),
|
||||
'提交者' => array('T9OiCe', 'contact'),
|
||||
'自动编号' => array('rwxgkc', 'autoNum'),
|
||||
'备注内容' => array('r3ft9n', 'input')
|
||||
);
|
||||
|
||||
// 反向映射:qid => array(title, type)
|
||||
$qid_mapping = array();
|
||||
foreach ($field_mapping as $title => $info) {
|
||||
$qid = $info[0];
|
||||
$type = $info[1];
|
||||
$qid_mapping[$qid] = array('title' => $title, 'type' => $type);
|
||||
}
|
||||
|
||||
// 记录日志的函数
|
||||
function log_message($message, $type = 'info') {
|
||||
global $log_file;
|
||||
$timestamp = date('Y-m-d H:i:s');
|
||||
$log_entry = "[$timestamp] [$type] $message\n";
|
||||
|
||||
// 检查日志目录是否存在,如果不存在则创建
|
||||
$log_dir = dirname($log_file);
|
||||
if (!is_dir($log_dir)) {
|
||||
mkdir($log_dir, 0755, true);
|
||||
}
|
||||
|
||||
file_put_contents($log_file, $log_entry, FILE_APPEND);
|
||||
}
|
||||
|
||||
// 获取请求方法
|
||||
$method = $_SERVER['REQUEST_METHOD'];
|
||||
|
||||
// 获取请求数据
|
||||
$request_body = file_get_contents('php://input');
|
||||
|
||||
// 记录所有请求
|
||||
log_message("收到请求 - 方法: $method");
|
||||
log_message("请求内容: $request_body");
|
||||
|
||||
// 处理验证请求
|
||||
if ($method == 'POST') {
|
||||
// 尝试解析请求体
|
||||
$data = json_decode($request_body, true);
|
||||
|
||||
// 如果解析失败,可能是验证请求
|
||||
if ($data === null) {
|
||||
// 返回绑定码进行验证
|
||||
log_message("返回绑定码进行验证");
|
||||
echo json_encode(array('bind_code' => $bind_code));
|
||||
exit;
|
||||
}
|
||||
|
||||
// 检查是否是绑定验证请求
|
||||
if (isset($data['event']) && $data['event'] === 'bind') {
|
||||
log_message("收到绑定验证请求");
|
||||
echo json_encode(array('bind_code' => $bind_code));
|
||||
exit;
|
||||
}
|
||||
|
||||
// 处理实际的表单数据
|
||||
log_message("处理表单数据");
|
||||
log_message("原始表单数据: " . print_r($data, true), 'data');
|
||||
|
||||
// 解析WPS表单数据结构
|
||||
$form_data = array(
|
||||
'rid' => isset($data['rid']) ? $data['rid'] : '',
|
||||
'form_id' => isset($data['formId']) ? $data['formId'] : '',
|
||||
'form_title' => isset($data['formTitle']) ? $data['formTitle'] : '',
|
||||
'aid' => isset($data['aid']) ? $data['aid'] : '',
|
||||
'event_ts' => isset($data['eventTs']) ? $data['eventTs'] : 0,
|
||||
'message_ts' => isset($data['messageTs']) ? $data['messageTs'] : 0,
|
||||
'creator_id' => isset($data['creatorId']) ? $data['creatorId'] : '',
|
||||
'creator_name' => isset($data['creatorName']) ? $data['creatorName'] : '',
|
||||
'event' => isset($data['event']) ? $data['event'] : '',
|
||||
'version' => isset($data['version']) ? $data['version'] : 1,
|
||||
'answers' => array()
|
||||
);
|
||||
|
||||
// 解析表单字段
|
||||
if (isset($data['answerContents']) && is_array($data['answerContents'])) {
|
||||
foreach ($data['answerContents'] as $content) {
|
||||
$qid = isset($content['qid']) ? $content['qid'] : '';
|
||||
$type = isset($content['type']) ? $content['type'] : '';
|
||||
$title = isset($content['title']) ? $content['title'] : '';
|
||||
$value = isset($content['value']) ? $content['value'] : '';
|
||||
|
||||
// 将数组值转换为字符串,便于处理
|
||||
if (is_array($value)) {
|
||||
$value = implode(', ', $value);
|
||||
}
|
||||
|
||||
$field = array(
|
||||
'qid' => $qid,
|
||||
'type' => $type,
|
||||
'title' => $title,
|
||||
'value' => $value
|
||||
);
|
||||
|
||||
$form_data['answers'][] = $field;
|
||||
|
||||
// 按标题索引存储,便于直接访问
|
||||
if (!empty($title)) {
|
||||
$form_data['answers_by_title'][$title] = $value;
|
||||
}
|
||||
|
||||
// 按qid索引存储,便于精确访问
|
||||
if (!empty($qid)) {
|
||||
$form_data['answers_by_qid'][$qid] = $value;
|
||||
}
|
||||
|
||||
// 使用字段对照表检查并标准化字段
|
||||
if (!empty($qid) && isset($qid_mapping[$qid])) {
|
||||
$standard_title = $qid_mapping[$qid]['title'];
|
||||
$standard_type = $qid_mapping[$qid]['type'];
|
||||
|
||||
// 存储标准化字段
|
||||
$form_data['standardized_answers'][$standard_title] = array(
|
||||
'qid' => $qid,
|
||||
'type' => $standard_type,
|
||||
'title' => $title, // 保留原始标题
|
||||
'value' => $value,
|
||||
'standard_title' => $standard_title
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log_message("解析后的表单数据: " . print_r($form_data, true), 'data');
|
||||
|
||||
// 在这里可以添加数据处理逻辑,例如:
|
||||
// 1. 将数据保存到数据库
|
||||
// 2. 发送通知
|
||||
// 3. 其他业务逻辑
|
||||
|
||||
// 示例:访问特定字段
|
||||
|
||||
// 通过标题访问
|
||||
if (isset($form_data['answers_by_title']['怎么称呼您'])) {
|
||||
$name = $form_data['answers_by_title']['怎么称呼您'];
|
||||
log_message("客户姓名: $name");
|
||||
}
|
||||
|
||||
// 注意:由于有两个"车牌号"字段,通过标题访问可能获取到任意一个
|
||||
// 建议使用qid或标准化字段访问
|
||||
if (isset($form_data['answers_by_title']['车牌号'])) {
|
||||
$license_plate = $form_data['answers_by_title']['车牌号'];
|
||||
log_message("车牌号(通过标题): $license_plate");
|
||||
}
|
||||
|
||||
// 通过qid精确访问特定的车牌号字段
|
||||
if (isset($form_data['answers_by_qid']['t2u2i4'])) {
|
||||
$license_plate_standard = $form_data['answers_by_qid']['t2u2i4'];
|
||||
log_message("车牌号(通过qid t2u2i4 - 标准): $license_plate_standard");
|
||||
}
|
||||
|
||||
// 通过标准化字段访问
|
||||
if (isset($form_data['standardized_answers']['怎么称呼您'])) {
|
||||
$name_standard = $form_data['standardized_answers']['怎么称呼您']['value'];
|
||||
log_message("客户姓名(标准化): $name_standard");
|
||||
}
|
||||
|
||||
if (isset($form_data['standardized_answers']['车型'])) {
|
||||
$car_model = $form_data['standardized_answers']['车型']['value'];
|
||||
log_message("车型: $car_model");
|
||||
}
|
||||
|
||||
if (isset($form_data['standardized_answers']['请输入手机号'])) {
|
||||
$phone = $form_data['standardized_answers']['请输入手机号']['value'];
|
||||
log_message("手机号: $phone");
|
||||
}
|
||||
|
||||
// 返回成功响应
|
||||
$response = array(
|
||||
'code' => 200,
|
||||
'message' => '数据接收成功',
|
||||
'received_at' => date('Y-m-d H:i:s'),
|
||||
'processed_fields' => count($form_data['answers'])
|
||||
);
|
||||
|
||||
// 将数据存储到数据库
|
||||
store_form_data_to_db($form_data);
|
||||
|
||||
log_message("返回成功响应");
|
||||
echo json_encode($response);
|
||||
} else {
|
||||
// 处理非POST请求
|
||||
log_message("不支持的请求方法: $method", 'error');
|
||||
http_response_code(405); // 方法不允许
|
||||
echo json_encode(array('error' => '只支持POST请求'));
|
||||
}
|
||||
|
||||
log_message("请求处理完成\n" . str_repeat('-', 50) . "\n");
|
||||
|
||||
// 将表单数据存储到数据库
|
||||
function store_form_data_to_db($form_data) {
|
||||
try {
|
||||
$pdo = get_db_connection();
|
||||
if (!$pdo) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 准备SQL语句(只包含实际WPS表单存在的字段)
|
||||
$sql = "INSERT INTO wps_form_submissions (
|
||||
rid, form_id, form_title, creator_id, create_time, update_time,
|
||||
mobile, name, license_plate, date, time_slot, car_type,
|
||||
has_car_coat, car_wash_habit, car_wash_experience, wash_frequency,
|
||||
age_group, remarks, auto_number, status, updated_at
|
||||
) VALUES (
|
||||
:rid, :form_id, :form_title, :creator_id, :create_time, :update_time,
|
||||
:mobile, :name, :license_plate, :date, :time_slot, :car_type,
|
||||
:has_car_coat, :car_wash_habit, :car_wash_experience, :wash_frequency,
|
||||
:age_group, :remarks, :auto_number, :status, :updated_at
|
||||
)";
|
||||
|
||||
$stmt = $pdo->prepare($sql);
|
||||
|
||||
// 绑定参数
|
||||
$stmt->bindValue(':rid', $form_data['rid']);
|
||||
$stmt->bindValue(':form_id', $form_data['form_id']);
|
||||
$stmt->bindValue(':form_title', $form_data['form_title']);
|
||||
$stmt->bindValue(':creator_id', $form_data['creator_id']);
|
||||
$stmt->bindValue(':create_time', date('Y-m-d H:i:s'));
|
||||
$stmt->bindValue(':update_time', date('Y-m-d H:i:s'));
|
||||
$stmt->bindValue(':updated_at', date('Y-m-d H:i:s'));
|
||||
|
||||
// 绑定表单字段值(只绑定实际存在的字段)
|
||||
$stmt->bindValue(':mobile', isset($form_data['answers_by_title']['请输入手机号']) ? $form_data['answers_by_title']['请输入手机号'] : '');
|
||||
$stmt->bindValue(':name', isset($form_data['answers_by_title']['怎么称呼您']) ? $form_data['answers_by_title']['怎么称呼您'] : '');
|
||||
|
||||
// 处理车牌号字段(只使用qid为t2u2i4、type为licensePlate的车牌号字段)
|
||||
$license_plate = '';
|
||||
if (isset($form_data['answers_by_qid']['t2u2i4'])) {
|
||||
$license_plate = $form_data['answers_by_qid']['t2u2i4'];
|
||||
}
|
||||
$stmt->bindValue(':license_plate', $license_plate);
|
||||
|
||||
// 日期处理
|
||||
$date = isset($form_data['answers_by_title']['日期']) ? $form_data['answers_by_title']['日期'] : '';
|
||||
$stmt->bindValue(':date', $date ? date('Y-m-d', strtotime($date)) : null);
|
||||
|
||||
// 绑定实际存在的其他字段
|
||||
$stmt->bindValue(':time_slot', isset($form_data['answers_by_title']['单选项']) ? $form_data['answers_by_title']['单选项'] : '');
|
||||
$stmt->bindValue(':car_type', isset($form_data['answers_by_title']['车型']) ? $form_data['answers_by_title']['车型'] : '');
|
||||
$stmt->bindValue(':has_car_coat', isset($form_data['answers_by_title']['是否有车衣']) ? $form_data['answers_by_title']['是否有车衣'] : '');
|
||||
$stmt->bindValue(':car_wash_habit', isset($form_data['answers_by_title']['有无自己撸车习惯']) ? $form_data['answers_by_title']['有无自己撸车习惯'] : '');
|
||||
$stmt->bindValue(':car_wash_experience', isset($form_data['answers_by_title']['撸车经验']) ? $form_data['answers_by_title']['撸车经验'] : '');
|
||||
$stmt->bindValue(':wash_frequency', isset($form_data['answers_by_title']['洗车频率']) ? $form_data['answers_by_title']['洗车频率'] : '');
|
||||
$stmt->bindValue(':age_group', isset($form_data['answers_by_title']['请选择年龄段']) ? $form_data['answers_by_title']['请选择年龄段'] : '');
|
||||
$stmt->bindValue(':remarks', isset($form_data['answers_by_title']['备注内容']) ? $form_data['answers_by_title']['备注内容'] : '');
|
||||
$stmt->bindValue(':auto_number', isset($form_data['answers_by_title']['自动编号']) ? $form_data['answers_by_title']['自动编号'] : '');
|
||||
$stmt->bindValue(':status', 'pending');
|
||||
|
||||
// 执行SQL
|
||||
$stmt->execute();
|
||||
|
||||
log_message("表单数据成功存储到数据库,插入ID: " . $pdo->lastInsertId());
|
||||
return true;
|
||||
} catch (Exception $e) {
|
||||
log_message("数据存储失败: " . $e->getMessage(), 'error');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
Reference in New Issue
Block a user