This commit is contained in:
2025-11-21 01:42:54 +08:00
parent ff026c6f32
commit f89196c73c
1953 changed files with 9 additions and 15246 deletions
+44
View File
@@ -0,0 +1,44 @@
<?php
namespace app\model;
use app\model\Base;
/**
* @property integer $id 主键(ID) - 无注释
* @property integer $user_id 用户ID
* @property string $title 名字
* @property string $network 网络
* @property string $address 账号
* @property string $img 图片
* @property integer $is_default 是否默认
* @property integer $created_at 创建时间
* @property integer $updated_at 更新时间
* @property integer $status 状态
*/
class Address extends Base
{
//protected $name = 'address';
function getNetworkList(){
return [
"BEP-20"=>"BEP-20",
"TRC-20"=>"TRC-20",
"WECHAT"=>"微信",
"ALIPAY"=>"支付宝"
];
}
function getStatusList(){
return [
'0' => '禁用',
'1' => '启用',
];
}
public function user()
{
return $this->belongsTo('User', 'user_id', 'id');//->setEagerlyType(0);
}
}
+93
View File
@@ -0,0 +1,93 @@
<?php
namespace app\model;
use support\think\Db;
use traits\model\SoftDelete;
class Archives extends Base
{
//use SoftDelete;// 表名
protected function getOptions(): array{
return array_merge(parent::getOptions(),[
'append' => [
'status_text'
],
]);
}
public static function onAfterInsert(Archives $row)
{
\support\Log::error(''. json_encode($row));
$pk = $row->getPk();
self::where($pk, $row->$pk)->update(['weigh' => $row[$pk]]);
$changedData = $row->getData();
if (isset($changedData['content'])) {
//在更新成功后刷新副表
$values = array_intersect_key($changedData, array_flip(['content']));
$values['id'] = $row['id'];
//更新副表
Db::name('content')->insert($values, true);
}
}
public static function onAfterUpdate($row)
{
\support\Log::info('onAfterUpdate'.$row->id. json_encode($row->getChangedData()));
$changedData = $row->getChangedData();
if (isset($changedData['content'])) {
//在更新成功后刷新副表
$values = array_intersect_key($row->getData(), array_flip(['content']));
//更新副表
Db::name('content')->where('id',$row->id)->update($values);
}
}
public static function onAfterDelete($row)
{
Db::name('content')->where('id',$row->id)->delete();
}
/**
* 批量设置数据
* @param $data
* @return $this
*/
public function setAddonData(string|array|object $data)
{
if (is_object($data)) {
$data = get_object_vars($data);
}
foreach($data as $k=>$v){
$this->$k=$v;
}
return $this;
}
public function getStatusList()
{
return ['1' => '正常', '0' => '隐藏'];
}
public function getStatusTextAttr($value, $data)
{
$value = $value ? $value : (isset($data['status']) ? $data['status'] : '');
$list = $this->getStatusList();
return isset($list[$value]) ? $list[$value] : '';
}
public function getCategoryOptions($type='default'){
return Category::where('status','1')->where('type',$type)->column('id,title');
}
function setCreatedAtAttr($v,$row=[]){
cp($v);
cp($row);
}
public function getFlagList()
{
return Config('site.flagtype');
}
public function category()
{
return $this->belongsTo('Category', 'category_id', 'id');//->setEagerlyType(0);
}
}
+233
View File
@@ -0,0 +1,233 @@
<?php
namespace app\model;
use Symfony\Component\Console\Input\Input;
use think\Model;
use think\facade\Db;
class BalanceLog extends Base
{
// 表结构定义(使用时间戳)
const TABLE_SCHEMA = [
'id' => 'int(11) NOT NULL AUTO_INCREMENT',
'user_id' => 'int(11) NOT NULL',
'currency' => 'varchar(20) NOT NULL',
'amount' => 'decimal(15,2) NOT NULL',
'before' => 'decimal(15,2) NOT NULL',
'after' => 'decimal(15,2) NOT NULL',
'type' => 'varchar(50) NOT NULL',
'created_at' => 'int(11) NOT NULL COMMENT \'UNIX timestamp\'', // 改为整型时间戳
'memo' => 'varchar(255) DEFAULT NULL',
'PRIMARY KEY (`id`)'
];
function getCreatedAtAttr($v){
return $v ? explode('.',$v)[0] : '';
}
protected function getOptions(): array{
return array_merge(parent::getOptions(),[
'connection' => 'mongodb',
// 'append' => [
// 'from_user',
// 'to_user'
// ],
]);
}
public static function create(array|object $data, array $allowField = [], bool $replace = false):\think\model\contract\Modelable
{
$model = new static();
if(isset($data['currency'])){
if(in_array($data['currency'],Config('site.allow_balance_log'))){
$data['status']=isset($data['status']) ? $data['status']:1;
$data['user_id'] = intval($data['user_id']);
$data['amount'] = floatval($data['amount']);
$data['before'] = floatval($data['before']);
$data['after'] = floatval($data['after']);
$data['type'] = $data['type'] instanceof \app\enum\BalanceType ? $data['type']->value : floatval($data['type']);
$model->setSuffix('_'.strtolower($data['currency']))->allowField($allowField)
->replace($replace)
->save($data, true);
}
}
return $model->fetchModel($model);
}
// 创建所有需要的表索引
public static function createAllIndexes(): array
{
$results = [];
$allow_balance_log = Config('site.allow_balance_log');
foreach ($allow_balance_log as $currency) {
$results[$currency] = self::createTableIndexes($currency);
}
return $results;
}
// 创建索引(适配时间戳查询)
public static function createTableIndexes(string $currency): array
{
$table = self::getTableName($currency);
$results = [];
try {
// 确保归档表存在
if (!self::tableExists($table)) {
self::createTableStructure($table);
}
// 主复合索引(使用时间戳)
if (!self::indexExists($table, 'idx_user_currency_type_created')) {
Db::execute("ALTER TABLE `{$table}` ADD INDEX `idx_user_currency_type_created` (`user_id`, `currency`, `type`, `created_at`)");
$results[] = "Created idx_user_currency_type_created on {$table}";
}
// 时间索引(降序优化)
if (!self::indexExists($table, 'idx_created_at')) {
Db::execute("ALTER TABLE `{$table}` ADD INDEX `idx_created_at` (`created_at` DESC)");
$results[] = "Created idx_created_at on {$table}";
}
} catch (\Throwable $e) {
$results['error'] = "Error on {$table}: " . $e->getMessage();
}
return $results;
}
// 检查索引是否存在
protected static function indexExists(string $table, string $indexName): bool
{
$indexes = Db::query("SHOW INDEX FROM `{$table}` WHERE Key_name = ?", [$indexName]);
return !empty($indexes);
}
// 检查表是否存在
protected static function tableExists(string $table): bool
{
try {
Db::query("SELECT 1 FROM `{$table}` LIMIT 1");
return true;
} catch (\Throwable $e) {
return false;
}
}
// 数据归档方法(可在定时任务中调用)
public static function archiveData(int $days = 3): array
{
$results = [];
$allow_balance_log = Config('site.allow_balance_log');
foreach ($allow_balance_log as $currency) {
$results[$currency] = self::archiveCurrencyData($currency, $days);
}
return $results;
}
// 归档指定货币的数据
protected static function archiveCurrencyData(string $currency, int $days): array
{
$table = self::getTableName($currency);
$archiveTable = $table . '_archive';
$cutoffTimestamp = time() - ($days * 86400); // 转为时间戳计算
$result = [
'table' => $table,
'archived' => 0,
'messages' => []
];
try {
// 确保归档表存在
if (!self::tableExists($archiveTable)) {
self::createTableStructure($archiveTable);
$result['messages'][] = "Created archive table: {$archiveTable}";
}
// 分批归档数据
$totalArchived = 0;
Db::table($table)
->where('created_at', '<=', $cutoffTimestamp)
->chunk(1000, function($logs) use ($archiveTable, $table, &$totalArchived) {
Db::table($archiveTable)->insertAll($logs);
$count = count($logs);
Db::table($table)->whereIn('id', array_column($logs, 'id'))->delete();
$totalArchived += $count;
});
$result['archived'] = $totalArchived;
$result['messages'][] = "Archived {$totalArchived} records from {$table}";
// 优化表
Db::execute("OPTIMIZE TABLE `{$table}`");
$result['messages'][] = "Optimized table: {$table}";
} catch (\Throwable $e) {
$result['error'] = $e->getMessage();
}
return $result;
}
// 查询方法(示例)
public static function queryLogs($userId, $currency, $type = null, $startTime = null, $endTime = null)
{
$model = new static;
$query = $model->setSuffix('_'.strtolower($currency))->where('currency', $currency)
->where('user_id', intval($userId))
->order('created_at', 'desc');
if ($type) {
if($type == '99999'){
$query->whereIn('type', [
\app\enum\BalanceType::OUTPUT_REWARD->value,
\app\enum\BalanceType::WITHDRAW_REWARD->value,
\app\enum\BalanceType::PRODUCT_INCOME->value,
\app\enum\BalanceType::AGENT_COMMISSION->value,
\app\enum\BalanceType::DIFFERENTIAL_COMMISSION->value
]);
}else{
$query->where('type', intval($type));
}
}
if ($startTime) {
// 支持传入时间戳或日期字符串
//$startTimestamp = is_numeric($startTime) ? intval($startTime) : strtotime($startTime);
$query->where('created_at', '>=', $startTime);
}
if ($endTime) {
// 支持传入时间戳或日期字符串
//$endTimestamp = is_numeric($endTime) ? intval($endTime) : strtotime($endTime);
$query->where('created_at', '<=', $endTime);
}
$limit = 10;
if(request()){
$limit = input('limit',10);
}
return $query->paginate($limit);
}
// 创建表结构
protected static function createTableStructure(string $table): bool
{
if (self::tableExists($table)) {
return false;
}
$columns = [];
foreach (self::TABLE_SCHEMA as $column => $definition) {
if (strpos($definition, 'PRIMARY KEY') === false) {
$columns[] = "`{$column}` {$definition}";
}
}
$primaryKey = self::TABLE_SCHEMA['PRIMARY KEY'] ?? 'PRIMARY KEY (`id`)';
$sql = "CREATE TABLE `{$table}` (" .
implode(', ', $columns) . ", " .
$primaryKey .
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci";
Db::execute($sql);
return true;
}
}
+51
View File
@@ -0,0 +1,51 @@
<?php
namespace app\model;
use DateTimeInterface;
use support\think\Model;
class Base extends Model
{
protected function getOptions(): array{
return [
'connection' => 'mysql',
'createTime' => 'created_at',
'updateTime' => 'updated_at',
'deleteTime' => 'deleted_at',
'autoWriteTimestamp' => 'int',
//'dateFormat' => false
// query 自定义数据库查询对象类名(默认为空)
// type 需要自动转换的字段及类型(数组,默认为空)
// autoValidate 是否自动验证(开启后会自动进行数据验证)
// validate 对应验证类名或验证规则(字符串或数组,autoValidate参数开启后有效)
// strict 是否严格区分字段大小写(默认为true)
// disuse 废弃字段(数组,默认为空)
// readonly 只读字段(数组,默认为空)
// hidden 输出隐藏字段(数组,默认为空)
// visible 输出显示字段(数组,默认为空)
// append 输出追加字段(数组,默认为空)
// mapping 字段映射(数组,默认为空)
// autoRelation 自动with关联(数组,默认为空)
// insert 自动新增写入(数组,默认为空)
// update 自动更新写入(数组,默认为空)
// dateFormat 时间输出格式化设置
];
}
/**
* 格式化日期
*
* @param DateTimeInterface $date
* @return string
*/
protected function serializeDate(DateTimeInterface $date)
{
return $date->format('Y-m-d H:i:s');
}
function getStatusList(){
return [
'0' => '隐藏',
'1' => '正常',
];
}
}
+21
View File
@@ -0,0 +1,21 @@
<?php
namespace app\model;
/**
* @property integer $id 主键(ID) - 无注释
* @property integer $user_id 用户ID
* @property float $amount 总价
* @property integer $type 类型
* @property string $title 标题
* @property integer $total 总数量
* @property integer $used 已使用的数量
* @property integer $expires 过期时间
* @property integer $days 量
* @property integer $created_at 创建时间
* @property integer $updated_at 更新时间
* @property integer $status 状态
*/
class Card extends Base
{
protected $name = 'card';
}
+23
View File
@@ -0,0 +1,23 @@
<?php
namespace app\model;
/**
* @property integer $id 主键(主键)
* @property string $username 用户名
* @property string $nickname 昵称
* @property string $password 密码
* @property string $created_at 创建时间
* @property string $updated_at 更新时间
* @property integer $status 禁用
*/
class Category extends Base
{
protected function getOptions(): array{
return array_merge(parent::getOptions(),[
'insert' => [
'status' => 1
],
]);
}
}
+26
View File
@@ -0,0 +1,26 @@
<?php
namespace app\model;
/**
* Cdkey模型
*
* @package app\model
*
* @property integer $id 主键(ID) - 无注释
* @property integer $type 标题
* @property integer $category_id 分类ID
* @property string $account cdkey
* @property string $password 密码
* @property integer $days 量
* @property integer $expires 过期时间
* @property integer $is_used 是否使用
* @property integer $record_id 使用记录
* @property integer $use_time 使用时间
* @property integer $created_at 创建时间
* @property integer $updated_at 更新时间
* @property integer $status 状态
*/
class Cdkey extends Base
{
protected $name = 'cdkey';
}
+14
View File
@@ -0,0 +1,14 @@
<?php
namespace app\model;
use app\model\Base;
/**
* @property integer $id 主键(ID) - 无注释
* @property string $content 无注释
* @property string $content1 无注释
* @property string $content2 无注释
*/
class Content extends Base
{
}
+107
View File
@@ -0,0 +1,107 @@
<?php
namespace app\model;
use app\model\Base;
/**
* @property integer $id 主键(ID) - 无注释
* @property string $title 标题
* @property string $image 封面
* @property string $amounts 面额列表
* @property integer $stock 库存
* @property integer $sales 銷售量
* @property integer $user_quantity 用户累计限购
* @property string $memo 备注
* @property integer $weight 权重
* @property integer $created_at 创建时间
* @property integer $updated_at 更新时间
* @property integer $status 状态
*/
/**
--
-- 表的结构 `wa_gift`
--
CREATE TABLE `wa_gift` (
`id` int NOT NULL,
`title` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '标题',
`image` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '封面',
`amounts` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '面额列表',
`stock` int DEFAULT '0' COMMENT '库存',
`sales` int DEFAULT '0' COMMENT '銷售量',
`user_quantity` int NOT NULL DEFAULT '0' COMMENT '用户累计限购',
`memo` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '备注',
`weight` int DEFAULT NULL COMMENT '权重',
`created_at` int DEFAULT NULL COMMENT '创建时间',
`updated_at` int DEFAULT NULL COMMENT '更新时间',
`status` tinyint DEFAULT NULL COMMENT '状态'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC;
--
-- 转存表中的数据 `wa_gift`
--
INSERT INTO `wa_gift` (`id`, `title`, `image`, `amounts`, `stock`, `sales`, `user_quantity`, `memo`, `weight`, `created_at`, `updated_at`, `status`) VALUES
(12, '京东礼品卡', '/upload/files/20250922/911fabdb0719edcbba3f0f7b84f59f85_68d09e2a8447e.png', '[\"1000\"]', 999, 405, 10, '', NULL, 1749809778, 1758502443, 1),
(13, '亚马逊电子礼品卡', '', '[1350.0]', 0, 530, 0, '', NULL, 1749809924, 1757263525, 1),
(14, '永辉电子礼品卡', '', '[3.5]', 0, 300, 0, '', NULL, 1749875587, 1757263532, 1),
(15, '盒马电子礼品卡', '', '[3.5]', 0, 300, 0, '', NULL, 1749953784, 1757263539, 1),
(42, '沃尔玛电子礼品卡', '', '[\"10\",\"22\"]', 99, 2, 0, '', NULL, 1757263545, 1759943587, 1);
--
-- 转储表的索引
--
--
-- 表的索引 `wa_gift`
--
ALTER TABLE `wa_gift`
ADD PRIMARY KEY (`id`) USING BTREE;
--
-- 在导出的表使用AUTO_INCREMENT
--
--
-- 使用表AUTO_INCREMENT `wa_gift`
--
ALTER TABLE `wa_gift`
MODIFY `id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=44;
COMMIT;
*/
class Gift extends Base
{
protected $autoWriteTimestamp = true;
protected function getOptions(): array{
return array_merge(parent::getOptions(),[
'insert' => [
'status' => 1,
'amounts' => '[]'
]
]);
}
function setAmountsAttr($v='',$row=[]){
if(is_array($v) || is_object($v)){
return json_encode($v,JSON_UNESCAPED_UNICODE);
}
if(is_string($v) && substr($v,0,1)!='['){
$v = explode(',',$v);
return json_encode($v,JSON_UNESCAPED_UNICODE);
}
return '[]';
}
function getAmountsAttr($v='',$row=[]){
if(!$v){return [];}
if(is_array($v) || is_object($v)){
return $v;
}
return json_decode($v,true);
}
function getStatusList(){
return [
'0' => '禁用',
'1' => '启用',
];
}
}
+36
View File
@@ -0,0 +1,36 @@
<?php
namespace app\model;
use app\model\Base;
/**
* @property integer $id 主键(ID) - 无注释
* @property integer $user_id 用户ID
* @property integer $gift_id 产品ID
* @property integer $quantity 购买数量
* @property float $amount 总价
* @property integer $denomination 面值
* @property string $cdkey 兑换码
* @property string $memo CDKEY
* @property integer $status 状态
* @property integer $created_at 创建时间
* @property integer $updated_at 更新时间
*/
class GiftOrder extends Base
{
public function gift()
{
return $this->belongsTo('Gift', 'gift_id', 'id');//->setEagerlyType(0);
}
public function user()
{
return $this->belongsTo('User', 'user_id', 'id');//->setEagerlyType(0);
}
function getStatusList(){
return [
'0' => '兑换中',
'1' => '成功',
'-1' => '失败',
];
}
}
+29
View File
@@ -0,0 +1,29 @@
<?php
namespace app\model;
use app\model\Base;
/**
* @property integer $id 主键(ID) - 无注释
* @property string $code 无注释
* @property integer $expire 无注释
* @property integer $created_at 无注释
* @property integer $updated_at 无注释
* @property integer $status 无注释
*/
class Invitecode extends Base
{
protected function getOptions(): array{
return array_merge(parent::getOptions(),[
'insert' => [
'status' => 0
]
]);
}
function getStatusList(){
return [
'0' => '隐藏',
'1' => '正常',
];
}
}
+72
View File
@@ -0,0 +1,72 @@
<?php
namespace app\model;
use app\model\Base;
/**
* 产品模型
*
* @package app\model
* @property integer $id 主键(ID) - 无注释
* @property string $title 标题
* @property string $image 封面
* @property float $price 价格
* @property float $accelerate_price 加速包价格
* @property integer $min_score 最少获得积分
* @property integer $max_score 最多获得积分
* @property integer $sales 銷售量
* @property integer $total 问卷数量
* @property integer $assign_count 每日分配
* @property integer $accelerate_assign_times 加速包分配次数
* @property integer $accelerate_assign_count 加速包每日分配
* @property integer $user_quantity 用户累计限购
* @property string $memo 备注
* @property integer $weight 权重
* @property integer $created_at 创建时间
* @property integer $updated_at 更新时间
* @property integer $status 状态
*/
class Product extends Base
{
function getCycleTypeList(){
return [
'hour' => '小时',
'day' => '天',
];
}
public function getCategoryOptions($type='product'){
return Category::where('status','1')->where('type',$type)->column('id,title');
}
public function category()
{
return $this->belongsTo('Category', 'category_id', 'id');//->setEagerlyType(0);
}
public function questionnaire()
{
return $this->belongsTo('Questionnaire', 'questionnaire_id', 'id');//->setEagerlyType(0);
}
function setStartTimeAttr($v='',$row=[]){
if($v){
return strtotime($v);
}
return $v;
}
// function getStartTimeAttr($v='',$row=[]){
// if($v){
// return is_numeric($v) ? date('Y-m-d H:i:s',$v) : $v;
// }
// return '';
// }
function setEndTimeAttr($v='',$row=[]){
if($v){
return strtotime($v);
}
return $v;
}
// function getEndTimeAttr($v='',$row=[]){
// if($v){
// return is_numeric($v) ? date('Y-m-d H:i:s',$v) : $v;
// }
// return '';
// }
}
+41
View File
@@ -0,0 +1,41 @@
<?php
namespace app\model;
use app\model\Base;
/**
* @property integer $id 主键(主键)
* @property integer $user_id 用户ID
* @property integer $product_id 产品ID
* @property float $price 购买单价
* @property integer $quantity 购买数量
* @property float $amount 总价
* @property integer $accelerate 是否加速
* @property integer $accelerate_times 总加速次数
* @property integer $accelerate_used 已加速次数
* @property integer $assigned 总数量
* @property integer $total 已分配数量
* @property integer $assigned 已分配数量
* @property integer $assigned 已分配数量
* @property integer $status 禁用
* @property string $created_at 创建时间
* @property string $updated_at 更新时间
* @property \app\model\Product $product 产品模型
* @property \app\model\User $user 用户模型
*/
class ProductOrder extends Base
{
public function product()
{
return $this->belongsTo('Product', 'product_id', 'id');//->setEagerlyType(0);
}
public function user()
{
return $this->belongsTo('User', 'user_id', 'id');//->setEagerlyType(0);
}
// function getStatusList(){
// return \app\enum\WithdrawlStatus::toArray();
// }
}
+67
View File
@@ -0,0 +1,67 @@
<?php
namespace app\model;
use app\model\Base;
/**
* @property integer $id 主键(ID) - 无注释
* @property integer $category_id 分类ID
* @property string $country 国别
* @property integer $score 积分
* @property integer $start_time 开始时间
* @property integer $end_time 结束时间
* @property string $title 标题
* @property integer $total 题目数量
* @property string $body 题目详情
* @property integer $created_at 创建时间
* @property integer $updated_at 更新时间
* @property integer $status 状态
*/
class Questionnaire extends Base
{
function setBodyAttr($v='',$row=[]){
if(is_array($v) || is_object($v)){
return json_encode($v,JSON_UNESCAPED_UNICODE);
}
return '[]';
}
function setStartTimeAttr($v='',$row=[]){
if($v){
return strtotime($v);
}
return $v;
}
// function getStartTimeAttr($v='',$row=[]){
// if($v){
// return is_numeric($v) ? date('Y-m-d H:i:s',$v) : $v;
// }
// return '';
// }
function setEndTimeAttr($v='',$row=[]){
if($v){
return strtotime($v);
}
return $v;
}
// function getEndTimeAttr($v='',$row=[]){
// if($v){
// return is_numeric($v) ? date('Y-m-d H:i:s',$v) : $v;
// }
// return '';
// }
public function getCategoryOptions(){
return Category::where('status','1')->where('type','questionnaire')->column('id,title');
}
function getBodyAttr($v='',$row=[]){
if($v){
return json_decode($v,true);
}
return [];
}
public function category()
{
return $this->belongsTo('Category', 'category_id', 'id');//->setEagerlyType(0);
}
}
+19
View File
@@ -0,0 +1,19 @@
<?php
namespace app\model;
use app\model\Base;
/**
* 实名认证模型
* @property int $user_id
* @property string $realname
* @property string $idcard
* @property int $created_at
* @property int $updated_at
*/
class Realname extends Base
{
public function user()
{
return $this->belongsTo('User', 'user_id', 'id');//->setEagerlyType(0);
}
}
+92
View File
@@ -0,0 +1,92 @@
<?php
namespace app\model;
use app\model\Base;
/**
* @property integer $id 主键(ID) - 无注释
* @property string $user_id 用户ID
* @property float $amount 金额
* @property string $network 网络
* @property string $address 充值地址
* @property string $extra 其他参数
* @property string $from 支付地址
* @property float $real_amount 实收金额
* @property string $txid 凭证
* @property integer $pay_time 支付时间
* @property integer $confirmations 确认数量
* @property string $result 结果
* @property string $reason 原因
* @property integer $status -1取消,0:创建,1支付中,2完成
* @property integer $created_at 创建时间
* @property integer $updated_at 更新时间
*/
class Recharge extends Base
{
protected function getOptions(): array
{
// 所有的参数配置统一返回
return array_merge(parent::getOptions(),[
'append' => ['status_text','remaining_sec','usdt_amount']
]);
}
function getStatusTextAttr($v,$row){
if($v){
return \app\enum\RechargeStatus::tryFromValue($row['status'])->getDescription();
}
}
function getRemainingSecAttr($v,$row){
$created_at = $row['created_at'];
if(!$created_at){
return 0;
}
if(false !== strpos($created_at,'-')){
$created_at = strtotime($created_at);
}
$v = $created_at + 900 -time() ;
return $v <=0 ? 0 : $v;
}
function getUsdtAmountAttr($v,$row){
if(in_array($row['network'],['BEP-20','TRC-20'])){
$amount = bcdiv($row['amount'],Config('site.money_to_usdt_rate'),4);
//折扣
$amount = bcmul($amount,Config('site.usdt_recharge_discount'),4);
return formatAmount($amount,4);
}
return 0;
}
function setTransferAtAttr($v){
if($v && strpos($v,'-')){
return strtotime($v);
}
return $v;
}
function setCreatedAtAttr($v){
if($v && strpos($v,'-')){
return strtotime($v);
}
return $v;
}
function setUpdatedAtAttr($v){
if($v && strpos($v,'-')){
return strtotime($v);
}
return $v;
}
function getStatusList(){
return \app\enum\RechargeStatus::toArray();
}
function getNetworkList(){
return [
"BEP-20"=>"BEP-20",
"TRC-20"=>"TRC-20"
];
}
public function user()
{
return $this->belongsTo('User', 'user_id', 'id');//->setEagerlyType(0);
}
}
+210
View File
@@ -0,0 +1,210 @@
<?php
namespace app\model;
use support\think\Db;
/**
* 用户模型
* @package app\model\User
*
* @property integer $id 主键(ID) - 主键
* @property integer $role_id 角色ID
* @property integer $parent_id 推荐人
* @property integer $group 用戶分組
* @property string $username 用户名
* @property string $nickname 昵称
* @property string $password 密码
* @property string $trade_password 交易密码
* @property string $sex 性别
* @property string $avatar 头像
* @property string $email 邮箱
* @property string $mobile 手机
* @property integer $level 等级
* @property string $birthday 生日
* @property float $money 余额(元)
* @property float $score 积分
* @property float $currency1 无注释
* @property float $currency2 无注释
* @property float $currency3 无注释
* @property float $currency4 无注释
* @property float $currency5 无注释
* @property float $currency6 无注释
* @property float $currency7 无注释
* @property float $currency8 无注释
* @property float $currency9 无注释
* @property integer $email_verify 邮箱认证
* @property integer $mobile_verify 手机认证
* @property integer $realname_verify 实名认证
* @property string $safe_email 安全邮箱
* @property integer $loginfailure 登录失败的次数
* @property integer $last_time 登录时间
* @property string $last_ip 登录ip
* @property integer $join_time 注册时间
* @property string $join_ip 注册ip
* @property string $totp_secret totp_secret
* @property integer $expire_at 过期时间
* @property integer $active 激活状态
* @property string $invite_code 邀请码
* @property integer $created_at 创建时间
* @property integer $updated_at 更新时间
* @property integer $status 状态
* Strings methods
* @method static bool money(int $user_id,float $amount, \app\enum\BalanceType $type, string $memo = null)
* 用户余额
* @method static bool score(int $user_id,float $amount, \app\enum\BalanceType $type, string $memo = null)
* 用户积分
* @method static bool currency1(int $user_id,float $amount, \app\enum\BalanceType $type, string $memo = null)
* 调研币
* @method static bool currency2(int $user_id,float $amount, \app\enum\BalanceType $type, string $memo = null)
*
* @method static bool currency3(int $user_id,float $amount, \app\enum\BalanceType $type, string $memo = null)
*
* @method static bool currency4(int $user_id,float $amount, \app\enum\BalanceType $type, string $memo = null)
*
* @method static bool currency5(int $user_id,float $amount, \app\enum\BalanceType $type, string $memo = null)
*
* @method static bool currency6(int $user_id,float $amount, \app\enum\BalanceType $type, string $memo = null)
* 可领取指标
* @method static bool currency7(int $user_id,float $amount, \app\enum\BalanceType $type, string $memo = null)
* 待分配指标
* @method static bool currency8(int $user_id,float $amount, \app\enum\BalanceType $type, string $memo = null)
* 已分配指标
* @method static bool currency9(int $user_id,float $amount, \app\enum\BalanceType $type, string $memo = null)
* 未通过指标
* @method static bool transform($from_currency,$to_currencyint $user_id,float $amount, \app\enum\BalanceType $type, string $memo = null)
*/
class User extends Base
{
public function role()
{
return $this->belongsTo('\\app\\model\\UserRole', 'role_id', 'id');//->bind(['name']);
}
public function realname()
{
return $this->hasOne('\\app\\model\\Realname', 'id', 'user_id');//->bind(['name']);
}
/**
* 扩展属性
* @param int $user_id 用户ID
* @return \think\model\relation\BelongsTo 用户扩展关联关系
*/
public function extend()
{
return $this->belongsTo('UserExtend', 'user_id', 'id');//->setEagerlyType(0);
}
// 定义与 UserTeam 的关联关系,假设用户的 id 对应 UserTeam 表中的 ancestor_id
public function team()
{
return $this->hasMany(UserTeam::class, 'ancestor_id', 'id');
}
public static function transform($from_currency,$to_currency,$user_id,$amount,\app\enum\BalanceType $type,$memo=null){
if(!in_array($from_currency,Config('site.allow_currencys'))){
abort(__('Incorrect from_currency:%currency%',['%currency%'=>$from_currency]));
}
if(!in_array($to_currency,Config('site.allow_currencys'))){
abort(__('Incorrect to_currency:%currency%',['%currency%'=>$to_currency]));
}
$time = time();
$user = self::lock(true)->where('id',$user_id)->find();
if(!$user){
throw new \Exception(__('User not found'));
}
$from_after = bcadd($user->{$from_currency} , -$amount,4);
$to_after = bcadd($user->{$to_currency} , $amount,4);
if($to_after<0 || $from_after<0){
abort(__('not enougth to currency'));
}
$from_logData = [
'user_id' => $user_id.'',
'currency' => $from_currency,
'amount' => -$amount.'',
'before' => $user->{$from_currency}.'',
'after' => $from_after.'',
'type' => $type->value,
'created_at' => $time.'',
'memo' => $memo
];
$to_logData = [
'user_id' => $user_id.'',
'currency' => $to_currency,
'amount' => $amount.'',
'before' => $user->{$to_currency}.'',
'after' => $to_after.'',
'type' => $type->value,
'created_at' => $time.'',
'memo' => $memo
];
$user->{$from_currency} = $from_after;
$user->{$to_currency} = $to_after;
$user->save();
// 写入日志
BalanceLog::create($to_logData);
BalanceLog::create($from_logData);
}
/**
* 变更会员余额
* @param int $score 积分
* @param int $user_id 会员ID
* @param string $memo 备注
*/
public static function _setBalance($currency,$user_id,$amount,\app\enum\BalanceType $type,$memo=null){
//cp($currency,$user_id,$amount,$type->getDescription(),$memo);
if(!in_array($currency,Config('site.allow_currencys'))){
abort(__('Incorrect currency:%currency%',['%currency%'=>$currency]));
}
if(!$currency){
abort(__('Incorrect currency'));
}
if(!$user_id){
abort(__('Incorrect parameter user'));
}
if(!$amount){
abort(__('Incorrect amount'));
}
$user = self::lock(true)->where('id',$user_id)->find();
$after = bcadd($user->{$currency}, $amount, 8);
if($amount < 0 && $after < 0){
abort(__('Insufficient user balance'));
}
$logData = [
'user_id' => $user_id.'',
'currency' => $currency,
'amount' => $amount.'',
'before' => '0',
'after' => '0',
'type' => $type->value,
'created_at' => time().'',
'memo' => $memo
];
$logData['before'] = $user->{$currency};
$user->{$currency} = $after;
$logData['after'] = $user->{$currency};
$user->save();
// 写入日志
BalanceLog::create($logData);
}
public static function __callStatic($method, $args)
{
$currency = strtolower($method);
if(in_array($currency,Config('site.allow_currencys'))){
return self::_setBalance($currency,$args[0],$args[1],$args[2],$args[3]);
}else{
return parent::__callStatic($method, $args);
}
}
/**
* 扩展属性
* @param int $user_id 用户ID
* @return \think\model\relation\BelongsTo 用户扩展关联关系
*/
public function referrer()
{
return $this->belongsTo('User', 'parent_id', 'id');//->setEagerlyType(0);
}
}
+16
View File
@@ -0,0 +1,16 @@
<?php
namespace app\model;
use app\model\Base;
/**
* @property integer $user_id 用户ID
* @property integer $direct_total 直推数量
* @property integer $team_total 团队成员数量
* @property float $consume 消费统计
* @property float $sales 销售额
*/
class UserExtend extends Base
{
}
+33
View File
@@ -0,0 +1,33 @@
<?php
namespace app\model;
/**
* @property integer $id 主键(主键)
* @property string $name 角色名
* @property string $rules 权限
* @property string $created_at 创建时间
* @property string $updated_at 更新时间
* @property integer $pid 上级id
*/
class UserRole extends Base
{
public function setRulesAttr($v='',$row=[])
{
if(is_array($v)){
return implode(',',$v);
}
return $v;
}
/**
* @return mixed
*/
public function getRuleIds()
{
return $this->rules ? explode(',', $this->rules) : [];
}
}
+32
View File
@@ -0,0 +1,32 @@
<?php
namespace app\model;
use app\model\Base;
use support\Model;
/**
* @property integer $id 主键(主键)
* @property string $title 标题
* @property string $icon 图标
* @property string $key 标识
* @property integer $pid 上级菜单
* @property string $created_at 创建时间
* @property string $updated_at 更新时间
* @property string $href url
* @property integer $type 类型
* @property integer $weight 排序
*/
class UserRule extends Base
{
protected function getOptions(): array{
return array_merge(parent::getOptions(),[
'insert' => [
'status' => 1
],
]);
}
}
+15
View File
@@ -0,0 +1,15 @@
<?php
namespace app\model;
/**
* @property integer $id 主键(ID) - 主键
* @property integer $user_id 用户ID
* @property string $sign_date 签到日期
* @property integer $reward 签到奖励
* @property integer $continuous_days 连续签到天数
* @property integer $created_at 创建时间
*/
class UserSignin extends Base
{
protected $name = 'user_signin';
protected $autoWriteTimestamp = true;
}
+56
View File
@@ -0,0 +1,56 @@
<?php
namespace app\model;
use app\model\Base;
/**
* @property integer $ancestor_id 上级用户ID
* @property integer $descendant_id 下级用户ID
* @property integer $depth 层级深度(0表示自己)
* @property integer $status 用户有效性状态,0表示无效,1表示有效
*/
class UserTeam extends Base
{
public function user()
{
return $this->belongsTo(User::class, 'descendant_id', 'id');
}
/**
* 根据用户ID向上查询团队成员
* @param mixed $user_id
* @param mixed $user_field
* @return array
*/
static function getTeamByChild($user_id = 0,$user_field=''){
$list = self::alias('ut')
->join('user u', 'ut.ancestor_id = u.id')
->where('ut.descendant_id', $user_id)
//->where('ut.ancestor_id','<>', $data['user_id'])
//->where('ut.depth', '<=', 3) // 限制三级内
->field('u.id as user_id,u.group, ut.depth')
->order('ut.depth ASC')->select();
if(!is_array($list)){
$list = $list->toArray();
}
return $list;
}
/**
* 根据用户ID向下查询团队
* @param mixed $user_id
* @param mixed $user_field
* @return array
*/
static function getTeamByParent($user_id = 0,$user_field=''){
$list = self::alias('ut')
->join('user u', 'ut.ancestor_id = u.id')
//->where('ut.descendant_id', $user_id)
->where('ut.ancestor_id','<>', $user_id)
//->where('ut.depth', '<=', 3) // 限制三级内
->field('u.id as user_id,u.group, ut.depth')
->order('ut.depth ASC')->select();
if(!is_array($list)){
$list = $list->toArray();
}
return $list;
}
}
+53
View File
@@ -0,0 +1,53 @@
<?php
namespace app\model;
/**
* @property integer $id 主键(ID) - 无注释
* @property integer $user_id 用户ID
* @property string $files 文件列表
* @property string $type 类型
* @property string $memo 原因
* @property integer $created_at 创建时间
* @property integer $updated_at 更新时间
* @property integer $status 状态
*/
class UserXuanchuan extends Base{
protected $name = 'user_xuanzhuan';
protected $autoWriteTimestamp = true;
protected function getOptions(): array{
return array_merge(parent::getOptions(),[
'insert' => [
'status' => 0
],
]);
}
public static function onAfterUpdate($row)
{
$changedData = $row->getChangedData();
if (isset($changedData['status']) && $changedData['status']==1) {
if($row->type == 'pyq'){
\app\model\User::currency1($row->user_id,70,\app\enum\BalanceType::POSTPYQ);
}else{
\app\model\User::currency1($row->user_id,70,\app\enum\BalanceType::POSTGROUP);
}
}
}
function getStatusList(){
return [
'0' => '等待审核',
'1' => '审核通过',
'-1' => '审核失败',
];
}
function getTypeList(){
return [
'pyq' => '朋友圈',
'group' => 'QQ群'
];
}
public function user()
{
return $this->belongsTo('User', 'user_id', 'id');//->setEagerlyType(0);
}
}
+70
View File
@@ -0,0 +1,70 @@
<?php
namespace app\model;
use app\model\Base;
/**
* @property integer $id 主键(ID) - 无注释
* @property integer $user_id 用户ID
* @property float $deduction_amount 提现金额
* @property float $recive_amount 到账金额
* @property float $fee 后续费
* @property string $title 姓名
* @property string $network 方式
* @property string $address 地址
* @property integer $transfer_at 转账时间
* @property string $txid 凭证
* @property string $memo 备注
* @property integer $created_at 创建时间
* @property integer $updated_at 更新时间
* @property integer $status 状态
*/
class Withdrawl extends Base
{
protected function getOptions(): array
{
// 所有的参数配置统一返回
return array_merge(parent::getOptions(),[
'append' => ['status_text']
]);
}
function getStatusTextAttr($v,$row){
if($v){
return \app\enum\WithdrawlStatus::tryFromValue($row['status'])->getDescription();
}
}
function setTransferAtAttr($v){
if($v && strpos($v,'-')){
return strtotime($v);
}
}
function setCreatedAtAttr($v){
if($v && strpos($v,'-')){
return strtotime($v);
}
}
function setUpdatedAtAttr($v){
if($v && strpos($v,'-')){
return strtotime($v);
}
}
function getNetworkList(){
return [
"BEP-20"=>"BEP-20",
"TRC-20"=>"TRC-20",
"WECHAT"=>"微信",
"ALIPAY"=>"支付宝"
];
}
function getStatusList(){
return \app\enum\WithdrawlStatus::toArray();
}
public function user()
{
return $this->belongsTo('User', 'user_id', 'id');//->setEagerlyType(0);
}
}
+213
View File
@@ -0,0 +1,213 @@
<?php
namespace app\model;
use app\model\Base;
/**
* @property integer $id 主键(ID) - 无注释
* @property integer $user_id 用户ID
* @property integer $product_id 产品ID
* @property integer $questionnaire_id 问卷ID
* @property integer $order_id 订单ID
* @property array $answer 答案
* @property float $income 总收益
* @property integer $start_time 开始时间
* @property integer $end_time 结束时间
* @property string $settings 配置
* @property integer $status 状态
* @property integer $created_at 创建时间
* @property integer $updated_at 更新时间
*/
class WorkRecord extends Base
{
protected function getOptions(): array
{
// 所有的参数配置统一返回
return array_merge(parent::getOptions(),[
'hidden' => ['settings'],
'append' => ['status_text']
]);
}
function getStatusTextAttr($v,$row){
return \app\enum\ServerStatus::tryFromValue($row['status']?:0)->getDescription();
}
public static function onBeforeWrite(&$row)
{
if(!$row->settings){
$step_before_array = [
'bootstrap' => rand(10,20),
'http_proxy' => rand(10,20),
'browser_env' => rand(10,20),
'enter_questionnaire' => rand(10,20),
'match_virtual_person' => rand(10,20),
];
//后置耗时
$step_after_array = [
'submit_result' => rand(5,10),
'wait_submit_result' => rand(5,10),
'wait_settlement' => 0,
];
$answer_array=[];
/** @var Questionnaire $questionnaire */
$questionnaire = Questionnaire::where('id',$row->questionnaire_id)->find();
for ($i=0; $i < $questionnaire->total ; $i++) {
$answer_array['answer_'.($i+1)] = 30;
}
$row->settings = array_merge($step_before_array,$answer_array,$step_after_array);
}
if(!$row->answer){
$answer = [];
foreach($row->questionnaire->body as $k=>$vo){
$answer[$k]= rand(0,count($vo['answer'])-1);
}
$row->answer = $answer;
}
// if($row->start_time && !$row->end_time){
// $row->end_time = 0;
// if(is_array($row->settings)){
// $end_time = $row->start_time + array_sum($row->settings);
// $row->end_time = $end_time;
// }
// }
//cp('onBeforeWrite');
}
public function user()
{
return $this->belongsTo(User::class, 'user_id', 'id');
}
public function product()
{
return $this->belongsTo(Product::class, 'product_id', 'id');
}
public function questionnaire()
{
return $this->hasOne(Questionnaire::class, 'id', 'questionnaire_id');
}
/**
* 自动写入Settings字段
*/
function setSettingsAttr($v='',$row=[]){
if(is_array($v)){
return json_encode($v);
}
return $v;
}
/**
* Settings自动转为数组
*/
public function getSettingsAttr($v='',$row=[]){
//cp('getSettingsAttr');
if(!$v){
return [];
}
if(!is_array($v)){
return json_decode($v,true);
}
return $v;
}
public function start(){
if($this->status != \app\enum\ServerStatus::WAITING->value){
return $this->error(__('Server could not start'));
}
$this->status = \app\enum\ServerStatus::WORKING->value;
$this->start_time = time();
if(is_array($this->settings)){
$end_time = $this->start_time + array_sum($this->settings);
$this->end_time = $end_time;
}
$this->save();
addJob(['server_id'=>$this->id,'action'=>'workcomplete'],'Questionnaire',$this->end_time-time());
}
public function getStep(){
if ($this->status != \app\enum\ServerStatus::WORKING->value) {
return [
'step' => 0,
'msg' => \app\enum\ServerStatus::tryFromValue($this->status)->name,
'next_time' => null,
'remaining_seconds' => 0
];
}
$settings = $this->settings;
//cp($settings);
$start_time = $this->start_time; // 使用实际的开始时间
//$start_time = time();
$current_time = time();
//$current_time = $start_time+140;//time(); // 当前时间戳
if ($current_time < $start_time) {
//根据时间判断未开始
return [
'step' => 0,
'msg' => \app\enum\ServerStatus::tryFromValue($this->status)->name,
'next_time' => $start_time,
'remaining_seconds' => 0
];
}
// 计算已经过去的时间
$elapsed = $current_time - $start_time;
// 初始化结果
$result = [
'step' => 7,
'current' => __('bootstrap'),
'next_time' => null,
'msg' => __('anser'),
'remaining_seconds' => 0
];
// 遍历所有阶段
$accumulated = 0;
$found_current = false;
$stepArr = [
'bootstrap' => 1,
'http_proxy' => 2,
'browser_env' => 3,
'enter_questionnaire' => 4,
'match_virtual_person' => 5,
'submit_result' => 7,
'wait_submit_result' => 7,
'wait_settlement' => 7,
];
foreach ($settings as $name => $duration) {
$accumulated += $duration;
// 1. 找到当前阶段
if (!$found_current && $elapsed < $accumulated) {
$found_current = true;
if(substr($name,0,7) == 'answer_'){
$_name = explode('_',$name);
$result['index'] = intval($_name[1]);
$result['current'] = __($_name[0].'_%index%',['%index%'=>$result['index']]);
$result['step'] = 6;
$result['msg'] = 'AI Thinking';
}else{
$result['step'] = $stepArr[$name];
$result['msg'] = __($name);
}
// 当前阶段剩余时间 = 当前阶段结束时间 - 当前时间
$result['remaining_seconds'] = $accumulated - $elapsed;
}
// 2. 找到下一阶段开始时间
if ($elapsed < $accumulated) {
$result['next_time'] = $start_time + $accumulated;
break;
}
}
// 如果已经超过所有阶段
if ($elapsed >= $accumulated) {
return [
'step' => 7,
'msg' => \app\enum\ServerStatus::tryFromValue(4)->name,
'next_time' => null,
'remaining_seconds' => 0
];
}
return $result;
}
}