| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127 |
- <?php
- namespace App\Services;
- use App\Services\Sharing\ShareDefaults;
- use Illuminate\Support\Facades\DB;
- use Illuminate\Validation\Rule;
- use Illuminate\Validation\ValidationException;
- use InvalidArgumentException;
- /** Per-category tiers for 笔笔返 (rebate) and 回水 (backflow); each save replaces one category's list. */
- class RewardRuleService
- {
- private const TABLES = ['rebate' => 'rebate_rules', 'backflow' => 'backflow_rules'];
- public static function categories(): array
- {
- return ShareDefaults::categories();
- }
- public static function get(string $type): array
- {
- $table = self::table($type);
- $categories = $rules = [];
- foreach (self::categories() as $value => $label) {
- $categories[] = ['value' => $value, 'label' => $label];
- $rules[$value] = [];
- }
- $query = DB::table($table)->orderBy('category');
- if ($type === 'rebate') $query->orderBy('level')->orderBy('min_effective_amount');
- else $query->orderBy('min_loss_amount');
- foreach ($query->get() as $row) {
- if (!array_key_exists($row->category, $rules)) continue;
- $rules[$row->category][] = self::present($type, $row);
- }
- $result = ['type' => $type, 'categories' => $categories, 'rules' => $rules];
- if ($type === 'rebate') {
- $result['levels'] = DB::table('level')->orderBy('level')->get(['level', 'level_name'])
- ->map(fn ($row) => ['level' => (int) $row->level, 'level_name' => (string) $row->level_name])->all();
- }
- return $result;
- }
- public static function save(string $type, array $params): array
- {
- $table = self::table($type);
- $plain = 'regex:/^\d{1,12}(?:\.\d{1,4})?$/D';
- $money = 'regex:/^\d{1,12}(?:\.\d{1,2})?$/D';
- $amount = ['required', 'numeric', $money, 'min:0'];
- $rules = ['category' => ['required', 'string', Rule::in(array_keys(self::categories()))],
- 'rules' => ['present', 'array', 'max:200'],
- 'rules.*' => ['array'],
- 'rules.*.turnover_multiple' => ['required', 'numeric', $money, 'min:0', 'max:999999']];
- if ($type === 'rebate') {
- $rules['rules.*.level'] = ['required', 'integer', Rule::in(DB::table('level')->pluck('level')->map(fn ($v) => (int) $v)->all())];
- $rules['rules.*.min_effective_amount'] = $amount;
- $rules['rules.*.percentage'] = ['required', 'numeric', $plain, 'min:0', 'max:100'];
- } else {
- $rules['rules.*.min_loss_amount'] = $amount;
- $rules['rules.*.reward_amount'] = $amount;
- }
- $params = validator($params, $rules, [], ['rules.*.level' => '会员等级', 'rules.*.percentage' => '返回百分比',
- 'rules.*.min_effective_amount' => '有效投注', 'rules.*.min_loss_amount' => '亏损金额',
- 'rules.*.reward_amount' => '奖励金额', 'rules.*.turnover_multiple' => '需要打码量'])->validate();
- $rows = $seen = [];
- $now = now();
- foreach ($params['rules'] as $rule) {
- $row = ['category' => $params['category'], 'turnover_multiple' => self::decimal($rule['turnover_multiple']),
- 'created_at' => $now, 'updated_at' => $now];
- if ($type === 'rebate') {
- $row += ['level' => (int) $rule['level'], 'min_effective_amount' => self::decimal($rule['min_effective_amount']),
- 'percentage' => bcadd((string) $rule['percentage'], '0', 4)];
- $key = $row['level'] . '|' . $row['min_effective_amount'];
- } else {
- $row += ['min_loss_amount' => self::decimal($rule['min_loss_amount']),
- 'reward_amount' => self::decimal($rule['reward_amount'])];
- $key = $row['min_loss_amount'];
- }
- if (isset($seen[$key])) {
- throw ValidationException::withMessages(['rules' => $type === 'rebate'
- ? '同一会员等级的有效投注门槛不能重复' : '亏损金额门槛不能重复']);
- }
- $seen[$key] = true;
- $rows[] = $row;
- }
- DB::transaction(function () use ($table, $params, $rows) {
- DB::table($table)->where('category', $params['category'])->delete();
- if ($rows) DB::table($table)->insert($rows);
- });
- return self::get($type);
- }
- /** Rules of one type grouped by category, highest threshold first, for settlement. */
- public static function tiers(string $type): array
- {
- $tiers = [];
- $threshold = $type === 'rebate' ? 'min_effective_amount' : 'min_loss_amount';
- foreach (DB::table(self::table($type))->orderByDesc($threshold)->get() as $row) {
- $tiers[$row->category][] = self::present($type, $row);
- }
- return $tiers;
- }
- private static function present(string $type, object $row): array
- {
- $data = ['id' => (int) $row->id, 'category' => (string) $row->category];
- if ($type === 'rebate') {
- $data += ['level' => (int) $row->level, 'min_effective_amount' => self::decimal($row->min_effective_amount),
- 'percentage' => bcadd((string) $row->percentage, '0', 4)];
- } else {
- $data += ['min_loss_amount' => self::decimal($row->min_loss_amount), 'reward_amount' => self::decimal($row->reward_amount)];
- }
- return $data + ['turnover_multiple' => self::decimal($row->turnover_multiple)];
- }
- private static function decimal($value): string
- {
- return bcadd((string) $value, '0', 2);
- }
- private static function table(string $type): string
- {
- if (!isset(self::TABLES[$type])) throw new InvalidArgumentException('奖励类型错误');
- return self::TABLES[$type];
- }
- }
|