user = $this->auth->getUser(); } /** * 当前用户ID */ protected function uid() { return (int)$this->auth->id; } /** * 师傅档案 */ protected function masterInfo($reload = false) { if ($this->uid() <= 0) { return false; } if ($this->master === null || $reload) { $row = Db::name('master')->where('user_id', $this->uid())->find(); $this->master = $row ? $row : false; } return $this->master; } /** * 要求是已通过审核的师傅 */ protected function requireMaster() { $master = $this->masterInfo(); if (!$master) { $this->error('请先完成师傅入驻', ['need_apply' => 1], 2001); } if ($master['verify_status'] === 'pending') { $this->error('师傅资质审核中,请耐心等待', ['verify_status' => 'pending'], 2002); } if ($master['verify_status'] === 'reject') { $this->error('资质未通过:' . $master['audit_msg'], ['verify_status' => 'reject'], 2003); } return $master; } /** * 取本人订单(越权拦截) * @param int $orderId * @param string $side user|master */ protected function ownOrder($orderId, $side = 'user') { $orderId = (int)$orderId; if ($orderId <= 0) { $this->error('订单参数错误'); } $order = Db::name('order')->where('id', $orderId)->find(); if (!$order) { $this->error('订单不存在'); } if ($side === 'user' && (int)$order['user_id'] !== $this->uid()) { $this->error('无权操作该订单', null, 403); } if ($side === 'master') { $master = $this->requireMaster(); if ((int)$order['master_id'] !== (int)$master['id']) { $this->error('无权操作该订单', null, 403); } } return $order; } /** * 订单状态流转:校验 → 更新 → 写流水 */ protected function transit(array $order, $to, $remark = '', $extra = [], $operatorType = 'system', $operatorId = 0) { $from = (int)$order['status']; if (!OrderStatus::can($from, $to)) { $this->error('订单状态「' . OrderStatus::label($from) . '」不允许变更为「' . OrderStatus::label($to) . '」'); } $data = array_merge($extra, [ 'status' => (int)$to, 'updatetime' => time(), ]); Db::name('order')->where('id', (int)$order['id'])->update($data); $this->addLog((int)$order['id'], (int)$to, $remark, $operatorType, $operatorId); return array_merge($order, $data); } /** * 写订单流水 */ protected function addLog($orderId, $status, $remark = '', $operatorType = 'system', $operatorId = 0) { Db::name('order_log')->insert([ 'order_id' => (int)$orderId, 'status' => (int)$status, 'remark' => mb_substr((string)$remark, 0, 255), 'operator_type' => $operatorType, 'operator_id' => (int)$operatorId, 'createtime' => time(), ]); } /** * 消息通知 */ protected function notify($receiveType, $receiveId, $title, $content, $type = 'order', $orderId = 0) { if (!$receiveId) { return; } Db::name('message')->insert([ 'receive_type' => $receiveType, 'receive_id' => (int)$receiveId, 'type' => $type, 'title' => mb_substr((string)$title, 0, 100), 'content' => mb_substr((string)$content, 0, 500), 'order_id' => (int)$orderId, 'is_read' => 0, 'createtime' => time(), ]); } /** * 生成订单号 */ protected function makeOrderNo() { return date('YmdHis') . str_pad((string)mt_rand(0, 999999), 6, '0', STR_PAD_LEFT); } /** * 同步写入或隐藏独立晒单表 fa_showcase(仅存储脱敏后的必要公开信息) * @param array $order 订单行 * @param int $isShowcase 1=公开晒单, 0=取消晒单 * @return int 晒单表主键 ID(取消时返回 0) */ protected function syncOrderShowcase(array $order, $isShowcase = 1) { $orderId = (int)$order['id']; $now = time(); if (!$isShowcase) { Db::name('showcase')->where('order_id', $orderId)->update([ 'status' => 'hidden', 'updatetime' => $now, ]); return 0; } $rev = Db::name('review')->where('order_id', $orderId)->where('status', 'normal')->find(); $u = Db::name('user')->where('id', (int)$order['user_id'])->find(); $cat = Db::name('service_cat')->where('id', (int)$order['cat_id'])->find(); $service = Db::name('service')->where('id', (int)$order['service_id'])->find(); $isAnon = $rev ? (int)$rev['is_anonymous'] : 0; $userName = '匿名业主'; $userAvatar = ''; if (!$isAnon && $u) { $nick = trim((string)$u['nickname']); if ($nick !== '' && !preg_match('/^[0-9]/', $nick)) { $userName = mb_substr($nick, 0, 1) . '业主'; } else { $userName = '优匠业主'; } $userAvatar = !empty($u['avatar']) ? (string)$u['avatar'] : ''; } // 地区仅保留区县或城市,绝不保存具体街道/小区/门牌 $district = trim((string)$order['district']); $city = trim((string)$order['city']); $region = $district ?: ($city ?: '同城服务'); // 收集晒单实拍图(评价晒图 > 完工实况图,过滤无效测试域名) $imgList = []; if ($rev && !empty($rev['images'])) { $dec = json_decode((string)$rev['images'], true); $arr = is_array($dec) ? $dec : array_filter(explode(',', (string)$rev['images'])); foreach ($arr as $it) { $it = trim((string)$it); if ($it !== '' && substr($it, -4) !== '.svg' && strpos($it, 'example.com') === false) { $imgList[] = $it; } } } if (empty($imgList)) { $mediaUrls = Db::name('order_media')->where('order_id', $orderId)->order('id desc')->limit(6)->column('url'); foreach ($mediaUrls as $mu) { $mu = trim((string)$mu); if ($mu !== '' && substr($mu, -4) !== '.svg' && strpos($mu, 'example.com') === false) { $imgList[] = $mu; } } } // 封面图提取 $cover = !empty($imgList) ? $imgList[0] : ''; if ($cover === '' && !empty($order['service_image']) && substr($order['service_image'], -4) !== '.svg') { $cover = (string)$order['service_image']; } if ($cover === '' && $service && !empty($service['image']) && substr($service['image'], -4) !== '.svg') { $cover = (string)$service['image']; } if ($cover === '') { $fallback = Db::name('service') ->where('cat_id', (int)$order['cat_id']) ->where('image', '<>', '') ->where('image', 'not like', '%.svg%') ->order('weigh desc, id asc') ->value('image'); if ($fallback) { $cover = (string)$fallback; } } // 师傅脱敏称呼与评分 $masterName = '优匠师傅'; $masterScore = 5.00; if ((int)$order['master_id'] > 0) { $m = Db::name('master')->where('id', (int)$order['master_id'])->find(); if ($m) { $rName = trim((string)$m['realname']); if ($rName !== '' && strpos($rName, '师傅') === false) { $masterName = mb_substr($rName, 0, 1) . '师傅'; } elseif ($rName !== '') { $masterName = $rName; } $masterScore = (float)$m['score'] > 0 ? round((float)$m['score'], 2) : 5.00; } } $score = $rev ? max(1, min(5, (int)$rev['score'])) : 5; $tags = ($rev && !empty($rev['tags'])) ? (string)$rev['tags'] : '准时,专业,值得推荐'; $content = ($rev && !empty($rev['content'])) ? (string)$rev['content'] : '师傅准时上门,施工专业细致,安装效果很满意!'; $payload = [ 'order_id' => $orderId, 'user_id' => (int)$order['user_id'], 'user_name' => $userName, 'user_avatar' => $userAvatar, 'cat_id' => (int)$order['cat_id'], 'cat_name' => $cat ? (string)$cat['name'] : '上门服务', 'service_id' => (int)$order['service_id'], 'service_name' => $order['service_name'] ?: ($service ? (string)$service['name'] : '上门服务'), 'region' => $region, 'cover_image' => $cover, 'images' => !empty($imgList) ? json_encode(array_values($imgList), JSON_UNESCAPED_UNICODE) : '', 'score' => $score, 'tags' => $tags, 'content' => mb_substr($content, 0, 500), 'master_name' => $masterName, 'master_score' => $masterScore, 'status' => 'normal', 'updatetime' => $now, ]; $existing = Db::name('showcase')->where('order_id', $orderId)->find(); if ($existing) { Db::name('showcase')->where('id', (int)$existing['id'])->update($payload); return (int)$existing['id']; } else { $payload['createtime'] = !empty($order['showcase_time']) ? (int)$order['showcase_time'] : $now; return (int)Db::name('showcase')->insertGetId($payload); } } }