feat: 完善协议管理、师傅资质认证、分账配置并完成测试环境全量部署与同步
这个提交包含在:
@@ -10,6 +10,7 @@ use app\common\model\Version;
|
||||
use fast\Random;
|
||||
use think\captcha\Captcha;
|
||||
use think\Config;
|
||||
use think\Db;
|
||||
use think\Hook;
|
||||
|
||||
/**
|
||||
@@ -17,7 +18,7 @@ use think\Hook;
|
||||
*/
|
||||
class Common extends Api
|
||||
{
|
||||
protected $noNeedLogin = ['init', 'captcha'];
|
||||
protected $noNeedLogin = ['init', 'captcha', 'areas', 'expressCompanies', 'dicts'];
|
||||
protected $noNeedRight = '*';
|
||||
|
||||
public function _initialize()
|
||||
@@ -146,4 +147,166 @@ class Common extends Api
|
||||
$captcha = new Captcha((array)Config::get('captcha'));
|
||||
return $captcha->entry($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 行政区划真实数据库查询接口(源自 fa_area)
|
||||
* - mode=tree: 返回完整省-市-区三级联树
|
||||
* - pid: 按父级ID逐级查询下级行政区
|
||||
*/
|
||||
public function areas()
|
||||
{
|
||||
$mode = trim((string)$this->request->param('mode', ''));
|
||||
if ($mode === 'tree') {
|
||||
$rows = Db::name('area')
|
||||
->field('id,pid,name,shortname,level')
|
||||
->where('level', 'in', [1, 2, 3])
|
||||
->order('id asc')
|
||||
->select();
|
||||
if (empty($rows)) {
|
||||
$this->error('行政区划基础数据尚未初始化');
|
||||
}
|
||||
|
||||
$provinces = [];
|
||||
$citiesByPid = [];
|
||||
$districtsByPid = [];
|
||||
foreach ($rows as $row) {
|
||||
$lvl = (int)$row['level'];
|
||||
$pid = (int)$row['pid'];
|
||||
$id = (int)$row['id'];
|
||||
if ($lvl === 1) {
|
||||
$provinces[] = [
|
||||
'id' => $id,
|
||||
'name' => (string)$row['name'],
|
||||
'cities' => [],
|
||||
];
|
||||
} elseif ($lvl === 2) {
|
||||
if (!isset($citiesByPid[$pid])) {
|
||||
$citiesByPid[$pid] = [];
|
||||
}
|
||||
$citiesByPid[$pid][] = [
|
||||
'id' => $id,
|
||||
'name' => (string)$row['name'],
|
||||
'districts' => [],
|
||||
];
|
||||
} elseif ($lvl === 3) {
|
||||
if (!isset($districtsByPid[$pid])) {
|
||||
$districtsByPid[$pid] = [];
|
||||
}
|
||||
$districtsByPid[$pid][] = (string)$row['name'];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($provinces as &$prov) {
|
||||
$cities = isset($citiesByPid[$prov['id']]) ? $citiesByPid[$prov['id']] : [];
|
||||
foreach ($cities as &$city) {
|
||||
$city['districts'] = isset($districtsByPid[$city['id']]) ? $districtsByPid[$city['id']] : [];
|
||||
}
|
||||
unset($city);
|
||||
$prov['cities'] = $cities;
|
||||
}
|
||||
unset($prov);
|
||||
|
||||
$this->success('', ['tree' => $provinces]);
|
||||
}
|
||||
|
||||
$pid = (int)$this->request->param('pid', 0);
|
||||
$list = Db::name('area')
|
||||
->field('id,pid,name,shortname,mergename,level')
|
||||
->where('pid', $pid)
|
||||
->order('id asc')
|
||||
->select();
|
||||
$this->success('', ['list' => $list]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 物流公司真实字典(源自 fa_express_company,按首字母分组)
|
||||
*/
|
||||
public function expressCompanies()
|
||||
{
|
||||
$rows = Db::name('express_company')
|
||||
->field('id,code,name,initial')
|
||||
->where('status', 'normal')
|
||||
->order('initial asc, weigh desc, id asc')
|
||||
->select();
|
||||
|
||||
$groupMap = [];
|
||||
foreach ($rows as $r) {
|
||||
$letter = strtoupper(trim((string)$r['initial'])) ?: 'Q';
|
||||
if (!isset($groupMap[$letter])) {
|
||||
$groupMap[$letter] = [];
|
||||
}
|
||||
$groupMap[$letter][] = (string)$r['name'];
|
||||
}
|
||||
$groups = [];
|
||||
foreach ($groupMap as $letter => $items) {
|
||||
$groups[] = [
|
||||
'letter' => $letter,
|
||||
'items' => $items,
|
||||
];
|
||||
}
|
||||
// 将“Q 其它”组调整到末尾
|
||||
usort($groups, function ($a, $b) {
|
||||
$isQa = ($a['letter'] === 'Q');
|
||||
$isQb = ($b['letter'] === 'Q');
|
||||
if ($isQa !== $isQb) {
|
||||
return $isQa ? 1 : -1;
|
||||
}
|
||||
return strcmp($a['letter'], $b['letter']);
|
||||
});
|
||||
|
||||
$this->success('', [
|
||||
'groups' => $groups,
|
||||
'list' => $rows,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 平台通用业务字典接口(真实表与系统配置驱动)
|
||||
*/
|
||||
public function dicts()
|
||||
{
|
||||
$parseCsv = static function ($name) {
|
||||
$val = Db::name('config')->where('name', $name)->value('value');
|
||||
if ($val === null || trim((string)$val) === '') {
|
||||
$val = Config::get('site.' . $name);
|
||||
}
|
||||
if (!$val) {
|
||||
return [];
|
||||
}
|
||||
return array_values(array_filter(array_map('trim', explode(',', (string)$val))));
|
||||
};
|
||||
|
||||
$reviewTags = $parseCsv('review_tags');
|
||||
$orderSources = $parseCsv('order_source_options');
|
||||
$rawQuoteHours = $parseCsv('auto_quote_hours_options');
|
||||
$autoQuoteHours = [];
|
||||
foreach ($rawQuoteHours as $h) {
|
||||
$hi = (int)$h;
|
||||
if ($hi > 0) {
|
||||
$autoQuoteHours[] = $hi;
|
||||
}
|
||||
}
|
||||
|
||||
// 师傅资质要求列表直接读取真实证书字典表 fa_cert_dict
|
||||
$certRows = Db::name('cert_dict')
|
||||
->field('id,cert_code,cert_name,sample_type')
|
||||
->where('status', 'normal')
|
||||
->where('cert_code', '<>', 'other')
|
||||
->order('weigh desc, id asc')
|
||||
->select();
|
||||
$qualReqs = [];
|
||||
foreach ($certRows as $cr) {
|
||||
if (!empty($cr['cert_name'])) {
|
||||
$qualReqs[] = (string)$cr['cert_name'];
|
||||
}
|
||||
}
|
||||
|
||||
$this->success('', [
|
||||
'review_tags' => $reviewTags,
|
||||
'order_sources' => $orderSources,
|
||||
'auto_quote_hours' => $autoQuoteHours,
|
||||
'qual_reqs' => $qualReqs,
|
||||
'cert_dict' => $certRows,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
在新工单中引用
屏蔽一个用户