model = new \app\admin\model\Master; $this->view->assign("workStatusList", $this->model->getWorkStatusList()); $this->view->assign("verifyStatusList", $this->model->getVerifyStatusList()); } public function index() { $this->request->filter(['strip_tags', 'trim']); if (false === $this->request->isAjax()) { return $this->view->fetch(); } if ($this->request->request('keyField')) { return $this->selectpage(); } [$where, $sort, $order, $offset, $limit] = $this->buildparams(); $list = $this->model ->with(['user']) ->where($where) ->order($sort, $order) ->paginate($limit); $items = $list->items(); $masterIds = array_column($items, 'id'); $skillsGroup = []; if (!empty($masterIds)) { $allSkills = Db::name('master_skill') ->where('master_id', 'in', $masterIds) ->where('status', 'normal') ->select(); foreach ($allSkills as $sk) { $name = !empty($sk['skill_name']) ? $sk['skill_name'] : ''; if ($name) { $skillsGroup[$sk['master_id']][] = $name; } } } foreach ($items as &$it) { $it['skills_list'] = $skillsGroup[$it['id']] ?? []; } unset($it); return json(['total' => $list->total(), 'rows' => $items]); } /** * 新增认证师傅档案(校验用户唯一绑定,强制初始化资金与履约统计为 0) */ public function add() { if (false === $this->request->isPost()) { return $this->view->fetch(); } $params = $this->request->post('row/a', []); if (!$params) { $this->error(__('Parameter %s can not be empty', '')); } $params = $this->preExcludeFields($params); $uid = (int)($params['user_id'] ?? 0); if ($uid <= 0) { $this->error('请选择关联的平台注册用户账号'); } if (Db::name('master')->where('user_id', $uid)->count() > 0) { $this->error('该用户已绑定过师傅档案,请勿重复创建'); } $params['money'] = 0.00; $params['frozen_money'] = 0.00; $params['order_count'] = 0; $params['level'] = max(1, (int)($params['level'] ?? 1)); $params['score'] = 5.00; $params['accept_rate'] = 100.00; $params['ontime_rate'] = 100.00; $result = $this->model->allowField(true)->save($params); if ($result !== false) { $this->success(); } $this->error($this->model->getError() ?: __('No rows were inserted')); } /** * 删除保护:已产生承接工单、钱包余额或结算流水的师傅禁止物理删除 */ public function del($ids = null) { if (false === $this->request->isPost()) { $this->error(__('Invalid parameters')); } $ids = $ids ?: $this->request->post('ids'); $idArr = array_values(array_filter(array_map('intval', explode(',', (string)$ids)))); if (!$idArr) { $this->error(__('Parameter %s can not be empty', 'ids')); } $hasOrder = Db::name('order')->where('master_id', 'in', $idArr)->count(); $hasFund = Db::name('master')->where('id', 'in', $idArr)->where('money > 0 OR frozen_money > 0')->count(); $hasSettle = Db::name('settle')->where('master_id', 'in', $idArr)->count(); if ($hasOrder > 0 || $hasFund > 0 || $hasSettle > 0) { $this->error('所选师傅已关联历史工单、结算记录或存在钱包资金,禁止物理删除;如需停用请关闭听单状态'); } Db::startTrans(); try { Db::name('master_skill')->where('master_id', 'in', $idArr)->delete(); $deleted = Db::name('master')->where('id', 'in', $idArr)->delete(); Db::commit(); } catch (Exception $e) { Db::rollback(); $this->error($e->getMessage()); } if ($deleted) { $this->success(); } $this->error(__('No rows were deleted')); } /** * 编辑师傅资料与入驻资质审核(状态变更自动触发通知) */ public function edit($ids = null) { $row = $this->model->get($ids); if (!$row) { $this->error(__('No Results were found')); } $oldVerify = (string)$row['verify_status']; if ($this->request->isPost()) { $params = $this->request->post("row/a"); if ($params) { $params = $this->preExcludeFields($params); // 禁止在师傅档案编辑中直接篡改钱包可用余额、冻结资金与累计完单数 unset($params['money'], $params['frozen_money'], $params['order_count']); // 若提交的身份证号包含脱敏星号 *,保留原数据库记录不覆盖;若为新填写明文则做合法性校验 if (isset($params['idcard_no'])) { $idcardInput = trim((string)$params['idcard_no']); if (strpos($idcardInput, '*') !== false) { unset($params['idcard_no']); } elseif ($idcardInput !== '' && !preg_match('/^(^\d{15}$)|(^\d{17}([0-9]|X|x)$)$/', $idcardInput)) { $this->error('请输入正确的15位或18位身份证号码'); } } // 若 city-picker 因省市格式差异未选新值导致提交为空而原库有城市,保留原城市不清空 if (isset($params['city']) && trim((string)$params['city']) === '' && !empty($row['city'])) { unset($params['city']); } if (isset($params['verify_status']) && $params['verify_status'] === 'reject' && empty(trim((string)($params['audit_msg'] ?? '')))) { $this->error('驳回实名认证时请填写审核意见说明'); } $result = false; Db::startTrans(); try { $result = $row->allowField(true)->save($params); $newVerify = isset($params['verify_status']) ? (string)$params['verify_status'] : $oldVerify; if ($newVerify !== $oldVerify && in_array($newVerify, ['pass', 'reject'], true)) { $now = time(); $title = $newVerify === 'pass' ? '师傅入驻审核通过' : '师傅入驻审核未通过'; $content = $newVerify === 'pass' ? '您的师傅实名认证已审核通过,现在可开启听单承接同城服务工单。' : ('您的师傅认证申请未通过,原因:' . (trim((string)($params['audit_msg'] ?? '')) ?: '请核对资料后重新提交')); Db::name('message')->insert([ 'receive_type' => 'master', 'receive_id' => (int)$row['id'], 'type' => 'audit', 'title' => $title, 'content' => $content, 'order_id' => 0, 'is_read' => 0, 'createtime' => $now, ]); if ((int)$row['user_id'] > 0) { Db::name('message')->insert([ 'receive_type' => 'user', 'receive_id' => (int)$row['user_id'], 'type' => 'audit', 'title' => $title, 'content' => $content, 'order_id' => 0, 'is_read' => 0, 'createtime' => $now, ]); } } Db::commit(); } catch (Exception $e) { Db::rollback(); $this->error($e->getMessage()); } if ($result !== false) { $this->success(); } $this->error(__('No rows were updated')); } $this->error(__('Parameter %s can not be empty', '')); } $this->view->assign("row", $row); return $this->view->fetch(); } /** * 师傅服务技能管理(保留既有证书与元数据) */ public function skills($ids = null) { $row = $this->model->find($ids); if (!$row) { $this->error(__('No Results were found')); } if ($this->request->isPost()) { $selectedSkills = (array)$this->request->post('skills/a', []); $customText = trim((string)$this->request->post('custom_text', '')); $customNames = []; if (!empty($customText)) { $parts = preg_split('/[,,\s]+/u', $customText); foreach ($parts as $p) { $p = trim($p); if (!empty($p) && !in_array($p, $customNames, true) && !in_array($p, $selectedSkills, true)) { $customNames[] = $p; } } } // 缓存师傅原有技能的证书图片与分类ID,防止保存时丢失证书 $existingRows = Db::name('master_skill')->where('master_id', $row['id'])->select(); $existingMap = []; foreach ($existingRows as $er) { $existingMap[(string)$er['skill_name']] = $er; } $dictRows = !empty($selectedSkills) ? Db::name('skill_dict')->where('name', 'in', $selectedSkills)->select() : []; $dictMap = []; foreach ($dictRows as $dr) { $dictMap[$dr['name']] = $dr; } $now = time(); Db::startTrans(); try { Db::name('master_skill')->where('master_id', $row['id'])->delete(); foreach ($selectedSkills as $sName) { $dict = $dictMap[$sName] ?? null; $old = $existingMap[$sName] ?? null; Db::name('master_skill')->insert([ 'master_id' => $row['id'], 'cat_id' => $old ? (int)$old['cat_id'] : 0, 'skill_id' => $dict ? (int)$dict['id'] : ($old ? (int)$old['skill_id'] : 0), 'skill_name' => $sName, 'category' => $dict ? $dict['category'] : ($old && !empty($old['category']) ? $old['category'] : '服务技能'), 'is_custom' => 0, 'cert_image' => $old ? (string)$old['cert_image'] : '', 'status' => 'normal', 'createtime' => $old ? (int)$old['createtime'] : $now, ]); } foreach ($customNames as $cName) { $exist = Db::name('skill_dict')->where('name', $cName)->find(); if (!$exist) { $skillDictId = Db::name('skill_dict')->insertGetId([ 'category_id' => 0, 'category' => '其他技能', 'name' => $cName, 'source' => 'custom', 'master_id' => $row['id'], 'weigh' => 0, 'status' => 'normal', 'createtime' => $now, 'updatetime' => $now, ]); } else { $skillDictId = (int)$exist['id']; } $old = $existingMap[$cName] ?? null; Db::name('master_skill')->insert([ 'master_id' => $row['id'], 'cat_id' => $old ? (int)$old['cat_id'] : 0, 'skill_id' => $skillDictId, 'skill_name' => $cName, 'category' => $exist && !empty($exist['category']) ? $exist['category'] : '其他技能', 'is_custom' => 1, 'cert_image' => $old ? (string)$old['cert_image'] : '', 'status' => 'normal', 'createtime' => $old ? (int)$old['createtime'] : $now, ]); } Db::commit(); } catch (Exception $e) { Db::rollback(); $this->error($e->getMessage()); } $this->success('技能配置更新成功'); } $categories = Db::name('skill_category')->where('status', 'normal')->order('weigh desc')->select(); $allDictSkills = Db::name('skill_dict')->where('status', 'normal')->order('weigh desc')->select(); $catTree = []; foreach ($categories as $cat) { $catTree[$cat['name']] = []; } foreach ($allDictSkills as $item) { $cName = $item['category']; if (!isset($catTree[$cName])) { $catTree[$cName] = []; } $catTree[$cName][] = $item; } $mySkills = Db::name('master_skill')->where('master_id', $row['id'])->select(); $selectedSkills = []; $customArr = []; foreach ($mySkills as $ms) { if ((int)$ms['is_custom'] === 1 && !Db::name('skill_dict')->where('name', $ms['skill_name'])->where('source', '<>', 'custom')->count()) { $customArr[] = $ms['skill_name']; } else { $selectedSkills[] = $ms['skill_name']; } } $this->view->assign('row', $row); $this->view->assign('catTree', $catTree); $this->view->assign('selectedSkills', $selectedSkills); $this->view->assign('customText', implode(' ', $customArr)); return $this->view->fetch(); } /** * 师傅全景档案画像 */ public function detail($ids = null) { $row = $this->model->with(['user'])->where('master.id', (int)$ids)->find(); if (!$row) { $this->error(__('No Results were found')); } $orderModel = new \app\admin\model\Order; $statusMap = $orderModel->getStatusList(); // 1. 最近接单记录 $recentOrders = Db::name('order') ->where('master_id', $row['id']) ->order('id desc') ->limit(8) ->select(); foreach ($recentOrders as &$o) { $o['status_label'] = $statusMap[(int)$o['status']] ?? ('状态' . $o['status']); } unset($o); // 2. 提现流水 $recentWithdraws = Db::name('withdraw') ->where('master_id', $row['id']) ->order('id desc') ->limit(6) ->select(); // 3. 工单佣金结算记录 $recentSettles = Db::name('settle') ->alias('s') ->join('order o', 'o.id = s.order_id', 'LEFT') ->where('s.master_id', $row['id']) ->field('s.*, o.order_no, o.service_name') ->order('s.id desc') ->limit(6) ->select(); // 4. 客户评价记录 $recentReviews = Db::name('review') ->alias('r') ->join('order o', 'o.id = r.order_id', 'LEFT') ->join('user u', 'u.id = r.user_id', 'LEFT') ->where('r.master_id', $row['id']) ->field('r.*, o.order_no, o.service_name, u.nickname as user_nickname') ->order('r.id desc') ->limit(5) ->select(); // 5. 服务技能与认证证书 $skills = Db::name('master_skill') ->alias('s') ->join('service_cat c', 'c.id = s.cat_id', 'LEFT') ->where('s.master_id', $row['id']) ->field('s.*, c.name as cat_name') ->select(); // 6. 钱包资金变动流水 $walletLogs = Db::name('wallet_log') ->where('user_type', 'master') ->where('user_id', $row['id']) ->order('id desc') ->limit(6) ->select(); $this->view->assign([ 'row' => $row, 'recentOrders' => $recentOrders, 'recentWithdraws' => $recentWithdraws, 'recentSettles' => $recentSettles, 'recentReviews' => $recentReviews, 'skills' => $skills, 'walletLogs' => $walletLogs, ]); return $this->view->fetch(); } }