需求落地: - 置顶:fc/post 新增 top 动作(批量置顶/取消置顶 + 站内消息通知),列表加「置顶/取消置顶」工具栏按钮、 行内按钮与置顶状态徽章;置顶信息在用户端首页与「我的发布」中优先展示(Home::feed 按 is_top desc) - 发布与费用配置(新增页面:同城业务 → 基础数据 → 发布与费用配置,fc/publish): 发布模式(免费发布 / 付费发布)、会员单条发布价格、按月(价格 + 含条数)、按年(价格 + 含条数); 真源为 fc_config 的 post.*,保存时同步 fc_plan 的月卡(M1)/年卡(M12) 价格与 post_quota - FcInstall:新增 post.* 六项配置(默认免费发布,避免上线即撞付费墙);fc_user_ext 加 post_quota_left / post_quota_expire(套餐额度与有效期)、fc_plan 加 post_quota;新增 upgradePublish() 幂等升级老库并同步月/年卡 - tools/verify-api.mjs:新增 P9 发布配置 / P8 我的发布 契约校验,P1 发布改为「免费模式/额度内发布」并按付费模式容错 - 语言包与视图同步中文,均通过 php -l 与 node --check
140 行
4.9 KiB
PHP
140 行
4.9 KiB
PHP
<?php
|
||
|
||
namespace app\admin\controller\fc;
|
||
|
||
use app\common\controller\Backend;
|
||
use think\Db;
|
||
|
||
/**
|
||
* 同城-发布与费用配置
|
||
*
|
||
* 本页是「发布模式 + 发布价格/条数」的唯一入口:
|
||
* 真源 = fa_fc_config 的 post.* 分组;保存时同步 fa_fc_plan 的月/年卡价格与含发布条数,
|
||
* 保证「发布页价格」「会员中心套餐价」「后端计费」三者一致。
|
||
*
|
||
* post.mode free 免费发布 / paid 付费发布
|
||
* post.singlePrice 单条发布价格(元)
|
||
* post.monthPrice 按月发布价格(元)
|
||
* post.monthQuota 按月含发布条数
|
||
* post.yearPrice 按年发布价格(元)
|
||
* post.yearQuota 按年含发布条数
|
||
*
|
||
* @icon fa fa-cny
|
||
*/
|
||
class Publish extends Backend
|
||
{
|
||
/** 发布模式取值 */
|
||
const MODES = ['free', 'paid'];
|
||
|
||
/**
|
||
* 配置页(GET 显示 / POST 保存)
|
||
*/
|
||
public function index()
|
||
{
|
||
if ($this->request->isPost()) {
|
||
return $this->save();
|
||
}
|
||
|
||
$this->view->assign('row', $this->current());
|
||
$this->view->assign('plans', Db::name('fc_plan')->order('sort asc')->select());
|
||
return $this->view->fetch();
|
||
}
|
||
|
||
/**
|
||
* 保存配置并同步会员套餐
|
||
*/
|
||
protected function save()
|
||
{
|
||
$params = $this->request->post('row/a');
|
||
if (!is_array($params)) {
|
||
$params = $this->request->post();
|
||
}
|
||
|
||
$mode = isset($params['mode']) ? trim((string)$params['mode']) : 'paid';
|
||
if (!in_array($mode, self::MODES, true)) {
|
||
$this->error('发布模式只支持「免费发布」或「付费发布」');
|
||
}
|
||
|
||
$num = function ($key, $default) use ($params) {
|
||
if (!isset($params[$key]) || $params[$key] === '') {
|
||
return $default;
|
||
}
|
||
return round((float)$params[$key], 2);
|
||
};
|
||
$int = function ($key, $default) use ($params) {
|
||
if (!isset($params[$key]) || $params[$key] === '') {
|
||
return $default;
|
||
}
|
||
return (int)$params[$key];
|
||
};
|
||
|
||
$single = $num('singlePrice', 5);
|
||
$monthPrice = $num('monthPrice', 19.9);
|
||
$yearPrice = $num('yearPrice', 168);
|
||
$monthQuota = $int('monthQuota', 30);
|
||
$yearQuota = $int('yearQuota', 500);
|
||
|
||
foreach (['单条发布价格' => $single, '按月发布价格' => $monthPrice, '按年发布价格' => $yearPrice] as $label => $v) {
|
||
if ($v < 0) {
|
||
$this->error($label . '不能为负');
|
||
}
|
||
}
|
||
if ($monthQuota < 0 || $yearQuota < 0) {
|
||
$this->error('含发布条数不能为负');
|
||
}
|
||
|
||
$now = time();
|
||
$values = [
|
||
'post.mode' => $mode,
|
||
'post.singlePrice' => $single,
|
||
'post.monthPrice' => $monthPrice,
|
||
'post.monthQuota' => $monthQuota,
|
||
'post.yearPrice' => $yearPrice,
|
||
'post.yearQuota' => $yearQuota,
|
||
];
|
||
foreach ($values as $key => $val) {
|
||
$exist = Db::name('fc_config')->where('key', $key)->find();
|
||
if ($exist) {
|
||
Db::name('fc_config')->where('key', $key)->update(['value' => (string)$val, 'updatetime' => $now]);
|
||
} else {
|
||
Db::name('fc_config')->insert([
|
||
'key' => $key, 'value' => (string)$val, 'group_key' => 'post',
|
||
'remark' => '发布与费用配置', 'createtime' => $now, 'updatetime' => $now,
|
||
]);
|
||
}
|
||
}
|
||
|
||
// 同步月/年卡(价格 + 含发布条数);季卡价格按「月卡 ×3」的默认口径不动
|
||
Db::name('fc_plan')->where('plan_code', 'M1')->update([
|
||
'price' => $monthPrice, 'post_quota' => $monthQuota, 'updatetime' => $now,
|
||
]);
|
||
Db::name('fc_plan')->where('plan_code', 'M12')->update([
|
||
'price' => $yearPrice, 'post_quota' => $yearQuota, 'updatetime' => $now,
|
||
]);
|
||
|
||
$this->success('发布配置已保存并同步会员套餐');
|
||
}
|
||
|
||
/** 当前配置(缺失时回落 fee.* 与内置默认) */
|
||
protected function current()
|
||
{
|
||
$cfg = [];
|
||
foreach (Db::name('fc_config')->select() as $r) {
|
||
$cfg[$r['key']] = $r['value'];
|
||
}
|
||
$pick = function ($key, $default) use ($cfg) {
|
||
return (isset($cfg[$key]) && $cfg[$key] !== '') ? $cfg[$key] : $default;
|
||
};
|
||
$mode = (string)$pick('post.mode', 'paid');
|
||
|
||
return [
|
||
'mode' => in_array($mode, self::MODES, true) ? $mode : 'paid',
|
||
'singlePrice' => $pick('post.singlePrice', $pick('fee.singlePost', '5')),
|
||
'monthPrice' => $pick('post.monthPrice', $pick('fee.memberMonth', '19.9')),
|
||
'monthQuota' => $pick('post.monthQuota', '30'),
|
||
'yearPrice' => $pick('post.yearPrice', $pick('fee.memberYear', '168')),
|
||
'yearQuota' => $pick('post.yearQuota', '500'),
|
||
];
|
||
}
|
||
}
|