功能:全局奖励接收次数上限(职务管理页配置)

新增全局 sysparam 配置 reward_recipient_daily_max:
- 控制每位用户单日内从所有职务持有者处累计接收奖励的最高次数
- 0 = 不限制

后端变更:
- PositionController::saveRewardConfig() 保存配置
- POST admin/positions/reward-config 路由
- AdminCommandController::reward() 新增第④层校验:
  全局次数上限(优先级低于职务级别的 recipient_daily_limit)

视图变更:
- 职务管理页顶部加橙色配置卡片(行内表单,即改即存)
- 显示当前全局配置值
This commit is contained in:
2026-03-01 11:22:02 +08:00
parent a145c6fc0a
commit baaa7087b0
4 changed files with 79 additions and 1 deletions

View File

@@ -15,6 +15,7 @@ namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Department;
use App\Models\Position;
use App\Models\Sysparam;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;
@@ -34,7 +35,10 @@ class PositionController extends Controller
// 全部职务(供任命白名单多选框使用)
$allPositions = Position::with('department')->orderByDesc('rank')->get();
return view('admin.positions.index', compact('departments', 'allPositions'));
// 全局奖励接收次数上限0 = 不限)
$globalRecipientDailyMax = (int) Sysparam::getValue('reward_recipient_daily_max', '0');
return view('admin.positions.index', compact('departments', 'allPositions', 'globalRecipientDailyMax'));
}
/**
@@ -68,6 +72,36 @@ class PositionController extends Controller
return redirect()->route('admin.positions.index')->with('success', "职务【{$data['name']}】创建成功!");
}
/**
* 保存全局奖励金币接收次数上限
*
* 控制每位用户单日内可从所有职务持有者处累计接收奖励的最高次数。
* 0 表示不限制,保存到 sysparam 表中key: reward_recipient_daily_max
*/
public function saveRewardConfig(Request $request): RedirectResponse
{
$request->validate([
'reward_recipient_daily_max' => 'required|integer|min:0|max:9999',
]);
$value = (string) $request->integer('reward_recipient_daily_max');
Sysparam::updateOrCreate(
['alias' => 'reward_recipient_daily_max'],
[
'body' => $value,
'guidetxt' => '用户单日最多接收奖励金币次数0=不限,统计所有职务持有者的发放总次数)',
]
);
Sysparam::clearCache('reward_recipient_daily_max');
$label = $value === '0' ? '不限' : "{$value}";
return redirect()->route('admin.positions.index')
->with('success', "全局接收次数上限已更新为:{$label}");
}
/**
* 更新职务(含任命白名单同步)
*/

View File

@@ -499,6 +499,22 @@ class AdminCommandController extends Controller
}
}
// ④ 全局系统级别:每位用户单日最多接收奖励次数(后台职务管理页统一配置)
$globalMax = (int) Sysparam::getValue('reward_recipient_daily_max', '0');
if ($globalMax > 0) {
$globalCount = PositionAuthorityLog::where('target_user_id', $target->id)
->where('action_type', 'reward')
->whereDate('created_at', today())
->count();
if ($globalCount >= $globalMax) {
return response()->json([
'status' => 'error',
'message' => "{$targetUsername} 今日已累计接收 {$globalCount} 次奖励,已达全局每日上限({$globalMax}",
], 422);
}
}
// 发放金币(通过 UserCurrencyService 原子性更新 + 写流水)
$this->currencyService->change(
$target,

View File

@@ -101,6 +101,33 @@
{{ session('error') }}</div>
@endif
{{-- 全局奖励接收上限配置卡片 --}}
<div class="mb-6 bg-amber-50 border border-amber-200 rounded-xl p-5">
<div class="flex items-start justify-between gap-4 flex-wrap">
<div class="flex-1 min-w-0">
<h3 class="text-sm font-bold text-amber-800 mb-1">🪙 全局奖励接收上限</h3>
<p class="text-xs text-amber-700 leading-relaxed">
每位用户单日内可从<b>所有职务持有者</b>处累计接收奖励金币的最高次数。
设为 <code class="bg-amber-100 px-1 rounded">0</code> 表示不限制。
当前配置:<b>{{ $globalRecipientDailyMax > 0 ? $globalRecipientDailyMax . ' 次' : '不限' }}</b>
</p>
</div>
<form action="{{ route('admin.positions.reward_config') }}" method="POST"
class="flex items-center gap-2 shrink-0">
@csrf
<label class="text-xs text-amber-700 font-bold whitespace-nowrap">每日上限:</label>
<input type="number" name="reward_recipient_daily_max" value="{{ $globalRecipientDailyMax }}"
min="0" max="9999"
class="w-24 h-8 px-2 text-sm border border-amber-300 rounded-md bg-white text-amber-900 focus:outline-none focus:ring-2 focus:ring-amber-400">
<span class="text-xs text-amber-600">0=不限)</span>
<button type="submit"
class="h-8 px-3 bg-amber-500 text-white text-xs font-bold rounded-md hover:bg-amber-600 transition">
保存
</button>
</form>
</div>
</div>
{{-- 按部门分组展示职务 --}}
@foreach ($departments as $dept)
<div class="mb-8">

View File

@@ -191,6 +191,7 @@ Route::middleware(['chat.auth', 'chat.has_position'])->prefix('admin')->name('ad
Route::get('/positions', [\App\Http\Controllers\Admin\PositionController::class, 'index'])->name('positions.index');
Route::put('/departments/{department}', [\App\Http\Controllers\Admin\DepartmentController::class, 'update'])->name('departments.update');
Route::put('/positions/{position}', [\App\Http\Controllers\Admin\PositionController::class, 'update'])->name('positions.update');
Route::post('/positions/reward-config', [\App\Http\Controllers\Admin\PositionController::class, 'saveRewardConfig'])->name('positions.reward_config');
// 大卡片通知广播(仅超级管理员,安全隔离:普通用户无此接口)
Route::post('/banner/broadcast', [\App\Http\Controllers\Admin\BannerBroadcastController::class, 'send'])->name('admin.banner.broadcast');