medal management

This commit is contained in:
xiaomlove
2022-01-19 23:54:55 +08:00
parent 3fa8fd19c0
commit 64a1f2bb0c
39 changed files with 1282 additions and 171 deletions

View File

@@ -38,6 +38,9 @@
<el-menu-item-group>
<el-menu-item index="/exam-user"><i class="el-icon-menu" />Exam user</el-menu-item>
</el-menu-item-group>
<el-menu-item-group>
<el-menu-item index="/medal"><i class="el-icon-menu" />Medal</el-menu-item>
</el-menu-item-group>
<el-menu-item-group>
<el-menu-item index="/setting"><i class="el-icon-menu" />Setting</el-menu-item>
</el-menu-item-group>

View File

@@ -54,6 +54,16 @@ const router = createRouter({
name: 'agent-allow-form',
component: () => import('../views/agent-allow/form.vue')
},
{
path: '/medal',
name: 'medal',
component: () => import('../views/medal/index.vue')
},
{
path: '/medal-form',
name: 'medal-form',
component: () => import('../views/medal/form.vue')
},
{
path: '/setting',
name: 'setting',

View File

@@ -72,6 +72,24 @@ const api = {
deleteExam: (id) => {
return axios.delete('exams/' + id);
},
listMedal: (params = {}) => {
return axios.get('medals', {params: params});
},
storeMedal: (params = {}) => {
return axios.post('medals', params);
},
updateMedal: (id, params = {}) => {
return axios.put('medals/' + id, params);
},
getMedal: (id) => {
return axios.get('medals/' + id);
},
deleteMedal: (id) => {
return axios.delete('medals/' + id);
},
listClass: (params = {}) => {
return axios.get('user-classes', {params: params});
},
@@ -107,7 +125,10 @@ const api = {
},
listSystemInfo: () => {
return axios.get('dashboard/system-info')
}
},
removeUserMedal: (id) => {
return axios.delete('user-medals/' + id);
},
}

View File

@@ -47,4 +47,6 @@ export const pathMap = {
'exam-form': 'Exam form',
'exam-user': 'Exam user',
'setting': "Setting",
'medal': 'Medal',
'medal-form': 'Medal form',
}

View File

@@ -0,0 +1,162 @@
<template>
<div>
<el-row>
<el-col :span="12">
<el-form :model="formData" :rules="rules" ref="formRef" label-width="200px" class="formData">
<el-form-item label="Name" prop="name">
<el-input v-model="formData.name" placeholder=""></el-input>
</el-form-item>
<el-form-item label="Price" prop="price">
<el-input v-model="formData.price" placeholder="Seed bonus"></el-input>
</el-form-item>
<el-form-item label="Get type" prop="get_type">
<el-radio-group v-model="formData.get_type">
<el-radio :label="1">Exchange</el-radio>
<el-radio :label="2">Grant</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="Image large" prop="image_large">
<el-input v-model="formData.image_large" placeholder=""></el-input>
</el-form-item>
<el-form-item label="Image small" prop="image_small">
<el-input v-model="formData.image_small" placeholder=""></el-input>
</el-form-item>
<el-form-item label="Duration" prop="duration">
<el-input v-model="formData.duration" placeholder="Unit: day, if empty, it's valid forever"></el-input>
</el-form-item>
<el-form-item label="Description" prop="description">
<el-input type="textarea" v-model="formData.description" placeholder=""></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitAdd()">Submit</el-button>
</el-form-item>
</el-form>
</el-col>
</el-row>
</div>
</template>
<script>
import { reactive, ref, toRefs, onMounted, onBeforeUnmount, getCurrentInstance } from 'vue'
import { ElMessage } from 'element-plus'
import { useRoute, useRouter } from 'vue-router'
import { localGet } from '../../utils'
import api from "../../utils/api";
export default {
name: 'MedalForm',
setup() {
const { proxy } = getCurrentInstance()
console.log('proxy', proxy)
const formRef = ref(null)
const route = useRoute()
const router = useRouter()
const { id } = route.query
const state = reactive({
token: localGet('token') || '',
id: id,
allClasses: [],
formData: {
name: '',
description: '',
image_large: '',
image_small: '',
duration: '',
price: '',
get_type: ''
},
rules: {
name: [
{ required: 'true', }
],
price: [
{ required: 'true', }
],
image_large: [
{ required: 'true', }
],
image_small: [
{ required: 'true', }
],
description: [
{ required: 'true', }
],
get_type: [
{required: 'true'}
]
},
})
onMounted( async () => {
if (id) {
api.getMedal(id).then(res => {
state.formData.name = res.data.name
state.formData.image_large = res.data.image_large
state.formData.image_small = res.data.image_small
state.formData.description = res.data.description
state.formData.price = res.data.price
state.formData.duration = res.data.duration
state.formData.get_type = res.data.get_type
})
}
})
onBeforeUnmount(() => {
})
const submitAdd = () => {
formRef.value.validate(async (vaild) => {
if (vaild) {
let params = state.formData;
console.log(params)
if (id) {
await api.updateMedal(id, params)
} else {
await api.storeMedal(params)
}
await router.push({name: 'medal'})
}
})
}
const handleBeforeUpload = (file) => {
const sufix = file.name.split('.')[1] || ''
if (!['jpg', 'jpeg', 'png'].includes(sufix)) {
ElMessage.error('请上传 jpg、jpeg、png 格式的图片')
return false
}
}
const handleUrlSuccess = (val) => {
state.formData.goodsCoverImg = val.data || ''
}
const handleChangeCate = (val) => {
state.categoryId = val[2] || 0
}
const listAllClass = async () => {
let res = await api.listClass()
state.allClasses = res.data
}
const listAllIndex = async () => {
let res = await api.listMedalIndex()
state.formData.indexes = res.data
}
const getMedal = async (id) => {
let res = await api.getMedal(id)
console.log(res)
}
return {
...toRefs(state),
formRef,
submitAdd,
handleBeforeUpload,
handleUrlSuccess,
handleChangeCate,
}
}
}
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,151 @@
<template>
<el-card class="">
<template #header>
<div class="nexus-table-header">
<div class="left">
</div>
<div class="right">
<el-button type="primary" size="small" icon="el-icon-plus" @click="handleAdd">Add</el-button>
</div>
</div>
</template>
<el-table
v-loading="loading"
ref="multipleTable"
:data="tableData"
tooltip-effect="dark"
@selection-change="handleSelectionChange">
<el-table-column
type="selection"
width="55">
</el-table-column>
<el-table-column
prop="id"
label="Id"
width="50"
>
</el-table-column>
<el-table-column
prop="name"
label="Name"
></el-table-column>
<el-table-column
prop="image_large"
label="Large image"
></el-table-column>
<el-table-column
prop="image_small"
label="Small image"
></el-table-column>
<el-table-column
prop="get_type_text"
label="Get type"
></el-table-column>
<el-table-column
prop="price"
label="Price(bonus)"
></el-table-column>
<el-table-column
prop="duration"
label="Duration(day)"
></el-table-column>
<el-table-column
label="Action"
width="100"
>
<template #default="scope">
<a style="cursor: pointer; margin-right: 10px" @click="handleEdit(scope.row.id)">Edit</a>
<el-popconfirm
title="Confirm Delete ?"
@confirm="handleDelete(scope.row.id)"
>
<template #reference>
<a style="cursor: pointer">Delete</a>
</template>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
<!--总数超过一页再展示分页器-->
<el-pagination
background
layout="prev, pager, next"
:total="total"
:page-size="perPage"
:current-page="currentPage"
@current-change="changePage"
/>
</el-card>
</template>
<script>
import { onMounted, reactive, ref, toRefs } from 'vue'
import { ElMessage } from 'element-plus'
import { useRouter } from 'vue-router'
import api from '../../utils/api'
import { useTable, renderTableData } from '../../utils/table'
export default {
name: 'MedalTable',
setup() {
const multipleTable = ref(null)
const router = useRouter()
const state = useTable()
onMounted(() => {
console.log('MedalTable onMounted')
fetchTableData()
})
const fetchTableData = async () => {
state.loading = true
let res = await api.listMedal(state.query)
renderTableData(res, state)
state.loading = false
}
const handleAdd = () => {
router.push({ name: 'medal-form' })
}
const handleEdit = (id) => {
router.push({ path: '/medal-form', query: { id } })
}
const handleDelete = async (id) => {
let res = await api.deleteMedal(id)
ElMessage.success(res.msg)
state.query.page = 1;
await fetchTableData()
}
const handleSelectionChange = (val) => {
state.multipleSelection = val
}
const changePage = (val) => {
state.query.page = val
fetchTableData()
}
return {
...toRefs(state),
multipleTable,
handleSelectionChange,
handleAdd,
handleEdit,
handleDelete,
fetchTableData,
changePage,
}
}
}
</script>
<style scoped>
.swiper-container {
min-height: 100%;
}
.el-card.is-always-shadow {
min-height: 100%!important;
}
</style>

View File

@@ -153,12 +153,62 @@
</el-col>
</el-row>
</el-card>
<el-row v-if="baseInfo.valid_medals && baseInfo.valid_medals.length">
<el-col :span="12">
<el-card >
<template #header>
<div class="card-header">
<span>Medal</span>
</div>
</template>
<el-table
v-loading="loading"
ref="multipleTable"
:data="baseInfo.valid_medals"
tooltip-effect="dark"
>
<el-table-column
prop="name"
label="Name"
></el-table-column>
<el-table-column
prop="image_large"
label="Image"
>
</el-table-column>
<el-table-column
prop="expire_at"
label="Expire at"
></el-table-column>
<el-table-column
label="Action"
width="100"
>
<template #default="scope">
<el-popconfirm
title="Confirm Remove ?"
@confirm="handleRemoveUserMedal(scope.row.user_medal_id)"
>
<template #reference>
<a style="cursor: pointer">Remove</a>
</template>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
</el-card>
</el-col>
</el-row>
</div>
<DialogAssignExam ref="assignExam" :reload="fetchPageData"/>
<DialogViewInviteInfo ref="viewInviteInfo" />
<DialogDisableUser ref="disableUser" :reload="fetchPageData" />
<DialogModComment ref="modComment" />
<DialogModComment ref="modComment" />
<DialogResetPassword ref="resetPassword" />
</template>
@@ -240,6 +290,12 @@ export default {
const handleResetPassword = async () => {
resetPassword.value.open(id)
}
const handleRemoveUserMedal = async (id) => {
let res = await api.removeUserMedal(id)
ElMessage.success(res.msg)
await fetchPageData()
}
return {
...toRefs(state),
handleRemoveExam,
@@ -252,6 +308,7 @@ export default {
handleGetModComment,
handleResetPassword,
fetchPageData,
handleRemoveUserMedal,
assignExam,
viewInviteInfo,
disableUser,
@@ -271,10 +328,10 @@ export default {
text-align: left;
tr {
th {
padding-bottom: 10px;
padding-bottom: 4px;
}
td {
padding: 10px 0;
padding: 4px 0;
}
}
}

View File

@@ -7,12 +7,14 @@ use App\Models\Exam;
use App\Models\ExamProgress;
use App\Models\ExamUser;
use App\Models\HitAndRun;
use App\Models\Medal;
use App\Models\SearchBox;
use App\Models\Snatch;
use App\Models\User;
use App\Repositories\ExamRepository;
use App\Repositories\SearchBoxRepository;
use App\Repositories\TorrentRepository;
use App\Repositories\UserRepository;
use Carbon\Carbon;
use GeoIp2\Database\Reader;
use Illuminate\Console\Command;
@@ -55,11 +57,8 @@ class Test extends Command
*/
public function handle()
{
$user = User::query()->first();
$user->update([
'page' => DB::raw('dddd')
]);
dd(last_query());
$rep = new UserRepository();
$r = $rep->getDetail(1);
}
}

View File

@@ -0,0 +1,103 @@
<?php
namespace App\Http\Controllers;
use App\Http\Resources\MedalResource;
use App\Repositories\MedalRepository;
use Illuminate\Http\Request;
class MedalController extends Controller
{
private $repository;
public function __construct(MedalRepository $repository)
{
$this->repository = $repository;
}
/**
* Display a listing of the resource.
*
* @param Request $request
* @return array
*/
public function index(Request $request)
{
$result = $this->repository->getList($request->all());
$resource = MedalResource::collection($result);
$resource->additional([
'page_title' => nexus_trans('medal.admin.list.page_title'),
]);
return $this->success($resource);
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function store(Request $request)
{
$rules = [
'name' => 'required|string',
'price' => 'required|integer|min:1',
'image_large' => 'required|url',
'image_small' => 'required|url',
'duration' => 'nullable|integer|min:-1',
];
$request->validate($rules);
$result = $this->repository->store($request->all());
$resource = new MedalResource($result);
return $this->success($resource);
}
/**
* Display the specified resource.
*
* @param int $id
* @return array
*/
public function show($id)
{
$result = $this->repository->getDetail($id);
$resource = new MedalResource($result);
return $this->success($resource);
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return array
*/
public function update(Request $request, $id)
{
$rules = [
'name' => 'required|string',
'price' => 'required|integer|min:1',
'image_large' => 'required|url',
'image_small' => 'required|url',
'duration' => 'nullable|integer|min:-1',
];
$request->validate($rules);
$result = $this->repository->update($request->all(), $id);
$resource = new MedalResource($result);
return $this->success($resource);
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return array
*/
public function destroy($id)
{
$result = $this->repository->delete($id);
return $this->success($result, 'Delete medal success!');
}
}

View File

@@ -0,0 +1,106 @@
<?php
namespace App\Http\Controllers;
use App\Http\Resources\MedalResource;
use App\Models\UserMedal;
use App\Repositories\MedalRepository;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class UserMedalController extends Controller
{
private $repository;
public function __construct(MedalRepository $repository)
{
$this->repository = $repository;
}
/**
* Display a listing of the resource.
*
* @param Request $request
* @return array
*/
public function index(Request $request)
{
$result = $this->repository->getList($request->all());
$resource = MedalResource::collection($result);
$resource->additional([
'page_title' => nexus_trans('medal.admin.list.page_title'),
]);
return $this->success($resource);
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function store(Request $request)
{
$rules = [
'name' => 'required|string',
'price' => 'required|integer|min:1',
'image_large' => 'required|url',
'image_small' => 'required|url',
'duration' => 'nullable|integer|min:-1',
];
$request->validate($rules);
$result = $this->repository->store($request->all());
$resource = new MedalResource($result);
return $this->success($resource);
}
/**
* Display the specified resource.
*
* @param int $id
* @return array
*/
public function show($id)
{
$result = $this->repository->getDetail($id);
$resource = new MedalResource($result);
return $this->success($resource);
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return array
*/
public function update(Request $request, $id)
{
$rules = [
'name' => 'required|string',
'price' => 'required|integer|min:1',
'image_large' => 'required|url',
'image_small' => 'required|url',
'duration' => 'nullable|integer|min:-1',
];
$request->validate($rules);
$result = $this->repository->update($request->all(), $id);
$resource = new MedalResource($result);
return $this->success($resource);
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return array
*/
public function destroy($id)
{
$userMedal = UserMedal::query()->findOrFail($id);
$result = $userMedal->delete();
return $this->success($result, 'Remove user medal success!');
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class MedalResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'get_type' => $this->get_type,
'get_type_text' => $this->getTypeText,
'image_large' => $this->image_large,
'image_small' => $this->image_small,
'price' => $this->price,
'duration' => $this->duration,
'description' => $this->description,
'expire_at' => $this->whenPivotLoaded('user_medals', function () {return $this->pivot->expire_at;}),
'user_medal_id' => $this->whenPivotLoaded('user_medals', function () {return $this->pivot->id;}),
];
}
}

View File

@@ -34,6 +34,7 @@ class UserResource extends JsonResource
'leechtime' => $this->leechtime,
'leechtime_text' => mkprettytime($this->leechtime),
'inviter' => new UserResource($this->whenLoaded('inviter')),
'valid_medals' => MedalResource::collection($this->whenLoaded('valid_medals')),
];
if ($request->routeIs('user.me')) {
$out['downloaded_human'] = mksize($this->downloaded);

View File

@@ -12,9 +12,11 @@ class BonusLogs extends NexusModel
const DEFAULT_BONUS_CANCEL_ONE_HIT_AND_RUN = 10000;
const BUSINESS_TYPE_CANCEL_HIT_AND_RUN = 1;
const BUSINESS_TYPE_BUY_MEDAL = 2;
public static $businessTypes = [
self::BUSINESS_TYPE_CANCEL_HIT_AND_RUN => ['text' => 'Cancel H&R'],
self::BUSINESS_TYPE_BUY_MEDAL => ['text' => 'Buy medal'],
];
public static function getBonusForCancelHitAndRun()

39
app/Models/Medal.php Normal file
View File

@@ -0,0 +1,39 @@
<?php
namespace App\Models;
use Carbon\Carbon;
class Medal extends NexusModel
{
const GET_TYPE_EXCHANGE = 1;
const GET_TYPE_GRANT = 2;
public static $getTypeText = [
self::GET_TYPE_EXCHANGE => ['text' => 'Exchange'],
self::GET_TYPE_GRANT => ['text' => 'Grant'],
];
protected $fillable = ['name', 'description', 'image_large', 'image_small', 'price', 'duration', 'get_type'];
public $timestamps = true;
public function getGetTypeTextAttribute($value): string
{
return self::$getTypeText[$this->get_type]['text'] ?? '';
}
public function users(): \Illuminate\Database\Eloquent\Relations\BelongsToMany
{
return $this->belongsToMany(User::class, 'user_medals', 'medal_id', 'uid')->withTimestamps();
}
public function valid_users(): \Illuminate\Database\Eloquent\Relations\BelongsToMany
{
return $this->users()->where(function ($query) {
$query->whereNull('user_medals.expire_at')->orWhere('user_medals.expire_at', '>=', Carbon::now());
});
}
}

View File

@@ -3,6 +3,7 @@
namespace App\Models;
use App\Http\Middleware\Locale;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
@@ -263,6 +264,20 @@ class User extends Authenticatable
return $this->hasMany(HitAndRun::class, 'uid');
}
public function medals(): \Illuminate\Database\Eloquent\Relations\BelongsToMany
{
return $this->belongsToMany(Medal::class, 'user_medals', 'uid', 'medal_id')
->withPivot(['id', 'expire_at'])
->withTimestamps();
}
public function valid_medals(): \Illuminate\Database\Eloquent\Relations\BelongsToMany
{
return $this->medals()->where(function ($query) {
$query->whereNull('user_medals.expire_at')->orWhere('user_medals.expire_at', '>=', Carbon::now());
});
}
public function getAvatarAttribute($value)
{
if ($value) {

8
app/Models/UserMedal.php Normal file
View File

@@ -0,0 +1,8 @@
<?php
namespace App\Models;
class UserMedal extends NexusModel
{
protected $fillable = ['uid', 'medal_id', 'expire_at'];
}

View File

@@ -3,17 +3,16 @@ namespace App\Repositories;
use App\Models\BonusLogs;
use App\Models\HitAndRun;
use App\Models\Medal;
use App\Models\Setting;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Query\Expression;
use Illuminate\Support\Facades\DB;
use Nexus\Database\NexusDB;
class BonusRepository extends BaseRepository
{
public function consumeToCancelHitAndRun($uid, $hitAndRunId)
public function consumeToCancelHitAndRun($uid, $hitAndRunId): bool
{
if (!HitAndRun::getIsEnabled()) {
throw new \LogicException("H&R not enabled.");
@@ -24,47 +23,23 @@ class BonusRepository extends BaseRepository
throw new \LogicException("H&R: $hitAndRunId not belongs to user: $uid.");
}
$requireBonus = BonusLogs::getBonusForCancelHitAndRun();
if ($user->seedbonus < $requireBonus) {
do_log("user: $uid, bonus: {$user->seedbonus} < requireBonus: $requireBonus", 'error');
throw new \LogicException("User bonus point not enough.");
}
$result = NexusDB::transaction(function () use ($user, $hitAndRun, $requireBonus) {
$oldUserBonus = $user->seedbonus;
$newUserBonus = bcsub($oldUserBonus, $requireBonus);
$log = "user: {$user->id}, hitAndRun: {$hitAndRun->id}, requireBonus: $requireBonus, oldUserBonus: $oldUserBonus, newUserBonus: $newUserBonus";
do_log($log);
$affectedRows = NexusDB::table($user->getTable())
->where('id', $user->id)
->where('seedbonus', $oldUserBonus)
->update(['seedbonus' => $newUserBonus]);
if ($affectedRows != 1) {
do_log("update user seedbonus affected rows != 1, query: " . last_query(), 'error');
throw new \RuntimeException("Update user seedbonus fail.");
}
NexusDB::transaction(function () use ($user, $hitAndRun, $requireBonus) {
$comment = nexus_trans('hr.bonus_cancel_comment', [
'now' => Carbon::now()->toDateTimeString(),
'bonus' => $requireBonus,
], $user->locale);
$comment = addslashes($comment);
do_log("comment: $comment");
$this->consumeUserBonus($user, $requireBonus, BonusLogs::BUSINESS_TYPE_CANCEL_HIT_AND_RUN, "$comment(H&R ID: {$hitAndRun->id})");
$hitAndRun->update([
'status' => HitAndRun::STATUS_PARDONED,
'comment' => new Expression("concat(comment, '\n$comment')"),
'comment' => NexusDB::raw("concat(comment, '\n$comment')"),
]);
$bonusLog = [
'business_type' => BonusLogs::BUSINESS_TYPE_CANCEL_HIT_AND_RUN,
'uid' => $user->id,
'old_total_value' => $oldUserBonus,
'value' => $requireBonus,
'new_total_value' => $newUserBonus,
'comment' => "$comment(H&R ID: {$hitAndRun->id})",
];
BonusLogs::query()->insert($bonusLog);
do_log("bonusLog: " . json_encode($bonusLog));
return true;
});
return $result;
return true;
}
@@ -74,11 +49,11 @@ class BonusRepository extends BaseRepository
$tableName = (new User())->getTable();
$result = 0;
do {
$affectedRows = DB::table($tableName)
$affectedRows = NexusDB::table($tableName)
->whereNull('seed_points')
->limit($size)
->update([
'seed_points' => DB::raw('seed_points = seedbonus')
'seed_points' => NexusDB::raw('seed_points = seedbonus')
]);
$result += $affectedRows;
do_log("affectedRows: $affectedRows, query: " . last_query());
@@ -88,4 +63,67 @@ class BonusRepository extends BaseRepository
}
public function consumeToBuyMedal($uid, $medalId): bool
{
$user = User::query()->findOrFail($uid);
$medal = Medal::query()->findOrFail($medalId);
$exists = $user->valid_medals()->where('medal_id', $medalId)->exists();
do_log(last_query());
if ($exists) {
throw new \LogicException("user: $uid already own this medal: $medalId.");
}
$requireBonus = $medal->price;
NexusDB::transaction(function () use ($user, $medal, $requireBonus) {
$comment = nexus_trans('bonus.comment_buy_medal', [
'bonus' => $requireBonus,
'medal_name' => $medal->name,
], $user->locale);
$comment = addslashes($comment);
do_log("comment: $comment");
$this->consumeUserBonus($user, $requireBonus, BonusLogs::BUSINESS_TYPE_BUY_MEDAL, "$comment(medal ID: {$medal->id})");
$expireAt = null;
if ($medal->duration > 0) {
$expireAt = Carbon::now()->addDays($medal->duration)->toDateTimeString();
}
$user->medals()->attach([$medal->id => ['expire_at' => $expireAt]]);
});
return true;
}
private function consumeUserBonus($user, $requireBonus, $logBusinessType, $logComment = '')
{
if ($user->seedbonus < $requireBonus) {
do_log("user: {$user->id}, bonus: {$user->seedbonus} < requireBonus: $requireBonus", 'error');
throw new \LogicException("User bonus point not enough.");
}
NexusDB::transaction(function () use ($user, $requireBonus, $logBusinessType, $logComment) {
$oldUserBonus = $user->seedbonus;
$newUserBonus = bcsub($oldUserBonus, $requireBonus);
$log = "user: {$user->id}, requireBonus: $requireBonus, oldUserBonus: $oldUserBonus, newUserBonus: $newUserBonus, logBusinessType: $logBusinessType, logComment: $logComment";
do_log($log);
$affectedRows = NexusDB::table($user->getTable())
->where('id', $user->id)
->where('seedbonus', $oldUserBonus)
->update(['seedbonus' => $newUserBonus]);
if ($affectedRows != 1) {
do_log("update user seedbonus affected rows != 1, query: " . last_query(), 'error');
throw new \RuntimeException("Update user seedbonus fail.");
}
$bonusLog = [
'business_type' => $logBusinessType,
'uid' => $user->id,
'old_total_value' => $oldUserBonus,
'value' => $requireBonus,
'new_total_value' => $newUserBonus,
'comment' => $logComment,
];
BonusLogs::query()->insert($bonusLog);
do_log("bonusLog: " . nexus_json_encode($bonusLog));
});
}
}

View File

@@ -0,0 +1,54 @@
<?php
namespace App\Repositories;
use App\Models\Medal;
use App\Models\UserMedal;
use Nexus\Database\NexusDB;
class MedalRepository extends BaseRepository
{
public function getList(array $params): \Illuminate\Contracts\Pagination\LengthAwarePaginator
{
$query = Medal::query();
list($sortField, $sortType) = $this->getSortFieldAndType($params);
$query->orderBy($sortField, $sortType);
return $query->paginate();
}
public function store(array $params)
{
return Medal::query()->create($params);
}
public function update(array $params, $id)
{
$medal = Medal::query()->findOrFail($id);
$medal->update($params);
return $medal;
}
public function getDetail($id)
{
return Medal::query()->findOrFail($id);
}
/**
* delete a medal, also will delete all user medal.
*
* @param $id
* @return bool
*/
public function delete($id): bool
{
$medal = Medal::query()->findOrFail($id);
NexusDB::transaction(function () use ($medal) {
do {
$deleted = UserMedal::query()->where('medal_id', $medal->id)->limit(10000)->delete();
} while ($deleted > 0);
$medal->delete();
});
return true;
}
}

View File

@@ -30,14 +30,15 @@ class UserRepository extends BaseRepository
public function getDetail($id)
{
$with = [
'inviter' => function ($query) {return $query->select(User::$commonFields);}
'inviter' => function ($query) {return $query->select(User::$commonFields);},
'valid_medals'
];
$user = User::query()->with($with)->findOrFail($id, User::$commonFields);
$userResource = new UserResource($user);
$baseInfo = $userResource->response()->getData(true)['data'];
$examRep = new ExamRepository();
$examProgress = $examRep->getUserExamProgress($id, null, ['exam']);
$examProgress = $examRep->getUserExamProgress($id, null);
if ($examProgress) {
$examResource = new ExamUserResource($examProgress);
$examInfo = $examResource->response()->getData(true)['data'];
@@ -45,6 +46,8 @@ class UserRepository extends BaseRepository
$examInfo = null;
}
return [
'base_info' => $baseInfo,
'exam_info' => $examInfo,
@@ -156,4 +159,7 @@ class UserRepository extends BaseRepository
$user = User::query()->findOrFail($id, ['modcomment']);
return $user->modcomment;
}
}

View File

@@ -0,0 +1,38 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateMedalsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('medals', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->integer('get_type');
$table->text('description')->nullable();
$table->string('image_large')->nullable();
$table->string('image_small')->nullable();
$table->integer('price')->default(0);
$table->integer('duration')->default(0);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('medals');
}
}

View File

@@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateUserMedalsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('user_medals', function (Blueprint $table) {
$table->id();
$table->integer('uid')->index();
$table->integer('medal_id')->index();
$table->dateTime('expire_at')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('user_medals');
}
}

View File

@@ -1,6 +1,6 @@
<?php
defined('VERSION_NUMBER') || define('VERSION_NUMBER', '1.6.0-beta12');
defined('RELEASE_DATE') || define('RELEASE_DATE', '2020-06-23');
defined('RELEASE_DATE') || define('RELEASE_DATE', '2021-06-23');
defined('IN_TRACKER') || define('IN_TRACKER', true);
defined('PROJECTNAME') || define("PROJECTNAME","NexusPHP");
defined('NEXUSPHPURL') || define("NEXUSPHPURL","https://nexusphp.org");

View File

@@ -2920,9 +2920,16 @@ function commenttable($rows, $type, $parent_id, $review = false)
$count = 0;
if ($Advertisement->enable_ad())
$commentad = $Advertisement->get_ad('comment');
$uidArr = array_unique(array_column($rows, 'user'));
$neededColumns = array('id', 'noad', 'class', 'enabled', 'privacy', 'avatar', 'signature', 'uploaded', 'downloaded', 'last_access', 'username', 'donor', 'leechwarn', 'warned', 'title');
$userInfoArr = \App\Models\User::query()->with(['valid_medals'])->find($uidArr, $neededColumns)->keyBy('id');
foreach ($rows as $row)
{
$userRow = get_user_row($row['user']);
// $userRow = get_user_row($row['user']);
$userInfo = $userInfoArr->get($row['user']);
$userRow = $userInfo->toArray();
if ($count>=1)
{
if ($Advertisement->enable_ad()){
@@ -2931,7 +2938,7 @@ function commenttable($rows, $type, $parent_id, $review = false)
}
}
print("<div style=\"margin-top: 8pt; margin-bottom: 8pt;\"><table id=\"cid".$row["id"]."\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\" width=\"100%\"><tr><td class=\"embedded\" width=\"99%\">#" . $row["id"] . "&nbsp;&nbsp;<font color=\"gray\">".$lang_functions['text_by']."</font>");
print(get_username($row["user"],false,true,true,false,false,true));
print(build_medal_image($userInfo->valid_medals, 20) . get_username($row["user"],false,true,true,false,false,true));
print("&nbsp;&nbsp;<font color=\"gray\">".$lang_functions['text_at']."</font>".gettime($row["added"]).
($row["editedby"] && get_user_class() >= $commanage_class ? " - [<a href=\"comment.php?action=vieworiginal&amp;cid=".$row['id']."&amp;type=".$type."\">".$lang_functions['text_view_original']."</a>]" : "") . "</td><td class=\"embedded nowrap\" width=\"1%\"><a href=\"#top\"><img class=\"top\" src=\"pic/trans.gif\" alt=\"Top\" title=\"Top\" /></a>&nbsp;&nbsp;</td></tr></table></div>");
$avatar = ($CURUSER["avatars"] == "yes" ? htmlspecialchars(trim($userRow["avatar"])) : "");
@@ -5259,4 +5266,13 @@ function msgalert($url, $text, $bgcolor = "red")
print("</td></tr></table></p><br />");
}
function build_medal_image(\Illuminate\Support\Collection $medals, $maxHeight = 200): string
{
$medalImages = [];
foreach ($medals as $medal) {
$medalImages[] = sprintf('<img src="%s" title="%s" style="max-height: %spx"/>', $medal->image_large, $medal->name, $maxHeight);
}
return implode('', $medalImages);
}
?>

View File

@@ -126,6 +126,7 @@ $lang_mybonus = array
'text_cancel_hr_title' => 'H&R 消除',
'text_cancel_hr_label' => '输入要消除的 H&R ID',
'text_success_cancel_hr' => "成功消除了一个 H&R。",
'text_success_buy_medal' => '成功购买该勋章。',
);
?>

View File

@@ -147,5 +147,6 @@ $lang_userdetails = array
'text_no_ad_until_note' => "时间格式为'年年年年-月月-日日 时时:分分:秒秒'。无广告待遇结束的时间。",
'disable_user_migrated' => '启用或禁用用户请到管理后台操作',
'text_user_id'=> "用户ID/UID",
'row_medal' => '勋章',
);
?>

View File

@@ -126,6 +126,7 @@ $lang_mybonus = array
'text_cancel_hr_title' => 'H&R 消除',
'text_cancel_hr_label' => '輸入要消除的 H&R ID',
'text_success_cancel_hr' => "成功消除了一個 H&R。",
'text_success_buy_medal' => '成功購買該勛章。',
);
?>

View File

@@ -147,5 +147,6 @@ $lang_userdetails = array
'text_no_ad_until_note' => "時間格式為'年年年年-月月-日日 時時:分分:秒秒'。無廣告待遇結束的時間。",
'disable_user_migrated' => '啟用或禁用用戶請到管理後臺操作',
'text_user_id'=> "用戶ID/UID",
'row_medal' => '勛章',
);
?>

View File

@@ -126,6 +126,7 @@ where<ul><li><b>A</b> is an intermediate variable</li><li><b>Ti</b> is the <b>i<
'text_cancel_hr_title' => 'H&R cancel',
'text_cancel_hr_label' => 'Type in H&R ID:',
'text_success_cancel_hr' => "Success cancel one H&R.",
'text_success_buy_medal' => 'Success buy the medal.',
);
?>

View File

@@ -147,5 +147,6 @@ $lang_userdetails = array
'text_no_ad_until_note' => "Time format is YYYY-MM-DD hh:mm:ss. The time until when the user can turn ads off.",
'disable_user_migrated' => 'Enable or disable use please go to the new management system.',
'text_user_id'=> "User ID",
'row_medal' => 'Medal',
);
?>

View File

@@ -3,6 +3,8 @@
namespace Nexus\Database;
use Illuminate\Database\Capsule\Manager as Capsule;
use Illuminate\Database\Query\Expression;
use Illuminate\Support\Facades\DB;
class NexusDB
{
@@ -227,17 +229,34 @@ class NexusDB
public static function schema(): \Illuminate\Database\Schema\Builder
{
return Capsule::schema(self::ELOQUENT_CONNECTION_NAME);
if (IN_NEXUS) {
return Capsule::schema(self::ELOQUENT_CONNECTION_NAME);
}
throw new \RuntimeException('can not call this when not in nexus.');
}
public static function table($table): \Illuminate\Database\Query\Builder
{
return Capsule::table($table);
if (IN_NEXUS) {
return Capsule::table($table);
}
return DB::table($table);
}
public static function raw($value): \Illuminate\Database\Query\Expression
{
if (IN_NEXUS) {
return new Expression($value);
}
return DB::raw($value);
}
public static function transaction(\Closure $callback, $attempts = 1)
{
return Capsule::connection(self::ELOQUENT_CONNECTION_NAME)->transaction($callback, $attempts);
if (IN_NEXUS) {
return Capsule::connection(self::ELOQUENT_CONNECTION_NAME)->transaction($callback, $attempts);
}
return DB::transaction($callback, $attempts);
}
public static function getMysqlColumnInfo($table, $column)

View File

@@ -38,6 +38,7 @@ if (!$row) {
) {
permissiondenied();
} else {
$owner = \App\Models\User::query()->with(['valid_medals'])->findOrFail($row['owner']);
$torrentRep = new \App\Repositories\TorrentRepository();
$torrentUpdate = [];
if (!empty($_GET["hit"])) {
@@ -83,10 +84,10 @@ if (!$row) {
if (get_user_class() < $viewanonymous_class)
$uprow = "<i>".$lang_details['text_anonymous']."</i>";
else
$uprow = "<i>".$lang_details['text_anonymous']."</i> (" . get_username($row['owner'], false, true, true, false, false, true) . ")";
$uprow = "<i>".$lang_details['text_anonymous']."</i> (" . build_medal_image($owner->valid_medals, 20) . get_username($row['owner'], false, true, true, false, false, true) . ")";
}
else {
$uprow = (isset($row['owner']) ? get_username($row['owner'], false, true, true, false, false, true) : "<i>".$lang_details['text_unknown']."</i>");
$uprow = (isset($row['owner']) ? build_medal_image($owner->valid_medals, 20) . get_username($row['owner'], false, true, true, false, false, true) : "<i>".$lang_details['text_unknown']."</i>");
}
if ($CURUSER["id"] == $row["owner"])

View File

@@ -624,13 +624,22 @@ if ($action == "viewtopic")
begin_frame();
$pc = mysql_num_rows($res);
$allPosts = $uidArr = [];
while ($arr = mysql_fetch_assoc($res)) {
$allPosts[] = $arr;
$uidArr[$arr['userid']] = 1;
}
$uidArr = array_keys($uidArr);
unset($arr);
$neededColumns = array('id', 'noad', 'class', 'enabled', 'privacy', 'avatar', 'signature', 'uploaded', 'downloaded', 'last_access', 'username', 'donor', 'leechwarn', 'warned', 'title');
$userInfoArr = \App\Models\User::query()->with(['valid_medals'])->find($uidArr, $neededColumns)->keyBy('id');
$pn = 0;
$lpr = get_last_read_post_id($topicid);
if ($Advertisement->enable_ad())
$forumpostad=$Advertisement->get_ad('forumpost');
while ($arr = mysql_fetch_assoc($res))
foreach ($allPosts as $arr)
{
if ($pn>=1)
{
@@ -648,7 +657,10 @@ if ($action == "viewtopic")
//---- Get poster details
$arr2 = get_user_row($posterid);
// $arr2 = get_user_row($posterid);
$userInfo = $userInfoArr->get($posterid);
$arr2 = $userInfo->toArray();
$uploaded = mksize($arr2["uploaded"]);
$downloaded = mksize($arr2["downloaded"]);
$ratio = get_ratio($arr2['id']);
@@ -662,7 +674,7 @@ if ($action == "viewtopic")
$avatar = ($CURUSER["avatars"] == "yes" ? htmlspecialchars($arr2["avatar"]) : "");
$uclass = get_user_class_image($arr2["class"]);
$by = get_username($posterid,false,true,true,false,false,true);
$by = build_medal_image($userInfo->valid_medals, 20) . get_username($posterid,false,true,true,false,false,true);
if (!$avatar)
$avatar = "pic/default_avatar.png";

View File

@@ -6,104 +6,223 @@ require(get_langfile_path("",true));
loggedinorreturn();
parked();
function bonusarray($option){
function bonusarray($option = 0){
global $onegbupload_bonus,$fivegbupload_bonus,$tengbupload_bonus,$oneinvite_bonus,$customtitle_bonus,$vipstatus_bonus, $basictax_bonus, $taxpercentage_bonus, $bonusnoadpoint_advertisement, $bonusnoadtime_advertisement;
global $lang_mybonus;
$bonus = array();
switch ($option)
{
case 1: {//1.0 GB Uploaded
$bonus['points'] = $onegbupload_bonus;
$bonus['art'] = 'traffic';
$bonus['menge'] = 1073741824;
$bonus['name'] = $lang_mybonus['text_uploaded_one'];
$bonus['description'] = $lang_mybonus['text_uploaded_note'];
break;
}
case 2: {//5.0 GB Uploaded
$bonus['points'] = $fivegbupload_bonus;
$bonus['art'] = 'traffic';
$bonus['menge'] = 5368709120;
$bonus['name'] = $lang_mybonus['text_uploaded_two'];
$bonus['description'] = $lang_mybonus['text_uploaded_note'];
break;
}
case 3: {//10.0 GB Uploaded
$bonus['points'] = $tengbupload_bonus;
$bonus['art'] = 'traffic';
$bonus['menge'] = 10737418240;
$bonus['name'] = $lang_mybonus['text_uploaded_three'];
$bonus['description'] = $lang_mybonus['text_uploaded_note'];
break;
}
case 4: {//Invite
$bonus['points'] = $oneinvite_bonus;
$bonus['art'] = 'invite';
$bonus['menge'] = 1;
$bonus['name'] = $lang_mybonus['text_buy_invite'];
$bonus['description'] = $lang_mybonus['text_buy_invite_note'];
break;
}
case 5: {//Custom Title
$bonus['points'] = $customtitle_bonus;
$bonus['art'] = 'title';
$bonus['menge'] = 0;
$bonus['name'] = $lang_mybonus['text_custom_title'];
$bonus['description'] = $lang_mybonus['text_custom_title_note'];
break;
}
case 6: {//VIP Status
$bonus['points'] = $vipstatus_bonus;
$bonus['art'] = 'class';
$bonus['menge'] = 0;
$bonus['name'] = $lang_mybonus['text_vip_status'];
$bonus['description'] = $lang_mybonus['text_vip_status_note'];
break;
}
case 7: {//Bonus Gift
$bonus['points'] = 25;
$bonus['art'] = 'gift_1';
$bonus['menge'] = 0;
$bonus['name'] = $lang_mybonus['text_bonus_gift'];
$bonus['description'] = $lang_mybonus['text_bonus_gift_note'];
if ($basictax_bonus || $taxpercentage_bonus){
$onehundredaftertax = 100 - $taxpercentage_bonus - $basictax_bonus;
$bonus['description'] .= "<br /><br />".$lang_mybonus['text_system_charges_receiver']."<b>".($basictax_bonus ? $basictax_bonus.$lang_mybonus['text_tax_bonus_point'].add_s($basictax_bonus).($taxpercentage_bonus ? $lang_mybonus['text_tax_plus'] : "") : "").($taxpercentage_bonus ? $taxpercentage_bonus.$lang_mybonus['text_percent_of_transfered_amount'] : "")."</b>".$lang_mybonus['text_as_tax'].$onehundredaftertax.$lang_mybonus['text_tax_example_note'];
}
break;
}
case 8: {
$bonus['points'] = $bonusnoadpoint_advertisement;
$bonus['art'] = 'noad';
$bonus['menge'] = $bonusnoadtime_advertisement * 86400;
$bonus['name'] = $bonusnoadtime_advertisement.$lang_mybonus['text_no_advertisements'];
$bonus['description'] = $lang_mybonus['text_no_advertisements_note'];
break;
}
case 9: {
$bonus['points'] = 1000;
$bonus['art'] = 'gift_2';
$bonus['menge'] = 0;
$bonus['name'] = $lang_mybonus['text_charity_giving'];
$bonus['description'] = $lang_mybonus['text_charity_giving_note'];
break;
}
case 10: {
$bonus['points'] = \App\Models\BonusLogs::getBonusForCancelHitAndRun();
$bonus['art'] = 'cancel_hr';
$bonus['menge'] = 0;
$bonus['name'] = $lang_mybonus['text_cancel_hr_title'];
$bonus['description'] = '<p>
$results = [];
//1.0 GB Uploaded
$bonus = array();
$bonus['points'] = $onegbupload_bonus;
$bonus['art'] = 'traffic';
$bonus['menge'] = 1073741824;
$bonus['name'] = $lang_mybonus['text_uploaded_one'];
$bonus['description'] = $lang_mybonus['text_uploaded_note'];
$results[] = $bonus;
//5.0 GB Uploaded
$bonus = array();
$bonus['points'] = $fivegbupload_bonus;
$bonus['art'] = 'traffic';
$bonus['menge'] = 5368709120;
$bonus['name'] = $lang_mybonus['text_uploaded_two'];
$bonus['description'] = $lang_mybonus['text_uploaded_note'];
$results[] = $bonus;
//10.0 GB Uploaded
$bonus = array();
$bonus['points'] = $tengbupload_bonus;
$bonus['art'] = 'traffic';
$bonus['menge'] = 10737418240;
$bonus['name'] = $lang_mybonus['text_uploaded_three'];
$bonus['description'] = $lang_mybonus['text_uploaded_note'];
$results[] = $bonus;
//Invite
$bonus = array();
$bonus['points'] = $oneinvite_bonus;
$bonus['art'] = 'invite';
$bonus['menge'] = 1;
$bonus['name'] = $lang_mybonus['text_buy_invite'];
$bonus['description'] = $lang_mybonus['text_buy_invite_note'];
//Custom Title
$bonus = array();
$bonus['points'] = $customtitle_bonus;
$bonus['art'] = 'title';
$bonus['menge'] = 0;
$bonus['name'] = $lang_mybonus['text_custom_title'];
$bonus['description'] = $lang_mybonus['text_custom_title_note'];
$results[] = $bonus;
//VIP Status
$bonus = array();
$bonus['points'] = $vipstatus_bonus;
$bonus['art'] = 'class';
$bonus['menge'] = 0;
$bonus['name'] = $lang_mybonus['text_vip_status'];
$bonus['description'] = $lang_mybonus['text_vip_status_note'];
$results[] = $bonus;
//Bonus Gift
$bonus = array();
$bonus['points'] = 25;
$bonus['art'] = 'gift_1';
$bonus['menge'] = 0;
$bonus['name'] = $lang_mybonus['text_bonus_gift'];
$bonus['description'] = $lang_mybonus['text_bonus_gift_note'];
if ($basictax_bonus || $taxpercentage_bonus){
$onehundredaftertax = 100 - $taxpercentage_bonus - $basictax_bonus;
$bonus['description'] .= "<br /><br />".$lang_mybonus['text_system_charges_receiver']."<b>".($basictax_bonus ? $basictax_bonus.$lang_mybonus['text_tax_bonus_point'].add_s($basictax_bonus).($taxpercentage_bonus ? $lang_mybonus['text_tax_plus'] : "") : "").($taxpercentage_bonus ? $taxpercentage_bonus.$lang_mybonus['text_percent_of_transfered_amount'] : "")."</b>".$lang_mybonus['text_as_tax'].$onehundredaftertax.$lang_mybonus['text_tax_example_note'];
}
$results[] = $bonus;
//No ad for 15 days
$bonus = array();
$bonus['points'] = $bonusnoadpoint_advertisement;
$bonus['art'] = 'noad';
$bonus['menge'] = $bonusnoadtime_advertisement * 86400;
$bonus['name'] = $bonusnoadtime_advertisement.$lang_mybonus['text_no_advertisements'];
$bonus['description'] = $lang_mybonus['text_no_advertisements_note'];
$results[] = $bonus;
//Donate
$bonus = array();
$bonus['points'] = 1000;
$bonus['art'] = 'gift_2';
$bonus['menge'] = 0;
$bonus['name'] = $lang_mybonus['text_charity_giving'];
$bonus['description'] = $lang_mybonus['text_charity_giving_note'];
$results[] = $bonus;
//Cancel hit and run
$bonus = array();
$bonus['points'] = \App\Models\BonusLogs::getBonusForCancelHitAndRun();
$bonus['art'] = 'cancel_hr';
$bonus['menge'] = 0;
$bonus['name'] = $lang_mybonus['text_cancel_hr_title'];
$bonus['description'] = '<p>
<span style="">' . $lang_mybonus['text_cancel_hr_label'] . '</span>
<input type="number" name="hr_id" />
</p>';
break;
}
default: break;
}
return $bonus;
$results[] = $bonus;
//Buy medal
$medals = \App\Models\Medal::query()->get();
foreach ($medals as $medal) {
$results[] = [
'points' => $medal->price,
'art' => 'buy_medal',
'menge' => 0,
'name' => $medal->name,
'description' => sprintf('<span>%s</span><input type="hidden" name="medal_id" value="%s">', $medal->description, $medal->id),
'medal_id' => $medal->id,
];
}
return $results;
//
// switch ($option)
// {
// case 1: {//1.0 GB Uploaded
// $bonus['points'] = $onegbupload_bonus;
// $bonus['art'] = 'traffic';
// $bonus['menge'] = 1073741824;
// $bonus['name'] = $lang_mybonus['text_uploaded_one'];
// $bonus['description'] = $lang_mybonus['text_uploaded_note'];
// break;
// }
// case 2: {//5.0 GB Uploaded
// $bonus['points'] = $fivegbupload_bonus;
// $bonus['art'] = 'traffic';
// $bonus['menge'] = 5368709120;
// $bonus['name'] = $lang_mybonus['text_uploaded_two'];
// $bonus['description'] = $lang_mybonus['text_uploaded_note'];
// break;
// }
// case 3: {//10.0 GB Uploaded
// $bonus['points'] = $tengbupload_bonus;
// $bonus['art'] = 'traffic';
// $bonus['menge'] = 10737418240;
// $bonus['name'] = $lang_mybonus['text_uploaded_three'];
// $bonus['description'] = $lang_mybonus['text_uploaded_note'];
// break;
// }
// case 4: {//Invite
// $bonus['points'] = $oneinvite_bonus;
// $bonus['art'] = 'invite';
// $bonus['menge'] = 1;
// $bonus['name'] = $lang_mybonus['text_buy_invite'];
// $bonus['description'] = $lang_mybonus['text_buy_invite_note'];
// break;
// }
// case 5: {//Custom Title
// $bonus['points'] = $customtitle_bonus;
// $bonus['art'] = 'title';
// $bonus['menge'] = 0;
// $bonus['name'] = $lang_mybonus['text_custom_title'];
// $bonus['description'] = $lang_mybonus['text_custom_title_note'];
// break;
// }
// case 6: {//VIP Status
// $bonus['points'] = $vipstatus_bonus;
// $bonus['art'] = 'class';
// $bonus['menge'] = 0;
// $bonus['name'] = $lang_mybonus['text_vip_status'];
// $bonus['description'] = $lang_mybonus['text_vip_status_note'];
// break;
// }
// case 7: {//Bonus Gift
// $bonus['points'] = 25;
// $bonus['art'] = 'gift_1';
// $bonus['menge'] = 0;
// $bonus['name'] = $lang_mybonus['text_bonus_gift'];
// $bonus['description'] = $lang_mybonus['text_bonus_gift_note'];
// if ($basictax_bonus || $taxpercentage_bonus){
// $onehundredaftertax = 100 - $taxpercentage_bonus - $basictax_bonus;
// $bonus['description'] .= "<br /><br />".$lang_mybonus['text_system_charges_receiver']."<b>".($basictax_bonus ? $basictax_bonus.$lang_mybonus['text_tax_bonus_point'].add_s($basictax_bonus).($taxpercentage_bonus ? $lang_mybonus['text_tax_plus'] : "") : "").($taxpercentage_bonus ? $taxpercentage_bonus.$lang_mybonus['text_percent_of_transfered_amount'] : "")."</b>".$lang_mybonus['text_as_tax'].$onehundredaftertax.$lang_mybonus['text_tax_example_note'];
// }
// break;
// }
// case 8: {
// $bonus['points'] = $bonusnoadpoint_advertisement;
// $bonus['art'] = 'noad';
// $bonus['menge'] = $bonusnoadtime_advertisement * 86400;
// $bonus['name'] = $bonusnoadtime_advertisement.$lang_mybonus['text_no_advertisements'];
// $bonus['description'] = $lang_mybonus['text_no_advertisements_note'];
// break;
// }
// case 9: {
// $bonus['points'] = 1000;
// $bonus['art'] = 'gift_2';
// $bonus['menge'] = 0;
// $bonus['name'] = $lang_mybonus['text_charity_giving'];
// $bonus['description'] = $lang_mybonus['text_charity_giving_note'];
// break;
// }
// case 10: {
// $bonus['points'] = \App\Models\BonusLogs::getBonusForCancelHitAndRun();
// $bonus['art'] = 'cancel_hr';
// $bonus['menge'] = 0;
// $bonus['name'] = $lang_mybonus['text_cancel_hr_title'];
// $bonus['description'] = '<p>
// <span style="">' . $lang_mybonus['text_cancel_hr_label'] . '</span>
// <input type="number" name="hr_id" />
// </p>';
// break;
// }
// default: break;
// }
// return $bonus;
}
$allBonus = bonusarray();
if ($bonus_tweak == "disable" || $bonus_tweak == "disablesave")
stderr($lang_mybonus['std_sorry'],$lang_mybonus['std_karma_system_disabled'].($bonus_tweak == "disablesave" ? "<b>".$lang_mybonus['std_points_active']."</b>" : ""),false);
@@ -129,6 +248,8 @@ if (isset($do)) {
$msg = $lang_mybonus['text_success_charity'];
elseif ($do == "cancel_hr")
$msg = $lang_mybonus['text_success_cancel_hr'];
elseif ($do == "buy_medal")
$msg = $lang_mybonus['text_success_buy_medal'];
else
$msg = '';
}
@@ -150,42 +271,44 @@ print("<tr><td class=\"colhead\" align=\"center\">".$lang_mybonus['col_option'].
"<td class=\"colhead\" align=\"center\">".$lang_mybonus['col_points']."</td>".
"<td class=\"colhead\" align=\"center\">".$lang_mybonus['col_trade']."</td>".
"</tr>");
for ($i=1; $i <=10; $i++)
for ($i=0; $i < count($allBonus); $i++)
{
$bonusarray = bonusarray($i);
$bonusarray = $allBonus[$i];
if (
($i == 7 && $bonusgift_bonus == 'no')
|| ($i == 8 && ($enablead_advertisement == 'no' || $bonusnoad_advertisement == 'no'))
|| ($i == 10 && !\App\Models\HitAndRun::getIsEnabled())
($bonusarray['art'] == 'gift_1' && $bonusgift_bonus == 'no')
|| ($bonusarray['art'] == 'noad' && ($enablead_advertisement == 'no' || $bonusnoad_advertisement == 'no'))
|| ($bonusarray['art'] == 'cancel_hr' && !\App\Models\HitAndRun::getIsEnabled())
) {
continue;
}
print("<tr>");
print("<form action=\"?action=exchange\" method=\"post\">");
print("<td class=\"rowhead_center\"><input type=\"hidden\" name=\"option\" value=\"".$i."\" /><b>".$i."</b></td>");
if ($i==5){ //for Custom Title!
$otheroption_title = "<input type=\"text\" name=\"title\" style=\"width: 200px\" maxlength=\"30\" />";
print("<td class=\"rowfollow\" align='left'><h1>".$bonusarray['name']."</h1>".$bonusarray['description']."<br /><br />".$lang_mybonus['text_enter_titile'].$otheroption_title.$lang_mybonus['text_click_exchange']."</td><td class=\"rowfollow\" align='center'>".number_format($bonusarray['points'])."</td>");
print("<td class=\"rowhead_center\"><input type=\"hidden\" name=\"option\" value=\"".$i."\" /><b>".($i + 1)."</b></td>");
if ($bonusarray['art'] == 'invite'){ //for Custom Title!
$otheroption_title = "<input type=\"text\" name=\"title\" style=\"width: 200px\" maxlength=\"30\" />";
print("<td class=\"rowfollow\" align='left'><h1>".$bonusarray['name']."</h1>".$bonusarray['description']."<br /><br />".$lang_mybonus['text_enter_titile'].$otheroption_title.$lang_mybonus['text_click_exchange']."</td><td class=\"rowfollow\" align='center'>".number_format($bonusarray['points'])."</td>");
}
elseif ($i==7){ //for Give A Karma Gift
elseif ($bonusarray['art'] == 'gift_1'){ //for Give A Karma Gift
$otheroption = "<table width=\"100%\"><tr><td class=\"embedded\"><b>".$lang_mybonus['text_username']."</b><input type=\"text\" name=\"username\" style=\"width: 200px\" maxlength=\"24\" /></td><td class=\"embedded\"><b>".$lang_mybonus['text_to_be_given']."</b><select name=\"bonusgift\" id=\"giftselect\" onchange=\"customgift();\"> <option value=\"25\"> 25</option><option value=\"50\"> 50</option><option value=\"100\"> 100</option> <option value=\"200\"> 200</option> <option value=\"300\"> 300</option> <option value=\"400\"> 400</option><option value=\"500\"> 500</option><option value=\"1000\" selected=\"selected\"> 1,000</option><option value=\"5000\"> 5,000</option><option value=\"10000\"> 10,000</option><option value=\"0\">".$lang_mybonus['text_custom']."</option></select><input type=\"text\" name=\"bonusgift\" id=\"giftcustom\" style='width: 80px' disabled=\"disabled\" />".$lang_mybonus['text_karma_points']."</td></tr><tr><td class=\"embedded\" colspan=\"2\"><b>".$lang_mybonus['text_message']."</b><input type=\"text\" name=\"message\" style=\"width: 400px\" maxlength=\"100\" /></td></tr></table>";
print("<td class=\"rowfollow\" align='left'><h1>".$bonusarray['name']."</h1>".$bonusarray['description']."<br /><br />".$lang_mybonus['text_enter_receiver_name']."<br />$otheroption</td><td class=\"rowfollow nowrap\" align='center'>".$lang_mybonus['text_min']."25<br />".$lang_mybonus['text_max']."10,000</td>");
}
elseif ($i==9){ //charity giving
elseif ($bonusarray['art'] == 'gift_2'){ //charity giving
$otheroption = "<table width=\"100%\"><tr><td class=\"embedded\">".$lang_mybonus['text_ratio_below']."<select name=\"ratiocharity\"> <option value=\"0.1\"> 0.1</option><option value=\"0.2\"> 0.2</option><option value=\"0.3\" selected=\"selected\"> 0.3</option> <option value=\"0.4\"> 0.4</option> <option value=\"0.5\"> 0.5</option> <option value=\"0.6\"> 0.6</option><option value=\"0.7\"> 0.7</option><option value=\"0.8\"> 0.8</option></select>".$lang_mybonus['text_and_downloaded_above']." 10 GB</td><td class=\"embedded\"><b>".$lang_mybonus['text_to_be_given']."</b><select name=\"bonuscharity\" id=\"charityselect\" > <option value=\"1000\"> 1,000</option><option value=\"2000\"> 2,000</option><option value=\"3000\" selected=\"selected\"> 3000</option> <option value=\"5000\"> 5,000</option> <option value=\"8000\"> 8,000</option> <option value=\"10000\"> 10,000</option><option value=\"20000\"> 20,000</option><option value=\"50000\"> 50,000</option></select>".$lang_mybonus['text_karma_points']."</td></tr></table>";
print("<td class=\"rowfollow\" align='left'><h1>".$bonusarray['name']."</h1>".$bonusarray['description']."<br /><br />".$lang_mybonus['text_select_receiver_ratio']."<br />$otheroption</td><td class=\"rowfollow nowrap\" align='center'>".$lang_mybonus['text_min']."1,000<br />".$lang_mybonus['text_max']."50,000</td>");
}
else{ //for VIP or Upload
else { //for VIP or Upload
print("<td class=\"rowfollow\" align='left'><h1>".$bonusarray['name']."</h1>".$bonusarray['description']."</td><td class=\"rowfollow\" align='center'>".number_format($bonusarray['points'])."</td>");
}
if($CURUSER['seedbonus'] >= $bonusarray['points'])
{
if ($i==7){
if ($bonusarray['art'] == 'gift_1'){
print("<td class=\"rowfollow\" align=\"center\"><input type=\"submit\" name=\"submit\" value=\"".$lang_mybonus['submit_karma_gift']."\" /></td>");
}
elseif ($i==8){
elseif ($bonusarray['art'] == 'noad'){
if ($enablenoad_advertisement == 'yes' && get_user_class() >= $noad_advertisement)
print("<td class=\"rowfollow\" align=\"center\"><input type=\"submit\" name=\"submit\" value=\"".$lang_mybonus['submit_class_above_no_ad']."\" disabled=\"disabled\" /></td>");
elseif (strtotime($CURUSER['noaduntil']) >= TIMENOW)
@@ -195,26 +318,26 @@ for ($i=1; $i <=10; $i++)
else
print("<td class=\"rowfollow\" align=\"center\"><input type=\"submit\" name=\"submit\" value=\"".$lang_mybonus['submit_exchange']."\" /></td>");
}
elseif ($i==9){
elseif ($bonusarray['art'] == 'gift_2'){
print("<td class=\"rowfollow\" align=\"center\"><input type=\"submit\" name=\"submit\" value=\"".$lang_mybonus['submit_charity_giving']."\" /></td>");
}
elseif($i==4)
elseif($bonusarray['art'] == 'invite')
{
if(get_user_class() < $buyinvite_class)
print("<td class=\"rowfollow\" align=\"center\"><input type=\"submit\" name=\"submit\" value=\"".get_user_class_name($buyinvite_class,false,false,true).$lang_mybonus['text_plus_only']."\" disabled=\"disabled\" /></td>");
else
print("<td class=\"rowfollow\" align=\"center\"><input type=\"submit\" name=\"submit\" value=\"".$lang_mybonus['submit_exchange']."\" /></td>");
}
elseif ($i==6)
elseif ($bonusarray['art'] == 'class')
{
if (get_user_class() >= UC_VIP)
print("<td class=\"rowfollow\" align=\"center\"><input type=\"submit\" name=\"submit\" value=\"".$lang_mybonus['std_class_above_vip']."\" disabled=\"disabled\" /></td>");
else
print("<td class=\"rowfollow\" align=\"center\"><input type=\"submit\" name=\"submit\" value=\"".$lang_mybonus['submit_exchange']."\" /></td>");
}
elseif ($i==5)
elseif ($bonusarray['art'] == 'title')
print("<td class=\"rowfollow\" align=\"center\"><input type=\"submit\" name=\"submit\" value=\"".$lang_mybonus['submit_exchange']."\" /></td>");
else
elseif ($bonusarray['art'] == 'traffic')
{
if ($CURUSER['downloaded'] > 0){
if ($CURUSER['uploaded'] > $dlamountlimit_bonus * 1073741824)//Uploaded amount reach limit
@@ -226,7 +349,9 @@ for ($i=1; $i <=10; $i++)
print("<td class=\"rowfollow\" align=\"center\"><input type=\"submit\" name=\"submit\" value=\"".$lang_mybonus['text_ratio_too_high']."\" disabled=\"disabled\" /></td>");
}
else print("<td class=\"rowfollow\" align=\"center\"><input type=\"submit\" name=\"submit\" value=\"".$lang_mybonus['submit_exchange']."\" /></td>");
}
} else {
print("<td class=\"rowfollow\" align=\"center\"><input type=\"submit\" name=\"submit\" value=\"".$lang_mybonus['submit_exchange']."\" /></td>");
}
}
else
{
@@ -323,15 +448,14 @@ print($lang_mybonus['text_howto_get_karma_five'].$uploadtorrent_bonus.$lang_mybo
<?php
}
// Bonus exchange
if ($action == "exchange") {
if (isset($_POST["userid"]) || isset($_POST["points"]) || isset($_POST["bonus"]) || isset($_POST["art"])){
if (isset($_POST["userid"]) || isset($_POST["points"]) || isset($_POST["bonus"]) || isset($_POST["art"]) || !isset($_POST['option']) || !isset($allBonus[$_POST['option']])){
write_log("User " . $CURUSER["username"] . "," . $CURUSER["ip"] . " is trying to cheat at bonus system",'mod');
die($lang_mybonus['text_cheat_alert']);
}
$option = intval($_POST["option"] ?? 0);
$bonusarray = bonusarray($option);
$bonusarray = $allBonus[$option];
$points = $bonusarray['points'];
$userid = $CURUSER['id'];
$art = $bonusarray['art'];
@@ -503,6 +627,18 @@ if ($action == "exchange") {
do_log($exception->getMessage(), 'error');
stderr('Error', "Something wrong...", false, false);
}
} elseif ($art == 'buy_medal') {
if (empty($_POST['medal_id'])) {
stderr("Error","Invalid Medal ID: " . ($_POST['medal_id'] ?? ''), false, false);
}
try {
$bonusRep = new \App\Repositories\BonusRepository();
$bonusRep->consumeToBuyMedal($userid, $_POST['medal_id']);
nexus_redirect("" . get_protocol_prefix() . "$BASEURL/mybonus.php?do=buy_medal");
} catch (\Exception $exception) {
do_log($exception->getMessage(), 'error');
stderr('Error', "Something wrong...", false, false);
}
}
}
}

View File

@@ -28,6 +28,8 @@ else
if ($user["status"] == "pending")
stderr($lang_userdetails['std_sorry'], $lang_userdetails['std_user_not_confirmed']);
$userInfo = \App\Models\User::query()->with(['valid_medals'])->findOrFail($user['id']);
if ($user['added'] == "0000-00-00 00:00:00" || $user['added'] == null)
$joindate = $lang_userdetails['text_not_available'];
else
@@ -271,6 +273,11 @@ tr_small($lang_userdetails['row_donated'], "$".htmlspecialchars($user['donated']
if ($user["avatar"])
tr_small($lang_userdetails['row_avatar'], return_avatar_image(htmlspecialchars(trim($user["avatar"]))), 1);
if ($userInfo->valid_medals->isNotEmpty()) {
tr_small($lang_userdetails['row_medal'], build_medal_image($userInfo->valid_medals), 1);
}
$uclass = get_user_class_image($user["class"]);
$utitle = get_user_class_name($user["class"],false,false,true);
$uclassImg = "<img alt=\"".get_user_class_name($user["class"],false,false,true)."\" title=\"".get_user_class_name($user["class"],false,false,true)."\" src=\"".$uclass."\" /> ".($user['title']!=="" ? "&nbsp;".htmlspecialchars(trim($user["title"]))."" : "");

View File

@@ -0,0 +1,9 @@
<?php
return [
'admin' => [
'list' => [
'page_title' => 'Medal list'
]
]
];

View File

@@ -0,0 +1,5 @@
<?php
return [
'comment_buy_medal' => '花费 :bonus 魔力购买了 :medal_name',
];

View File

@@ -0,0 +1,9 @@
<?php
return [
'admin' => [
'list' => [
'page_title' => '勋章列表'
]
]
];

View File

@@ -0,0 +1,9 @@
<?php
return [
'admin' => [
'list' => [
'page_title' => '勛章列表'
]
]
];

View File

@@ -60,6 +60,8 @@ Route::group(['middleware' => ['auth:sanctum', 'locale']], function () {
Route::get('dashboard/latest-torrent', [\App\Http\Controllers\DashboardController::class, 'latestTorrent']);
Route::resource('settings', \App\Http\Controllers\SettingController::class);
Route::resource('medals', \App\Http\Controllers\MedalController::class);
Route::resource('user-medals', \App\Http\Controllers\UserMedalController::class);
});
});