优化每日推送和积分转化多语言样式
This commit is contained in:
@@ -144,9 +144,17 @@ class PlayxConfig extends Backend
|
||||
} catch (\Throwable $e) {
|
||||
Db::rollback();
|
||||
|
||||
return $this->error($e->getMessage());
|
||||
return $this->error(__($e->getMessage()));
|
||||
}
|
||||
|
||||
return $this->success(__('Operation completed'), $stats);
|
||||
return $this->success(
|
||||
sprintf(
|
||||
__('Lifetime points calculation completed'),
|
||||
$stats['processed'],
|
||||
$stats['updated'],
|
||||
$stats['skipped']
|
||||
),
|
||||
$stats
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,8 @@ return [
|
||||
'Deleted successfully' => 'Deleted successfully!',
|
||||
'Parameter error' => 'Parameter error!',
|
||||
'Lifetime points source is disabled' => 'Lifetime points source is disabled',
|
||||
'Operation completed' => 'Operation completed!',
|
||||
'Lifetime points calculation completed' => 'Lifetime conversion done: %d processed, %d updated, %d skipped',
|
||||
'File uploaded successfully' => 'File uploaded successfully',
|
||||
'No files were uploaded' => 'No files were uploaded',
|
||||
'The uploaded file format is not allowed' => 'The uploaded file format is no allowance.',
|
||||
|
||||
@@ -28,6 +28,8 @@ return [
|
||||
'Deleted successfully' => '删除成功!',
|
||||
'Parameter error' => '参数错误!',
|
||||
'Lifetime points source is disabled' => '终身总输赢积分来源未启用',
|
||||
'Operation completed' => '操作完成!',
|
||||
'Lifetime points calculation completed' => '终身总输赢积分折算完成:共处理 %d 条,更新 %d 条,跳过 %d 条',
|
||||
'Please use the %s field to sort before operating' => '请使用 %s 字段排序后再操作',
|
||||
'File uploaded successfully' => '文件上传成功!',
|
||||
'No files were uploaded' => '没有文件被上传',
|
||||
|
||||
@@ -142,13 +142,13 @@ final class MallPointsConversion
|
||||
public static function recalculateAllLifetimePoints(MallBusinessConfig $row): array
|
||||
{
|
||||
if (intval($row->lifetime_points_enabled ?? 0) !== 1) {
|
||||
throw new \RuntimeException(__('Lifetime points source is disabled'));
|
||||
throw new \RuntimeException('Lifetime points source is disabled');
|
||||
}
|
||||
|
||||
$config = self::configFromRow($row);
|
||||
$ratio = floatval($config['lifetime_points_ratio'] ?? 0);
|
||||
if ($ratio < 0) {
|
||||
throw new \RuntimeException(__('Parameter error'));
|
||||
throw new \RuntimeException('Parameter error');
|
||||
}
|
||||
|
||||
$stats = ['processed' => 0, 'updated' => 0, 'skipped' => 0];
|
||||
|
||||
@@ -29,13 +29,8 @@ export function save(data: anyObj) {
|
||||
}
|
||||
|
||||
export function calculateLifetimePoints() {
|
||||
return createAxios(
|
||||
{
|
||||
url: actionUrl.get('calculateLifetimePoints'),
|
||||
method: 'post',
|
||||
},
|
||||
{
|
||||
showSuccessMessage: true,
|
||||
}
|
||||
)
|
||||
return createAxios({
|
||||
url: actionUrl.get('calculateLifetimePoints'),
|
||||
method: 'post',
|
||||
})
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ export default {
|
||||
},
|
||||
dailyPush: {
|
||||
title: 'About this page',
|
||||
desc: 'Daily T+1 snapshots pushed by the partner; rows are stored here and drive user-asset related updates.',
|
||||
desc: 'Daily T+1 snapshots from the partner; each row stores converted points per mall settings and syncs user assets.',
|
||||
},
|
||||
claimLog: {
|
||||
title: 'About this page',
|
||||
@@ -57,6 +57,6 @@ export default {
|
||||
},
|
||||
playxConfig: {
|
||||
title: 'About this page',
|
||||
desc: 'Edit return/unlock/points-to-cash ratios used by daily push and asset APIs; values are stored in mall_business_config and apply immediately after save.',
|
||||
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.',
|
||||
},
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ export default {
|
||||
calculate_lifetime_btn: 'Calculate lifetime loss points',
|
||||
calculate_lifetime_tip: 'Batch update from latest daily push lifetime field and ratio above.',
|
||||
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',
|
||||
section_mall_open: 'Mall open date',
|
||||
mall_open_date: 'Mall open 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.',
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
export default {
|
||||
id: 'id',
|
||||
user_id: 'user_id',
|
||||
date: 'date',
|
||||
username: 'username',
|
||||
yesterday_win_loss_net: 'yesterday_win_loss_net',
|
||||
converted_points: 'converted_points',
|
||||
yesterday_total_deposit: 'yesterday_total_deposit',
|
||||
lifetime_total_deposit: 'lifetime_total_deposit',
|
||||
lifetime_total_withdraw: 'lifetime_total_withdraw',
|
||||
lifetime_win_loss_net: 'lifetime_win_loss_net',
|
||||
create_time: 'create_time',
|
||||
'quick Search Fields': 'id',
|
||||
id: 'ID',
|
||||
user_id: 'PlayX user ID',
|
||||
date: 'Business date',
|
||||
username: 'Username',
|
||||
yesterday_win_loss_net: 'Yesterday net win/loss',
|
||||
converted_points: 'Converted points',
|
||||
yesterday_total_deposit: 'Yesterday total deposit',
|
||||
lifetime_total_deposit: 'Lifetime total deposit',
|
||||
lifetime_total_withdraw: 'Lifetime total withdraw',
|
||||
lifetime_win_loss_net: 'Lifetime net win/loss',
|
||||
create_time: 'Created at',
|
||||
'quick Search Fields': 'ID',
|
||||
}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
export default {
|
||||
id: 'id',
|
||||
username: 'username',
|
||||
phone: 'phone',
|
||||
playx_user_id: 'playX_user_id',
|
||||
locked_points: 'locked_points',
|
||||
available_points: 'available_points',
|
||||
id: 'ID',
|
||||
username: 'Username',
|
||||
phone: 'Phone',
|
||||
playx_user_id: 'PlayX user ID',
|
||||
locked_points: 'Locked points',
|
||||
available_points: 'Available points',
|
||||
adjust_available_points: 'Adjust points',
|
||||
today_limit: 'today_limit',
|
||||
today_claimed: 'today_claimed',
|
||||
today_limit_date: 'today_limit_date',
|
||||
points_claim_cap: 'Points claim cap',
|
||||
today_limit: 'Daily claim cap',
|
||||
today_claimed: 'Claimed today',
|
||||
today_limit_date: 'Daily cap date',
|
||||
points_claim_cap: 'Lifetime claim cap',
|
||||
total_points_claimed: 'Total points claimed',
|
||||
create_time: 'create_time',
|
||||
update_time: 'update_time',
|
||||
'quick Search Fields': 'id, playX_user_id, username, phone',
|
||||
create_time: 'Created at',
|
||||
update_time: 'Updated at',
|
||||
'quick Search Fields': 'ID, PlayX user ID, username, phone',
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ export default {
|
||||
},
|
||||
dailyPush: {
|
||||
title: '页面说明',
|
||||
desc: '展示合作方 T+1 每日推送的经营数据快照;接口写入后在此留痕,并用于同步用户资产相关字段。',
|
||||
desc: '展示合作方 T+1 每日推送的经营数据快照;入库时按商城参数折算「转换积分」并同步用户资产。',
|
||||
},
|
||||
claimLog: {
|
||||
title: '页面说明',
|
||||
@@ -57,6 +57,6 @@ export default {
|
||||
},
|
||||
playxConfig: {
|
||||
title: '页面说明',
|
||||
desc: '配置每日推送用到的返还比例、解锁比例及积分折算现金比例;保存后立即作用于接口逻辑(mall_business_config)。',
|
||||
desc: '配置昨日净输赢、终身总输赢两种积分来源(开关与兑换比例)、商城开放日,以及解锁比例与积分折算现金比例;保存后按规则生效(开放日隔日生效)。',
|
||||
},
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ export default {
|
||||
calculate_lifetime_btn: '一键计算终身总输赢',
|
||||
calculate_lifetime_tip: '按各会员最新每日推送中的终身总输赢与上方比例,批量更新待领取积分与领取上限。无终身总输赢数据的会员将跳过。',
|
||||
calculate_lifetime_confirm: '将按当前终身总输赢兑换比例重新折算全部会员积分,已折算部分会先扣回再按新比例增加。是否继续?',
|
||||
calculate_lifetime_success: '终身总输赢积分折算完成:共处理 {processed} 条,更新 {updated} 条,跳过 {skipped} 条',
|
||||
section_mall_open: '商城开放日',
|
||||
mall_open_date: '商城开放日',
|
||||
mall_open_date_tip: '设置后隔日生效。例如设为 8 月 1 日,则从 8 月 2 日起对业务日期为 8 月 1 日及之后的每日推送执行「昨日净输赢」转积分(T+1:开放日前一日产生的输赢在开放日次日开始折算)。',
|
||||
|
||||
@@ -70,7 +70,9 @@ const baTable = new baTableClass(
|
||||
label: t('mall.dailyPush.username'),
|
||||
prop: 'username',
|
||||
align: 'center',
|
||||
minWidth: 95,
|
||||
operatorPlaceholder: t('Fuzzy query'),
|
||||
showOverflowTooltip: true,
|
||||
sortable: false,
|
||||
operator: 'LIKE',
|
||||
},
|
||||
@@ -78,6 +80,7 @@ const baTable = new baTableClass(
|
||||
label: t('mall.dailyPush.yesterday_win_loss_net'),
|
||||
prop: 'yesterday_win_loss_net',
|
||||
align: 'center',
|
||||
minWidth: 90,
|
||||
operator: 'RANGE',
|
||||
sortable: 'custom',
|
||||
},
|
||||
@@ -85,14 +88,15 @@ const baTable = new baTableClass(
|
||||
label: t('mall.dailyPush.converted_points'),
|
||||
prop: 'converted_points',
|
||||
align: 'center',
|
||||
minWidth: 80,
|
||||
operator: 'RANGE',
|
||||
sortable: 'custom',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
label: t('mall.dailyPush.yesterday_total_deposit'),
|
||||
prop: 'yesterday_total_deposit',
|
||||
align: 'center',
|
||||
minWidth: 90,
|
||||
operator: 'RANGE',
|
||||
sortable: 'custom',
|
||||
},
|
||||
@@ -100,6 +104,7 @@ const baTable = new baTableClass(
|
||||
label: t('mall.dailyPush.lifetime_total_deposit'),
|
||||
prop: 'lifetime_total_deposit',
|
||||
align: 'center',
|
||||
minWidth: 90,
|
||||
operator: 'RANGE',
|
||||
sortable: 'custom',
|
||||
},
|
||||
@@ -107,7 +112,7 @@ const baTable = new baTableClass(
|
||||
label: t('mall.dailyPush.lifetime_total_withdraw'),
|
||||
prop: 'lifetime_total_withdraw',
|
||||
align: 'center',
|
||||
minWidth: 95,
|
||||
minWidth: 90,
|
||||
operator: 'RANGE',
|
||||
sortable: 'custom',
|
||||
},
|
||||
@@ -115,7 +120,7 @@ const baTable = new baTableClass(
|
||||
label: t('mall.dailyPush.lifetime_win_loss_net'),
|
||||
prop: 'lifetime_win_loss_net',
|
||||
align: 'center',
|
||||
minWidth: 95,
|
||||
minWidth: 90,
|
||||
operator: 'RANGE',
|
||||
sortable: 'custom',
|
||||
},
|
||||
|
||||
@@ -113,7 +113,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
import { ElMessageBox } from 'element-plus'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { onMounted, reactive, ref, useTemplateRef } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { calculateLifetimePoints, index, save } from '/@/api/backend/mall/playxConfig'
|
||||
@@ -204,7 +204,15 @@ const onCalculateLifetime = () => {
|
||||
}).then(() => {
|
||||
calculating.value = true
|
||||
calculateLifetimePoints()
|
||||
.then(() => {
|
||||
.then((res) => {
|
||||
const stats = res.data ?? {}
|
||||
ElMessage.success(
|
||||
t('mall.playxConfig.calculate_lifetime_success', {
|
||||
processed: stats.processed ?? 0,
|
||||
updated: stats.updated ?? 0,
|
||||
skipped: stats.skipped ?? 0,
|
||||
})
|
||||
)
|
||||
loadData()
|
||||
})
|
||||
.finally(() => {
|
||||
|
||||
@@ -35,22 +35,94 @@ const baTable = new baTableClass(
|
||||
column: [
|
||||
{ type: 'selection', align: 'center', operator: false },
|
||||
{ label: t('mall.playxDailyPush.id'), prop: 'id', align: 'center', width: 70, operator: 'RANGE', sortable: 'custom' },
|
||||
{ label: t('mall.playxDailyPush.user_id'), prop: 'user_id', align: 'center', operatorPlaceholder: t('Fuzzy query'), sortable: false, operator: 'LIKE' },
|
||||
{ label: t('mall.playxDailyPush.date'), prop: 'date', align: 'center', render: 'date', operator: 'RANGE', comSearchRender: 'date', sortable: 'custom', width: 120, operatorPlaceholder: t('Fuzzy query') },
|
||||
{ label: t('mall.playxDailyPush.username'), prop: 'username', align: 'center', operatorPlaceholder: t('Fuzzy query'), sortable: false, operator: 'LIKE' },
|
||||
{ label: t('mall.playxDailyPush.yesterday_win_loss_net'), prop: 'yesterday_win_loss_net', align: 'center', operator: 'RANGE', sortable: false },
|
||||
{ label: t('mall.playxDailyPush.converted_points'), prop: 'converted_points', align: 'center', operator: 'RANGE', sortable: false, width: 100 },
|
||||
{ label: t('mall.playxDailyPush.yesterday_total_deposit'), prop: 'yesterday_total_deposit', align: 'center', operator: 'RANGE', sortable: false },
|
||||
{ label: t('mall.playxDailyPush.lifetime_total_deposit'), prop: 'lifetime_total_deposit', align: 'center', operator: 'RANGE', sortable: false },
|
||||
{ label: t('mall.playxDailyPush.lifetime_total_withdraw'), prop: 'lifetime_total_withdraw', align: 'center', operator: 'RANGE', sortable: false },
|
||||
{ label: t('mall.playxDailyPush.lifetime_win_loss_net'), prop: 'lifetime_win_loss_net', align: 'center', operator: 'RANGE', sortable: false },
|
||||
{ label: t('mall.playxDailyPush.create_time'), prop: 'create_time', align: 'center', render: 'datetime', operator: 'RANGE', comSearchRender: 'datetime', sortable: 'custom', width: 160, timeFormat: 'yyyy-mm-dd hh:MM:ss' },
|
||||
{
|
||||
label: t('mall.playxDailyPush.user_id'),
|
||||
prop: 'user_id',
|
||||
align: 'center',
|
||||
operatorPlaceholder: t('Fuzzy query'),
|
||||
sortable: false,
|
||||
operator: 'LIKE',
|
||||
},
|
||||
{
|
||||
label: t('mall.playxDailyPush.date'),
|
||||
prop: 'date',
|
||||
align: 'center',
|
||||
render: 'date',
|
||||
operator: 'RANGE',
|
||||
comSearchRender: 'date',
|
||||
sortable: 'custom',
|
||||
width: 120,
|
||||
operatorPlaceholder: t('Fuzzy query'),
|
||||
},
|
||||
{
|
||||
label: t('mall.playxDailyPush.username'),
|
||||
prop: 'username',
|
||||
align: 'center',
|
||||
operatorPlaceholder: t('Fuzzy query'),
|
||||
sortable: false,
|
||||
operator: 'LIKE',
|
||||
},
|
||||
{
|
||||
label: t('mall.playxDailyPush.yesterday_win_loss_net'),
|
||||
prop: 'yesterday_win_loss_net',
|
||||
align: 'center',
|
||||
operator: 'RANGE',
|
||||
sortable: false,
|
||||
},
|
||||
{
|
||||
label: t('mall.playxDailyPush.converted_points'),
|
||||
prop: 'converted_points',
|
||||
align: 'center',
|
||||
minWidth: 160,
|
||||
operator: 'RANGE',
|
||||
sortable: false,
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
label: t('mall.playxDailyPush.yesterday_total_deposit'),
|
||||
prop: 'yesterday_total_deposit',
|
||||
align: 'center',
|
||||
operator: 'RANGE',
|
||||
sortable: false,
|
||||
},
|
||||
{
|
||||
label: t('mall.playxDailyPush.lifetime_total_deposit'),
|
||||
prop: 'lifetime_total_deposit',
|
||||
align: 'center',
|
||||
operator: 'RANGE',
|
||||
sortable: false,
|
||||
},
|
||||
{
|
||||
label: t('mall.playxDailyPush.lifetime_total_withdraw'),
|
||||
prop: 'lifetime_total_withdraw',
|
||||
align: 'center',
|
||||
operator: 'RANGE',
|
||||
sortable: false,
|
||||
},
|
||||
{
|
||||
label: t('mall.playxDailyPush.lifetime_win_loss_net'),
|
||||
prop: 'lifetime_win_loss_net',
|
||||
align: 'center',
|
||||
operator: 'RANGE',
|
||||
sortable: false,
|
||||
},
|
||||
{
|
||||
label: t('mall.playxDailyPush.create_time'),
|
||||
prop: 'create_time',
|
||||
align: 'center',
|
||||
render: 'datetime',
|
||||
operator: 'RANGE',
|
||||
comSearchRender: 'datetime',
|
||||
sortable: 'custom',
|
||||
width: 160,
|
||||
timeFormat: 'yyyy-mm-dd hh:MM:ss',
|
||||
},
|
||||
],
|
||||
dblClickNotEditColumn: [undefined],
|
||||
},
|
||||
{
|
||||
defaultItems: {},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
provide('baTable', baTable)
|
||||
@@ -66,4 +138,3 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ const baTable = new baTableClass(
|
||||
prop: 'username',
|
||||
align: 'center',
|
||||
operatorPlaceholder: t('Fuzzy query'),
|
||||
showOverflowTooltip: true,
|
||||
sortable: false,
|
||||
operator: 'LIKE',
|
||||
},
|
||||
@@ -52,7 +53,9 @@ const baTable = new baTableClass(
|
||||
label: t('mall.userAsset.phone'),
|
||||
prop: 'phone',
|
||||
align: 'center',
|
||||
minWidth: 100,
|
||||
operatorPlaceholder: t('Fuzzy query'),
|
||||
showOverflowTooltip: true,
|
||||
sortable: false,
|
||||
operator: 'LIKE',
|
||||
},
|
||||
@@ -60,16 +63,59 @@ const baTable = new baTableClass(
|
||||
label: t('mall.userAsset.playx_user_id'),
|
||||
prop: 'playx_user_id',
|
||||
align: 'center',
|
||||
minWidth: 120,
|
||||
operatorPlaceholder: t('Fuzzy query'),
|
||||
sortable: false,
|
||||
operator: 'LIKE',
|
||||
},
|
||||
{ label: t('mall.userAsset.locked_points'), prop: 'locked_points', align: 'center', operator: 'RANGE', sortable: false },
|
||||
{ label: t('mall.userAsset.available_points'), prop: 'available_points', align: 'center', operator: 'RANGE', sortable: false },
|
||||
{ label: t('mall.userAsset.today_limit'), prop: 'today_limit', align: 'center', operator: 'RANGE', sortable: false },
|
||||
{ label: t('mall.userAsset.today_claimed'), prop: 'today_claimed', align: 'center', operator: 'RANGE', sortable: false },
|
||||
{ label: t('mall.userAsset.points_claim_cap'), prop: 'points_claim_cap', align: 'center', operator: 'RANGE', sortable: false },
|
||||
{ label: t('mall.userAsset.total_points_claimed'), prop: 'total_points_claimed', align: 'center', operator: 'RANGE', sortable: false },
|
||||
{
|
||||
label: t('mall.userAsset.locked_points'),
|
||||
prop: 'locked_points',
|
||||
align: 'center',
|
||||
minWidth: 120,
|
||||
operator: 'RANGE',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
label: t('mall.userAsset.available_points'),
|
||||
prop: 'available_points',
|
||||
align: 'center',
|
||||
minWidth: 120,
|
||||
operator: 'RANGE',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
label: t('mall.userAsset.today_limit'),
|
||||
prop: 'today_limit',
|
||||
align: 'center',
|
||||
minWidth: 160,
|
||||
operator: 'RANGE',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
label: t('mall.userAsset.today_claimed'),
|
||||
prop: 'today_claimed',
|
||||
align: 'center',
|
||||
minWidth: 120,
|
||||
operator: 'RANGE',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
label: t('mall.userAsset.points_claim_cap'),
|
||||
prop: 'points_claim_cap',
|
||||
align: 'center',
|
||||
minWidth: 160,
|
||||
operator: 'RANGE',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
label: t('mall.userAsset.total_points_claimed'),
|
||||
prop: 'total_points_claimed',
|
||||
align: 'center',
|
||||
minWidth: 160,
|
||||
operator: 'RANGE',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
label: t('mall.userAsset.today_limit_date'),
|
||||
prop: 'today_limit_date',
|
||||
|
||||
Reference in New Issue
Block a user