|
@@ -0,0 +1,234 @@
|
|
|
+const express = require('express');
|
|
|
+const router = express.Router();
|
|
|
+const Contact = require('../models/contact');
|
|
|
+const { sendContactNotification, sendAutoReply } = require('../utils/emailService');
|
|
|
+const { successResponse, errorResponse } = require('../utils/responese');
|
|
|
+
|
|
|
+/**
|
|
|
+ * @api {post} /contact 提交联系我们表单
|
|
|
+ * @apiName CreateContact
|
|
|
+ * @apiGroup Contact
|
|
|
+ *
|
|
|
+ * @apiParam {String} name 联系人姓名(必填)
|
|
|
+ * @apiParam {String} email 联系人邮箱(必填)
|
|
|
+ * @apiParam {String} [phone] 联系电话
|
|
|
+ * @apiParam {String} [company] 公司名称
|
|
|
+ * @apiParam {String} subject 留言主题(必填)
|
|
|
+ * @apiParam {String} message 留言内容(必填)
|
|
|
+ *
|
|
|
+ * @apiSuccess {Boolean} status 请求状态
|
|
|
+ * @apiSuccess {String} message 响应消息
|
|
|
+ * @apiSuccess {Object} data 创建的联系信息
|
|
|
+ *
|
|
|
+ * @apiSuccessExample {json} Success-Response:
|
|
|
+ * HTTP/1.1 201 Created
|
|
|
+ * {
|
|
|
+ * "status": true,
|
|
|
+ * "message": "留言提交成功,我们会尽快回复您",
|
|
|
+ * "data": {
|
|
|
+ * "id": 1,
|
|
|
+ * "message": "感谢您的留言,我们已收到并会在24小时内回复"
|
|
|
+ * }
|
|
|
+ * }
|
|
|
+ */
|
|
|
+router.post('/', async (req, res) => {
|
|
|
+ try {
|
|
|
+ const { name, email, phone, company, subject, message } = req.body;
|
|
|
+
|
|
|
+ // 参数验证
|
|
|
+ const errors = [];
|
|
|
+ if (!name || name.trim().length === 0) {
|
|
|
+ errors.push('联系人姓名不能为空');
|
|
|
+ }
|
|
|
+ if (!email || email.trim().length === 0) {
|
|
|
+ errors.push('联系人邮箱不能为空');
|
|
|
+ }
|
|
|
+ if (!subject || subject.trim().length === 0) {
|
|
|
+ errors.push('留言主题不能为空');
|
|
|
+ }
|
|
|
+ if (!message || message.trim().length === 0) {
|
|
|
+ errors.push('留言内容不能为空');
|
|
|
+ }
|
|
|
+
|
|
|
+ // 邮箱格式验证
|
|
|
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
|
+ if (email && !emailRegex.test(email)) {
|
|
|
+ errors.push('邮箱格式不正确');
|
|
|
+ }
|
|
|
+
|
|
|
+ // 字符长度验证
|
|
|
+ if (name && name.trim().length > 100) {
|
|
|
+ errors.push('联系人姓名不能超过100个字符');
|
|
|
+ }
|
|
|
+ if (subject && subject.trim().length > 200) {
|
|
|
+ errors.push('留言主题不能超过200个字符');
|
|
|
+ }
|
|
|
+ if (company && company.trim().length > 200) {
|
|
|
+ errors.push('公司名称不能超过200个字符');
|
|
|
+ }
|
|
|
+ if (phone && phone.trim().length > 20) {
|
|
|
+ errors.push('联系电话不能超过20个字符');
|
|
|
+ }
|
|
|
+
|
|
|
+ if (errors.length > 0) {
|
|
|
+ return res.status(400).json(errorResponse('参数验证失败', errors));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 保存到数据库
|
|
|
+ const contactData = await Contact.create({
|
|
|
+ name: name.trim(),
|
|
|
+ email: email.trim().toLowerCase(),
|
|
|
+ phone: phone ? phone.trim() : null,
|
|
|
+ company: company ? company.trim() : null,
|
|
|
+ subject: subject.trim(),
|
|
|
+ message: message.trim()
|
|
|
+ });
|
|
|
+
|
|
|
+ // 异步发送邮件(不阻塞响应)
|
|
|
+ setImmediate(async () => {
|
|
|
+ try {
|
|
|
+ // 发送通知邮件给管理员
|
|
|
+ const notificationSent = await sendContactNotification(contactData.toJSON());
|
|
|
+
|
|
|
+ // 发送自动回复给客户
|
|
|
+ const autoReplySent = await sendAutoReply(contactData.toJSON());
|
|
|
+
|
|
|
+ // 更新邮件发送状态
|
|
|
+ if (notificationSent || autoReplySent) {
|
|
|
+ await contactData.update({ isEmailSent: true });
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log(`联系表单处理完成 - ID: ${contactData.id}, 通知邮件: ${notificationSent ? '成功' : '失败'}, 自动回复: ${autoReplySent ? '成功' : '失败'}`);
|
|
|
+ } catch (emailError) {
|
|
|
+ console.error('邮件发送过程出错:', emailError);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 立即返回成功响应
|
|
|
+ res.status(201).json(successResponse('留言提交成功,我们会尽快回复您', {
|
|
|
+ id: contactData.id,
|
|
|
+ message: '感谢您的留言,我们已收到并会在24小时内回复'
|
|
|
+ }));
|
|
|
+
|
|
|
+ } catch (error) {
|
|
|
+ console.error('提交联系表单失败:', error);
|
|
|
+ res.status(500).json(errorResponse('服务器内部错误'));
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+/**
|
|
|
+ * @api {get} /contact 获取联系我们列表(管理员用)
|
|
|
+ * @apiName GetContacts
|
|
|
+ * @apiGroup Contact
|
|
|
+ *
|
|
|
+ * @apiParam {Number} [page=1] 页码
|
|
|
+ * @apiParam {Number} [limit=10] 每页数量
|
|
|
+ * @apiParam {Number} [status] 处理状态筛选(0-未处理,1-已处理)
|
|
|
+ */
|
|
|
+router.get('/', async (req, res) => {
|
|
|
+ try {
|
|
|
+ const { page = 1, limit = 10, status } = req.query;
|
|
|
+ const offset = (page - 1) * limit;
|
|
|
+
|
|
|
+ const whereConditions = {};
|
|
|
+ if (status !== undefined) {
|
|
|
+ whereConditions.status = parseInt(status);
|
|
|
+ }
|
|
|
+
|
|
|
+ const { count, rows } = await Contact.findAndCountAll({
|
|
|
+ where: whereConditions,
|
|
|
+ order: [['createdAt', 'DESC']],
|
|
|
+ limit: parseInt(limit),
|
|
|
+ offset: parseInt(offset)
|
|
|
+ });
|
|
|
+
|
|
|
+ res.json(successResponse('获取成功', {
|
|
|
+ contacts: rows,
|
|
|
+ pagination: {
|
|
|
+ total: count,
|
|
|
+ page: parseInt(page),
|
|
|
+ limit: parseInt(limit),
|
|
|
+ totalPages: Math.ceil(count / limit)
|
|
|
+ }
|
|
|
+ }));
|
|
|
+ } catch (error) {
|
|
|
+ console.error('获取联系列表失败:', error);
|
|
|
+ res.status(500).json(errorResponse('服务器内部错误'));
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+/**
|
|
|
+ * @api {get} /contact/:id 获取联系详情
|
|
|
+ * @apiName GetContactDetail
|
|
|
+ * @apiGroup Contact
|
|
|
+ */
|
|
|
+router.get('/:id', async (req, res) => {
|
|
|
+ try {
|
|
|
+ const { id } = req.params;
|
|
|
+
|
|
|
+ const contact = await Contact.findByPk(id);
|
|
|
+ if (!contact) {
|
|
|
+ return res.status(404).json(errorResponse('联系记录不存在'));
|
|
|
+ }
|
|
|
+
|
|
|
+ res.json(successResponse('获取成功', contact));
|
|
|
+ } catch (error) {
|
|
|
+ console.error('获取联系详情失败:', error);
|
|
|
+ res.status(500).json(errorResponse('服务器内部错误'));
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+/**
|
|
|
+ * @api {put} /contact/:id/status 更新处理状态
|
|
|
+ * @apiName UpdateContactStatus
|
|
|
+ * @apiGroup Contact
|
|
|
+ *
|
|
|
+ * @apiParam {Number} status 处理状态(0-未处理,1-已处理)
|
|
|
+ */
|
|
|
+router.put('/:id/status', async (req, res) => {
|
|
|
+ try {
|
|
|
+ const { id } = req.params;
|
|
|
+ const { status } = req.body;
|
|
|
+
|
|
|
+ if (![0, 1].includes(parseInt(status))) {
|
|
|
+ return res.status(400).json(errorResponse('状态值只能是0或1'));
|
|
|
+ }
|
|
|
+
|
|
|
+ const contact = await Contact.findByPk(id);
|
|
|
+ if (!contact) {
|
|
|
+ return res.status(404).json(errorResponse('联系记录不存在'));
|
|
|
+ }
|
|
|
+
|
|
|
+ await contact.update({ status: parseInt(status) });
|
|
|
+
|
|
|
+ res.json(successResponse('状态更新成功', contact));
|
|
|
+ } catch (error) {
|
|
|
+ console.error('更新联系状态失败:', error);
|
|
|
+ res.status(500).json(errorResponse('服务器内部错误'));
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+/**
|
|
|
+ * @api {delete} /contact/:id 删除联系记录
|
|
|
+ * @apiName DeleteContact
|
|
|
+ * @apiGroup Contact
|
|
|
+ */
|
|
|
+router.delete('/:id', async (req, res) => {
|
|
|
+ try {
|
|
|
+ const { id } = req.params;
|
|
|
+
|
|
|
+ const contact = await Contact.findByPk(id);
|
|
|
+ if (!contact) {
|
|
|
+ return res.status(404).json(errorResponse('联系记录不存在'));
|
|
|
+ }
|
|
|
+
|
|
|
+ await contact.destroy();
|
|
|
+
|
|
|
+ res.json(successResponse('删除成功'));
|
|
|
+ } catch (error) {
|
|
|
+ console.error('删除联系记录失败:', error);
|
|
|
+ res.status(500).json(errorResponse('服务器内部错误'));
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+module.exports = router;
|