contact.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. const express = require('express');
  2. const router = express.Router();
  3. const { Contact } = require('../models');
  4. const { sendContactNotification, sendAutoReply } = require('../utils/emailService');
  5. const { successResponse, errorResponse } = require('../utils/responese');
  6. /**
  7. * @api {post} /contact 提交联系我们表单
  8. * @apiName CreateContact
  9. * @apiGroup Contact
  10. *
  11. * @apiParam {String} name 联系人姓名(必填)
  12. * @apiParam {String} email 联系人邮箱(必填)
  13. * @apiParam {String} [phone] 联系电话
  14. * @apiParam {String} [company] 公司名称
  15. * @apiParam {String} subject 留言主题(必填)
  16. * @apiParam {String} message 留言内容(必填)
  17. *
  18. * @apiSuccess {Boolean} status 请求状态
  19. * @apiSuccess {String} message 响应消息
  20. * @apiSuccess {Object} data 创建的联系信息
  21. *
  22. * @apiSuccessExample {json} Success-Response:
  23. * HTTP/1.1 201 Created
  24. * {
  25. * "status": true,
  26. * "message": "留言提交成功,我们会尽快回复您",
  27. * "data": {
  28. * "id": 1,
  29. * "message": "感谢您的留言,我们已收到并会在24小时内回复"
  30. * }
  31. * }
  32. */
  33. router.post('/', async (req, res) => {
  34. try {
  35. const { name, email, phone, company, subject, message } = req.body;
  36. // 参数验证
  37. const errors = [];
  38. if (!name || name.trim().length === 0) {
  39. errors.push('联系人姓名不能为空');
  40. }
  41. if (!email || email.trim().length === 0) {
  42. errors.push('联系人邮箱不能为空');
  43. }
  44. if (!subject || subject.trim().length === 0) {
  45. errors.push('留言主题不能为空');
  46. }
  47. if (!message || message.trim().length === 0) {
  48. errors.push('留言内容不能为空');
  49. }
  50. // 邮箱格式验证
  51. const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  52. if (email && !emailRegex.test(email)) {
  53. errors.push('邮箱格式不正确');
  54. }
  55. // 字符长度验证
  56. if (name && name.trim().length > 100) {
  57. errors.push('联系人姓名不能超过100个字符');
  58. }
  59. if (subject && subject.trim().length > 200) {
  60. errors.push('留言主题不能超过200个字符');
  61. }
  62. if (company && company.trim().length > 200) {
  63. errors.push('公司名称不能超过200个字符');
  64. }
  65. if (phone && phone.trim().length > 20) {
  66. errors.push('联系电话不能超过20个字符');
  67. }
  68. if (errors.length > 0) {
  69. return res.status(400).json(errorResponse('参数验证失败', errors));
  70. }
  71. // 保存到数据库
  72. const contactData = await Contact.create({
  73. name: name.trim(),
  74. email: email.trim().toLowerCase(),
  75. phone: phone ? phone.trim() : null,
  76. company: company ? company.trim() : null,
  77. subject: subject.trim(),
  78. message: message.trim()
  79. });
  80. // 异步发送邮件(不阻塞响应)
  81. setImmediate(async () => {
  82. try {
  83. // 发送通知邮件给管理员
  84. const notificationSent = await sendContactNotification(contactData.toJSON());
  85. // 发送自动回复给客户
  86. const autoReplySent = await sendAutoReply(contactData.toJSON());
  87. // 更新邮件发送状态
  88. if (notificationSent || autoReplySent) {
  89. await contactData.update({ isEmailSent: true });
  90. }
  91. console.log(`联系表单处理完成 - ID: ${contactData.id}, 通知邮件: ${notificationSent ? '成功' : '失败'}, 自动回复: ${autoReplySent ? '成功' : '失败'}`);
  92. } catch (emailError) {
  93. console.error('邮件发送过程出错:', emailError);
  94. }
  95. });
  96. // 立即返回成功响应
  97. res.status(201).json(successResponse('留言提交成功,我们会尽快回复您', {
  98. id: contactData.id,
  99. message: '感谢您的留言,我们已收到并会在24小时内回复'
  100. }));
  101. } catch (error) {
  102. console.error('提交联系表单失败:', error);
  103. res.status(500).json(errorResponse('服务器内部错误'));
  104. }
  105. });
  106. /**
  107. * @api {get} /contact 获取联系我们列表(管理员用)
  108. * @apiName GetContacts
  109. * @apiGroup Contact
  110. *
  111. * @apiParam {Number} [page=1] 页码
  112. * @apiParam {Number} [limit=10] 每页数量
  113. * @apiParam {Number} [status] 处理状态筛选(0-未处理,1-已处理)
  114. */
  115. router.get('/', async (req, res) => {
  116. try {
  117. const { page = 1, limit = 10, status } = req.query;
  118. const offset = (page - 1) * limit;
  119. const whereConditions = {};
  120. if (status !== undefined) {
  121. whereConditions.status = parseInt(status);
  122. }
  123. const { count, rows } = await Contact.findAndCountAll({
  124. where: whereConditions,
  125. order: [['createdAt', 'DESC']],
  126. limit: parseInt(limit),
  127. offset: parseInt(offset)
  128. });
  129. res.json(successResponse('获取成功', {
  130. contacts: rows,
  131. pagination: {
  132. total: count,
  133. page: parseInt(page),
  134. limit: parseInt(limit),
  135. totalPages: Math.ceil(count / limit)
  136. }
  137. }));
  138. } catch (error) {
  139. console.error('获取联系列表失败:', error);
  140. res.status(500).json(errorResponse('服务器内部错误'));
  141. }
  142. });
  143. /**
  144. * @api {get} /contact/:id 获取联系详情
  145. * @apiName GetContactDetail
  146. * @apiGroup Contact
  147. */
  148. router.get('/:id', async (req, res) => {
  149. try {
  150. const { id } = req.params;
  151. const contact = await Contact.findByPk(id);
  152. if (!contact) {
  153. return res.status(404).json(errorResponse('联系记录不存在'));
  154. }
  155. res.json(successResponse('获取成功', contact));
  156. } catch (error) {
  157. console.error('获取联系详情失败:', error);
  158. res.status(500).json(errorResponse('服务器内部错误'));
  159. }
  160. });
  161. /**
  162. * @api {put} /contact/:id/status 更新处理状态
  163. * @apiName UpdateContactStatus
  164. * @apiGroup Contact
  165. *
  166. * @apiParam {Number} status 处理状态(0-未处理,1-已处理)
  167. */
  168. router.put('/:id/status', async (req, res) => {
  169. try {
  170. const { id } = req.params;
  171. const { status } = req.body;
  172. if (![0, 1].includes(parseInt(status))) {
  173. return res.status(400).json(errorResponse('状态值只能是0或1'));
  174. }
  175. const contact = await Contact.findByPk(id);
  176. if (!contact) {
  177. return res.status(404).json(errorResponse('联系记录不存在'));
  178. }
  179. await contact.update({ status: parseInt(status) });
  180. res.json(successResponse('状态更新成功', contact));
  181. } catch (error) {
  182. console.error('更新联系状态失败:', error);
  183. res.status(500).json(errorResponse('服务器内部错误'));
  184. }
  185. });
  186. /**
  187. * @api {delete} /contact/:id 删除联系记录
  188. * @apiName DeleteContact
  189. * @apiGroup Contact
  190. */
  191. router.delete('/:id', async (req, res) => {
  192. try {
  193. const { id } = req.params;
  194. const contact = await Contact.findByPk(id);
  195. if (!contact) {
  196. return res.status(404).json(errorResponse('联系记录不存在'));
  197. }
  198. await contact.destroy();
  199. res.json(successResponse('删除成功'));
  200. } catch (error) {
  201. console.error('删除联系记录失败:', error);
  202. res.status(500).json(errorResponse('服务器内部错误'));
  203. }
  204. });
  205. module.exports = router;