| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311 |
- <?php
- namespace app\index\controller;
- use app\common\controller\Frontend;
- use app\common\library\Ems as EmsLib;
- use app\common\library\Sms as SmsLib;
- use app\common\model\AppointmentEmailLog;
- use app\common\model\AppointmentRecord;
- use app\common\model\AppointmentSmsLog;
- use think\facade\Cache;
- use think\facade\Db;
- use think\facade\Event;
- use think\facade\Validate;
- class Appointment extends Frontend
- {
- protected $noNeedLogin = ['submit'];
- protected $noNeedRight = ['*'];
- protected function frontendCurrentUrl(): string
- {
- $url = (string)$this->request->url(true);
- return function_exists('cms_lang_url') ? cms_lang_url($url) : $url;
- }
- protected function resolveNotifyChannel(string $lang): string
- {
- $lang = strtolower(trim($lang));
- return in_array($lang, ['cn', 'zh-cn', 'zh'], true) ? 'sms' : 'email';
- }
- protected function getTranslatedActivity(int $activityId, string $lang): array
- {
- $lang = strtolower(trim($lang));
- if ($lang === '') {
- $lang = 'cn';
- }
- $query = Db::name('cms_yuyue_data')->where('did', $activityId);
- $row = (clone $query)->where('lang', $lang)->find();
- if (!$row && $lang !== 'cn') {
- $row = (clone $query)->where('lang', 'cn')->find();
- }
- return is_array($row) ? $row : [];
- }
- protected function getTranslatedNotifyContent(array $activity, string $field, string $lang): string
- {
- return trim((string)($activity[$field] ?? ''));
- }
- protected function sendAppointmentEmail(string $email, string $subject, string $content): array
- {
- try {
- $config = get_addon_config('phpmailer');
- if (!$config) {
- return [false, 'NO_CONFIG', '未找到 phpmailer 插件配置'];
- }
- $mailer = new \addons\phpmailer\library\Mailer($config);
- $result = $mailer->to($email)->subject($subject)->message($content, true)->send();
- if ($result) {
- return [true, 'SUCCESS', '邮件发送成功'];
- }
- return [false, 'FAIL', $mailer->getError() ?: '邮件发送失败'];
- } catch (\Throwable $e) {
- return [false, 'EXCEPTION', $e->getMessage()];
- }
- }
- public function submit()
- {
- if (!$this->request->isPost()) {
- $this->error('请求方式错误');
- }
- $activityId = (int)$this->request->post('activity_id/d', 0);
- $mobile = trim((string)$this->request->post('mobile/s', ''));
- $email = trim((string)$this->request->post('email/s', ''));
- $name = trim((string)$this->request->post('name/s', ''));
- $remark = trim((string)$this->request->post('remark/s', ''));
- $sourcePage = trim((string)$this->request->post('source_page/s', $this->frontendCurrentUrl()));
- $lang = strtolower(trim((string)$this->request->post('lang/s', '')));
- if ($lang === '' && function_exists('cms_current_region_lang')) {
- $lang = strtolower(trim((string)cms_current_region_lang('cn')));
- }
- if ($lang === '') {
- $lang = 'cn';
- }
- $notifyChannel = $this->resolveNotifyChannel($lang);
- if ($activityId <= 0) {
- $this->error('预约活动信息缺失');
- }
- if ($notifyChannel === 'sms') {
- if (!$mobile || !Validate::regex($mobile, '^1\d{10}$')) {
- $this->error('手机号格式不正确');
- }
- } else {
- if (!$email || !Validate::is($email, 'email')) {
- $this->error('Email格式不正确');
- }
- }
- $now = time();
- $ip = $this->request->ip();
- $activity = Db::name('cms_yuyue')->where('id', $activityId)->find();
- if (!$activity) {
- $this->error('预约活动不存在或已下线');
- }
- $translatedActivity = $this->getTranslatedActivity($activityId, $lang);
- if ($translatedActivity) {
- $activity = array_merge($activity, $translatedActivity);
- }
- $activityTitle = trim((string)($activity['title'] ?? ''));
- if ($activityTitle === '') {
- $this->error('预约活动标题缺失');
- }
- $starttime = isset($activity['starttime']) ? (int)$activity['starttime'] : 0;
- $endtime = isset($activity['endtime']) ? (int)$activity['endtime'] : 0;
- if ($starttime > 0 && $now < $starttime) {
- $this->error('预约尚未开始');
- }
- if ($endtime > 0 && $now > $endtime) {
- $this->error('预约已结束');
- }
- $ipLimitWindowSeconds = 60;
- $ipLimitWindowMax = max(0, (int) config('site.sms_rpm', 5));
- $ipLimitDailyMax = max(0, (int) config('site.sms_drl', 10));
- $dayKey = date('Ymd', $now);
- $ipWindowKey = 'appointment_submit_ip_window_' . md5($ip);
- $ipDailyKey = 'appointment_submit_ip_daily_' . md5($ip . '_' . $dayKey);
- $ipWindowHits = (int) Cache::get($ipWindowKey, 0);
- if ($ipLimitWindowMax > 0 && $ipWindowHits >= $ipLimitWindowMax) {
- $this->error('提交过于频繁,请稍后再试');
- }
- $ipDailyHits = (int) Cache::get($ipDailyKey, 0);
- if ($ipLimitDailyMax > 0 && $ipDailyHits >= $ipLimitDailyMax) {
- $this->error('今日提交次数已达上限,请明天再试');
- }
- if ($ipLimitWindowMax > 0) {
- Cache::set($ipWindowKey, $ipWindowHits + 1, $ipLimitWindowSeconds);
- }
- if ($ipLimitDailyMax > 0) {
- Cache::set($ipDailyKey, $ipDailyHits + 1, 86400);
- }
- if ($notifyChannel === 'sms') {
- $exists = AppointmentRecord::where(['activity_id' => $activityId, 'mobile' => $mobile])->find();
- } else {
- $exists = AppointmentRecord::where(['activity_id' => $activityId, 'email' => $email])->find();
- }
- if ($exists) {
- $duplicateMsg = $notifyChannel === 'sms'
- ? '您已提交过该活动预约,请勿重复提交'
- : 'You have already submitted this reservation for the activity.';
- $this->success($duplicateMsg, null, [
- 'record_id' => $exists->id,
- 'duplicate' => 1,
- ]);
- }
- $submitNotifyTpl = $this->getTranslatedNotifyContent($activity, 'submit_sms_content', $lang);
- $autoNotify = !empty($activity['appointment_auto_sms']) ? 1 : 0;
- Db::startTrans();
- try {
- $record = new AppointmentRecord();
- $record->save([
- 'activity_id' => $activityId,
- 'activity_title' => $activityTitle,
- 'lang' => $lang,
- 'notify_channel' => $notifyChannel,
- 'mobile' => $mobile,
- 'email' => $email,
- 'name' => $name,
- 'remark' => $remark,
- 'status' => 1,
- 'submit_sms_status' => 0,
- 'submit_sms_time' => 0,
- 'batch_sms_status' => 0,
- 'batch_sms_time' => 0,
- 'source_page' => $sourcePage,
- 'ip' => $ip,
- 'admin_remark' => '',
- 'createtime' => $now,
- 'updatetime' => $now,
- ]);
- if ($notifyChannel === 'sms') {
- $submitContent = $submitNotifyTpl !== ''
- ? str_replace('{title}', $activityTitle, $submitNotifyTpl)
- : '您已成功预约【' . $activityTitle . '】,新品动态发布后我们将第一时间短信通知您。';
- $smsLog = new AppointmentSmsLog();
- $smsLog->save([
- 'record_id' => $record->id,
- 'activity_id' => $activityId,
- 'mobile' => $mobile,
- 'sms_type' => 'appointment_submit',
- 'template_code' => '',
- 'content' => $submitContent,
- 'status' => 0,
- 'response_code' => '',
- 'response_msg' => $autoNotify ? '待自动发送' : '当前活动未开启提交后自动短信',
- 'retry_count' => 0,
- 'send_time' => 0,
- 'createtime' => $now,
- 'updatetime' => $now,
- ]);
- if ($autoNotify) {
- if (Event::hasListener('sms_notice')) {
- $sendResult = SmsLib::notice($mobile, $submitContent, null);
- $smsLog->status = $sendResult ? 1 : 2;
- $smsLog->response_code = $sendResult ? 'SUCCESS' : 'FAIL';
- $smsLog->response_msg = $sendResult ? '提交后自动短信发送成功' : '提交后自动短信发送失败';
- $smsLog->send_time = $sendResult ? $now : 0;
- $smsLog->updatetime = $now;
- $smsLog->save();
- $record->submit_sms_status = $sendResult ? 1 : 2;
- $record->submit_sms_time = $sendResult ? $now : 0;
- $record->updatetime = $now;
- $record->save();
- } else {
- $smsLog->status = 2;
- $smsLog->response_code = 'NO_PLUGIN';
- $smsLog->response_msg = '未检测到短信通知插件监听 sms_notice';
- $smsLog->updatetime = $now;
- $smsLog->save();
- $record->submit_sms_status = 2;
- $record->updatetime = $now;
- $record->save();
- }
- }
- } else {
- $submitContent = $submitNotifyTpl !== ''
- ? str_replace('{title}', $activityTitle, $submitNotifyTpl)
- : 'You have successfully reserved [' . $activityTitle . ']. We will notify you by email when updates are available.';
- $subject = $activityTitle . ' Reservation Confirmation';
- $emailLog = new AppointmentEmailLog();
- $emailLog->save([
- 'record_id' => $record->id,
- 'activity_id' => $activityId,
- 'email' => $email,
- 'mail_type' => 'appointment_submit',
- 'subject' => $subject,
- 'content' => $submitContent,
- 'status' => 0,
- 'response_code' => '',
- 'response_msg' => $autoNotify ? '待自动发送' : '当前活动未开启提交后自动邮件',
- 'retry_count' => 0,
- 'send_time' => 0,
- 'createtime' => $now,
- 'updatetime' => $now,
- ]);
- if ($autoNotify) {
- [$sendResult, $responseCode, $responseMsg] = $this->sendAppointmentEmail($email, $subject, $submitContent);
- $emailLog->status = $sendResult ? 1 : 2;
- $emailLog->response_code = $responseCode;
- $emailLog->response_msg = $responseMsg ?: ($sendResult ? '提交后自动邮件发送成功' : '提交后自动邮件发送失败');
- $emailLog->send_time = $sendResult ? $now : 0;
- $emailLog->updatetime = $now;
- $emailLog->save();
- $record->submit_sms_status = $sendResult ? 1 : 2;
- $record->submit_sms_time = $sendResult ? $now : 0;
- $record->updatetime = $now;
- $record->save();
- }
- }
- Db::commit();
- } catch (\Throwable $e) {
- Db::rollback();
- $this->error('预约提交失败:' . $e->getMessage());
- }
- if ($notifyChannel === 'sms') {
- $msg = $autoNotify
- ? ($record->submit_sms_status == 1 ? '预约成功,短信已发送,请留意手机通知' : '预约成功,但自动短信发送未完成,请稍后关注后续通知')
- : '预约成功,我们会在合适时间统一发送短信通知';
- } else {
- $msg = $autoNotify
- ? ($record->submit_sms_status == 1 ? 'Reservation successful. A confirmation email has been sent.' : 'Reservation successful, but the automatic notification was not completed.')
- : 'Reservation successful. We will contact you later by email.';
- }
- $this->success($msg, null, [
- 'record_id' => $record->id,
- 'lang' => $lang,
- 'notify_channel' => $notifyChannel,
- 'submit_sms_status' => $record->submit_sms_status,
- 'batch_sms_status' => $record->batch_sms_status,
- 'appointment_auto_sms' => $autoNotify,
- ]);
- }
- }
|