request->isPost()) { $appName = trim((string)$this->input('appName', '')); if ($appName === '') { $this->fail('小程序名称不能为空'); } $this->setCfg('base.appName', $appName); $this->setCfg('base.logo', (string)$this->input('logo', $this->cfg('base.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', ''))); $this->adminLog('修改基础配置'); } $this->ok($this->baseConfig()); } /** * AS2 微信支付配置 * POST /api/v1/admin/setting/pay 入参 {mchId,apiKey} → {configured:true} * * 【写入目标:epay 插件配置】支付能力已改由 epay 插件承载,商户号/密钥的真源是插件配置 * (后台「插件管理 → 微信支付宝整合插件 → 配置」,持久化在 addons/epay/config.php)。 * 因此本接口把 mchId/apiKey 合并写入插件的 wechat.mch_id / wechat.key,而不再写 fc_config 的 pay.* * (历史键 pay.mchId/pay.apiKey 保留在库中仅为兼容,代码已不读取)。 * 小程序 AppID(wechat.miniapp_id)与回调地址不在此维护:前者请到插件配置页填写,后者由服务端按请求域名动态生成。 */ public function pay() { $mchId = trim((string)$this->input('mchId', '')); $apiKey = trim((string)$this->input('apiKey', '')); if ($mchId === '') { $this->fail('微信支付商户号不能为空'); } if ($apiKey === '') { $this->fail('支付密钥不能为空'); } $addonConfig = (array)get_addon_config('epay'); $wechat = (isset($addonConfig['wechat']) && is_array($addonConfig['wechat'])) ? $addonConfig['wechat'] : []; $wechat['mch_id'] = $mchId; $wechat['key'] = $apiKey; try { // 与后台 Addon::config 同一套写入逻辑(VarExporter 导出回 addons/epay/config.php) set_addon_config('epay', ['wechat' => $wechat]); } catch (\Exception $e) { $this->fail('支付配置写入插件失败:' . $e->getMessage()); } $this->adminLog('修改微信支付配置(写入 epay 插件)'); // 返回体保持原契约({configured:true});是否已具备小程序支付条件(miniapp_id 是否填写) // 请看后台「插件管理 → 微信支付宝整合插件 → 配置」或 /pay/prepay 的返回提示。 $this->ok(['configured' => true]); } /** * AS3 费用配置 * GET → {singlePost,freePostPerDay,memberMonth,memberQuarter,memberYear} * POST 入参同字段 → 合并后配置;同时同步 fc_plan 价格 */ public function fee() { if ($this->request->isPost()) { $singlePost = (float)$this->input('singlePost', $this->cfg('fee.singlePost', 5)); if ($singlePost < 0) { $this->fail('单条发布价格不能为负'); } $freePostPerDay = (int)$this->input('freePostPerDay', $this->cfg('fee.freePostPerDay', 2)); $memberMonth = (float)$this->input('memberMonth', $this->cfg('fee.memberMonth', 19.9)); $memberQuarter = (float)$this->input('memberQuarter', $this->cfg('fee.memberQuarter', 49.9)); $memberYear = (float)$this->input('memberYear', $this->cfg('fee.memberYear', 168)); $this->setCfg('fee.singlePost', $singlePost); $this->setCfg('fee.freePostPerDay', $freePostPerDay); $this->setCfg('fee.memberMonth', $memberMonth); $this->setCfg('fee.memberQuarter', $memberQuarter); $this->setCfg('fee.memberYear', $memberYear); Db::name('fc_plan')->where('plan_code', 'M1')->update(['price' => $memberMonth]); Db::name('fc_plan')->where('plan_code', 'M3')->update(['price' => $memberQuarter]); Db::name('fc_plan')->where('plan_code', 'M12')->update(['price' => $memberYear]); $this->adminLog('修改费用配置'); } $this->ok($this->feeConfig()); } /** * AS4 区域列表 * GET /api/v1/admin/setting/areas → 数组 [{id,name,sort,enabled}] */ public function areas() { $rows = Db::name('fc_area')->order('sort asc')->select(); $list = []; foreach ($rows as $row) { $list[] = [ 'id' => (int)$row['id'], 'name' => (string)$row['name'], 'sort' => (int)$row['sort'], 'enabled' => (bool)$row['enabled'], ]; } $this->ok($list); } /** * AS5 保存区域 * POST /api/v1/admin/setting/area 入参 {id?,name,sort,enabled} → {id,name} */ public function saveArea() { $id = (int)$this->input('id', 0); $name = trim((string)$this->input('name', '')); $sort = (int)$this->input('sort', 0); $enabled = (bool)$this->input('enabled', true); if ($name === '') { $this->fail('区域名称不能为空'); } $data = [ 'name' => $name, 'sort' => $sort, 'enabled' => $enabled ? 1 : 0, ]; if ($id && Db::name('fc_area')->where('id', $id)->find()) { Db::name('fc_area')->where('id', $id)->update($data); } else { $id = Db::name('fc_area')->insertGetId($data); } $this->adminLog('保存区域 #' . $id . ' ' . $name); $this->ok(['id' => (int)$id, 'name' => $name]); } /** * AS6 站点 UI 配置(LOGO / 图标) * GET → {siteName,logo,icons,updatedAt} * POST 入参 {siteName?,logo?,icons?}(合并语义:只覆盖传入的 key) → 完整配置 */ public function ui() { if ($this->request->isPost()) { $siteName = $this->input('siteName', null); if ($siteName !== null) { $this->setCfg('ui.siteName', (string)$siteName); } $logo = $this->input('logo', null); if ($logo !== null) { $this->setCfg('ui.logo', (string)$logo); } // 注意:icons 可能是数组,不能走 $this->input()(TP5.0 Request::input 默认按字符串强转, // 传入数组会抛 "variable type error:array" 导致 HTTP 500);统一从 inputs() 取原始值。 $allInputs = $this->inputs(); $icons = array_key_exists('icons', $allInputs) ? $allInputs['icons'] : null; if ($icons !== null) { if (is_string($icons)) { $icons = json_decode($icons, true); } $icons = is_array($icons) ? $icons : (array)$icons; $merged = (array)$this->cfg('ui.icons', []); foreach ($icons as $k => $v) { if (!is_string($v)) { $this->fail('图标 ' . $k . ' 的地址必须是字符串'); } $merged[$k] = $v; } $this->setCfg('ui.icons', $merged); } $this->setCfg('ui.updatedAt', date('c')); $this->adminLog('修改LOGO与图标配置'); } $this->ok($this->uiConfig()); } /** * AS7 重置 UI 配置 * POST /api/v1/admin/setting/ui/reset → {siteName,logo,icons,reset} */ public function uiReset() { $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, ]); } /** * AS8 文件上传 * POST /api/v1/admin/upload multipart 字段 file → {url} */ public function upload() { $file = $this->request->file('file'); if (!$file) { $this->fail('未接收到上传文件'); } try { $upload = new \app\common\library\Upload($file); $attachment = $upload->upload(); } catch (\Exception $e) { $this->fail($e->getMessage()); } $this->ok(['url' => cdnurl($attachment->url, true)]); } /** 基础配置结构 */ protected function baseConfig() { return [ 'appName' => (string)$this->cfg('base.appName', '同城分类信息'), 'logo' => (string)$this->cfg('base.logo', ''), 'servicePhone' => (string)$this->cfg('base.servicePhone', '400-000-0000'), 'aboutUs' => (string)$this->cfg('base.aboutUs', ''), 'shareTitle' => (string)$this->cfg('base.shareTitle', ''), ]; } /** 费用配置结构 */ protected function feeConfig() { return [ 'singlePost' => (float)$this->cfg('fee.singlePost', 5), 'freePostPerDay' => (int)$this->cfg('fee.freePostPerDay', 2), 'memberMonth' => (float)$this->cfg('fee.memberMonth', 19.9), 'memberQuarter' => (float)$this->cfg('fee.memberQuarter', 49.9), 'memberYear' => (float)$this->cfg('fee.memberYear', 168), ]; } /** UI 配置结构(icons 保证为对象) */ protected function uiConfig() { $icons = $this->cfg('ui.icons', []); $icons = is_array($icons) ? $icons : (array)$icons; return [ 'siteName' => (string)$this->cfg('ui.siteName', '同城分类信息'), 'logo' => (string)$this->cfg('ui.logo', ''), 'icons' => (object)$icons, 'updatedAt' => (string)$this->cfg('ui.updatedAt', date('c')), ]; } }