feat: 新增地址选择插件及订阅消息等多项功能优化
- 新增地址选择插件,支持地图位置选择并返回地址和经纬度 - 新增微信订阅消息发送功能,支持审核结果、提现通知等场景 - 新增订单支付状态查询、提现收款人姓名保存等接口 - 新增服务区域管理,支持按省市区划分并同步到信息发布 - 新增资金流水和营收报表导出功能 - 新增可用优惠券查询、订单退款拆分处理等功能 - 优化后台角标显示、首页数据统计、UI配置下发等 - 修复订单支付并发问题、地区管理逻辑等
这个提交包含在:
@@ -18,6 +18,13 @@ use think\Db;
|
||||
*/
|
||||
class AdminSetting extends V1
|
||||
{
|
||||
/** 管理端接口必须校验管理员身份(V1 默认关闭了 URI 权限校验) */
|
||||
public function _initialize()
|
||||
{
|
||||
parent::_initialize();
|
||||
$this->adminOnly();
|
||||
}
|
||||
|
||||
/**
|
||||
* AS1 基础配置
|
||||
* GET → {appName,logo,servicePhone,aboutUs,shareTitle}
|
||||
@@ -30,11 +37,15 @@ class AdminSetting extends V1
|
||||
if ($appName === '') {
|
||||
$this->fail('小程序名称不能为空');
|
||||
}
|
||||
$logo = (string)$this->input('logo', $this->cfg('base.logo', ''));
|
||||
$this->setCfg('base.appName', $appName);
|
||||
$this->setCfg('base.logo', (string)$this->input('logo', $this->cfg('base.logo', '')));
|
||||
$this->setCfg('base.logo', $logo);
|
||||
$this->setCfg('base.servicePhone', (string)$this->input('servicePhone', $this->cfg('base.servicePhone', '400-000-0000')));
|
||||
$this->setCfg('base.aboutUs', (string)$this->input('aboutUs', $this->cfg('base.aboutUs', '')));
|
||||
$this->setCfg('base.shareTitle', (string)$this->input('shareTitle', $this->cfg('base.shareTitle', '')));
|
||||
// 兼容旧 ui.* 键,保持名称/LOGO 一致
|
||||
$this->setCfg('ui.siteName', $appName);
|
||||
$this->setCfg('ui.logo', $logo);
|
||||
$this->adminLog('修改基础配置');
|
||||
}
|
||||
|
||||
@@ -116,20 +127,26 @@ class AdminSetting extends V1
|
||||
}
|
||||
|
||||
/**
|
||||
* AS4 区域列表
|
||||
* GET /api/v1/admin/setting/areas → 数组 [{id,name,sort,enabled}]
|
||||
* AS4 服务区域列表(平铺:每条记录自带省 / 市 / 区县)
|
||||
* GET /api/v1/admin/setting/areas → [{id,province,city,district,fullName,lat,lng,sort,enabled}]
|
||||
* 规则:按 province asc, city asc, sort asc, id asc;返回全量(含停用)。
|
||||
*/
|
||||
public function areas()
|
||||
{
|
||||
$rows = Db::name('fc_area')->order('sort asc')->select();
|
||||
$rows = Db::name('fc_area')->order('province asc, city asc, sort asc, id asc')->select();
|
||||
|
||||
$list = [];
|
||||
foreach ($rows as $row) {
|
||||
$list[] = [
|
||||
'id' => (int)$row['id'],
|
||||
'name' => (string)$row['name'],
|
||||
'sort' => (int)$row['sort'],
|
||||
'enabled' => (bool)$row['enabled'],
|
||||
'id' => (int)$row['id'],
|
||||
'province' => (string)$row['province'],
|
||||
'city' => (string)$row['city'],
|
||||
'district' => (string)$row['district'],
|
||||
'fullName' => $row['province'] . ' / ' . $row['city'] . ' / ' . $row['district'],
|
||||
'lat' => $row['lat'] !== null ? (float)$row['lat'] : null,
|
||||
'lng' => $row['lng'] !== null ? (float)$row['lng'] : null,
|
||||
'sort' => (int)$row['sort'],
|
||||
'enabled' => (bool)$row['enabled'],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -137,35 +154,82 @@ class AdminSetting extends V1
|
||||
}
|
||||
|
||||
/**
|
||||
* AS5 保存区域
|
||||
* POST /api/v1/admin/setting/area 入参 {id?,name,sort,enabled} → {id,name}
|
||||
* AS5 保存服务区域
|
||||
* POST /api/v1/admin/setting/area 入参 {id?,province,city,district,sort,enabled,lat?,lng?} → {id,fullName}
|
||||
* 规则:省 / 市 / 区县均必填;同一「省+市+区县」不允许重复。
|
||||
*/
|
||||
public function saveArea()
|
||||
{
|
||||
$id = (int)$this->input('id', 0);
|
||||
$name = trim((string)$this->input('name', ''));
|
||||
$province = trim((string)$this->input('province', ''));
|
||||
$city = trim((string)$this->input('city', ''));
|
||||
$district = trim((string)$this->input('district', ''));
|
||||
$sort = (int)$this->input('sort', 0);
|
||||
$enabled = (bool)$this->input('enabled', true);
|
||||
$lat = $this->input('lat', null);
|
||||
$lng = $this->input('lng', null);
|
||||
|
||||
if ($name === '') {
|
||||
$this->fail('区域名称不能为空');
|
||||
$values = ['省份' => $province, '城市' => $city, '区县' => $district];
|
||||
foreach ($values as $label => $value) {
|
||||
if ($value === '') {
|
||||
$this->fail($label . '不能为空');
|
||||
}
|
||||
if (mb_strlen($value) > 32) {
|
||||
$this->fail($label . '不能超过 32 个字符');
|
||||
}
|
||||
}
|
||||
|
||||
// 同一条服务区域(省+市+区县)不允许重复
|
||||
$duplicate = Db::name('fc_area')
|
||||
->where('province', $province)
|
||||
->where('city', $city)
|
||||
->where('district', $district)
|
||||
->where('id', '<>', $id)
|
||||
->find();
|
||||
if ($duplicate) {
|
||||
$this->fail('该省市区组合已存在');
|
||||
}
|
||||
|
||||
if ($lat !== null && $lat !== '') {
|
||||
if (!is_numeric($lat) || (float)$lat < -90 || (float)$lat > 90) {
|
||||
$this->fail('纬度格式不正确');
|
||||
}
|
||||
$lat = round((float)$lat, 6);
|
||||
} else {
|
||||
$lat = null;
|
||||
}
|
||||
if ($lng !== null && $lng !== '') {
|
||||
if (!is_numeric($lng) || (float)$lng < -180 || (float)$lng > 180) {
|
||||
$this->fail('经度格式不正确');
|
||||
}
|
||||
$lng = round((float)$lng, 6);
|
||||
} else {
|
||||
$lng = null;
|
||||
}
|
||||
|
||||
$now = time();
|
||||
$data = [
|
||||
'name' => $name,
|
||||
'sort' => $sort,
|
||||
'enabled' => $enabled ? 1 : 0,
|
||||
'province' => $province,
|
||||
'city' => $city,
|
||||
'district' => $district,
|
||||
'lat' => $lat,
|
||||
'lng' => $lng,
|
||||
'sort' => $sort,
|
||||
'enabled' => $enabled ? 1 : 0,
|
||||
'updatetime' => $now,
|
||||
];
|
||||
|
||||
if ($id && Db::name('fc_area')->where('id', $id)->find()) {
|
||||
Db::name('fc_area')->where('id', $id)->update($data);
|
||||
} else {
|
||||
$data['createtime'] = $now;
|
||||
$id = Db::name('fc_area')->insertGetId($data);
|
||||
}
|
||||
|
||||
$this->adminLog('保存区域 #' . $id . ' ' . $name);
|
||||
$fullName = $province . ' / ' . $city . ' / ' . $district;
|
||||
$this->adminLog('保存服务区域 #' . $id . ' ' . $fullName);
|
||||
|
||||
$this->ok(['id' => (int)$id, 'name' => $name]);
|
||||
$this->ok(['id' => (int)$id, 'fullName' => $fullName]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -178,11 +242,19 @@ class AdminSetting extends V1
|
||||
if ($this->request->isPost()) {
|
||||
$siteName = $this->input('siteName', null);
|
||||
if ($siteName !== null) {
|
||||
$this->setCfg('ui.siteName', (string)$siteName);
|
||||
$siteName = trim((string)$siteName);
|
||||
if ($siteName === '') {
|
||||
$this->fail('小程序名称不能为空');
|
||||
}
|
||||
$this->setCfg('base.appName', $siteName);
|
||||
// 兼容旧 ui.siteName(用户端已统一优先读 base.appName)
|
||||
$this->setCfg('ui.siteName', $siteName);
|
||||
}
|
||||
|
||||
$logo = $this->input('logo', null);
|
||||
if ($logo !== null) {
|
||||
$this->setCfg('base.logo', (string)$logo);
|
||||
// 兼容旧 ui.logo
|
||||
$this->setCfg('ui.logo', (string)$logo);
|
||||
}
|
||||
|
||||
@@ -206,7 +278,7 @@ class AdminSetting extends V1
|
||||
}
|
||||
|
||||
$this->setCfg('ui.updatedAt', date('c'));
|
||||
$this->adminLog('修改LOGO与图标配置');
|
||||
$this->adminLog('修改小程序名称/LOGO/图标配置');
|
||||
}
|
||||
|
||||
$this->ok($this->uiConfig());
|
||||
@@ -218,17 +290,16 @@ class AdminSetting extends V1
|
||||
*/
|
||||
public function uiReset()
|
||||
{
|
||||
// LOGO 已统一到 base.logo,重置时两处都清空,避免旧 ui.logo 回落“复活”
|
||||
$this->setCfg('base.logo', '');
|
||||
$this->setCfg('ui.logo', '');
|
||||
$this->setCfg('ui.icons', []);
|
||||
$this->setCfg('ui.updatedAt', date('c'));
|
||||
$this->adminLog('重置LOGO与图标配置');
|
||||
|
||||
$this->ok([
|
||||
'siteName' => (string)$this->cfg('ui.siteName', '同城分类信息'),
|
||||
'logo' => '',
|
||||
'icons' => (object)[],
|
||||
'reset' => true,
|
||||
]);
|
||||
$config = $this->uiConfig();
|
||||
$config['reset'] = true;
|
||||
$this->ok($config);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -275,17 +346,29 @@ class AdminSetting extends V1
|
||||
];
|
||||
}
|
||||
|
||||
/** UI 配置结构(icons 保证为对象) */
|
||||
/** UI 配置结构(名称/LOGO 以 base.* 为准,icons 保证为对象) */
|
||||
protected function uiConfig()
|
||||
{
|
||||
$icons = $this->cfg('ui.icons', []);
|
||||
$icons = is_array($icons) ? $icons : (array)$icons;
|
||||
|
||||
$baseName = trim((string)$this->cfg('base.appName', ''));
|
||||
$siteName = $baseName !== '' ? $baseName : (string)$this->cfg('ui.siteName', '同城分类信息');
|
||||
|
||||
$baseLogo = (string)$this->cfg('base.logo', '');
|
||||
$logo = $baseLogo !== '' ? $baseLogo : (string)$this->cfg('ui.logo', '');
|
||||
|
||||
$shareTitle = trim((string)$this->cfg('base.shareTitle', ''));
|
||||
|
||||
return [
|
||||
'siteName' => (string)$this->cfg('ui.siteName', '同城分类信息'),
|
||||
'logo' => (string)$this->cfg('ui.logo', ''),
|
||||
'icons' => (object)$icons,
|
||||
'updatedAt' => (string)$this->cfg('ui.updatedAt', date('c')),
|
||||
'appName' => $siteName,
|
||||
'siteName' => $siteName,
|
||||
'logo' => $logo,
|
||||
'servicePhone' => (string)$this->cfg('base.servicePhone', ''),
|
||||
'aboutUs' => (string)$this->cfg('base.aboutUs', ''),
|
||||
'shareTitle' => $shareTitle !== '' ? $shareTitle : $siteName,
|
||||
'icons' => (object)$icons,
|
||||
'updatedAt' => (string)$this->cfg('ui.updatedAt', date('c')),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
在新工单中引用
屏蔽一个用户