/**
  * 修改广告
  * 部分属性的修改不会直接体现在表中,而是以请求的方式存在
  * 针对状态`status`、每日投放量`job_num`、今日余量`num`、广告链接`ad_url`,
  * 报价`quote_rmb`的修改会产生申请
  *
  * 其它修改会直接入库
  *
  * 暂时禁止CP用户修改报价
  *
  * @author Meathill
  * @since 0.1.0
  *
  * @param string $id 广告id
  * @param array [optional] $attr
  *
  * @return null
  */
 public function update($id, $attr = null)
 {
     $attr = $attr ? $attr : $this->get_post_data();
     $attr['id'] = $id;
     $ad = new ADModel($attr);
     $im_cp = $_SESSION['role'] == Auth::$CP_PERMISSION;
     $now = date('Y-m-d H:i:s');
     if ($ad->error) {
         $this->exit_with_error($ad->error);
     }
     $ad->fetch();
     if (!$ad->check_owner()) {
         $this->exit_with_error(10, '不是您的广告,您不能修改', 401);
     }
     if ($im_cp && array_diff_key($attr, ['id' => '', 'status' => 0])) {
         $this->exit_with_error(11, '您不能修改广告。', 401);
     }
     // 需要发申请的修改,只有未上线的需要申请
     $status = $ad->get('status');
     $to_status = null;
     $passed = [ADModel::ONLINE, ADModel::OFFLINE];
     if (array_intersect(self::$FIELDS_APPLY, array_keys($attr)) && in_array($status, $passed)) {
         $this->send_apply($id, $attr, $ad);
     }
     if (array_key_exists('status', $attr)) {
         $to_status = $attr['status'];
         if (!in_array($to_status, [ADModel::ONLINE, ADModel::OFFLINE, ADModel::DELETED])) {
             $this->exit_with_error(14, '您只能上下线或者删除广告', 403);
         }
         if ($status == ADModel::APPLY && in_array($to_status, $passed)) {
             $this->exit_with_error(12, '您的广告正在等待审核,不能申请上/下线。', 403);
         }
         if ($to_status == ADModel::DELETED && in_array($status, $passed)) {
             $this->exit_with_error(13, '您的广告已经通过审核,不能删除。', 403);
         }
         if ($status == ADModel::REJECTED && $to_status == ADModel::ONLINE) {
             $this->reapply_ad($id, $attr, $ad);
         }
         if ($to_status == ADModel::ONLINE) {
             // 上线需要审批
             $this->send_apply($id, $attr, $ad);
         }
         // 大家可以自己控制下线,不过要给运营发通知
         if ($to_status == ADModel::OFFLINE) {
             $mail = new Mailer();
             $mail->send(OP_MAIL, '广告自助下线通知', $mail->create('ad-offline', $ad->attributes, ['message' => $attr['message'], 'owner' => $_SESSION['fullname'], 'status-time' => $attr['status-time']]));
         }
         $job = new Job();
         if ($attr['status-time'] && $attr['status-time'] > $now) {
             // 定时下线,创建计划任务
             $job->remove_on_off_job($id, $attr['status-time']);
             $job->create_job($id, Job::OFFLINE, Job::AT, $attr['status-time']);
             $this->output(['code' => 0, 'msg' => '已添加计划任务,该广告届时将下线。']);
         }
         $job->remove_all_job($id);
         $attr['status_time'] = date('Y-m-d H:i:s');
     }
     if ($attr['comment']) {
         // 广告备注修改,需发送一枚通知
         //设置备注
         $ad->save_ad_comment($id, $attr['comment']);
         $commentID = SQLHelper::$lastInsertId;
         $notice = new Notification();
         $check = $notice->send(array('uid' => $commentID, 'ad_id' => $id, 'alarm_type' => Notification::$EDIT_AD_COMMENT, 'create_time' => $now));
         $mail = new Mailer();
         $mail->send(OP_MAIL, '广告备注修改', $mail->create('comment-modified', array('id' => $id)));
         //重新取出comment表的备注集合
         $service = new AD();
         $comments = $service->get_ad_comments(array($id));
         $this->output(array('code' => 0, 'msg' => '添加成功。' . ($check ? '通知已发送。' : ''), 'ad' => array('cm_others' => $comments[$id], 'comment' => '')));
     }
     try {
         $message = $attr['message'];
         unset($attr['message'], $attr['rmb'], $attr['status-time']);
         // 不需要的值
         $ad->update($attr, $message);
         if ($attr['ad_shoot']) {
             $attr['ad_shoot'] = preg_split('/,+/', $attr['ad_shoot']);
         }
     } catch (ADException $e) {
         $this->exit_with_error($e->getCode(), $e->getMessage(), 400, SQLHelper::$info);
     }
     $result = array('code' => 0, 'msg' => '修改完成', 'ad' => $attr);
     // cp用户撤回未审批广告,给他退钱
     if ($im_cp && $to_status == ADModel::DELETED && $status == ADModel::APPLY) {
         $user = new User();
         $balance = $user->unlock_money_for_ad($id);
         if (!$balance) {
             $this->exit_with_error(15, '退回暂扣费用失败。', 400);
         }
         $result['me'] = $balance;
     }
     $this->output($result);
     return null;
 }
 public function get_list($output = true)
 {
     $query = trim($_REQUEST['keyword']);
     if (!$query) {
         $this->output(array('code' => 0, 'msg' => '没有关键词'));
     }
     // 取广告,100个基本等于不限
     $season = date('Y-m-d', time() - 86400 * 90);
     $today = date('Y-m-d');
     $yesterday = date('Y-m-d', time() - 86400);
     $service = new AD();
     $transfer = new Transfer();
     $ads = $service->get_ad_info(array('keyword' => $query, 'status' => array(0, 1), 'oversea' => 0, 'ad_app_type' => 1), 0, 100);
     $ad_ids = array_keys($ads);
     // 取广告运行状态
     $rmb_out = $transfer->get_ad_transfer(array('ad_id' => $ad_ids, 'start' => $season, 'end' => $today), 'ad_id');
     // 取下线申请
     $apply_service = new Apply();
     $applies = $apply_service->get_offline_apply(array('adid' => $ad_ids));
     // 取广告结算状态
     $payment_service = new Payment();
     $quote_service = new Quote();
     $payments = $payment_service->get_payment($ad_ids, $season, $today);
     $quotes = $quote_service->get_quote($ad_ids, $season, $today);
     foreach ($payments as $payment) {
         $ad_id = $payment['id'];
         $month = substr($payment['month'], 0, 7);
         $ads[$ad_id]['payment'] += (int) $payment['rmb'];
         $ads[$ad_id]['quote'] += (int) $quotes[$ad_id][$month];
     }
     // 取饱和度
     $job = new Job();
     $yesterday_job = $job->get_log(array('ad_id' => $ad_ids, 'start' => $yesterday, 'end' => $today));
     $yesterday = $transfer->get_ad_transfer(array('ad_id' => $ad_ids, 'date' => $yesterday), 'ad_id');
     $delivery = array();
     foreach ($ad_ids as $ad_id) {
         $pack_name = $ads[$ad_id]['pack_name'];
         $delivery[$pack_name] = $this->parse_history($ad_id, $ads[$ad_id], $yesterday_job[$ad_id], $yesterday[$ad_id], $delivery[$pack_name]);
     }
     // 取点评记录
     $pack_info = array();
     foreach ($ads as $ad) {
         $pack_info[$ad['pack_name']] = $ad['ad_name'];
     }
     $pack_names = array_unique(array_filter(array_keys($pack_info)));
     $comments_by_pack_name = array();
     if ($pack_names) {
         $comments = $service->get_comments(array('pack_name' => $pack_names));
         foreach ($comments as $comment) {
             $pack_name = $comment['pack_name'];
             $array = $comments_by_pack_name[$pack_name];
             $array = is_array($array) ? $array : array('ad_name' => $pack_info[$pack_name], 'pack_name' => $pack_name, 'comments' => array());
             $array['comments'][] = $comment;
             $comments_by_pack_name[$pack_name] = $array;
         }
     }
     $result = array();
     foreach ($ads as $key => $ad) {
         $item = Utils::array_pick($ad, 'ad_name', 'others', 'create_time', 'quote_rmb', 'payment', 'quote');
         $item['transfer'] = (int) $rmb_out[$key];
         $item['payment_percent'] = $item['quote'] != 0 ? round($item['payment'] / $item['quote'] * 100, 2) : 0;
         $item['id'] = $key;
         $item['offline_msg'] = $applies[$key];
         $item['feedback'] = $ad['feedback'];
         $item['is_full'] = $this->check_is_full($delivery[$ad['pack_name']]);
         if (!$item['is_full']) {
             $item['fullness'] = $this->get_fullness($delivery[$ad['pack_name']]);
         }
         $result[] = $item;
     }
     // 按照回款率第一,下线请求,有无备注,有无推广的优先级进行排序
     usort($result, function ($a, $b) {
         if ($a['payment_percent'] != $b['payment_percent']) {
             return $a['payment_percent'] < $b['payment_percent'] ? 1 : -1;
         }
         if ($a['offline_msg'] && !$b['offline_msg'] || !$a['offline_msg'] && $b['offline_msg']) {
             return $a['offline_msg'] ? -1 : 1;
         }
         if ($a['others'] && !$b['others'] || !$a['others'] && $b['others']) {
             return $a['others'] ? -1 : 1;
         }
         if ($a['transfer'] != $b['transfer']) {
             return $b['transfer'] - $a['transfer'];
         }
         return strcmp($a['create_time'], $b['create_time']);
     });
     $result = ['code' => 0, 'msg' => 'fetch', 'list' => array_slice($result, 0, 20), 'ad_comments' => array_values($comments_by_pack_name)];
     if ($output) {
         $this->output($result);
     }
     return $result;
 }