diff --git a/application/api/controller/EmailClient.php b/application/api/controller/EmailClient.php index e771ed7..2ae06b1 100644 --- a/application/api/controller/EmailClient.php +++ b/application/api/controller/EmailClient.php @@ -1362,84 +1362,48 @@ class EmailClient extends Base // ==================== Promotion Tasks ==================== /** - * Create a promotion sending task - * Params: - * - journal_id, template_id, style_id, scene, task_name - * - expert_ids (comma separated) OR field + major_id (auto-query from DB) - * - smtp_ids (comma separated, optional: restrict to specific SMTP accounts) - * - min_interval, max_interval (seconds between emails) - * - max_bounce_rate (%), no_repeat_days - * - send_start_hour, send_end_hour (UTC, default 8-22) - * - send_date (Y-m-d,计划发送日期;有则「今日准备明日发」由定时任务处理,无则创建后需手动 startTask) + * 基于任务工厂创建单个推广任务(手动触发) + * + * 参数: + * - promotion_factory_id (必填)工厂ID,任务的模板/领域/国家/发送参数均从工厂读取 + * - send_date (可选,默认明天)Y-m-d + * - task_name (可选,默认 "Manual-{journal_title}-F{factory_id}-{send_date}") + * - no_repeat_days (可选,默认 30)覆盖默认不重复天数 + * - min_interval/max_interval/max_bounce_rate/send_start_hour/send_end_hour 可选覆盖 */ public function createTask() { - $journalId = intval($this->request->param('journal_id', 0)); - $templateId = intval($this->request->param('template_id', 0)); - $styleId = intval($this->request->param('style_id', 0)); - $scene = trim($this->request->param('scene', '')); - $taskName = trim($this->request->param('task_name', '')); - $expertIds = trim($this->request->param('expert_ids', '')); - $field = trim($this->request->param('field', '')); - $fetchIds = trim($this->request->param('fetch_ids', '')); - $smtpIds = trim($this->request->param('smtp_ids', '')); - $minInterval = intval($this->request->param('min_interval', 30)); - $maxInterval = intval($this->request->param('max_interval', 60)); + $factoryId = intval($this->request->param('promotion_factory_id', 0)); + $sendDate = trim($this->request->param('send_date', date('Y-m-d', strtotime('+1 day')))); + $taskName = trim($this->request->param('task_name', '')); + $noRepeatDays = intval($this->request->param('no_repeat_days', 30)); + $minInterval = intval($this->request->param('min_interval', 30)); + $maxInterval = intval($this->request->param('max_interval', 60)); $maxBounceRate = intval($this->request->param('max_bounce_rate', 5)); - $noRepeatDays = intval($this->request->param('no_repeat_days', 7)); - $sendStartHour = intval($this->request->param('send_start_hour', 8)); - $sendEndHour = intval($this->request->param('send_end_hour', 22)); - $sendDate = trim($this->request->param('send_date', date("Y-m-d",strtotime('+1 day')))); - $targetPartitions = trim($this->request->param('target_partitions', '')); - $targetCountryIds = trim($this->request->param('target_country_ids', '')); - $type = intval($this->request->param('type', 0)); - $expertType = intval($this->request->param('expert_type', 5)); + $sendStartHour = intval($this->request->param('send_start_hour', 8)); + $sendEndHour = intval($this->request->param('send_end_hour', 22)); - if (!$journalId) { - return jsonError('journal_id is required'); + if (!$factoryId) { + return jsonError('promotion_factory_id is required'); } - $journal_info = $this->journal_obj->where("journal_id",$journalId)->find(); - $templateId = ($templateId == 0) ? intval($journal_info['default_template_id']) : $templateId; - $styleId = ($styleId == 0) ? intval(isset($journal_info['default_style_id']) ? $journal_info['default_style_id'] : 0) : $styleId; - if($templateId==0){ - return jsonError("template is not set!"); - } - $tpl = Db::name('mail_template') - ->where('template_id', $templateId) - ->where('journal_id', $journalId) - ->where('state', 0) + + $factory = Db::name('promotion_factory') + ->where('promotion_factory_id', $factoryId) ->find(); - if (!$tpl) { - return jsonError('Template not found for this journal'); + if (!$factory) { + return jsonError('Factory not found'); + } + if (intval($factory['state']) !== 0) { + return jsonError('Factory is disabled'); + } + if (intval($factory['expert_type']) !== 5) { + return jsonError('Only expert_type=5 is supported currently'); } - if (empty($scene)) { - $scene = $tpl['scene']; - } - - $experts = []; - if (!empty($expertIds)) { - $ids = array_map('intval', explode(',', $expertIds)); - $experts = Db::name('expert') - ->where('expert_id', 'in', $ids) - ->where('state', 0) - ->select(); - } else { - // 领域来源优先级:前端传 field > 前端传 fetch_ids > journal_promotion_field - $fields = []; - if ($field !== '') { - $fields = [$field]; - } elseif ($fetchIds !== '') { - $fields = $this->resolveFieldsByFetchIds($fetchIds); - } else { - $fields = $this->resolveFieldsByJournal($journalId); - } - - $experts = $this->findEligibleExperts($fields, $noRepeatDays, 100, $targetPartitions, $targetCountryIds); - } - - if (empty($experts)) { - return jsonError('No eligible experts found (all may have been promoted recently)'); + $journalId = intval($factory['journal_id']); + $journal = Db::name('journal')->where('journal_id', $journalId)->find(); + if (!$journal) { + return jsonError('Journal not found'); } $sendDateVal = null; @@ -1450,16 +1414,78 @@ class EmailClient extends Base } } - $now = time(); + if ($sendDateVal) { + $existTask = Db::name('promotion_task') + ->where('factory_id', $factoryId) + ->where('send_date', $sendDateVal) + ->where('state', 'in', [0, 1, 5]) + ->find(); + if ($existTask) { + return jsonError('Factory already has an active task for send_date=' . $sendDateVal . ' (task_id=' . $existTask['task_id'] . ')'); + } + } + + $templateId = intval($factory['template_id']) > 0 + ? intval($factory['template_id']) + : intval($journal['default_template_id']); + $styleId = intval($factory['style_id']) > 0 + ? intval($factory['style_id']) + : intval(isset($journal['default_style_id']) ? $journal['default_style_id'] : 0); + + if ($templateId <= 0) { + return jsonError('template_id is not set on factory or journal'); + } + + $tpl = Db::name('mail_template') + ->where('template_id', $templateId) + ->where('journal_id', $journalId) + ->where('state', 0) + ->find(); + if (!$tpl) { + return jsonError('Template not found for this journal'); + } + $scene = $tpl['scene'] ?? 'promotion'; + + $smtpIds = trim((string)$factory['email_ids']); + if ($smtpIds === '') { + $smtpCount = Db::name('journal_email') + ->where('journal_id', $journalId) + ->where('state', 0) + ->count(); + if ($smtpCount == 0) { + return jsonError('No active SMTP account for this journal'); + } + } + + $fields = $this->resolveFieldsByFetchIds($factory['fetch_ids']); + if (empty($fields)) { + return jsonError('No valid fields resolved from factory.fetch_ids'); + } + + $targetPartitions = trim((string)$factory['target_partitions']); + $targetCountryIds = trim((string)$factory['target_country_ids']); + $dailyLimit = max(1, intval($factory['send_count'])); + + $experts = $this->findEligibleExperts($fields, $noRepeatDays, $dailyLimit, $targetPartitions, $targetCountryIds); + + if (empty($experts)) { + return jsonError('No eligible experts found (all may have been promoted recently)'); + } + + if ($taskName === '') { + $taskName = 'Manual-' . ($journal['title'] ?? $journalId) . '-F' . $factoryId . '-' . ($sendDateVal ?: date('Y-m-d H:i')); + } + + $now = time(); $taskId = Db::name('promotion_task')->insertGetId([ 'journal_id' => $journalId, - 'factory_id' => 0, + 'factory_id' => $factoryId, 'template_id' => $templateId, 'style_id' => $styleId, 'scene' => $scene, - 'type' => $type, - 'expert_type' => $expertType, - 'task_name' => $taskName ?: ('Task ' . date('Y-m-d H:i')), + 'type' => intval($factory['type']), + 'expert_type' => intval($factory['expert_type']), + 'task_name' => $taskName, 'total_count' => count($experts), 'sent_count' => 0, 'fail_count' => 0, @@ -1501,6 +1527,7 @@ class EmailClient extends Base } return jsonSuccess([ 'task_id' => $taskId, + 'factory_id' => $factoryId, 'total_count' => count($experts), 'state' => 0, 'send_date' => $sendDateVal, @@ -1583,32 +1610,7 @@ class EmailClient extends Base // ==================== Journal Promotion Field Mapping ==================== - /** - * 获取某期刊已选的推广领域 - * 参数: journal_id - */ - public function getJournalPromotionFields() - { - $journalId = intval($this->request->param('journal_id', 0)); - if (!$journalId) { - return jsonError('journal_id is required'); - } - $list = Db::name('journal_promotion_field') - ->alias('jpf') - ->join('t_expert_fetch ef', 'ef.expert_fetch_id = jpf.expert_fetch_id', 'left') - ->where('jpf.journal_id', $journalId) - ->where('jpf.state', 0) - ->field('jpf.jpf_id, jpf.expert_fetch_id, ef.field, ef.source, ef.state as fetch_state, jpf.ctime') - ->order('jpf.jpf_id asc') - ->select(); - - foreach ($list as &$item) { - $item['ctime_text'] = $item['ctime'] ? date('Y-m-d H:i:s', $item['ctime']) : ''; - } - - return jsonSuccess($list); - } /** * 设置/更新期刊的推广领域(全量覆盖) @@ -1872,6 +1874,8 @@ class EmailClient extends Base public function getTaskList() { $journalId = intval($this->request->param('journal_id', 0)); + $factoryId = $this->request->param('factory_id', ''); + $type = $this->request->param('type', ''); $state = $this->request->param('state', '-1'); $page = max(1, intval($this->request->param('page', 1))); $perPage = max(1, min(intval($this->request->param('per_page', 20)), 100)); @@ -1880,6 +1884,12 @@ class EmailClient extends Base if ($journalId) { $where['journal_id'] = $journalId; } + if ($factoryId !== '' && $factoryId !== '-1') { + $where['factory_id'] = intval($factoryId); + } + if ($type !== '' && $type !== '-1') { + $where['type'] = intval($type); + } if ($state !== '-1' && $state !== '') { $where['state'] = intval($state); } diff --git a/application/api/controller/PromotionFactory.php b/application/api/controller/PromotionFactory.php index adb4ca9..b258728 100644 --- a/application/api/controller/PromotionFactory.php +++ b/application/api/controller/PromotionFactory.php @@ -133,6 +133,19 @@ class PromotionFactory extends Base return jsonSuccess(['promotion_factory_id' => $id]); } + public function changePromotionAct(){ + $data = $this->request->post(); + $rule = new Validate([ + "promotion_factory_id"=>"require", + "start_promotion"=>"require" + ]); + if(!$rule->check($data)){ + return jsonError($rule->getError()); + } + Db::name("promotion_factory")->where("promotion_factory_id",$data['promotion_factory_id'])->update(['start_promotion'=>$data['start_promotion']]); + return jsonSuccess(); + } + public function getCountForPromotionEmailIds(){ $data = $this->request->post(); $rule = new Validate([