diff --git a/application/admin/command/FcInstall.php b/application/admin/command/FcInstall.php
index 8bba16f..3a4ba3e 100644
--- a/application/admin/command/FcInstall.php
+++ b/application/admin/command/FcInstall.php
@@ -141,12 +141,13 @@ class FcInstall extends Command
*
* 背景:v1.0.7 新增「免费发布 / 付费发布」开关与付费发布的三档定价
* · 单条价格 post.singlePrice
+ * · 免费发布条数 post.freeQuota / post.freeQuotaCycle(v1.0.10:达到条数后按单条价格计费)
* · 按月(价格 + 含条数)post.monthPrice / post.monthQuota
* · 按年(价格 + 含条数)post.yearPrice / post.yearQuota
* 老库缺列/缺配置时补齐,**不覆盖运营已改过的值**:
* ① fc_user_ext 补 post_quota_left / post_quota_expire(剩余发布条数 + 有效期)
* ② fc_plan 补 post_quota(套餐含发布条数,月/年卡赠送额度)
- * ③ fc_config 补 post.* 六项(已存在则跳过)
+ * ③ fc_config 补 post.* 八项(已存在则跳过)
* ④ 月卡/年卡的价格与条数以配置为准做一次同步(配置缺失时反向用套餐值填配置)
*/
private function upgradePublish(Output $output)
@@ -181,7 +182,9 @@ class FcInstall extends Command
// ③ 配置项(只补缺失的,不覆盖运营已改的值)
$defaults = [
['post.mode', 'free', 'post', '发布模式:free 免费发布 / paid 付费发布'],
- ['post.singlePrice', '5', 'post', '单条发布价格(元)'],
+ ['post.singlePrice', '5', 'post', '单条发布价格(元)=付费发布标准价'],
+ ['post.freeQuota', '0', 'post', '免费发布条数(0 = 不限制/免费到底;达到条数后按单条发布价格计费)'],
+ ['post.freeQuotaCycle', 'day', 'post', '免费条数统计周期:day 每日 / month 每月 / total 累计'],
['post.monthPrice', '19.9', 'post', '按月发布价格(元)'],
['post.monthQuota', '30', 'post', '按月含发布条数'],
['post.yearPrice', '168', 'post', '按年发布价格(元)'],
@@ -689,7 +692,9 @@ class FcInstall extends Command
['fee.memberYear', '168', 'fee', '年卡价格'],
// 发布模式与发布套餐(后台「发布与费用配置」页维护;真源=配置,保存时同步 fc_plan)
['post.mode', 'free', 'post', '发布模式:free 免费发布 / paid 付费发布(默认免费,避免上线即撞付费墙)'],
- ['post.singlePrice', '5', 'post', '单条发布价格(元)'],
+ ['post.singlePrice', '5', 'post', '单条发布价格(元)=付费发布标准价'],
+ ['post.freeQuota', '0', 'post', '免费发布条数(0 = 不限制/免费到底;达到条数后按单条发布价格计费)'],
+ ['post.freeQuotaCycle', 'day', 'post', '免费条数统计周期:day 每日 / month 每月 / total 累计'],
['post.monthPrice', '19.9', 'post', '按月发布价格(元)'],
['post.monthQuota', '30', 'post', '按月含发布条数'],
['post.yearPrice', '168', 'post', '按年发布价格(元)'],
diff --git a/application/admin/controller/fc/Publish.php b/application/admin/controller/fc/Publish.php
index 9ea7790..6dcfb85 100644
--- a/application/admin/controller/fc/Publish.php
+++ b/application/admin/controller/fc/Publish.php
@@ -12,12 +12,14 @@ use think\Db;
* 真源 = fa_fc_config 的 post.* 分组;保存时同步 fa_fc_plan 的月/年卡价格与含发布条数,
* 保证「发布页价格」「会员中心套餐价」「后端计费」三者一致。
*
- * post.mode free 免费发布 / paid 付费发布
- * post.singlePrice 单条发布价格(元)
- * post.monthPrice 按月发布价格(元)
- * post.monthQuota 按月含发布条数
- * post.yearPrice 按年发布价格(元)
- * post.yearQuota 按年含发布条数
+ * post.mode free 免费发布 / paid 付费发布
+ * post.singlePrice 单条发布价格(元)=「付费发布」标准价
+ * post.freeQuota 免费发布条数(0 = 不限制/免费到底;超出即按 singlePrice 计费)
+ * post.freeQuotaCycle 免费条数统计周期:day 每日 / month 每月 / total 累计
+ * post.monthPrice 按月发布价格(元)
+ * post.monthQuota 按月含发布条数
+ * post.yearPrice 按年发布价格(元)
+ * post.yearQuota 按年含发布条数
*
* @icon fa fa-cny
*/
@@ -26,6 +28,9 @@ class Publish extends Backend
/** 发布模式取值 */
const MODES = ['free', 'paid'];
+ /** 免费条数统计周期取值 */
+ const CYCLES = ['day', 'month', 'total'];
+
/**
* 配置页(GET 显示 / POST 保存)
*/
@@ -73,6 +78,12 @@ class Publish extends Backend
$yearPrice = $num('yearPrice', 168);
$monthQuota = $int('monthQuota', 30);
$yearQuota = $int('yearQuota', 500);
+ // 免费发布条数:0 = 不限制(免费到底);达到条数后按「单条发布价格」计费
+ $freeQuota = $int('freeQuota', 0);
+ $freeQuotaCycle = isset($params['freeQuotaCycle']) ? trim((string)$params['freeQuotaCycle']) : 'day';
+ if (!in_array($freeQuotaCycle, self::CYCLES, true)) {
+ $this->error('免费条数统计周期只支持「每日 / 每月 / 累计」');
+ }
foreach (['单条发布价格' => $single, '按月发布价格' => $monthPrice, '按年发布价格' => $yearPrice] as $label => $v) {
if ($v < 0) {
@@ -82,15 +93,20 @@ class Publish extends Backend
if ($monthQuota < 0 || $yearQuota < 0) {
$this->error('含发布条数不能为负');
}
+ if ($freeQuota < 0) {
+ $this->error('免费发布条数不能为负(0 表示不限制)');
+ }
$now = time();
$values = [
- 'post.mode' => $mode,
- 'post.singlePrice' => $single,
- 'post.monthPrice' => $monthPrice,
- 'post.monthQuota' => $monthQuota,
- 'post.yearPrice' => $yearPrice,
- 'post.yearQuota' => $yearQuota,
+ 'post.mode' => $mode,
+ 'post.singlePrice' => $single,
+ 'post.freeQuota' => $freeQuota,
+ 'post.freeQuotaCycle' => $freeQuotaCycle,
+ '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();
@@ -128,12 +144,15 @@ class Publish extends Backend
$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'),
+ 'mode' => in_array($mode, self::MODES, true) ? $mode : 'paid',
+ 'singlePrice' => $pick('post.singlePrice', $pick('fee.singlePost', '5')),
+ // 免费发布条数:老库未保存过时回落 fee.freePostPerDay(默认 0 = 不限制)
+ 'freeQuota' => $pick('post.freeQuota', $pick('fee.freePostPerDay', '0')),
+ 'freeQuotaCycle' => $pick('post.freeQuotaCycle', 'day'),
+ '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'),
];
}
}
diff --git a/application/admin/lang/zh-cn/fc/publish.php b/application/admin/lang/zh-cn/fc/publish.php
index edc50f1..c2c974e 100644
--- a/application/admin/lang/zh-cn/fc/publish.php
+++ b/application/admin/lang/zh-cn/fc/publish.php
@@ -6,6 +6,11 @@ return [
'免费发布(所有用户均可免费发布)' => '免费发布(所有用户均可免费发布)',
'付费发布(按下面价格计费,套餐额度内免费)' => '付费发布(按下面价格计费,套餐额度内免费)',
'会员单条发布价格' => '会员单条发布价格',
+ '免费发布条数' => '免费发布条数',
+ '条' => '条',
+ '每日' => '每日',
+ '每月' => '每月',
+ '累计' => '累计',
'会员按月发布' => '会员按月发布',
'会员按年发布' => '会员按年发布',
'元/条' => '元/条',
@@ -30,6 +35,10 @@ return [
=> '「会员单条发布价格」= 未购买套餐时按条计费的价格;「按月/按年」= 会员套餐价格,并赠送对应条数的发布额度(额度随会员有效期,过期自动清零)。',
'未开通套餐的用户按此价格付费发布(金额由服务端计算,前端不可传价)'
=> '未开通套餐的用户按此价格付费发布(金额由服务端计算,前端不可传价)',
+ '免费发布条数:免费发布模式下前 N 条免费,达到条数后按「会员单条发布价格」计费(计费标准与付费发布一致);付费发布模式下同样保留这 N 条免费额度(避免一开付费就是硬付费墙)。填 0 = 不限制(免费到底)。'
+ => '免费发布条数:免费发布模式下前 N 条免费,达到条数后按「会员单条发布价格」计费(计费标准与付费发布一致);付费发布模式下同样保留这 N 条免费额度(避免一开付费就是硬付费墙)。填 0 = 不限制(免费到底)。',
+ '计费顺序:套餐额度 → 免费发布条数 → 单条付费;免费发布条数达到后即按「会员单条发布价格」计费,金额一律由服务端计算。'
+ => '计费顺序:套餐额度 → 免费发布条数 → 单条付费;免费发布条数达到后即按「会员单条发布价格」计费,金额一律由服务端计算。',
'价格与条数会同步到「会员套餐 → 月卡(M1)」;购买后立即赠送该条数的发布额度'
=> '价格与条数会同步到「会员套餐 → 月卡(M1)」;购买后立即赠送该条数的发布额度',
'价格与条数会同步到「会员套餐 → 年卡(M12)」;购买后立即赠送该条数的发布额度'
diff --git a/application/admin/view/fc/publish/index.html b/application/admin/view/fc/publish/index.html
index d5bff6e..33d2622 100644
--- a/application/admin/view/fc/publish/index.html
+++ b/application/admin/view/fc/publish/index.html
@@ -7,6 +7,8 @@
{:__('本页是「发布模式 + 发布价格/条数」的唯一入口;保存后会同步「会员套餐」的月/年卡价格与含发布条数,因此用户端发布页、会员中心与后端计费口径一致。')}
{:__('「会员单条发布价格」= 未购买套餐时按条计费的价格;「按月/按年」= 会员套餐价格,并赠送对应条数的发布额度(额度随会员有效期,过期自动清零)。')}
+
+ {:__('计费顺序:套餐额度 → 免费发布条数 → 单条付费;免费发布条数达到后即按「会员单条发布价格」计费,金额一律由服务端计算。')}