文件
MiAn fafddceaf2 feat: 新增发票抬头、晒单、钱包日志等模型及后台管理功能
- 新增 invoice_title、showcase、wallet_log 模型及对应语言包
- 新增后台控制器、视图及搜索字段配置
- 修复服务类目删除保护逻辑
- 优化后台登录页面文案及样式
- 新增前端角色切换逻辑及页面拦截
- 新增 SVG 图标及样式变量
- 修复接口权限及下拉框默认值问题
2026-11-11 17:02:35 +08:00

196 行
6.7 KiB
PHP

此文件含有模棱两可的 Unicode 字符

此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。

<?php
namespace app\admin\controller;
use app\common\controller\Backend;
use think\Db;
use think\exception\PDOException;
/**
* 申诉工单管理
*
* 申诉单由师傅端发起,后台承担平台客服核实与判责答复职责
*
* @icon fa fa-gavel
*/
class Appeal extends Backend
{
/**
* Appeal模型对象
* @var \app\admin\model\Appeal
*/
protected $model = null;
protected $relationSearch = true;
protected $searchFields = 'id,content,reply,order.order_no,master.realname,master.mobile';
public function _initialize()
{
parent::_initialize();
$this->model = new \app\admin\model\Appeal;
$this->view->assign("typeList", $this->model->getTypeList());
$this->view->assign("statusList", $this->model->getStatusList());
}
/**
* 申诉工单列表
*/
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(['order', 'master'])
->where($where)
->order($sort, $order)
->paginate($limit);
$result = ['total' => $list->total(), 'rows' => $list->items()];
return json($result);
}
/**
* 申诉全景详情(关联订单、雇主、现场凭证与客户评价)
*/
public function detail($ids = null)
{
$row = $this->model->get($ids);
if (!$row) {
$this->error(__('No Results were found'));
}
$this->assignAppealContext($row);
return $this->view->fetch();
}
/**
* 仲裁核查与答复(保护师傅原始申诉凭证只读,仅更新状态与平台答复)
*/
public function edit($ids = null)
{
$row = $this->model->get($ids);
if (!$row) {
$this->error(__('No Results were found'));
}
if (false === $this->request->isPost()) {
$this->assignAppealContext($row);
return $this->view->fetch();
}
$params = $this->request->post('row/a', []);
$status = (string)($params['status'] ?? $row['status']);
$reply = trim((string)($params['reply'] ?? ''));
$statusList = $this->model->getStatusList();
if (!isset($statusList[$status])) {
$this->error('工单流转状态不合法');
}
if (in_array($status, ['done', 'reject'], true) && $reply === '') {
$this->error('标记完结或驳回申诉时,必须填写平台核查答复说明');
}
$oldStatus = (string)$row['status'];
$oldReply = (string)$row['reply'];
Db::startTrans();
try {
$row->allowField(['status', 'reply'])->save([
'status' => $status,
'reply' => mb_substr($reply, 0, 500),
]);
// 状态流转或答复更新时,自动推送站内通知给申诉师傅
if (($status !== $oldStatus || $reply !== $oldReply) && (int)$row['master_id'] > 0) {
$statusText = $statusList[$status] ?? $status;
$orderNo = '';
if ((int)$row['order_id'] > 0) {
$orderNo = (string)Db::name('order')->where('id', (int)$row['order_id'])->value('order_no');
}
$msgTitle = '申诉工单处理通知(' . $statusText . ')';
$msgContent = ($orderNo !== '' ? '关联订单 ' . $orderNo . ' 的' : '您的') . '申诉状态已更新为「' . $statusText . '」';
if ($reply !== '') {
$msgContent .= '。平台答复:' . $reply;
}
Db::name('message')->insert([
'receive_type' => 'master',
'receive_id' => (int)$row['master_id'],
'type' => 'audit',
'title' => mb_substr($msgTitle, 0, 100),
'content' => mb_substr($msgContent, 0, 500),
'order_id' => (int)$row['order_id'],
'is_read' => 0,
'createtime' => time(),
]);
}
Db::commit();
} catch (PDOException|\Exception $e) {
Db::rollback();
$this->error($e->getMessage());
}
$this->success();
}
/**
* 申诉单由师傅端提交,禁止后台伪造录入
*/
public function add()
{
$this->error('申诉工单仅支持师傅端发起,后台不可手动新增');
}
/**
* 申诉仲裁记录需留痕备查,禁止物理删除
*/
public function del($ids = null)
{
$this->error('申诉工单与判责记录禁止删除');
}
/**
* 组装申诉关联上下文数据
*/
protected function assignAppealContext($row)
{
$master = (int)$row['master_id'] > 0
? Db::name('master')->where('id', (int)$row['master_id'])->find()
: null;
$order = (int)$row['order_id'] > 0
? Db::name('order')->where('id', (int)$row['order_id'])->find()
: null;
$customer = ($order && (int)$order['user_id'] > 0)
? Db::name('user')->where('id', (int)$order['user_id'])->field('id,nickname,username,mobile')->find()
: null;
$review = ($order && (int)$order['id'] > 0)
? Db::name('review')->where('order_id', (int)$order['id'])->find()
: null;
$orderMedias = ($order && (int)$order['id'] > 0)
? Db::name('order_media')->where('order_id', (int)$order['id'])->order('id asc')->select()
: [];
$appealImages = array_values(array_filter(array_map('trim', explode(',', (string)$row['images']))));
$reviewImages = $review ? array_values(array_filter(array_map('trim', explode(',', (string)$review['images'])))) : [];
$orderStatusMap = [
0 => '待支付', 1 => '待接单', 2 => '待上门', 3 => '已到达',
4 => '服务中', 5 => '待验收', 6 => '已完成', 9 => '已取消', 10 => '售后/异常'
];
$this->view->assign([
'row' => $row,
'master' => $master ?: [],
'order' => $order ?: [],
'customer' => $customer ?: [],
'review' => $review ?: [],
'orderMedias' => $orderMedias,
'appealImages' => $appealImages,
'reviewImages' => $reviewImages,
'orderStatusMap' => $orderStatusMap,
]);
}
}