更新增加统计【活动期间输赢总数据】
This commit is contained in:
@@ -30,6 +30,7 @@ class PlayxConfig extends Backend
|
|||||||
|
|
||||||
$payload = $row->toArray();
|
$payload = $row->toArray();
|
||||||
$payload['active_mall_open_date'] = MallPointsConversion::resolveActiveMallOpenDate($row);
|
$payload['active_mall_open_date'] = MallPointsConversion::resolveActiveMallOpenDate($row);
|
||||||
|
$payload['active_event_end_date'] = MallPointsConversion::resolveActiveEventEndDate($row);
|
||||||
|
|
||||||
return $this->success('', [
|
return $this->success('', [
|
||||||
'row' => $payload,
|
'row' => $payload,
|
||||||
@@ -75,6 +76,10 @@ class PlayxConfig extends Backend
|
|||||||
}
|
}
|
||||||
|
|
||||||
$mallOpenDate = MallPointsConversion::normalizeDate($data['mall_open_date'] ?? null);
|
$mallOpenDate = MallPointsConversion::normalizeDate($data['mall_open_date'] ?? null);
|
||||||
|
$mallEventEndDate = MallPointsConversion::normalizeDate($data['mall_event_end_date'] ?? null);
|
||||||
|
if ($mallOpenDate !== null && $mallEventEndDate !== null && $mallEventEndDate < $mallOpenDate) {
|
||||||
|
return $this->error(__('Event end date cannot be earlier than start date'));
|
||||||
|
}
|
||||||
|
|
||||||
$row = MallBusinessConfig::order('id', 'asc')->find();
|
$row = MallBusinessConfig::order('id', 'asc')->find();
|
||||||
$now = time();
|
$now = time();
|
||||||
@@ -90,6 +95,9 @@ class PlayxConfig extends Backend
|
|||||||
'mall_open_date' => $mallOpenDate,
|
'mall_open_date' => $mallOpenDate,
|
||||||
'mall_open_date_previous' => null,
|
'mall_open_date_previous' => null,
|
||||||
'mall_open_date_effective_on' => $effectiveOn,
|
'mall_open_date_effective_on' => $effectiveOn,
|
||||||
|
'mall_event_end_date' => $mallEventEndDate,
|
||||||
|
'mall_event_end_date_previous' => null,
|
||||||
|
'mall_event_end_date_effective_on' => MallPointsConversion::nextCalendarDate(),
|
||||||
'create_time' => $now,
|
'create_time' => $now,
|
||||||
'update_time' => $now,
|
'update_time' => $now,
|
||||||
]);
|
]);
|
||||||
@@ -98,6 +106,10 @@ class PlayxConfig extends Backend
|
|||||||
$currentPending = MallPointsConversion::normalizeDate($row->mall_open_date ?? null);
|
$currentPending = MallPointsConversion::normalizeDate($row->mall_open_date ?? null);
|
||||||
$openDateChanged = $mallOpenDate !== $currentPending;
|
$openDateChanged = $mallOpenDate !== $currentPending;
|
||||||
|
|
||||||
|
$previousEnd = MallPointsConversion::resolveActiveEventEndDate($row);
|
||||||
|
$currentPendingEnd = MallPointsConversion::normalizeDate($row->mall_event_end_date ?? null);
|
||||||
|
$endDateChanged = $mallEventEndDate !== $currentPendingEnd;
|
||||||
|
|
||||||
$row->return_ratio = $returnF;
|
$row->return_ratio = $returnF;
|
||||||
$row->unlock_ratio = $unlockF;
|
$row->unlock_ratio = $unlockF;
|
||||||
$row->points_to_cash_ratio = $cashF;
|
$row->points_to_cash_ratio = $cashF;
|
||||||
@@ -111,6 +123,12 @@ class PlayxConfig extends Backend
|
|||||||
$row->mall_open_date_effective_on = $mallOpenDate !== null ? MallPointsConversion::nextCalendarDate() : null;
|
$row->mall_open_date_effective_on = $mallOpenDate !== null ? MallPointsConversion::nextCalendarDate() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($endDateChanged) {
|
||||||
|
$row->mall_event_end_date_previous = $previousEnd;
|
||||||
|
$row->mall_event_end_date = $mallEventEndDate;
|
||||||
|
$row->mall_event_end_date_effective_on = MallPointsConversion::nextCalendarDate();
|
||||||
|
}
|
||||||
|
|
||||||
$row->save();
|
$row->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ class UserAsset extends Backend
|
|||||||
'today_limit_date',
|
'today_limit_date',
|
||||||
'points_claim_cap',
|
'points_claim_cap',
|
||||||
'total_points_claimed',
|
'total_points_claimed',
|
||||||
|
'event_period_win_loss_net',
|
||||||
'create_time',
|
'create_time',
|
||||||
'update_time',
|
'update_time',
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ return [
|
|||||||
'Added successfully' => 'Added successfully!',
|
'Added successfully' => 'Added successfully!',
|
||||||
'Deleted successfully' => 'Deleted successfully!',
|
'Deleted successfully' => 'Deleted successfully!',
|
||||||
'Parameter error' => 'Parameter error!',
|
'Parameter error' => 'Parameter error!',
|
||||||
|
'Event end date cannot be earlier than start date' => 'Event end date cannot be earlier than start date',
|
||||||
'Lifetime points source is disabled' => 'Lifetime points source is disabled',
|
'Lifetime points source is disabled' => 'Lifetime points source is disabled',
|
||||||
'Operation completed' => 'Operation completed!',
|
'Operation completed' => 'Operation completed!',
|
||||||
'Lifetime points calculation completed' => 'Lifetime conversion done: %d processed, %d updated, %d skipped',
|
'Lifetime points calculation completed' => 'Lifetime conversion done: %d processed, %d updated, %d skipped',
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ return [
|
|||||||
'Added successfully' => '添加成功!',
|
'Added successfully' => '添加成功!',
|
||||||
'Deleted successfully' => '删除成功!',
|
'Deleted successfully' => '删除成功!',
|
||||||
'Parameter error' => '参数错误!',
|
'Parameter error' => '参数错误!',
|
||||||
|
'Event end date cannot be earlier than start date' => '活动结束日不能早于开始日',
|
||||||
'Lifetime points source is disabled' => '终身总输赢积分来源未启用',
|
'Lifetime points source is disabled' => '终身总输赢积分来源未启用',
|
||||||
'Operation completed' => '操作完成!',
|
'Operation completed' => '操作完成!',
|
||||||
'Lifetime points calculation completed' => '终身总输赢积分折算完成:共处理 %d 条,更新 %d 条,跳过 %d 条',
|
'Lifetime points calculation completed' => '终身总输赢积分折算完成:共处理 %d 条,更新 %d 条,跳过 %d 条',
|
||||||
|
|||||||
@@ -139,6 +139,11 @@ class Playx extends Api
|
|||||||
return MallPointsConversion::configForConversion();
|
return MallPointsConversion::configForConversion();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function dailyPushSettlementDay(): string
|
||||||
|
{
|
||||||
|
return date('Y-m-d');
|
||||||
|
}
|
||||||
|
|
||||||
private function syncAssetByDailyPush(
|
private function syncAssetByDailyPush(
|
||||||
string $playxUserId,
|
string $playxUserId,
|
||||||
string $username,
|
string $username,
|
||||||
@@ -146,7 +151,10 @@ class Playx extends Api
|
|||||||
int $convertedPoints,
|
int $convertedPoints,
|
||||||
int $todayLimit,
|
int $todayLimit,
|
||||||
bool $applyAssetDelta,
|
bool $applyAssetDelta,
|
||||||
string $phone = ''
|
string $phone = '',
|
||||||
|
?array $pointsConfig = null,
|
||||||
|
?float $yesterdayWinLossNet = null,
|
||||||
|
?float $previousYesterdayWinLossNet = null
|
||||||
): ?MallUserAsset
|
): ?MallUserAsset
|
||||||
{
|
{
|
||||||
$asset = $this->ensureAssetForPlayx($playxUserId, $username, $phone);
|
$asset = $this->ensureAssetForPlayx($playxUserId, $username, $phone);
|
||||||
@@ -169,6 +177,16 @@ class Playx extends Api
|
|||||||
MallPointsConversion::applyDailyConversionDelta($asset, $convertedPoints);
|
MallPointsConversion::applyDailyConversionDelta($asset, $convertedPoints);
|
||||||
}
|
}
|
||||||
$asset->today_limit = $todayLimit;
|
$asset->today_limit = $todayLimit;
|
||||||
|
if (is_array($pointsConfig) && $yesterdayWinLossNet !== null) {
|
||||||
|
MallPointsConversion::applyEventPeriodWinLossForDailyPush(
|
||||||
|
$asset,
|
||||||
|
$date,
|
||||||
|
$yesterdayWinLossNet,
|
||||||
|
$pointsConfig,
|
||||||
|
$this->dailyPushSettlementDay(),
|
||||||
|
$previousYesterdayWinLossNet
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$asset->save();
|
$asset->save();
|
||||||
@@ -234,7 +252,7 @@ class Playx extends Api
|
|||||||
}
|
}
|
||||||
|
|
||||||
$pointsConfig = $this->getPointsConversionConfig();
|
$pointsConfig = $this->getPointsConversionConfig();
|
||||||
$unlockRatio = floatval($pointsConfig['unlock_ratio'] ?? 0);
|
$settlementDay = $this->dailyPushSettlementDay();
|
||||||
|
|
||||||
$results = [];
|
$results = [];
|
||||||
$allDeduped = true;
|
$allDeduped = true;
|
||||||
@@ -255,8 +273,8 @@ class Playx extends Api
|
|||||||
|
|
||||||
$exists = MallDailyPush::where('user_id', $playxUserId)->where('date', $date)->find();
|
$exists = MallDailyPush::where('user_id', $playxUserId)->where('date', $date)->find();
|
||||||
if ($exists) {
|
if ($exists) {
|
||||||
$convertedPoints = MallPointsConversion::dailyConvertedPoints($date, floatval($yesterdayWinLossNet), $pointsConfig);
|
$convertedPoints = MallPointsConversion::dailyConvertedPoints($date, floatval($yesterdayWinLossNet), $pointsConfig, $settlementDay);
|
||||||
$todayLimit = intval(round(floatval($yesterdayTotalDeposit) * $unlockRatio));
|
$todayLimit = MallPointsConversion::dailyPushTodayLimit($date, floatval($yesterdayTotalDeposit), $pointsConfig, $settlementDay);
|
||||||
$asset = $this->syncAssetByDailyPush($playxUserId, $username, $date, $convertedPoints, $todayLimit, false, $phone);
|
$asset = $this->syncAssetByDailyPush($playxUserId, $username, $date, $convertedPoints, $todayLimit, false, $phone);
|
||||||
if (!$asset) {
|
if (!$asset) {
|
||||||
return $this->error(__('Failed to ensure PlayX user asset'));
|
return $this->error(__('Failed to ensure PlayX user asset'));
|
||||||
@@ -283,16 +301,16 @@ class Playx extends Api
|
|||||||
'lifetime_total_withdraw' => $lifetimeTotalWithdraw,
|
'lifetime_total_withdraw' => $lifetimeTotalWithdraw,
|
||||||
'create_time' => time(),
|
'create_time' => time(),
|
||||||
];
|
];
|
||||||
$convertedPoints = MallPointsConversion::dailyConvertedPoints($date, floatval($yesterdayWinLossNet), $pointsConfig);
|
$convertedPoints = MallPointsConversion::dailyConvertedPoints($date, floatval($yesterdayWinLossNet), $pointsConfig, $settlementDay);
|
||||||
$pushData['converted_points'] = $convertedPoints;
|
$pushData['converted_points'] = $convertedPoints;
|
||||||
if ($lifetimeWinLossNet !== null) {
|
if ($lifetimeWinLossNet !== null) {
|
||||||
$pushData['lifetime_win_loss_net'] = $lifetimeWinLossNet;
|
$pushData['lifetime_win_loss_net'] = $lifetimeWinLossNet;
|
||||||
}
|
}
|
||||||
MallDailyPush::create($pushData);
|
MallDailyPush::create($pushData);
|
||||||
|
|
||||||
$todayLimit = intval(round(floatval($yesterdayTotalDeposit) * $unlockRatio));
|
$todayLimit = MallPointsConversion::dailyPushTodayLimit($date, floatval($yesterdayTotalDeposit), $pointsConfig, $settlementDay);
|
||||||
|
|
||||||
$asset = $this->syncAssetByDailyPush($playxUserId, $username, $date, $convertedPoints, $todayLimit, true, $phone);
|
$asset = $this->syncAssetByDailyPush($playxUserId, $username, $date, $convertedPoints, $todayLimit, true, $phone, $pointsConfig, floatval($yesterdayWinLossNet));
|
||||||
if (!$asset) {
|
if (!$asset) {
|
||||||
throw new \RuntimeException(__('Failed to ensure PlayX user asset'));
|
throw new \RuntimeException(__('Failed to ensure PlayX user asset'));
|
||||||
}
|
}
|
||||||
@@ -336,9 +354,9 @@ class Playx extends Api
|
|||||||
$exists = MallDailyPush::where('user_id', $playxUserId)->where('date', $date)->find();
|
$exists = MallDailyPush::where('user_id', $playxUserId)->where('date', $date)->find();
|
||||||
if ($exists) {
|
if ($exists) {
|
||||||
$pointsConfig = $this->getPointsConversionConfig();
|
$pointsConfig = $this->getPointsConversionConfig();
|
||||||
$unlockRatio = floatval($pointsConfig['unlock_ratio'] ?? 0);
|
$settlementDay = $this->dailyPushSettlementDay();
|
||||||
$convertedPoints = MallPointsConversion::dailyConvertedPoints($date, floatval($yesterdayWinLossNet), $pointsConfig);
|
$convertedPoints = MallPointsConversion::dailyConvertedPoints($date, floatval($yesterdayWinLossNet), $pointsConfig, $settlementDay);
|
||||||
$todayLimit = intval(round(floatval($yesterdayTotalDeposit) * $unlockRatio));
|
$todayLimit = MallPointsConversion::dailyPushTodayLimit($date, floatval($yesterdayTotalDeposit), $pointsConfig, $settlementDay);
|
||||||
$asset = $this->syncAssetByDailyPush(
|
$asset = $this->syncAssetByDailyPush(
|
||||||
$playxUserId,
|
$playxUserId,
|
||||||
strval($body['username'] ?? ''),
|
strval($body['username'] ?? ''),
|
||||||
@@ -363,6 +381,7 @@ class Playx extends Api
|
|||||||
Db::startTrans();
|
Db::startTrans();
|
||||||
try {
|
try {
|
||||||
$pointsConfig = $this->getPointsConversionConfig();
|
$pointsConfig = $this->getPointsConversionConfig();
|
||||||
|
$settlementDay = $this->dailyPushSettlementDay();
|
||||||
$lifetimeWinLossNet = null;
|
$lifetimeWinLossNet = null;
|
||||||
if (isset($body['lifetime_win_loss_net']) && is_numeric($body['lifetime_win_loss_net'])) {
|
if (isset($body['lifetime_win_loss_net']) && is_numeric($body['lifetime_win_loss_net'])) {
|
||||||
$lifetimeWinLossNet = floatval($body['lifetime_win_loss_net']);
|
$lifetimeWinLossNet = floatval($body['lifetime_win_loss_net']);
|
||||||
@@ -377,15 +396,14 @@ class Playx extends Api
|
|||||||
'lifetime_total_withdraw' => $body['lifetime_total_withdraw'] ?? 0,
|
'lifetime_total_withdraw' => $body['lifetime_total_withdraw'] ?? 0,
|
||||||
'create_time' => time(),
|
'create_time' => time(),
|
||||||
];
|
];
|
||||||
$convertedPoints = MallPointsConversion::dailyConvertedPoints($date, floatval($yesterdayWinLossNet), $pointsConfig);
|
$convertedPoints = MallPointsConversion::dailyConvertedPoints($date, floatval($yesterdayWinLossNet), $pointsConfig, $settlementDay);
|
||||||
$pushData['converted_points'] = $convertedPoints;
|
$pushData['converted_points'] = $convertedPoints;
|
||||||
if ($lifetimeWinLossNet !== null) {
|
if ($lifetimeWinLossNet !== null) {
|
||||||
$pushData['lifetime_win_loss_net'] = $lifetimeWinLossNet;
|
$pushData['lifetime_win_loss_net'] = $lifetimeWinLossNet;
|
||||||
}
|
}
|
||||||
MallDailyPush::create($pushData);
|
MallDailyPush::create($pushData);
|
||||||
|
|
||||||
$unlockRatio = floatval($pointsConfig['unlock_ratio'] ?? 0);
|
$todayLimit = MallPointsConversion::dailyPushTodayLimit($date, floatval($yesterdayTotalDeposit), $pointsConfig, $settlementDay);
|
||||||
$todayLimit = intval(round(floatval($yesterdayTotalDeposit) * $unlockRatio));
|
|
||||||
|
|
||||||
$asset = $this->syncAssetByDailyPush(
|
$asset = $this->syncAssetByDailyPush(
|
||||||
$playxUserId,
|
$playxUserId,
|
||||||
@@ -394,7 +412,9 @@ class Playx extends Api
|
|||||||
$convertedPoints,
|
$convertedPoints,
|
||||||
$todayLimit,
|
$todayLimit,
|
||||||
true,
|
true,
|
||||||
$phone
|
$phone,
|
||||||
|
$pointsConfig,
|
||||||
|
floatval($yesterdayWinLossNet)
|
||||||
);
|
);
|
||||||
if (!$asset) {
|
if (!$asset) {
|
||||||
throw new \RuntimeException(__('Failed to ensure PlayX user asset'));
|
throw new \RuntimeException(__('Failed to ensure PlayX user asset'));
|
||||||
|
|||||||
@@ -260,13 +260,24 @@ class MallDailyPushLogReplay
|
|||||||
$newLocked = MallPointsConversion::dailyConvertedPoints(
|
$newLocked = MallPointsConversion::dailyConvertedPoints(
|
||||||
$record['date'],
|
$record['date'],
|
||||||
floatval($record['yesterday_win_loss_net']),
|
floatval($record['yesterday_win_loss_net']),
|
||||||
$pointsConfig
|
$pointsConfig,
|
||||||
|
MallPointsConversion::nextCalendarDate($record['date'])
|
||||||
);
|
);
|
||||||
$oldLocked = $daily
|
$oldLocked = $daily
|
||||||
? MallPointsConversion::dailyConvertedPoints($record['date'], $oldWinLossNet, $pointsConfig)
|
? MallPointsConversion::dailyConvertedPoints(
|
||||||
|
$record['date'],
|
||||||
|
$oldWinLossNet,
|
||||||
|
$pointsConfig,
|
||||||
|
MallPointsConversion::nextCalendarDate($record['date'])
|
||||||
|
)
|
||||||
: 0;
|
: 0;
|
||||||
$lockedDelta = $newLocked - $oldLocked;
|
$lockedDelta = $newLocked - $oldLocked;
|
||||||
$todayLimit = intval(round($record['yesterday_total_deposit'] * $unlockRatio));
|
$todayLimit = MallPointsConversion::dailyPushTodayLimit(
|
||||||
|
$record['date'],
|
||||||
|
floatval($record['yesterday_total_deposit']),
|
||||||
|
$pointsConfig,
|
||||||
|
MallPointsConversion::nextCalendarDate($record['date'])
|
||||||
|
);
|
||||||
$dailyData = [
|
$dailyData = [
|
||||||
'user_id' => $record['user_id'],
|
'user_id' => $record['user_id'],
|
||||||
'date' => $record['date'],
|
'date' => $record['date'],
|
||||||
@@ -352,6 +363,15 @@ class MallDailyPushLogReplay
|
|||||||
MallPointsConversion::applyDailyConversionDelta($asset, $lockedDelta);
|
MallPointsConversion::applyDailyConversionDelta($asset, $lockedDelta);
|
||||||
}
|
}
|
||||||
$this->applyDailyLimit($asset, $record['date'], $todayLimit);
|
$this->applyDailyLimit($asset, $record['date'], $todayLimit);
|
||||||
|
$settlementDay = MallPointsConversion::nextCalendarDate($record['date']);
|
||||||
|
MallPointsConversion::applyEventPeriodWinLossForDailyPush(
|
||||||
|
$asset,
|
||||||
|
$record['date'],
|
||||||
|
floatval($record['yesterday_win_loss_net']),
|
||||||
|
$pointsConfig,
|
||||||
|
$settlementDay,
|
||||||
|
$daily ? $oldWinLossNet : null
|
||||||
|
);
|
||||||
$asset->save();
|
$asset->save();
|
||||||
}
|
}
|
||||||
Db::commit();
|
Db::commit();
|
||||||
|
|||||||
@@ -25,7 +25,11 @@ final class MallPointsConversion
|
|||||||
* mall_open_date: ?string,
|
* mall_open_date: ?string,
|
||||||
* mall_open_date_previous: ?string,
|
* mall_open_date_previous: ?string,
|
||||||
* mall_open_date_effective_on: ?string,
|
* mall_open_date_effective_on: ?string,
|
||||||
* active_mall_open_date: ?string
|
* active_mall_open_date: ?string,
|
||||||
|
* mall_event_end_date: ?string,
|
||||||
|
* mall_event_end_date_previous: ?string,
|
||||||
|
* mall_event_end_date_effective_on: ?string,
|
||||||
|
* active_event_end_date: ?string
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
public static function configFromRow(MallBusinessConfig $row): array
|
public static function configFromRow(MallBusinessConfig $row): array
|
||||||
@@ -41,6 +45,10 @@ final class MallPointsConversion
|
|||||||
'mall_open_date_previous' => self::normalizeDate($row->mall_open_date_previous ?? null),
|
'mall_open_date_previous' => self::normalizeDate($row->mall_open_date_previous ?? null),
|
||||||
'mall_open_date_effective_on' => self::normalizeDate($row->mall_open_date_effective_on ?? null),
|
'mall_open_date_effective_on' => self::normalizeDate($row->mall_open_date_effective_on ?? null),
|
||||||
'active_mall_open_date' => self::resolveActiveMallOpenDate($row),
|
'active_mall_open_date' => self::resolveActiveMallOpenDate($row),
|
||||||
|
'mall_event_end_date' => self::normalizeDate($row->mall_event_end_date ?? null),
|
||||||
|
'mall_event_end_date_previous' => self::normalizeDate($row->mall_event_end_date_previous ?? null),
|
||||||
|
'mall_event_end_date_effective_on' => self::normalizeDate($row->mall_event_end_date_effective_on ?? null),
|
||||||
|
'active_event_end_date' => self::resolveActiveEventEndDate($row),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,13 +82,16 @@ final class MallPointsConversion
|
|||||||
*
|
*
|
||||||
* @param array<string, mixed> $config
|
* @param array<string, mixed> $config
|
||||||
*/
|
*/
|
||||||
public static function dailyConvertedPoints(string $businessDate, float $yesterdayWinLossNet, array $config): int
|
public static function dailyConvertedPoints(
|
||||||
{
|
string $businessDate,
|
||||||
|
float $yesterdayWinLossNet,
|
||||||
|
array $config,
|
||||||
|
?string $settlementCalendarDate = null
|
||||||
|
): int {
|
||||||
if (empty($config['daily_points_enabled'])) {
|
if (empty($config['daily_points_enabled'])) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
$openDate = $config['active_mall_open_date'] ?? null;
|
if (!self::isDailyPushSettlementAllowed($businessDate, $config, $settlementCalendarDate)) {
|
||||||
if ($openDate !== null && $openDate !== '' && $businessDate < $openDate) {
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
$ratio = floatval($config['return_ratio'] ?? 0);
|
$ratio = floatval($config['return_ratio'] ?? 0);
|
||||||
@@ -88,6 +99,85 @@ final class MallPointsConversion
|
|||||||
return self::lossIntegerToPoints($yesterdayWinLossNet, $ratio);
|
return self::lossIntegerToPoints($yesterdayWinLossNet, $ratio);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 每日推送是否允许折算昨日净输赢、昨日充值解锁上限(活动开始/结束日规则)
|
||||||
|
*/
|
||||||
|
public static function isDailyPushSettlementAllowed(
|
||||||
|
string $businessDate,
|
||||||
|
array $config,
|
||||||
|
?string $settlementCalendarDate = null
|
||||||
|
): bool {
|
||||||
|
$settlementDay = self::normalizeDate($settlementCalendarDate ?? date('Y-m-d')) ?? date('Y-m-d');
|
||||||
|
$start = self::normalizeDate($config['active_mall_open_date'] ?? null);
|
||||||
|
if ($start !== null && $businessDate < $start) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$end = self::normalizeDate($config['active_event_end_date'] ?? null);
|
||||||
|
if ($end === null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if ($businessDate > $end) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$endPlusOne = self::nextCalendarDate($end);
|
||||||
|
if ($settlementDay === $endPlusOne && $businessDate >= $end) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ($settlementDay > $endPlusOne) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 活动开放期内按推送累加净输赢;关闭期间不累加,再开启后接着累加(不重置)。
|
||||||
|
*
|
||||||
|
* @param float|null $previousWinLossNet 更新已有每日推送时传旧「昨日净输赢」;新建推送传 null
|
||||||
|
*/
|
||||||
|
public static function applyEventPeriodWinLossForDailyPush(
|
||||||
|
MallUserAsset $asset,
|
||||||
|
string $businessDate,
|
||||||
|
float $yesterdayWinLossNet,
|
||||||
|
array $config,
|
||||||
|
?string $settlementCalendarDate = null,
|
||||||
|
?float $previousWinLossNet = null
|
||||||
|
): void {
|
||||||
|
if (!self::isDailyPushSettlementAllowed($businessDate, $config, $settlementCalendarDate)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$current = floatval($asset->event_period_win_loss_net ?? 0);
|
||||||
|
if ($previousWinLossNet === null) {
|
||||||
|
$asset->event_period_win_loss_net = round($current + $yesterdayWinLossNet, 2);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$delta = $yesterdayWinLossNet - $previousWinLossNet;
|
||||||
|
if ($delta === 0.0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$asset->event_period_win_loss_net = round($current + $delta, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function dailyPushTodayLimit(
|
||||||
|
string $businessDate,
|
||||||
|
float $yesterdayTotalDeposit,
|
||||||
|
array $config,
|
||||||
|
?string $settlementCalendarDate = null
|
||||||
|
): int {
|
||||||
|
if (!self::isDailyPushSettlementAllowed($businessDate, $config, $settlementCalendarDate)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
$unlockRatio = floatval($config['unlock_ratio'] ?? 0);
|
||||||
|
|
||||||
|
return intval(round($yesterdayTotalDeposit * $unlockRatio));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array<string, mixed> $config
|
* @param array<string, mixed> $config
|
||||||
* @deprecated 使用 dailyConvertedPoints
|
* @deprecated 使用 dailyConvertedPoints
|
||||||
@@ -219,6 +309,27 @@ final class MallPointsConversion
|
|||||||
return $previous;
|
return $previous;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function resolveActiveEventEndDate(MallBusinessConfig $row): ?string
|
||||||
|
{
|
||||||
|
$pending = self::normalizeDate($row->mall_event_end_date ?? null);
|
||||||
|
$previous = self::normalizeDate($row->mall_event_end_date_previous ?? null);
|
||||||
|
$effectiveOn = self::normalizeDate($row->mall_event_end_date_effective_on ?? null);
|
||||||
|
$today = date('Y-m-d');
|
||||||
|
|
||||||
|
if ($pending === null) {
|
||||||
|
if ($effectiveOn !== null && $today >= $effectiveOn) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $previous;
|
||||||
|
}
|
||||||
|
if ($effectiveOn === null || $today >= $effectiveOn) {
|
||||||
|
return $pending;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $previous;
|
||||||
|
}
|
||||||
|
|
||||||
public static function normalizeDate(mixed $value): ?string
|
public static function normalizeDate(mixed $value): ?string
|
||||||
{
|
{
|
||||||
if ($value === null || $value === '') {
|
if ($value === null || $value === '') {
|
||||||
|
|||||||
@@ -32,6 +32,9 @@ class MallBusinessConfig extends Model
|
|||||||
'mall_open_date' => 'string',
|
'mall_open_date' => 'string',
|
||||||
'mall_open_date_previous' => 'string',
|
'mall_open_date_previous' => 'string',
|
||||||
'mall_open_date_effective_on' => 'string',
|
'mall_open_date_effective_on' => 'string',
|
||||||
|
'mall_event_end_date' => 'string',
|
||||||
|
'mall_event_end_date_previous' => 'string',
|
||||||
|
'mall_event_end_date_effective_on' => 'string',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ class MallUserAsset extends Model
|
|||||||
'total_points_claimed' => 'integer',
|
'total_points_claimed' => 'integer',
|
||||||
'lifetime_converted_points' => 'integer',
|
'lifetime_converted_points' => 'integer',
|
||||||
'daily_converted_points' => 'integer',
|
'daily_converted_points' => 'integer',
|
||||||
|
'event_period_win_loss_net' => 'float',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -57,6 +57,6 @@ export default {
|
|||||||
},
|
},
|
||||||
playxConfig: {
|
playxConfig: {
|
||||||
title: 'About this page',
|
title: 'About this page',
|
||||||
desc: 'Configure daily and lifetime loss-to-points sources (switches and ratios), mall open date, unlock ratio, and points-to-cash ratio. Open date changes take effect the next calendar day.',
|
desc: 'Split into Event and General tabs: event start/end dates (effective next day; no conversion for end-day business data from the day after end), point sources and lifetime batch job; unlock and cash ratios under General.',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
export default {
|
export default {
|
||||||
|
tab_event: 'Event settings',
|
||||||
|
tab_regular: 'General settings',
|
||||||
|
section_event_period: 'Event period',
|
||||||
section_daily_source: 'Source 1: yesterday net win/loss',
|
section_daily_source: 'Source 1: yesterday net win/loss',
|
||||||
daily_points_enabled: 'Enable daily loss to points',
|
daily_points_enabled: 'Enable daily loss to points',
|
||||||
daily_points_enabled_tip: 'Convert yesterday net win/loss from daily push to locked points; only losses (negative net) count.',
|
daily_points_enabled_tip: 'Convert yesterday net win/loss from daily push to locked points; only losses (negative net) count.',
|
||||||
return_ratio: 'Yesterday loss conversion ratio',
|
return_ratio: 'Yesterday loss conversion ratio',
|
||||||
return_ratio_tip: 'On daily push: converted points = floor(|yesterday net|) × ratio (losses only, non-negative integer). Requires mall open date and enabled source.',
|
return_ratio_tip: 'On daily push: converted points = floor(|yesterday net|) × ratio (losses only). Requires event window and enabled source.',
|
||||||
section_lifetime_source: 'Source 2: lifetime net win/loss',
|
section_lifetime_source: 'Source 2: lifetime net win/loss',
|
||||||
lifetime_points_enabled: 'Enable lifetime loss to points',
|
lifetime_points_enabled: 'Enable lifetime loss to points',
|
||||||
lifetime_points_enabled_tip: 'Uses the latest daily push row per member; only losses. Skips when lifetime field is missing.',
|
lifetime_points_enabled_tip: 'Uses the latest daily push row per member; only losses. Skips when lifetime field is missing.',
|
||||||
@@ -14,12 +17,16 @@ export default {
|
|||||||
calculate_lifetime_confirm: 'Recalculate all members with the current lifetime ratio (reverses previous lifetime conversion first). Continue?',
|
calculate_lifetime_confirm: 'Recalculate all members with the current lifetime ratio (reverses previous lifetime conversion first). Continue?',
|
||||||
calculate_lifetime_success: 'Lifetime conversion done: {processed} processed, {updated} updated, {skipped} skipped',
|
calculate_lifetime_success: 'Lifetime conversion done: {processed} processed, {updated} updated, {skipped} skipped',
|
||||||
section_mall_open: 'Mall open date',
|
section_mall_open: 'Mall open date',
|
||||||
mall_open_date: 'Mall open date',
|
mall_open_date: 'Event start date',
|
||||||
mall_open_date_tip: 'Takes effect the next calendar day. E.g. set Aug 1: from Aug 2, daily push rows with business date on/after Aug 1 convert yesterday loss to points.',
|
mall_open_date_tip: 'Takes effect the next calendar day. From then, daily push rows with business date on/after start date convert yesterday loss.',
|
||||||
active_mall_open_date: 'Active open date',
|
active_mall_open_date: 'Active start date',
|
||||||
|
mall_event_end_date: 'Event end date',
|
||||||
|
mall_event_end_date_tip: 'Takes effect the next day; leave empty for no end. On the day after end, rows for the end business date no longer convert points or unlock cap; later business dates are excluded too.',
|
||||||
|
active_event_end_date: 'Active end date',
|
||||||
|
event_end_before_start: 'End date cannot be earlier than start date',
|
||||||
section_other: 'Other settings',
|
section_other: 'Other settings',
|
||||||
unlock_ratio: 'Unlock ratio (unlock_ratio)',
|
unlock_ratio: 'Unlock ratio (unlock_ratio)',
|
||||||
unlock_ratio_tip: 'Daily push: daily claim cap = yesterday total deposit × this ratio.',
|
unlock_ratio_tip: 'Daily push: daily claim cap = yesterday total deposit × this ratio (within event window).',
|
||||||
points_to_cash_ratio: 'Points to cash ratio',
|
points_to_cash_ratio: 'Points to cash ratio',
|
||||||
points_to_cash_ratio_tip: 'For withdrawable cash display: cash ≈ available points × this ratio.',
|
points_to_cash_ratio_tip: 'For withdrawable cash display: cash ≈ available points × this ratio.',
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ export default {
|
|||||||
today_limit_date: 'Daily cap date',
|
today_limit_date: 'Daily cap date',
|
||||||
points_claim_cap: 'Lifetime claim cap',
|
points_claim_cap: 'Lifetime claim cap',
|
||||||
total_points_claimed: 'Total points claimed',
|
total_points_claimed: 'Total points claimed',
|
||||||
|
event_period_win_loss_net: 'Event period net W/L (adds while open, pauses when closed, resumes on reopen)',
|
||||||
create_time: 'Created at',
|
create_time: 'Created at',
|
||||||
update_time: 'Updated at',
|
update_time: 'Updated at',
|
||||||
'quick Search Fields': 'ID, PlayX user ID, username, phone',
|
'quick Search Fields': 'ID, PlayX user ID, username, phone',
|
||||||
|
|||||||
@@ -57,6 +57,6 @@ export default {
|
|||||||
},
|
},
|
||||||
playxConfig: {
|
playxConfig: {
|
||||||
title: '页面说明',
|
title: '页面说明',
|
||||||
desc: '配置昨日净输赢、终身总输赢两种积分来源(开关与兑换比例)、商城开放日,以及解锁比例与积分折算现金比例;保存后按规则生效(开放日隔日生效)。',
|
desc: '分为「活动配置」与「常规配置」:活动开始/结束日(隔日生效,结束日次日起不再折算该日业务数据的昨日净输赢与解锁上限)、两种积分来源及终身一键计算;常规项为解锁比例与积分折现比例。',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
export default {
|
export default {
|
||||||
|
tab_event: '活动配置',
|
||||||
|
tab_regular: '常规配置',
|
||||||
|
section_event_period: '活动时间',
|
||||||
section_daily_source: '积分来源一:昨日净输赢',
|
section_daily_source: '积分来源一:昨日净输赢',
|
||||||
daily_points_enabled: '启用昨日净输赢转积分',
|
daily_points_enabled: '启用昨日净输赢转积分',
|
||||||
daily_points_enabled_tip: '根据每日推送中的「昨日净输赢」折算待领取积分;仅统计会员输的钱(净输赢为负),赢的不计。',
|
daily_points_enabled_tip: '根据每日推送中的「昨日净输赢」折算待领取积分;仅统计会员输的钱(净输赢为负),赢的不计。',
|
||||||
return_ratio: '昨日净输赢兑换比例',
|
return_ratio: '昨日净输赢兑换比例',
|
||||||
return_ratio_tip: '每日推送入库时:转换积分 = floor(|昨日净输赢|) × 本比例(仅负值,结果为非负整数)。需满足商城开放日规则且本来源已启用。',
|
return_ratio_tip: '每日推送入库时:转换积分 = floor(|昨日净输赢|) × 本比例(仅负值,结果为非负整数)。需满足活动时间规则且本来源已启用。',
|
||||||
section_lifetime_source: '积分来源二:终身总输赢',
|
section_lifetime_source: '积分来源二:终身总输赢',
|
||||||
lifetime_points_enabled: '启用终身总输赢转积分',
|
lifetime_points_enabled: '启用终身总输赢转积分',
|
||||||
lifetime_points_enabled_tip: '使用每日推送中每位会员最新一条记录的「终身总输赢」字段;仅统计输的钱。第三方未传该字段时跳过。',
|
lifetime_points_enabled_tip: '使用每日推送中每位会员最新一条记录的「终身总输赢」字段;仅统计输的钱。第三方未传该字段时跳过。',
|
||||||
@@ -14,12 +17,16 @@ export default {
|
|||||||
calculate_lifetime_confirm: '将按当前终身总输赢兑换比例重新折算全部会员积分,已折算部分会先扣回再按新比例增加。是否继续?',
|
calculate_lifetime_confirm: '将按当前终身总输赢兑换比例重新折算全部会员积分,已折算部分会先扣回再按新比例增加。是否继续?',
|
||||||
calculate_lifetime_success: '终身总输赢积分折算完成:共处理 {processed} 条,更新 {updated} 条,跳过 {skipped} 条',
|
calculate_lifetime_success: '终身总输赢积分折算完成:共处理 {processed} 条,更新 {updated} 条,跳过 {skipped} 条',
|
||||||
section_mall_open: '商城开放日',
|
section_mall_open: '商城开放日',
|
||||||
mall_open_date: '商城开放日',
|
mall_open_date: '活动开始日',
|
||||||
mall_open_date_tip: '设置后隔日生效。例如设为 8 月 1 日,则从 8 月 2 日起对业务日期为 8 月 1 日及之后的每日推送执行「昨日净输赢」转积分(T+1:开放日前一日产生的输赢在开放日次日开始折算)。',
|
mall_open_date_tip: '设置后隔日生效。从生效日起,对业务日期不早于开始日的每日推送执行「昨日净输赢」转积分(T+1)。',
|
||||||
active_mall_open_date: '当前生效的开放日',
|
active_mall_open_date: '当前生效的开始日',
|
||||||
|
mall_event_end_date: '活动结束日',
|
||||||
|
mall_event_end_date_tip: '设置后隔日生效,可留空表示不限制结束。结束日次日不再对结束日当天业务数据的昨日净输赢折算积分与解锁上限;结束日之后的业务日期也不再折算。',
|
||||||
|
active_event_end_date: '当前生效的结束日',
|
||||||
|
event_end_before_start: '活动结束日不能早于开始日',
|
||||||
section_other: '其他参数',
|
section_other: '其他参数',
|
||||||
unlock_ratio: '解锁比例(unlock_ratio)',
|
unlock_ratio: '解锁比例(unlock_ratio)',
|
||||||
unlock_ratio_tip: '每日推送中:今日可领取上限 = 昨日总充值 × 本比例。',
|
unlock_ratio_tip: '每日推送中:今日可领取上限 = 昨日总充值 × 本比例(受活动时间规则约束)。',
|
||||||
points_to_cash_ratio: '积分折算现金比例',
|
points_to_cash_ratio: '积分折算现金比例',
|
||||||
points_to_cash_ratio_tip: '用于资产接口中「可提现现金」等展示:现金 ≈ 可用积分 × 本比例。',
|
points_to_cash_ratio_tip: '用于资产接口中「可提现现金」等展示:现金 ≈ 可用积分 × 本比例。',
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ export default {
|
|||||||
today_limit_date: '今日上限日期',
|
today_limit_date: '今日上限日期',
|
||||||
points_claim_cap: '领取积分上限',
|
points_claim_cap: '领取积分上限',
|
||||||
total_points_claimed: '累计已领取积分',
|
total_points_claimed: '累计已领取积分',
|
||||||
|
event_period_win_loss_net: '活动期累计净输赢(开放时累加,关闭暂停,再开续累)',
|
||||||
create_time: '创建时间',
|
create_time: '创建时间',
|
||||||
update_time: '修改时间',
|
update_time: '修改时间',
|
||||||
'quick Search Fields': 'ID、playX用户ID、用户名、手机号',
|
'quick Search Fields': 'ID、playX用户ID、用户名、手机号',
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ const baTable = new baTableClass(
|
|||||||
prop: 'status',
|
prop: 'status',
|
||||||
align: 'center',
|
align: 'center',
|
||||||
effect: 'dark',
|
effect: 'dark',
|
||||||
custom: { PENDING: 'success', COMPLETED: 'primary', SHIPPED: 'info', REJECTED: 'loading' },
|
custom: { PENDING: 'success', COMPLETED: 'primary', SHIPPED: 'info', REJECTED: 'danger' },
|
||||||
minWidth: 100,
|
minWidth: 100,
|
||||||
operator: 'eq',
|
operator: 'eq',
|
||||||
sortable: false,
|
sortable: false,
|
||||||
|
|||||||
@@ -14,96 +14,117 @@
|
|||||||
@submit.prevent=""
|
@submit.prevent=""
|
||||||
@keyup.enter="onSubmit()"
|
@keyup.enter="onSubmit()"
|
||||||
>
|
>
|
||||||
<el-divider content-position="left">{{ t('mall.playxConfig.section_daily_source') }}</el-divider>
|
<el-tabs v-model="activeTab" class="playx-config-tabs">
|
||||||
<el-form-item :label="t('mall.playxConfig.daily_points_enabled')" prop="daily_points_enabled">
|
<el-tab-pane :label="t('mall.playxConfig.tab_event')" name="event">
|
||||||
<el-switch v-model="form.daily_points_enabled" />
|
<el-divider content-position="left">{{ t('mall.playxConfig.section_event_period') }}</el-divider>
|
||||||
<div class="form-tip">{{ t('mall.playxConfig.daily_points_enabled_tip') }}</div>
|
<el-form-item :label="t('mall.playxConfig.mall_open_date')" prop="mall_open_date">
|
||||||
</el-form-item>
|
<el-date-picker
|
||||||
<el-form-item :label="t('mall.playxConfig.return_ratio')" prop="return_ratio">
|
v-model="form.mall_open_date"
|
||||||
<el-input-number
|
type="date"
|
||||||
v-model="form.return_ratio"
|
value-format="YYYY-MM-DD"
|
||||||
:min="0"
|
class="w100"
|
||||||
:max="100"
|
:placeholder="t('Please select field', { field: t('mall.playxConfig.mall_open_date') })"
|
||||||
:step="0.01"
|
/>
|
||||||
:precision="2"
|
<div class="form-tip">{{ t('mall.playxConfig.mall_open_date_tip') }}</div>
|
||||||
controls-position="right"
|
<div v-if="activeMallOpenDate" class="form-tip">
|
||||||
class="w100"
|
{{ t('mall.playxConfig.active_mall_open_date') }}:{{ activeMallOpenDate }}
|
||||||
:disabled="!form.daily_points_enabled"
|
</div>
|
||||||
/>
|
</el-form-item>
|
||||||
<div class="form-tip">{{ t('mall.playxConfig.return_ratio_tip') }}</div>
|
<el-form-item :label="t('mall.playxConfig.mall_event_end_date')" prop="mall_event_end_date">
|
||||||
</el-form-item>
|
<el-date-picker
|
||||||
|
v-model="form.mall_event_end_date"
|
||||||
|
type="date"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
|
class="w100"
|
||||||
|
clearable
|
||||||
|
:placeholder="t('Please select field', { field: t('mall.playxConfig.mall_event_end_date') })"
|
||||||
|
/>
|
||||||
|
<div class="form-tip">{{ t('mall.playxConfig.mall_event_end_date_tip') }}</div>
|
||||||
|
<div v-if="activeEventEndDate" class="form-tip">
|
||||||
|
{{ t('mall.playxConfig.active_event_end_date') }}:{{ activeEventEndDate }}
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
<el-divider content-position="left">{{ t('mall.playxConfig.section_lifetime_source') }}</el-divider>
|
<el-divider content-position="left">{{ t('mall.playxConfig.section_daily_source') }}</el-divider>
|
||||||
<el-form-item :label="t('mall.playxConfig.lifetime_points_enabled')" prop="lifetime_points_enabled">
|
<el-form-item :label="t('mall.playxConfig.daily_points_enabled')" prop="daily_points_enabled">
|
||||||
<el-switch v-model="form.lifetime_points_enabled" />
|
<el-switch v-model="form.daily_points_enabled" />
|
||||||
<div class="form-tip">{{ t('mall.playxConfig.lifetime_points_enabled_tip') }}</div>
|
<div class="form-tip">{{ t('mall.playxConfig.daily_points_enabled_tip') }}</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item :label="t('mall.playxConfig.lifetime_points_ratio')" prop="lifetime_points_ratio">
|
<el-form-item :label="t('mall.playxConfig.return_ratio')" prop="return_ratio">
|
||||||
<el-input-number
|
<el-input-number
|
||||||
v-model="form.lifetime_points_ratio"
|
v-model="form.return_ratio"
|
||||||
:min="0"
|
:min="0"
|
||||||
:max="100"
|
:max="100"
|
||||||
:step="0.01"
|
:step="0.01"
|
||||||
:precision="2"
|
:precision="2"
|
||||||
controls-position="right"
|
controls-position="right"
|
||||||
class="w100"
|
class="w100"
|
||||||
:disabled="!form.lifetime_points_enabled"
|
:disabled="!form.daily_points_enabled"
|
||||||
/>
|
/>
|
||||||
<div class="form-tip">{{ t('mall.playxConfig.lifetime_points_ratio_tip') }}</div>
|
<div class="form-tip">{{ t('mall.playxConfig.return_ratio_tip') }}</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
|
||||||
<el-button
|
|
||||||
type="warning"
|
|
||||||
:loading="calculating"
|
|
||||||
:disabled="!form.lifetime_points_enabled"
|
|
||||||
@click="onCalculateLifetime()"
|
|
||||||
>
|
|
||||||
{{ t('mall.playxConfig.calculate_lifetime_btn') }}
|
|
||||||
</el-button>
|
|
||||||
<div class="form-tip">{{ t('mall.playxConfig.calculate_lifetime_tip') }}</div>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-divider content-position="left">{{ t('mall.playxConfig.section_mall_open') }}</el-divider>
|
<el-divider content-position="left">{{ t('mall.playxConfig.section_lifetime_source') }}</el-divider>
|
||||||
<el-form-item :label="t('mall.playxConfig.mall_open_date')" prop="mall_open_date">
|
<el-form-item :label="t('mall.playxConfig.lifetime_points_enabled')" prop="lifetime_points_enabled">
|
||||||
<el-date-picker
|
<el-switch v-model="form.lifetime_points_enabled" />
|
||||||
v-model="form.mall_open_date"
|
<div class="form-tip">{{ t('mall.playxConfig.lifetime_points_enabled_tip') }}</div>
|
||||||
type="date"
|
</el-form-item>
|
||||||
value-format="YYYY-MM-DD"
|
<el-form-item :label="t('mall.playxConfig.lifetime_points_ratio')" prop="lifetime_points_ratio">
|
||||||
class="w100"
|
<el-input-number
|
||||||
:placeholder="t('Please select field', { field: t('mall.playxConfig.mall_open_date') })"
|
v-model="form.lifetime_points_ratio"
|
||||||
/>
|
:min="0"
|
||||||
<div class="form-tip">{{ t('mall.playxConfig.mall_open_date_tip') }}</div>
|
:max="100"
|
||||||
<div v-if="activeMallOpenDate" class="form-tip">
|
:step="0.01"
|
||||||
{{ t('mall.playxConfig.active_mall_open_date') }}:{{ activeMallOpenDate }}
|
:precision="2"
|
||||||
</div>
|
controls-position="right"
|
||||||
</el-form-item>
|
class="w100"
|
||||||
|
:disabled="!form.lifetime_points_enabled"
|
||||||
|
/>
|
||||||
|
<div class="form-tip">{{ t('mall.playxConfig.lifetime_points_ratio_tip') }}</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button
|
||||||
|
type="warning"
|
||||||
|
:loading="calculating"
|
||||||
|
:disabled="!form.lifetime_points_enabled"
|
||||||
|
@click="onCalculateLifetime()"
|
||||||
|
>
|
||||||
|
{{ t('mall.playxConfig.calculate_lifetime_btn') }}
|
||||||
|
</el-button>
|
||||||
|
<div class="form-tip">{{ t('mall.playxConfig.calculate_lifetime_tip') }}</div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-tab-pane>
|
||||||
|
|
||||||
<el-divider content-position="left">{{ t('mall.playxConfig.section_other') }}</el-divider>
|
<el-tab-pane :label="t('mall.playxConfig.tab_regular')" name="regular">
|
||||||
<el-form-item :label="t('mall.playxConfig.unlock_ratio')" prop="unlock_ratio">
|
<el-divider content-position="left">{{ t('mall.playxConfig.section_other') }}</el-divider>
|
||||||
<el-input-number
|
<el-form-item :label="t('mall.playxConfig.unlock_ratio')" prop="unlock_ratio">
|
||||||
v-model="form.unlock_ratio"
|
<el-input-number
|
||||||
:min="0"
|
v-model="form.unlock_ratio"
|
||||||
:max="100"
|
:min="0"
|
||||||
:step="0.01"
|
:max="100"
|
||||||
:precision="2"
|
:step="0.01"
|
||||||
controls-position="right"
|
:precision="2"
|
||||||
class="w100"
|
controls-position="right"
|
||||||
/>
|
class="w100"
|
||||||
<div class="form-tip">{{ t('mall.playxConfig.unlock_ratio_tip') }}</div>
|
/>
|
||||||
</el-form-item>
|
<div class="form-tip">{{ t('mall.playxConfig.unlock_ratio_tip') }}</div>
|
||||||
<el-form-item :label="t('mall.playxConfig.points_to_cash_ratio')" prop="points_to_cash_ratio">
|
</el-form-item>
|
||||||
<el-input-number
|
<el-form-item :label="t('mall.playxConfig.points_to_cash_ratio')" prop="points_to_cash_ratio">
|
||||||
v-model="form.points_to_cash_ratio"
|
<el-input-number
|
||||||
:min="0"
|
v-model="form.points_to_cash_ratio"
|
||||||
:max="100"
|
:min="0"
|
||||||
:step="0.01"
|
:max="100"
|
||||||
:precision="2"
|
:step="0.01"
|
||||||
controls-position="right"
|
:precision="2"
|
||||||
class="w100"
|
controls-position="right"
|
||||||
/>
|
class="w100"
|
||||||
<div class="form-tip">{{ t('mall.playxConfig.points_to_cash_ratio_tip') }}</div>
|
/>
|
||||||
</el-form-item>
|
<div class="form-tip">{{ t('mall.playxConfig.points_to_cash_ratio_tip') }}</div>
|
||||||
<el-form-item>
|
</el-form-item>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
|
||||||
|
<el-form-item class="playx-config-submit">
|
||||||
<el-button type="primary" :loading="submitting" @click="onSubmit()">{{ t('Save') }}</el-button>
|
<el-button type="primary" :loading="submitting" @click="onSubmit()">{{ t('Save') }}</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
@@ -129,7 +150,9 @@ const loading = ref(true)
|
|||||||
const submitting = ref(false)
|
const submitting = ref(false)
|
||||||
const calculating = ref(false)
|
const calculating = ref(false)
|
||||||
const remark = ref('')
|
const remark = ref('')
|
||||||
|
const activeTab = ref('event')
|
||||||
const activeMallOpenDate = ref('')
|
const activeMallOpenDate = ref('')
|
||||||
|
const activeEventEndDate = ref('')
|
||||||
|
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
return_ratio: 0.1,
|
return_ratio: 0.1,
|
||||||
@@ -139,13 +162,26 @@ const form = reactive({
|
|||||||
lifetime_points_enabled: false,
|
lifetime_points_enabled: false,
|
||||||
lifetime_points_ratio: 0.1,
|
lifetime_points_ratio: 0.1,
|
||||||
mall_open_date: '' as string | null,
|
mall_open_date: '' as string | null,
|
||||||
|
mall_event_end_date: '' as string | null,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const validateEventDates = (_rule: unknown, _value: unknown, callback: (err?: Error) => void) => {
|
||||||
|
const start = form.mall_open_date
|
||||||
|
const end = form.mall_event_end_date
|
||||||
|
if (start && end && end < start) {
|
||||||
|
callback(new Error(t('mall.playxConfig.event_end_before_start')))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
|
||||||
const rules: FormRules = {
|
const rules: FormRules = {
|
||||||
return_ratio: [{ required: true, message: t('Please input field', { field: t('mall.playxConfig.return_ratio') }) }],
|
return_ratio: [{ required: true, message: t('Please input field', { field: t('mall.playxConfig.return_ratio') }) }],
|
||||||
unlock_ratio: [{ required: true, message: t('Please input field', { field: t('mall.playxConfig.unlock_ratio') }) }],
|
unlock_ratio: [{ required: true, message: t('Please input field', { field: t('mall.playxConfig.unlock_ratio') }) }],
|
||||||
points_to_cash_ratio: [{ required: true, message: t('Please input field', { field: t('mall.playxConfig.points_to_cash_ratio') }) }],
|
points_to_cash_ratio: [{ required: true, message: t('Please input field', { field: t('mall.playxConfig.points_to_cash_ratio') }) }],
|
||||||
lifetime_points_ratio: [{ required: true, message: t('Please input field', { field: t('mall.playxConfig.lifetime_points_ratio') }) }],
|
lifetime_points_ratio: [{ required: true, message: t('Please input field', { field: t('mall.playxConfig.lifetime_points_ratio') }) }],
|
||||||
|
mall_event_end_date: [{ validator: validateEventDates, trigger: 'change' }],
|
||||||
|
mall_open_date: [{ validator: validateEventDates, trigger: 'change' }],
|
||||||
}
|
}
|
||||||
|
|
||||||
const loadData = () => {
|
const loadData = () => {
|
||||||
@@ -155,6 +191,7 @@ const loadData = () => {
|
|||||||
remark.value = res.data.remark ?? ''
|
remark.value = res.data.remark ?? ''
|
||||||
const row = res.data.row ?? {}
|
const row = res.data.row ?? {}
|
||||||
activeMallOpenDate.value = row.active_mall_open_date ?? ''
|
activeMallOpenDate.value = row.active_mall_open_date ?? ''
|
||||||
|
activeEventEndDate.value = row.active_event_end_date ?? ''
|
||||||
if (row.return_ratio !== undefined && row.return_ratio !== null) {
|
if (row.return_ratio !== undefined && row.return_ratio !== null) {
|
||||||
form.return_ratio = parseFloat(String(row.return_ratio))
|
form.return_ratio = parseFloat(String(row.return_ratio))
|
||||||
}
|
}
|
||||||
@@ -170,6 +207,7 @@ const loadData = () => {
|
|||||||
form.lifetime_points_ratio = parseFloat(String(row.lifetime_points_ratio))
|
form.lifetime_points_ratio = parseFloat(String(row.lifetime_points_ratio))
|
||||||
}
|
}
|
||||||
form.mall_open_date = row.mall_open_date ? String(row.mall_open_date).slice(0, 10) : null
|
form.mall_open_date = row.mall_open_date ? String(row.mall_open_date).slice(0, 10) : null
|
||||||
|
form.mall_event_end_date = row.mall_event_end_date ? String(row.mall_event_end_date).slice(0, 10) : null
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
@@ -188,6 +226,7 @@ const onSubmit = () => {
|
|||||||
lifetime_points_enabled: form.lifetime_points_enabled ? 1 : 0,
|
lifetime_points_enabled: form.lifetime_points_enabled ? 1 : 0,
|
||||||
lifetime_points_ratio: form.lifetime_points_ratio,
|
lifetime_points_ratio: form.lifetime_points_ratio,
|
||||||
mall_open_date: form.mall_open_date || null,
|
mall_open_date: form.mall_open_date || null,
|
||||||
|
mall_event_end_date: form.mall_event_end_date || null,
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
loadData()
|
loadData()
|
||||||
@@ -236,4 +275,11 @@ onMounted(() => {
|
|||||||
color: var(--el-text-color-secondary);
|
color: var(--el-text-color-secondary);
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
|
.playx-config-tabs {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
.playx-config-submit {
|
||||||
|
margin-top: 16px;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -116,6 +116,14 @@ const baTable = new baTableClass(
|
|||||||
operator: 'RANGE',
|
operator: 'RANGE',
|
||||||
sortable: true,
|
sortable: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: t('mall.userAsset.event_period_win_loss_net'),
|
||||||
|
prop: 'event_period_win_loss_net',
|
||||||
|
align: 'center',
|
||||||
|
minWidth: 160,
|
||||||
|
operator: 'RANGE',
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: t('mall.userAsset.today_limit_date'),
|
label: t('mall.userAsset.today_limit_date'),
|
||||||
prop: 'today_limit_date',
|
prop: 'today_limit_date',
|
||||||
|
|||||||
@@ -77,6 +77,13 @@
|
|||||||
:input-attr="{ step: 1, min: 0 }"
|
:input-attr="{ step: 1, min: 0 }"
|
||||||
:placeholder="t('Please input field', { field: t('mall.userAsset.today_limit') })"
|
:placeholder="t('Please input field', { field: t('mall.userAsset.today_limit') })"
|
||||||
/>
|
/>
|
||||||
|
<FormItem
|
||||||
|
:label="t('mall.userAsset.event_period_win_loss_net')"
|
||||||
|
type="number"
|
||||||
|
v-model="baTable.form.items!.event_period_win_loss_net"
|
||||||
|
prop="event_period_win_loss_net"
|
||||||
|
:input-attr="{ disabled: true, step: 0.01 }"
|
||||||
|
/>
|
||||||
<FormItem
|
<FormItem
|
||||||
:label="t('mall.userAsset.today_claimed')"
|
:label="t('mall.userAsset.today_claimed')"
|
||||||
type="number"
|
type="number"
|
||||||
|
|||||||
Reference in New Issue
Block a user