feat: 新增发票抬头、晒单、钱包日志等模型及后台管理功能
- 新增 invoice_title、showcase、wallet_log 模型及对应语言包 - 新增后台控制器、视图及搜索字段配置 - 修复服务类目删除保护逻辑 - 优化后台登录页面文案及样式 - 新增前端角色切换逻辑及页面拦截 - 新增 SVG 图标及样式变量 - 修复接口权限及下拉框默认值问题
这个提交包含在:
@@ -3,21 +3,27 @@
|
||||
namespace app\admin\controller;
|
||||
|
||||
use app\common\controller\Backend;
|
||||
use think\Db;
|
||||
use think\exception\PDOException;
|
||||
|
||||
/**
|
||||
* 结算单
|
||||
* 结算单管理
|
||||
*
|
||||
* @icon fa fa-circle-o
|
||||
* 结算单由订单核销完工自动生成,后台负责对账审计及异常挂起单的人工结算归档
|
||||
*
|
||||
* @icon fa fa-calculator
|
||||
*/
|
||||
class Settle extends Backend
|
||||
{
|
||||
|
||||
/**
|
||||
* Settle模型对象
|
||||
* @var \app\admin\model\Settle
|
||||
*/
|
||||
protected $model = null;
|
||||
|
||||
protected $relationSearch = true;
|
||||
protected $searchFields = 'id,order.order_no,master.realname,master.mobile';
|
||||
|
||||
public function _initialize()
|
||||
{
|
||||
parent::_initialize();
|
||||
@@ -25,18 +31,9 @@ class Settle extends Backend
|
||||
$this->view->assign("statusList", $this->model->getStatusList());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 默认生成的控制器所继承的父类中有index/add/edit/del/multi五个基础方法、destroy/restore/recyclebin三个回收站方法
|
||||
* 因此在当前控制器中可不用编写增删改查的代码,除非需要自己控制这部分逻辑
|
||||
* 需要将application/admin/library/traits/Backend.php中对应的方法复制到当前控制器,然后进行修改
|
||||
* 结算单列表
|
||||
*/
|
||||
|
||||
|
||||
|
||||
protected $relationSearch = true;
|
||||
|
||||
public function index()
|
||||
{
|
||||
$this->request->filter(['strip_tags', 'trim']);
|
||||
@@ -55,4 +52,172 @@ class Settle extends Backend
|
||||
$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->assignSettleContext($row);
|
||||
return $this->view->fetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* 待结算单财务处理(严禁篡改结算金额,仅允许对 wait 状态执行线上入账或线下归档)
|
||||
*/
|
||||
public function edit($ids = null)
|
||||
{
|
||||
$row = $this->model->get($ids);
|
||||
if (!$row) {
|
||||
$this->error(__('No Results were found'));
|
||||
}
|
||||
if (false === $this->request->isPost()) {
|
||||
$this->assignSettleContext($row);
|
||||
return $this->view->fetch();
|
||||
}
|
||||
|
||||
if ($row['status'] !== 'wait') {
|
||||
$this->error('该结算单已归档完结,禁止重复结算或修改账务数据');
|
||||
}
|
||||
|
||||
$targetStatus = (string)$this->request->post('row.status', '');
|
||||
if (!in_array($targetStatus, ['settled', 'offline'], true)) {
|
||||
$this->error('请选择有效的结算归档方式(线上划拨入账 / 线下特殊结算)');
|
||||
}
|
||||
|
||||
$masterId = (int)$row['master_id'];
|
||||
$orderId = (int)$row['order_id'];
|
||||
$settleAmount = round((float)$row['settle_amount'], 2);
|
||||
$now = time();
|
||||
|
||||
Db::startTrans();
|
||||
try {
|
||||
// 乐观锁防并发重复点击结算
|
||||
$affected = Db::name('settle')
|
||||
->where('id', (int)$row['id'])
|
||||
->where('status', 'wait')
|
||||
->update([
|
||||
'status' => $targetStatus,
|
||||
'settle_time' => $now,
|
||||
]);
|
||||
if (!$affected) {
|
||||
throw new \Exception('结算单状态已变更,请刷新列表后重试');
|
||||
}
|
||||
|
||||
$master = Db::name('master')->where('id', $masterId)->lock(true)->find();
|
||||
if (!$master) {
|
||||
throw new \Exception('关联结算师傅不存在');
|
||||
}
|
||||
$orderNo = $orderId > 0 ? (string)Db::name('order')->where('id', $orderId)->value('order_no') : '';
|
||||
|
||||
if ($targetStatus === 'settled') {
|
||||
// 幂等校验:防止该订单此前已产生过 settle 流水
|
||||
$existsLog = $orderId > 0 && Db::name('wallet_log')
|
||||
->where('user_type', 'master')
|
||||
->where('user_id', $masterId)
|
||||
->where('order_id', $orderId)
|
||||
->where('type', 'settle')
|
||||
->find();
|
||||
if ($existsLog) {
|
||||
throw new \Exception('检测到该订单已存在线上结算入账流水,请核对后再操作');
|
||||
}
|
||||
|
||||
$newMoney = round((float)$master['money'] + $settleAmount, 2);
|
||||
Db::name('master')->where('id', $masterId)->update([
|
||||
'money' => $newMoney,
|
||||
'updatetime' => $now,
|
||||
]);
|
||||
Db::name('wallet_log')->insert([
|
||||
'user_type' => 'master',
|
||||
'user_id' => $masterId,
|
||||
'type' => 'settle',
|
||||
'amount' => $settleAmount,
|
||||
'balance' => $newMoney,
|
||||
'order_id' => $orderId,
|
||||
'remark' => '后台财务确认结算入账' . ($orderNo !== '' ? '(订单 ' . $orderNo . ')' : ''),
|
||||
'createtime' => $now,
|
||||
]);
|
||||
$msgText = ($orderNo !== '' ? '订单 ' . $orderNo . ' ' : '') . '服务佣金 ¥' . number_format($settleAmount, 2, '.', '') . ' 已结算至您的平台钱包余额。';
|
||||
} else {
|
||||
$msgText = ($orderNo !== '' ? '订单 ' . $orderNo . ' ' : '') . '服务佣金 ¥' . number_format($settleAmount, 2, '.', '') . ' 已由财务完成线下特殊结算归档。';
|
||||
}
|
||||
|
||||
if ($orderId > 0) {
|
||||
Db::name('order')->where('id', $orderId)->update([
|
||||
'settle_status' => 1,
|
||||
'settle_amount' => $settleAmount,
|
||||
'updatetime' => $now,
|
||||
]);
|
||||
}
|
||||
|
||||
Db::name('message')->insert([
|
||||
'receive_type' => 'master',
|
||||
'receive_id' => $masterId,
|
||||
'type' => 'order',
|
||||
'title' => '服务佣金结算通知',
|
||||
'content' => mb_substr($msgText, 0, 500),
|
||||
'order_id' => $orderId,
|
||||
'is_read' => 0,
|
||||
'createtime' => $now,
|
||||
]);
|
||||
|
||||
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('财务结算单据禁止删除');
|
||||
}
|
||||
|
||||
/**
|
||||
* 禁止跳过钱包流水的批量修改状态
|
||||
*/
|
||||
public function multi($ids = null)
|
||||
{
|
||||
$this->error('结算单涉及资金划拨,不支持批量修改状态,请逐单核对处理');
|
||||
}
|
||||
|
||||
/**
|
||||
* 组装结算单对账上下文
|
||||
*/
|
||||
protected function assignSettleContext($row)
|
||||
{
|
||||
$order = (int)$row['order_id'] > 0 ? Db::name('order')->where('id', (int)$row['order_id'])->find() : null;
|
||||
$master = (int)$row['master_id'] > 0 ? Db::name('master')->where('id', (int)$row['master_id'])->find() : null;
|
||||
$walletLogs = ((int)$row['master_id'] > 0 && (int)$row['order_id'] > 0)
|
||||
? Db::name('wallet_log')
|
||||
->where('user_type', 'master')
|
||||
->where('user_id', (int)$row['master_id'])
|
||||
->where('order_id', (int)$row['order_id'])
|
||||
->order('id desc')
|
||||
->select()
|
||||
: [];
|
||||
|
||||
$this->view->assign([
|
||||
'row' => $row,
|
||||
'order' => $order ?: [],
|
||||
'master' => $master ?: [],
|
||||
'walletLogs' => $walletLogs,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
在新工单中引用
屏蔽一个用户