feat: 完善协议管理、师傅资质认证、分账配置并完成测试环境全量部署与同步

这个提交包含在:
2026-11-12 17:19:49 +08:00
父节点 fafddceaf2
当前提交 72cea8445f
共有 179 个文件被更改,包括 29242 次插入 和 3340 次删除

查看文件

@@ -0,0 +1,363 @@
<?php
namespace app\admin\controller;
use app\common\controller\Backend;
use app\common\library\CommissionService;
use app\common\library\FinanceConfigService;
use think\Db;
use think\exception\PDOException;
/**
* 抽佣与提现配置中心
*
* 统一管理:
* 1. 按订单多维度(计价模式/客群/服务类目/加急/金额阶梯)动态抽佣规则与订单快照兜底策略
* 2. 师傅钱包提现规则与微信小程序商家转账(场景 1005 劳务报酬)合规参数
*
* @icon fa fa-sliders
*/
class FinanceConfig extends Backend
{
/**
* @var \app\admin\model\CommissionRule
*/
protected $model = null;
protected $searchFields = 'id,name,remark';
public function _initialize()
{
parent::_initialize();
$this->model = new \app\admin\model\CommissionRule;
$catRows = Db::name('service_cat')
->field('id,name,status')
->order('weigh desc, id asc')
->select();
$catMap = [0 => '不限服务分类'];
foreach ($catRows as $c) {
$catMap[(int)$c['id']] = (string)$c['name'] . ($c['status'] === 'hidden' ? '(已隐藏)' : '');
}
$this->view->assign([
'orderModeList' => $this->model->getOrderModeList(),
'customerTypeList' => $this->model->getCustomerTypeList(),
'urgentModeList' => $this->model->getUrgentModeList(),
'calcModeList' => $this->model->getCalcModeList(),
'statusList' => $this->model->getStatusList(),
'catMap' => $catMap,
]);
}
/**
* 配置中心首页(GET 渲染独立双页签配置界面;Ajax 返回按订单维度抽佣规则列表)
*/
public function index()
{
$this->request->filter(['strip_tags', 'trim']);
if (false === $this->request->isAjax()) {
$allCfg = FinanceConfigService::getAll(true);
$withdrawCfg = FinanceConfigService::getWithdrawConfig();
$commDefaults = FinanceConfigService::getCommissionDefaults();
$this->view->assign([
'cfg' => $allCfg,
'withdrawCfg' => $withdrawCfg,
'commDefaults' => $commDefaults,
]);
return $this->view->fetch();
}
[$where, $sort, $order, $offset, $limit] = $this->buildparams();
$orderExpr = $sort === 'weigh' ? "weigh {$order}, id desc" : "{$sort} {$order}";
$list = $this->model
->where($where)
->order($orderExpr)
->paginate($limit);
$rows = $list->items();
if (!empty($rows)) {
$cats = Db::name('service_cat')->column('name', 'id');
foreach ($rows as &$r) {
$cid = (int)$r['cat_id'];
$r['cat_name'] = $cid > 0 ? ($cats[$cid] ?? ('分类#' . $cid)) : '';
}
unset($r);
}
return json([
'total' => $list->total(),
'rows' => $rows,
]);
}
/**
* 保存订单默认兜底抽佣设置 / 提现与微信转账规则配置
*/
public function save()
{
if (false === $this->request->isPost()) {
$this->error(__('Invalid parameters'));
}
$this->token();
$section = (string)$this->request->post('section', '');
$row = $this->request->post('row/a', []);
if (!is_array($row) || empty($row)) {
$this->error('提交的配置数据不能为空');
}
// 处理多选框全部取消勾选时前端未上送对应键的情况
if ($section === 'withdraw') {
if (!array_key_exists('withdraw_channels', $row)) {
$this->error('请至少勾选一种开放提现渠道', null, ['token' => $this->request->token()]);
}
if (!array_key_exists('withdraw_allow_weekdays', $row)) {
$this->error('请至少选择一天允许提现的周期', null, ['token' => $this->request->token()]);
}
}
try {
FinanceConfigService::saveConfigs($row);
CommissionService::clearCache();
} catch (\InvalidArgumentException $e) {
$this->error($e->getMessage(), null, ['token' => $this->request->token()]);
} catch (\Throwable $e) {
$this->error('保存配置失败:' . $e->getMessage(), null, ['token' => $this->request->token()]);
}
$msg = $section === 'commission_default'
? '抽佣默认设置已保存(仅影响新订单,已下单工单不受影响)'
: '提现规则已保存生效';
$this->success($msg, null, ['token' => $this->request->token()]);
}
/**
* 新增按订单维度抽佣规则
*/
public function rule_add()
{
if (false === $this->request->isPost()) {
$this->view->assign('row', [
'name' => '',
'order_mode' => 'all',
'customer_type' => 'all',
'cat_id' => 0,
'urgent_mode' => 'all',
'min_amount' => '0.00',
'max_amount' => '0.00',
'calc_mode' => 'percent',
'commission_rate' => '10.00',
'fixed_fee' => '0.00',
'min_commission' => '0.00',
'max_commission' => '0.00',
'weigh' => 100,
'status' => 'normal',
'remark' => '',
]);
return $this->view->fetch();
}
$this->token();
$raw = $this->request->post('row/a', []);
try {
$clean = $this->validateAndCleanRuleInput($raw);
$this->model->allowField(true)->save($clean);
CommissionService::clearCache();
} catch (\InvalidArgumentException $e) {
$this->error($e->getMessage());
} catch (PDOException|\Exception $e) {
$this->error('新增规则失败:' . $e->getMessage());
}
$this->success('抽佣规则已创建(仅对后续新订单生效)');
}
/**
* 编辑按订单维度抽佣规则
*/
public function rule_edit($ids = null)
{
$row = $this->model->get($ids);
if (!$row) {
$this->error(__('No Results were found'));
}
if (false === $this->request->isPost()) {
$this->view->assign('row', $row);
return $this->view->fetch();
}
$this->token();
$raw = $this->request->post('row/a', []);
try {
$clean = $this->validateAndCleanRuleInput($raw);
$row->allowField(true)->save($clean);
CommissionService::clearCache();
} catch (\InvalidArgumentException $e) {
$this->error($e->getMessage());
} catch (PDOException|\Exception $e) {
$this->error('更新规则失败:' . $e->getMessage());
}
$this->success('抽佣规则已更新(历史已创订单不受影响)');
}
/**
* 删除抽佣规则
*/
public function rule_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 (empty($idArr)) {
$this->error(__('Parameter %s can not be empty', 'ids'));
}
$count = 0;
Db::startTrans();
try {
$list = $this->model->where('id', 'in', $idArr)->select();
foreach ($list as $item) {
$count += $item->delete();
}
Db::commit();
CommissionService::clearCache();
} catch (PDOException|\Exception $e) {
Db::rollback();
$this->error($e->getMessage());
}
if ($count) {
$this->success();
}
$this->error(__('No rows were deleted'));
}
/**
* 批量启用/停用抽佣规则
*/
public function rule_multi($ids = null)
{
if (false === $this->request->isPost()) {
$this->error(__('Invalid parameters'));
}
$ids = $ids ?: $this->request->post('ids');
$params = (string)$this->request->post('params', '');
parse_str($params, $values);
if (empty($ids) || empty($values)) {
$this->error(__('Parameter %s can not be empty', 'ids'));
}
$allowed = [];
if (isset($values['status']) && in_array($values['status'], ['normal', 'hidden'], true)) {
$allowed['status'] = $values['status'];
}
if (isset($values['weigh']) && is_numeric($values['weigh'])) {
$allowed['weigh'] = (int)$values['weigh'];
}
if (empty($allowed)) {
$this->error('仅允许批量修改启用状态或优先级权重');
}
$idArr = array_values(array_filter(array_map('intval', explode(',', (string)$ids))));
$allowed['updatetime'] = time();
Db::name('commission_rule')->where('id', 'in', $idArr)->update($allowed);
CommissionService::clearCache();
$this->success();
}
/**
* 抽佣规则入参强白名单与业务区间合法性校验
*
* @param array $raw
* @return array
* @throws \InvalidArgumentException
*/
protected function validateAndCleanRuleInput(array $raw): array
{
$name = trim((string)($raw['name'] ?? ''));
if ($name === '' || mb_strlen($name) > 80) {
throw new \InvalidArgumentException('请填写规则名称(1 ~ 80 个字符)');
}
$orderMode = (string)($raw['order_mode'] ?? 'all');
if (!array_key_exists($orderMode, $this->model->getOrderModeList())) {
throw new \InvalidArgumentException('请选择有效的适用订单计价模式');
}
$customerType = (string)($raw['customer_type'] ?? 'all');
if (!array_key_exists($customerType, $this->model->getCustomerTypeList())) {
throw new \InvalidArgumentException('请选择有效的适用发单客群');
}
$catId = max(0, (int)($raw['cat_id'] ?? 0));
if ($catId > 0 && !Db::name('service_cat')->where('id', $catId)->find()) {
throw new \InvalidArgumentException('所选服务类目不存在');
}
$urgentMode = (string)($raw['urgent_mode'] ?? 'all');
if (!array_key_exists($urgentMode, $this->model->getUrgentModeList())) {
throw new \InvalidArgumentException('请选择有效的加急属性条件');
}
$minAmount = round((float)($raw['min_amount'] ?? 0), 2);
$maxAmount = round((float)($raw['max_amount'] ?? 0), 2);
if ($minAmount < 0 || $maxAmount < 0) {
throw new \InvalidArgumentException('订单金额区间不能为负数');
}
if ($maxAmount > 0 && $minAmount > $maxAmount) {
throw new \InvalidArgumentException('订单起始金额不能大于订单上限金额(0 表示不限上限)');
}
$calcMode = (string)($raw['calc_mode'] ?? 'percent');
if (!array_key_exists($calcMode, $this->model->getCalcModeList())) {
throw new \InvalidArgumentException('请选择有效的计佣方式');
}
$rate = 0.00;
if (in_array($calcMode, ['percent', 'mixed'], true)) {
$rate = CommissionService::validateRateInput($raw['commission_rate'] ?? '');
}
$fixedFee = 0.00;
if (in_array($calcMode, ['fixed', 'mixed'], true)) {
if (!isset($raw['fixed_fee']) || !is_numeric($raw['fixed_fee']) || (float)$raw['fixed_fee'] < 0) {
throw new \InvalidArgumentException('请填写有效的每单固定抽佣金额(≥ 0 元)');
}
$fixedFee = round((float)$raw['fixed_fee'], 2);
}
$minComm = round(max(0.00, (float)($raw['min_commission'] ?? 0)), 2);
$maxComm = round(max(0.00, (float)($raw['max_commission'] ?? 0)), 2);
if ($minComm > 0 && $maxComm > 0 && $minComm > $maxComm) {
throw new \InvalidArgumentException('单笔保底抽佣金额不能大于单笔封顶抽佣金额');
}
$weigh = isset($raw['weigh']) && is_numeric($raw['weigh']) ? (int)$raw['weigh'] : 100;
$status = in_array($raw['status'] ?? 'normal', ['normal', 'hidden'], true) ? $raw['status'] : 'normal';
$remark = mb_substr(trim((string)($raw['remark'] ?? '')), 0, 255);
return [
'name' => $name,
'order_mode' => $orderMode,
'customer_type' => $customerType,
'cat_id' => $catId,
'urgent_mode' => $urgentMode,
'min_amount' => number_format($minAmount, 2, '.', ''),
'max_amount' => number_format($maxAmount, 2, '.', ''),
'calc_mode' => $calcMode,
'commission_rate' => number_format($rate, 2, '.', ''),
'fixed_fee' => number_format($fixedFee, 2, '.', ''),
'min_commission' => number_format($minComm, 2, '.', ''),
'max_commission' => number_format($maxComm, 2, '.', ''),
'weigh' => $weigh,
'status' => $status,
'remark' => $remark,
];
}
}