This commit is contained in:
2026-03-25 02:48:30 +08:00
parent 8704434c36
commit 66bcd8061a
23 changed files with 1204 additions and 208 deletions
@@ -0,0 +1,93 @@
<?php
namespace app\api\controller;
use app\model\User as UserModel;
use app\model\Collection as CollectionModel;
use support\Request;
use support\Response;
use support\think\Db;
use hg\apidoc\annotation as Apidoc;
/**
* 收藏
*/
class CollectionController extends BaseController{
/**
* 不需要鉴权的方法
* @var array
*/
public $noNeedAuth = ['*'];
/**
* 无需登录及鉴权的方法
* @var array
*/
public $noNeedLogin = [];
/**
* @Apidoc\Title("列表")
* @Apidoc\Method("GET")
* @Apidoc\Query("content_type", type="string", require=false, desc="内容类型 enum('text', 'image', 'file', 'video', 'link','audio')")
* @Apidoc\Query("kw", type="string", require=false, desc="关键字")
* @Apidoc\Query("page", type="int", require=true, desc="页码",default=1)
* @Apidoc\Query("limit", type="int", require=true, desc="分页大小",default=10)
*/
function list(Request $request): Response
{
$user = \support\Jwt::getUser();
$page = (int)Input('page', 1);
$content_type = Input('content_type');
$kw = Input('kw');
$limit = (int)Input('limit', 10);
$query = CollectionModel::where('status', 1)
->whereIn('user_id',$user->id)
->order('created_at', 'desc');
if($content_type){
$query->where('content_type',$content_type);
}
if($kw){
$query->whereLike('content','%'.$kw.'%');
}
$list = $query->paginate([
'list_rows' => $limit,
'page' => $page,
]);
return $this->success('ok', $list);
}
/**
* 创建收藏
* @Apidoc\Param("content_type", type="string",require=true, desc="内容类型 enum('text', 'image', 'file', 'video', 'link','audio')")
* @Apidoc\Param("content", type="string",require=true, desc="json结构化收藏内容本体")
* @Apidoc\Param("tags", type="string",require=true, desc="用户自定义标签,多个用逗号隔开,或者使用数组")
* @Apidoc\Param("is_pinned", type="int",require=true, desc="是否置顶")
* @param Request $request
* @return Response
*/
function create(Request $request): Response
{
$user = \support\Jwt::getUser();
$content = $request->post('content');
$content_type = $request->post('content_type', '');
$tags = $request->post('tags', '');
$is_pinned = $request->post('is_pinned', 0);
// 验证内容
if (empty($content_type)) {
return $this->fail(__('The field %field% must be not empty. ',['field'=>'content_type']));
}
if (empty($content)) {
return $this->fail(__('The field %field% must be not empty. ',['field'=>'content']));
}
if(is_array($content)) {
$content = json_encode($content);
}
// 创建朋友圈动态
$collection = CollectionModel::create([
'user_id' => $user->id,
'content_type' => $content_type,
'content' => $content,
'tags' => $tags,
'is_pinned' => $is_pinned
]);
return $this->success('发布成功', ['collection' => $collection]);
}
}
+1 -1
View File
@@ -151,7 +151,7 @@ class CommonController extends BaseController{
'role_id' => 1, 'role_id' => 1,
'group_id' => 0, 'group_id' => 0,
'region' => '86', 'region' => '86',
'nickname' => input('nickname'), 'nickname' => input('nickname','用户_'.substr($username,7)),
'avatar' => '/static/avatar/'.rand(0,17).'.png', 'avatar' => '/static/avatar/'.rand(0,17).'.png',
]; ];
if ($invite_code) { if ($invite_code) {
@@ -395,7 +395,7 @@ class FriendCircleController extends BaseController{
$res = request()->IM->friend->getFriendList(\support\Encrypt::userIDencode($user_id)); $res = request()->IM->friend->getFriendList(\support\Encrypt::userIDencode($user_id));
$friendsInfo = $res['friendsInfo']; $friendsInfo = $res['friendsInfo'];
foreach($friendsInfo as $k=>$v){ foreach($friendsInfo as $k=>$v){
array_push($result,$v['friendUser']['userID']); array_push($result,\support\Encrypt::userIDDecode($v['friendUser']['userID']));
} }
cache($cache_key,$result,3600); cache($cache_key,$result,3600);
} }
+450
View File
@@ -0,0 +1,450 @@
<?php
namespace app\api\controller;
use app\model\User as UserModel;
use app\model\FriendCircle as FriendCircleModel;
use app\model\FriendCircleLike as FriendCircleLikeModel;
use app\model\FriendCircleComment as FriendCircleCommentModel;
use support\Request;
use support\Response;
use support\think\Db;
use hg\apidoc\annotation as Apidoc;
/**
* 新朋友圈
*/
class MomentsController extends BaseController{
/**
* 不需要鉴权的方法
* @var array
*/
public $noNeedAuth = ['*'];
/**
* 无需登录及鉴权的方法
* @var array
*/
public $noNeedLogin = [];
public $user_display_fields = 'id,userID,nickname,avatar';
/**
* 朋友圈设置
* @param string $userID 用户userID,可选,不填就查询自己
* @return void
*/
function info(Request $request): Response{
$userID = Input('userID');
if($userID){
$user_id = \support\Encrypt::userIDDecode($userID);
$json= [
'top_unread_items' =>[],
'unread_item_ids' =>[],
'unread_count' =>0,
'settings' => Db::name('user_extend')->where('user_id',$user_id)->field('moments_allow_view_days,moments_banner')->findOrEmpty()
];
return $this->success('ok',$json);
}else{
$user = \support\Jwt::getUser();
if (!$user) {
return $this->fail('请先登录');
}
$userID = $user->userID;
$res = $this->newcount($request);
$response = $res->rawBody();
$json = json_decode($response,true);
$json['data']['settings'] = Db::name('user_extend')->where('user_id',$user->id)->field('moments_allow_view_days,moments_banner')->findOrEmpty();
$top_unread_items = FriendCircleModel::whereIn('id',$json['data']['unread_item_ids'])
->with(['user' => function($query) {
$query->field($this->user_display_fields);
}])
->order('id', 'desc')
->limit(0,3)
->select();
$json['data']['top_unread_items'] = $top_unread_items ?: [];
$res->withBody(json_encode($json));
return $res;
}
}
/**
* @Apidoc\Title("列表")
* @Apidoc\Method("GET")
* @Apidoc\Query("userID", type="string", require=false, desc="用户userID,不传则获取所有")
* @Apidoc\Query("page", type="int", require=true, desc="页码",default=1)
* @Apidoc\Query("limit", type="int", require=true, desc="分页大小",default=10)
*/
function list(Request $request): Response
{
$current_user = \support\Jwt::getUser();
$current_user_id = $current_user ? $current_user->id : 0;
$page = (int)Input('page', 1);
$limit = (int)Input('limit', 10);
$userID = Input('userID');
$query = FriendCircleModel::where('status', 1)
->with(['user' => function($query) {
$query->field($this->user_display_fields);
}])
->order('created_at', 'desc');
if($userID){
// 如果指定了用户ID,只获取该用户的朋友圈
$user_id = \support\Encrypt::userIDDecode($userID);
$query->where('user_id',$user_id);
}else{
$current_userID = \support\Encrypt::userIDencode($current_user_id);
$query->whereIn('user_id',$this->getFriendUserIds($current_userID));
}
$list = $query->paginate([
'list_rows' => $limit,
'page' => $page,
]);
if(!$userID){
cache('circle_last_read_id_'.$current_user_id,$list[0]['id']);
}
// 处理每条朋友圈数据
$items = $list->items();
$list->each(function($item) use ($current_user_id){
// 获取点赞列表
$likes = Db::name('friend_circle_like')->alias('f')
->join('user u','u.id=f.user_id')
->where('f.circle_id', $item->id)
->field('f.*,u.userID,u.avatar,u.nickname')
->order('f.created_at', 'desc')
->limit(20)
->select();
$likes = $likes ? $likes->toArray() : [];
// 检查当前用户是否已点赞
$is_liked = false;
if ($current_user_id > 0) {
$is_liked = null !== array_find($likes,function($item)use($current_user_id){
return $item['user_id'] == $current_user_id;
});
// FriendCircleLikeModel::where('circle_id', $item->id)
// ->where('user_id', $current_user_id)
// ->count() > 0;
}
// 获取评论列表(最新10条)
$comments = FriendCircleCommentModel::where('circle_id', $item->id)
->where('status', 1)
->with(['user' => function($query) {
$query->field($this->user_display_fields);
}, 'replyUser' => function($query) {
$query->field($this->user_display_fields);
}])
->order('created_at', 'asc')
->limit(10)
->select();
// 格式化数据
$item->is_liked = $is_liked;
$item->likes = $likes;
$item->comments = $comments;
// 处理图片URL
if (!empty($item->files)) {
$files = is_array($item->files) ? $item->files : json_decode($item->files, true);
if (is_array($files)) {
$item->files = array_map(function($file) {
return cdnurl($file);
}, $files);
} else {
$item->files = [];
}
} else {
$item->files = [];
}
// 处理用户头像
if ($item->user && $item->user->avatar) {
$item->user->avatar = cdnurl($item->user->avatar);
}
// 处理点赞用户头像
foreach ($item->likes as $like) {
if ($like->user) {
$like->avatar = cdnurl($like->avatar);
}
}
// 处理评论用户头像
foreach ($item->comments as $comment) {
if ($comment->user && $comment->user->avatar) {
$comment->user->avatar = cdnurl($comment->user->avatar);
}
if ($comment->replyUser && $comment->replyUser->avatar) {
$comment->replyUser->avatar = cdnurl($comment->replyUser->avatar);
}
}
return $item;
});
return $this->success('ok', $list);
}
/**
* @Apidoc\Title("最近更新的数量")
* @Apidoc\Method("POST")
* @Apidoc\Param("last_see", type="string",require=false, desc="最近查看的时间戳")
*/
function newcount(Request $request): Response
{
$user = \support\Jwt::getUser();
if (!$user) {
return $this->fail('请先登录');
}
$user_id = $user->id;
$circle_last_read_id = cache('circle_last_read_id_'.$user_id) ?: 0;
$userID = \support\Encrypt::userIDencode($user_id);
// 统计从上次查看时间到现在新增的朋友圈数量
$unread_item_ids = FriendCircleModel::where('status', 1)
->whereIn('user_id',$this->getFriendUserIds($userID))
->where('id', '>', $circle_last_read_id)
->order('id', 'desc')
->column('id');
return $this->success('ok', [
'unread_count' => count($unread_item_ids),
'unread_item_ids'=>$unread_item_ids
]);
}
/**
* @Apidoc\Title("发布朋友圈")
* @Apidoc\Method("POST")
* @Apidoc\Param("body", type="string",require=false, desc="内容")
* @Apidoc\Param("files", type="string",require=false, desc="图片列表(JSON数组)")
*/
function create(Request $request): Response
{
$user = \support\Jwt::getUser();
if (!$user) {
return $this->fail('请先登录');
}
$body = $request->post('content', '');
$files = $request->post('files', '');
$address = $request->post('address', '');
$releaseType = $request->post('releaseType', '');
// 验证内容
if (empty($body)) {
return $this->fail('什么内容都木有啊');
}
// 处理图片列表
$files_array = [];
if (!empty($files)) {
if (is_string($files)) {
$files_array = json_decode($files, true);
} elseif (is_array($files)) {
$files_array = $files;
}
if (!is_array($files_array)) {
return $this->fail('图片列表格式错误');
}
// 限制图片数量
if (count($files_array) > 9) {
return $this->fail('最多只能上传9张图片');
}
}
// 创建朋友圈动态
$circle = FriendCircleModel::create([
'user_id' => $user->id,
'releaseType' => $releaseType,
'body' => $body,
'files' => $files_array,
'address' => $address,
'status' => 1,
]);
return $this->success('发布成功', ['id' => $circle->id,'data' => $circle]);
}
/**
* @Apidoc\Title("发表评论")
* @Apidoc\Method("POST")
* @Apidoc\Param("body", type="string",require=true, desc="内容")
* @Apidoc\Param("id", type="int",require=true, desc="朋友圈动态ID")
* @Apidoc\Param("reply_userID", type="string",require=false, desc="回复的用户userID(回复评论时使用)")
*/
function comment(Request $request): Response
{
$user = \support\Jwt::getUser();
if (!$user) {
return $this->fail('请先登录');
}
$body = $request->post('body', '');
$circle_id = (int)$request->post('id');
$reply_userID = (int)$request->post('reply_userID');
if (empty($body)) {
return $this->fail('评论内容不能为空');
}
if ($circle_id <= 0) {
return $this->fail('朋友圈动态ID错误');
}
// 检查朋友圈动态是否存在
$circle = FriendCircleModel::where('id', $circle_id)
->where('status', 1)
->find();
if (!$circle) {
return $this->fail('朋友圈动态不存在');
}
// 如果回复评论,检查被回复的用户是否存在
$reply_user_id = 0;
if ($reply_userID ) {
$reply_user_id = \support\Encrypt::userIDDecode($reply_userID);
}
if ($reply_user_id>0 ) {
$reply_user = UserModel::where('id', $reply_user_id)->find();
if (!$reply_user) {
return $this->fail('被回复的用户不存在');
}
}
// 创建评论
$comment = FriendCircleCommentModel::create([
'circle_id' => $circle_id,
'user_id' => $user->id,
'reply_user_id' => $reply_user_id,
'body' => $body,
'status' => 1,
]);
// 更新朋友圈评论数
$circle->comment_count = FriendCircleCommentModel::where('circle_id', $circle_id)
->where('status', 1)
->count();
$circle->save();
$comment->user = Db::name('user')->field($this->user_display_fields)->where('id',$comment->user_id)->find();
$comment->replyUser=null;
if($comment->reply_user_id){
$comment->replyUser = Db::name('user')->field($this->user_display_fields)->where('id',$comment->reply_user_id)->find();
}
return $this->success('评论成功', $comment);
}
/**
* @Apidoc\Title("点赞")
* @Apidoc\Method("POST")
* @Apidoc\Param("id", type="int",require=true, desc="朋友圈动态ID")
*/
function like(Request $request): Response
{
$user = \support\Jwt::getUser();
if (!$user) {
return $this->fail('请先登录');
}
$circle_id = (int)$request->post('id', 0);
if ($circle_id <= 0) {
return $this->fail('朋友圈动态ID错误');
}
// 检查朋友圈动态是否存在
$circle = FriendCircleModel::where('id', $circle_id)
->where('status', 1)
->find();
if (!$circle) {
return $this->fail('朋友圈动态不存在');
}
// 检查是否已点赞
$like = FriendCircleLikeModel::where('circle_id', $circle_id)
->where('user_id', $user->id)
->find();
if ($like) {
// 取消点赞
$like->delete();
$circle->like_count = max(0, $circle->like_count - 1);
$circle->save();
return $this->success('取消点赞成功', ['is_liked' => false]);
} else {
// 添加点赞
FriendCircleLikeModel::create([
'circle_id' => $circle_id,
'user_id' => $user->id,
]);
$circle->like_count = $circle->like_count + 1;
$circle->save();
return $this->success('点赞成功', ['is_liked' => true]);
}
}
protected function getFriendUserIds($userID):array{
if (!$userID) {
return [];
}
$cache_key = 'friend_id_list_'.$userID;
$result = cache($cache_key) ?: [];
if(count($result) === 0){
$res = request()->IM->friend->getFriendList($userID);
$friendsInfo = $res['friendsInfo'];
foreach($friendsInfo as $k=>$v){
array_push($result,\support\Encrypt::userIDDecode($v['friendUser']['userID']));
}
cache($cache_key,$result,3600);
}
$result[] = \support\Encrypt::userIDDecode($userID);
return $result;
}
function delete(Request $request): Response{
$id = $request->post('id');
$user = \support\Jwt::getUser();
if (!$user) {
return $this->fail('请先登录');
}
if($id){
FriendCircleModel::where('id',$id)->where('user_id',$user->id)->delete();
}
return $this->success('删除成功');
}
/**
* 设置朋友圈背景
* @param Request $request
* @return Response
*/
function upload_bg(Request $request){
return $this->setBanner($request);
}
/**
* 设置朋友圈背景
* @param Request $request
* @return Response
*/
function setBanner(Request $request){
try {
$user = \support\Jwt::getUser();
if (!$user) {
return $this->fail('请先登录');
}
$res = $this->_upload($request);
if(is_string($res)){
return $this->fail( $res);
}
Db::name('user_extend')->where('user_id',$user->id)->save([
'moments_banner' => $res[0]['file_name'],
]);
return $this->success(__('successful'),[
'url'=>$res[0]['file_name']
]);
}catch (\Exception $e){
return $this->error($e->getMessage());
}
}
}
+22 -31
View File
@@ -31,20 +31,22 @@ class TeamController extends BaseController{
$user = \support\Jwt::getUserinfo(); $user = \support\Jwt::getUserinfo();
$user_id = $user['id']; $user_id = $user['id'];
$user= Hook('user.profile',$user); $user= Hook('user.profile',$user);
$team_ids = UserTeamModel::where('ancestor_id',$user_id)->where('depth','>',0)->column('descendant_id'); //$team_ids = UserTeamModel::where('ancestor_id',$user_id)->where('depth','>',0)->column('descendant_id');
$result=[ $result=[
'total_count' => count($team_ids),//团队总人数 'level' => $user['level'],
'direct_total' => cache('team_direct_total_'.$user_id)??0,//直属团队人数 'total_count' => cache_get('team_user_count_'.$user_id),//团队人数
'recharge_total' => cache('team_recharge_total_'.$user_id)??0, 'direct_total' => cache_get('team_direct_total_'.$user_id),//直属团队人数
'withdrawl_total' => cache('team_withdrawl_total_'.$user_id)??0, 'vip_total' => cache_get('team_vip_total_'.$user_id),//旗下会员总数
'income_total' => cache('team_income_total_'.$user_id)??0, // 'recharge_total' => cache('team_recharge_total_'.$user_id)??0,
'today_income_total' => cache('user_today_income_total_'.$user_id)??0, // 'withdrawl_total' => cache('team_withdrawl_total_'.$user_id)??0,
'promotion_income_total' => cache('user_promotion_income_total_'.$user_id)??0, // 'income_total' => cache('team_income_total_'.$user_id)??0,
'consume_total' => Db::name('user_extend')->where('user_id',$user_id)->value('sales'),//cache('team_consume_total_'.$user_id)??0,//团队总业绩 // 'today_income_total' => cache('user_today_income_total_'.$user_id)??0,
'user_sales_reward' => cache('user_sales_reward_'.$user_id)??0,//销售奖 // 'promotion_income_total' => cache('user_promotion_income_total_'.$user_id)??0,
'user_output_reward' => cache('user_output_reward_'.$user_id)??0,//产值奖 // 'consume_total' => cache('team_consume_total_'.$user_id)??0,//团队总业绩
'user_withdrawl_reward' => cache('user_withdrawl_reward'.$user_id)??0,//提现 // 'user_sales_reward' => cache('user_sales_reward_'.$user_id)??0,//销售
// 'user_output_reward' => cache('user_output_reward_'.$user_id)??0,//产值奖
// 'user_withdrawl_reward' => cache('user_withdrawl_reward'.$user_id)??0,//提现奖
'user' => $user[0], 'user' => $user[0],
]; ];
@@ -120,7 +122,7 @@ class TeamController extends BaseController{
->join('user_extend ue', 'u.id = ue.user_id') ->join('user_extend ue', 'u.id = ue.user_id')
->where('u.parent_id', $user['id']) ->where('u.parent_id', $user['id'])
//->where('ue.active', 1) //->where('ue.active', 1)
->field('u.id,u.userID, u.username,u.money,u.score,u.role_id, u.group,u.avatar, u.created_at') ->field('u.id,u.userID, u.username,u.nickname,u.money,u.score,u.role_id,u.avatar, u.created_at')
->order('u.created_at desc'); ->order('u.created_at desc');
if($kw){ if($kw){
$model = $model->whereLike("u.username",'%'.$kw.'%'); $model = $model->whereLike("u.username",'%'.$kw.'%');
@@ -139,33 +141,22 @@ class TeamController extends BaseController{
}else{ }else{
$result = $model->paginate($limit); $result = $model->paginate($limit);
} }
$role_arr = [
'0' => __('普通用户'),
'1' => __('V1'),
'2' => __('V2'),
'3' => __('V3'),
'4' => __('V4'),
'5' => __('V5'),
];
$result = $result->toArray(); $result = $result->toArray();
foreach($result['data'] as $k=>$item){ foreach($result['data'] as $k=>$item){
$result['data'][$k]['avatar'] = cdnurl($item['avatar'] ?: '/storage/avatar/default.png'); $result['data'][$k]['avatar'] = cdnurl($item['avatar'] ?: '/storage/avatar/default.png');
$result['data'][$k]['recharge_total'] = cache('user_recharge_total_'.$item['id'])??0; //$result['data'][$k]['recharge_total'] = cache('user_recharge_total_'.$item['id'])??0;
$result['data'][$k]['withdrawl_total'] = cache('user_withdrawl_total_'.$item['id'])??0; //$result['data'][$k]['withdrawl_total'] = cache('user_withdrawl_total_'.$item['id'])??0;
$result['data'][$k]['withdrawl_reward'] = cache('user_withdrawl_reward_'.$item['id'])??0; //$result['data'][$k]['withdrawl_reward'] = cache('user_withdrawl_reward_'.$item['id'])??0;
$result['data'][$k]['income_total'] = cache('user_income_total_'.$item['id'])??0; //$result['data'][$k]['income_total'] = cache('user_income_total_'.$item['id'])??0;
$result['data'][$k]['consume_total'] = cache('user_consume_total_'.$item['id'])??0; //$result['data'][$k]['consume_total'] = cache('user_consume_total_'.$item['id'])??0;
$result['data'][$k]['play_count'] = cache('user_play_count_'.$item['id'])??0;
//$result['data'][$k]['created_at'] = date('Y-m-d H:i:s', $item['created_at']); //$result['data'][$k]['created_at'] = date('Y-m-d H:i:s', $item['created_at']);
$result['data'][$k]['total_count'] = UserTeamModel::where('ancestor_id',$item['id'])->where('status',1)->where('depth','>',0)->count('descendant_id');
$result['data'][$k]['direct_total'] = cache('team_direct_total_'.$item['id'])??0;
$result['data'][$k]['role'] = isset($role_arr[$item['role_id']]) ? $role_arr[$item['role_id']] : __('普通用户');
//$result['data'][$k]['questionnaire_count'] = WorkRecordModel::where('user_id',$item['id'])->count('id');
//return $item; //return $item;
} }
return $this->success(__('successful'),$result); return $this->success(__('successful'),$result);
} }
/** /**
* @Apidoc\NotParse()
* @Apidoc\NotDebug()
* @Apidoc\Title("改变用户等级") * @Apidoc\Title("改变用户等级")
* @Apidoc\Method("POST") * @Apidoc\Method("POST")
* @Apidoc\Param("id", type="string",require=false, desc="ID") * @Apidoc\Param("id", type="string",require=false, desc="ID")
+14 -8
View File
@@ -113,6 +113,11 @@ class ThaliController extends BaseController{
if($quantity == 12){ if($quantity == 12){
$price = $thali->year_price; $price = $thali->year_price;
} }
//新开通
$isNew=false;
if(is_null($user->role_id)){
$isNew = true;
}
//升级 //升级
$isUpgrade=true; $isUpgrade=true;
//续费 //续费
@@ -145,14 +150,15 @@ class ThaliController extends BaseController{
\app\model\User::score($user->id,-$amount,\app\enum\BalanceType::PURCHASE_ROLE,json_encode(['role_id'=>$role_id,'quantity'=>$quantity,'role_name'=>$thali->title])); \app\model\User::score($user->id,-$amount,\app\enum\BalanceType::PURCHASE_ROLE,json_encode(['role_id'=>$role_id,'quantity'=>$quantity,'role_name'=>$thali->title]));
cache('user_rights_'.$user->id,null); cache('user_rights_'.$user->id,null);
//Hook('user.roleup', $user); if($isNew){
// $data = [ Hook('user.role_up', $user);
// 'role_id' => $role_id, }
// 'user_id' => $user->id, $data = [
// 'parent_id' => $user->parent_id, 'role_id' => $role_id,
// 'amount' => $amount, 'user_id' => $user->id,
// ]; 'amount' => $amount,
// Hook('role.buy', $data); ];
Hook('user.role_buy', $data);
return $this->success(__('successful'),$user); return $this->success(__('successful'),$user);
} }
} }
+35 -26
View File
@@ -31,7 +31,8 @@ class UserController extends BaseController{
* @Apidoc\Desc("GET为获取用户信息,POST为修改数据") * @Apidoc\Desc("GET为获取用户信息,POST为修改数据")
* @Apidoc\Param("nickname", type="string",require=true, desc="昵称") * @Apidoc\Param("nickname", type="string",require=true, desc="昵称")
*/ */
public function profile(){ public function profile()
{
$data = \support\Jwt::getUser(); $data = \support\Jwt::getUser();
if(Request()->method() == 'POST'){ if(Request()->method() == 'POST'){
$nickname = input('nickname'); $nickname = input('nickname');
@@ -60,6 +61,7 @@ class UserController extends BaseController{
} }
return $this->success(__('successful')); return $this->success(__('successful'));
} }
$data = \support\Jwt::getUserInfo($data);
$data= Hook('user.profile',$data); $data= Hook('user.profile',$data);
return $this->success(__('successful'),$data[0]); return $this->success(__('successful'),$data[0]);
} }
@@ -147,6 +149,25 @@ class UserController extends BaseController{
\support\Jwt::getUser()->save($data); \support\Jwt::getUser()->save($data);
return $this->success(__('successful'),$data); return $this->success(__('successful'),$data);
} }
/**
* 设置个人banner
* @Apidoc\Method("POST")
* @Apidoc\Param("file", type="File", require=true, desc="文件")
*/
public function setBanner(Request $request)
{
$user_id = \support\Jwt\JwtToken::getCurrentId();
//单文件上传
$res = $this->_upload($request);
if(is_string($res)){
return $this->fail( $res);
}
$data = [
'profile_banner' => $res[0]['file_name'],
];
Db::name('user_extend')->where('user_id',$user_id)->save($data);
return $this->success(__('successful'),$data);
}
function realname(Request $request): Response function realname(Request $request): Response
{ {
/** /**
@@ -201,30 +222,15 @@ class UserController extends BaseController{
} }
//$userIDs = array_map('\support\Encrypt::userIDDecode',$ids); //$userIDs = array_map('\support\Encrypt::userIDDecode',$ids);
//$res = $request->IM->user->getUsersInfo($userIDs); //$res = $request->IM->user->getUsersInfo($userIDs);
$list = Db::name('user')-> $list = Db::name('user')->alias('u')
whereIn('userID',$ids) ->leftJoin('user_extend ue','ue.user_id=u.id')
->field('u.*,ue.profile_banner')
->whereIn('u.userID',$ids)
->paginate(Input('limit',10)); ->paginate(Input('limit',10));
$list->each(function($user){ $list->each(function($user){
$user['id'] = $user['userID']; $data = \support\Jwt::getUserInfo($user);
unset($user['password']); $data= Hook('user.profile',$data);
unset($user['trade_password']); return $data[0];
//unset($user['avatar']);
unset($user['online']);
unset($user['token']);
unset($user['prev_time']);
unset($user['loginfailure']);
unset($user['successions']);
unset($user['maxsuccessions']);
unset($user['currency1']);
unset($user['currency2']);
unset($user['currency3']);
unset($user['currency4']);
unset($user['currency5']);
unset($user['currency6']);
unset($user['currency7']);
unset($user['currency8']);
unset($user['currency9']);
return $user;
//$user->hidden(['password']); //$user->hidden(['password']);
}); });
@@ -240,9 +246,12 @@ class UserController extends BaseController{
{ {
$keyword = Input('keyword'); $keyword = Input('keyword');
$searchtype = Input('searchtype'); $searchtype = Input('searchtype');
$fields = 'userID,avatar,username,nickname,avatar,sex,email,mobile,birthday,bio'; $fields = 'u.userID,u.avatar,u.username,u.nickname,u.avatar,u.sex,u.email,u.mobile,u.birthday,u.bio,ue.profile_banner';
$model = Db::name('user')->field($fields)->where('status',1); $model = Db::name('user')->alias('u')
$model = $model->where('userID',$keyword); ->join('user_extend ue','ue.user_id=u.id')
->field($fields)
->where('status',1);
$model = $model->where('u.userID',$keyword);
// if($searchtype =='id'){ // if($searchtype =='id'){
// $model = $model->where('id',$keyword); // $model = $model->where('id',$keyword);
// }else{ // }else{
+132 -6
View File
@@ -10,6 +10,9 @@ use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
use support\think\Db; use support\think\Db;
use app\model\User as UserModel; use app\model\User as UserModel;
use \think\db\PDOConnection;
use think\db\exception\PDOException;
use think\db\exception\InvalidArgumentException;
class Database extends Command class Database extends Command
@@ -22,10 +25,12 @@ class Database extends Command
*/ */
protected function configure() protected function configure()
{ {
$this->addOption('action','a', InputArgument::OPTIONAL, '要做什么操作'); $this->addOption('action','a', InputOption::VALUE_OPTIONAL, '要做什么操作');
$this->addOption('table','t', InputArgument::OPTIONAL, '表名'); $this->addOption('table','t', InputOption::VALUE_OPTIONAL, '表名');
$this->addOption('domain','ym', InputArgument::OPTIONAL, 'domain'); $this->addOption('domain','ym', InputOption::VALUE_OPTIONAL, 'domain');
$this->addOption('robot_id','rid', InputArgument::OPTIONAL, 'robot_id'); $this->addOption('connection','c', InputOption::VALUE_OPTIONAL, '数据库链接名,默认mysql','mysql');
$this->addOption('dir','d', InputOption::VALUE_OPTIONAL, '缓存目录');
$this->addOption('robot_id','rid', InputOption::VALUE_OPTIONAL, 'robot_id');
} }
/** /**
@@ -36,12 +41,32 @@ class Database extends Command
protected function execute(InputInterface $input, OutputInterface $output): int protected function execute(InputInterface $input, OutputInterface $output): int
{ {
$action = $input->getOption('action'); $action = $input->getOption('action');
if($action == 'prototype'){ if(method_exists($this, $action)){
return $this->prototype($input, $output); return $this->$action($input, $output);
} }
cp('操作不存在:'.$action); cp('操作不存在:'.$action);
return 0; return 0;
} }
function optimize_schema(InputInterface $input, OutputInterface $output)
{
$table = $input->getOption('table');
try {
if ($table) {
$this->cacheTable($table, $input->getOption('connection'));
} else {
$dirs = ((array) $input->getOption('dir')) ?: $this->getDefaultDirs();
foreach ($dirs as $dir) {
$this->cacheModel($dir);
}
}
} catch (\Exception $e) {
$output->write($e->getMessage());
return self::FAILURE;
}
$output->write('Succeed!');
return self::SUCCESS;
}
function prototype(InputInterface $input, OutputInterface $output){ function prototype(InputInterface $input, OutputInterface $output){
$table = $input->getOption('table'); $table = $input->getOption('table');
// 获取表前缀并构建完整表名 // 获取表前缀并构建完整表名
@@ -132,4 +157,105 @@ class Database extends Command
// 默认返回混合类型 // 默认返回混合类型
return 'mixed'; return 'mixed';
} }
protected function buildModelSchema(string $class): void
{
$reflect = new \ReflectionClass($class);
if ($reflect->isAbstract() || ! $reflect->isSubclassOf('\think\Model')) {
return;
}
try {
/** @var \think\Model $model */
$model = new $class;
$connection = $model->db()->getConnection();
if ($connection instanceof PDOConnection) {
$table = $model->getTable();
//预读字段信息
$connection->getSchemaInfo($table, true);
}
} catch (Exception $e) {
}
}
protected function buildDataBaseSchema(PDOConnection $connection, array $tables, string $dbName): void
{
foreach ($tables as $table) {
//预读字段信息
$connection->getSchemaInfo("{$dbName}.{$table}", true);
}
}
/**
* 缓存表
*/
private function cacheTable(string $table, ?string $connectionName = null): void
{
$connection = Db::connect($connectionName);
if (! $connection instanceof PDOConnection) {
throw new Exception('only PDO connection support schema cache!');
}
if (str_contains($table, '.')) {
[$dbName, $table] = explode('.', $table);
} else {
$dbName = $connection->getConfig('database');
}
if ($table == '*') {
$table = $connection->getTables($dbName);
}
$this->buildDataBaseSchema($connection, (array) $table, $dbName);
}
/**
* 缓存模型
*/
private function cacheModel(?string $dir = null): void
{
if ($dir) {
$modelDir = app_path('model') . $dir . DIRECTORY_SEPARATOR;
$namespace = 'app\\' . $dir;
} else {
$modelDir = app_path('model').DIRECTORY_SEPARATOR;
$namespace = 'app';
}
if (! is_dir($modelDir)) {
throw new InvalidArgumentException("{$modelDir} directory does not exist");
}
/** @var \SplFileInfo[] $iterator */
$iterator = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($modelDir, \RecursiveDirectoryIterator::SKIP_DOTS),
\RecursiveIteratorIterator::SELF_FIRST
);
foreach ($iterator as $fileInfo) {
$relativePath = substr($fileInfo->getRealPath(), strlen($modelDir));
if (! str_ends_with($relativePath, '.php')) {
continue;
}
// 去除 .php
$relativePath = substr($relativePath, 0, -4);
$class = '\\' . $namespace . '\\model\\' . str_replace('/', '\\', $relativePath);
if (! class_exists($class)) {
continue;
}
$this->buildModelSchema($class);
}
}
/**
* 获取默认目录名
* @return array<int, ?string>
*/
private function getDefaultDirs(): array
{
// 包含默认的模型目录
$dirs = [null];
return $dirs;
}
} }
+59 -9
View File
@@ -32,26 +32,76 @@ class User extends Command
*/ */
protected function execute(InputInterface $input, OutputInterface $output): int protected function execute(InputInterface $input, OutputInterface $output): int
{ {
$action = $input->getOption('action','login'); $action = $input->getOption('action');
if($action == 'login'){ if(method_exists($this, $action)){
$IM = new \support\OpenImSdk\Client([ return $this->$action($input, $output);
'host' => 'http://127.0.0.1:10002', // OpenIM API地址 }
'secret' => 'n1e5a6s6m7', // OpenIM密钥 cp('操作不存在:'.$action);
]); return 0;
}
function login(InputInterface $input, OutputInterface $output){
// $IM = new \support\OpenImSdk\Client([
// 'host' => 'http://127.0.0.1:10002', // OpenIM API地址
// 'secret' => 'n1e5a6s6m7', // OpenIM密钥
// ]);
$user_id = $input->getOption('user_id'); $user_id = $input->getOption('user_id');
if(!$user_id){ if(!$user_id){
return false; return false;
} }
$user = \support\Jwt::direct($user_id); $user = \support\Jwt::direct($user_id);
$imToken = $IM->auth->getUserToken($user['userID'],2); //$imToken = $IM->auth->getUserToken($user['userID'],2);
cp('userID:' . $user['id']); cp('userID:' . $user['id']);
cp('nickname:' . $user['nickname']); cp('nickname:' . $user['nickname']);
cp('token:' . $user['token']); cp('token:' . $user['token']);
cp('imToken:' . $imToken['token']); //cp('imToken:' . $imToken['token']);
return 0; return 0;
} }
cp('action not exist'); function build_team(InputInterface $input, OutputInterface $output){
$list = Db::name('user')->field('id')->order('id','asc')->select();
foreach($list as $k=>$user){
//team_total
$team_user_ids = Db::name('user_team')->where('descendant_id',$user['id'])
->where('depth','>',0)
->order('depth','ASC')
->column('ancestor_id');
Db::name('user_extend')->where('user_id',$user['id'])->data([
'team_total'=> count($team_user_ids)
])->save();
cache('team_user_count_'.$user['id'],count($team_user_ids));
$direct_use_count = Db::name('user')->where('parent_id',$user['id'])->count('id');
$vip_user_count = Db::name('user')->whereIn('id',$team_user_ids)->where('role_id','>',0)->count('id');
Db::name('user_extend')->where('user_id',$user['id'])->data([
'direct_total'=> $direct_use_count,
'vip_total'=> $vip_user_count
])->save();
cache('team_direct_total_'.$user['id'],$direct_use_count);
cache('team_vip_total_'.$user['id'],$vip_user_count);
$this->level_up($user['id'],$vip_user_count);
}
return 0; return 0;
}
protected function level_up($user_id,$count=0){
$levels = [
0,
500,
1000,
5000,
10000,
20000,
];
$level = 0;
foreach($levels as $k=>$v){
if($count>=$v){
$level= $k;
}else{
break;
}
}
Db::name('user')->where('id',$user_id)->data(['level'=>$level])->save();
} }
} }
-1
View File
@@ -153,7 +153,6 @@ class HookController{
public function callbackAfterUserOfflineCommand(Request $request): Response{ public function callbackAfterUserOfflineCommand(Request $request): Response{
$user_id = Input('userID'); $user_id = Input('userID');
//Db::name('user')->where('id',$user_id)->update(['online'=>0]);
return $this->success(); return $this->success();
} }
+6
View File
@@ -58,6 +58,10 @@ enum BalanceType: int
* 购买积分卡 * 购买积分卡
*/ */
case GIFT_BUY = 407; case GIFT_BUY = 407;
/**
* 购买积分卡
*/
case SEE_POINT_AWARD = 901;
/** /**
* 获取所有类型映射数组 * 获取所有类型映射数组
@@ -79,6 +83,8 @@ enum BalanceType: int
self::PURCHASE_ROLE->value => __('购买角色'), self::PURCHASE_ROLE->value => __('购买角色'),
self::GIFT_BUY->value => __('购买积分卡'), self::GIFT_BUY->value => __('购买积分卡'),
self::SEE_POINT_AWARD->value => __('见点奖'),
]; ];
} }
+103 -46
View File
@@ -4,35 +4,55 @@ use support\think\Db;
use Request; use Request;
use Symfony\Component\Console\Input\Input; use Symfony\Component\Console\Input\Input;
class User{ class User{
function register_successed($user){ function register_successed($user)
{
$_user = $user;
if(!is_array($_user)){
$_user = $_user->toArray();
}
$date = date('Y-m-d'); $date = date('Y-m-d');
cache_add('statistics_register_'.$date,1); cache_add('statistics_register_'.$date,1);
$saveData = [ $saveData = [
'invite_code' => build_invite_code($user->id), //'invite_code' => build_invite_code($_user['id']),
'userID' => \support\Encrypt::userIDencode($user->id) 'invite_code' => \support\Encrypt::userIDencode($_user['id']),
'userID' => \support\Encrypt::userIDencode($_user['id'])
]; ];
//管理直推人数和团队人数 //管理直推人数和团队人数
if($user->parent_id){ if($_user['parent_id']){
parent_info( $user->id,[ parent_info( $_user['id'],[
'id' => $user->parent_id, 'id' => $_user['parent_id'],
'username' => Db::name('user')->where('id',$user->parent_id)->value('username') 'username' => Db::name('user')->where('id',$_user['parent_id'])->value('username')
]);
//管理直推人数
cache_add('team_direct_total_'.$user->parent_id,1);
\app\model\UserExtend::where('user_id',$user->parent_id)->save([
'direct_total' => Db::raw('direct_total+1'),
'team_total' => Db::raw('team_total+1'),
]); ]);
//直属团队人数
Db::name('user_extend')->where('user_id',$_user['parent_id'])
->data([
'direct_total'=> Db::raw('direct_total+1')
])->save();
cache_add('team_direct_total_'.$_user['parent_id'],1);
//管理团队人数
$team_user_ids = Db::name('user_team')->where('descendant_id',$user['id'])
->where('depth','>',0)
->order('depth','ASC')
->column('ancestor_id');
Db::name('user_extend')->whereIn('user_id',$team_user_ids)->data([
'team_total'=> Db::raw('team_total+1')
])->save();
$list = Db::name('user_extend')->whereIn('user_id',$team_user_ids)->field('user_id,team_total')->select();
foreach($list as $v){
cache('team_user_count_'.$v['user_id'],$v['team_total']);
}
} }
\app\model\User::where('id',$user->id)->update($saveData); \app\model\User::where('id',$_user['id'])->update($saveData);
//创建扩展数据 //创建扩展数据
\app\model\UserExtend::create([ Db::name('user_extend')->replace()->insert([
'user_id' => $user->id, 'user_id' => $_user['id'],
'direct_total' => 0,
'team_total' => 0,
'consume' => 0, 'consume' => 0,
'sales' => 0 // 'profile_banner' => '',
// 'moments_banner' => '',
// 'moments_allow_view_days'=>0,
]); ]);
@@ -44,41 +64,51 @@ class User{
* @var \support\OpenImSdk\Client $IM * @var \support\OpenImSdk\Client $IM
*/ */
$IM = request()->IM; $IM = request()->IM;
log_alert($data['userID']);
$imToken = $IM->auth->getUserToken($data['userID'],Input('platform')); $imToken = $IM->auth->getUserToken($data['userID'],Input('platform'));
$data['imToken'] = $imToken['token']; $data['imToken'] = $imToken['token'];
return $data; return $data;
} }
protected function level_up($user_id,$count=0){
$levels = [
0,
500,
1000,
5000,
10000,
20000,
];
$level = 0;
foreach($levels as $k=>$v){
if($count>=$v){
$level= $k;
}else{
break;
}
}
Db::name('user')->where('id',$user_id)->data(['level'=>$level])->save();
}
function profile($user=[]){ function profile($user=[]){
$data = $user; $data = $user;
if(!is_array($data)){ if(!is_array($data)){
$data = $data->toArray(); $data = $data->toArray();
} }
$role_arr = [
'1' => __('普通用户'),
'2' => __('VIP'),
];
$data['has_trade_password'] = $data['trade_password'] ? true: false;
$data['avatar'] = cdnurl($data['avatar']);
$disallowFields = ['trade_password','password','client','loginfailure'];
$data = array_diff_key($data, array_flip($disallowFields));
$data['recharge_total'] = cache('user_recharge_total_'.$data['id'])?:0;
$data['withdrawl_total'] = cache('user_withdrawl_total_'.$data['id'])?:0;
$data['income_total'] = cache('user_income_total_'.$data['id'])?:0;
$data['today_income'] = cache('user_today_income_'.date('Ymd').'_'.$data['id'])?:0;
$data['month_income'] = cache('user_month_income_'.date('Ym').'_'.$data['id'])?:0;
$data['consume_total'] = cache('user_consume_total_'.$data['id'])?:0;
$data['power_total'] = cache('user_power_total_'.$data['id'])?:0;
$data['role_reward_total'] = cache('user_role_reward_total_'.$data['id'])?:0;
$data['avatar'] = $data['avatar']?:"/static/img/avatar.png";
$data['role'] = isset($role_arr[$data['role_id']]) ? $role_arr[$data['role_id']] : __('普通用户');//\app\model\UserRole::where('id',$data['role_id'])->value('name');
$last_see = $last_see ?? cache('last_see_'.$data['id']); $last_see = $last_see ?? cache('last_see_'.$data['id']);
$count = 0; $count = 0;
$data['friend_settings'] = [ $ff = [
'unread_count' => $count ??0, 'unread_count' => 0,
'userHeadImg' => null, 'userHeadImg' => null,
]; ];
try {
$ff = Db::name('user_extend')->where('user_id',$user->id)->field('moments_allow_view_days,profile_banner,moments_banner')->find();
$data['moments_allow_view_days'] = $ff['moments_allow_view_days'];
$data['moments_banner'] = $ff['moments_banner'];
$data['profile_banner'] = $ff['profile_banner'];
$ff['userHeadImg'] = $ff['moments_banner'];
} catch (\Exception $e) {
}
$ff['unread_count'] = $count ?:0;
$data['friend_settings'] = $ff;
return $data; return $data;
} }
function changepwd_successed($data=[]){ function changepwd_successed($data=[]){
@@ -94,18 +124,45 @@ class User{
return $data; return $data;
} }
//用户角色组变化 //用户角色组变化
function roleup($user=[]){ function role_up($user=[]){
$data = $user; $data = $user;
if(!is_array($data)){ if(!is_array($data)){
$data = $data->toArray(); $data = $data->toArray();
} }
if(!$user->active){ //旗下会员总数
$user->active = 1; $team_user_ids = Db::name('user_team')->where('descendant_id',$user['id'])
$user->save(); ->where('depth','>',0)
cache_add('team_direct_total_'.$user->parent_id,1); ->order('depth','ASC')
->column('ancestor_id');
Db::name('user_extend')->whereIn('user_id',$team_user_ids)
->data([
'vip_total'=> Db::raw('vip_total+1')
])
->save();
$list = Db::name('user_extend')->whereIn('user_id',$team_user_ids)->field('user_id,vip_total')->select();
foreach($list as $v){
cache('team_vip_total_'.$v['user_id'],$v['vip_total']);
$this->level_up($v['user_id'],$v['vip_total']);
} }
// if(!$user->active){
// $user->active = 1;
// $user->save();
// cache_add('team_direct_total_'.$user->parent_id,1);
// }
return $user; return $user;
} }
//用户角色组变化
function role_buy($data=[])
{
// $data = [
// 'role_id'=>1,
// 'user_id'=>100008,
// 'amount'=>1000
// ];
//addJob($data,'Settlement');
}
function buildTeam($user){ function buildTeam($user){
+36 -24
View File
@@ -179,7 +179,7 @@ if (!function_exists('captcha_verfiy')) {
$list = cache($cache_key); $list = cache($cache_key);
$list = $list ?: []; $list = $list ?: [];
if (!isset($list[$code])) { if (!isset($list[$code])) {
abort(__('Captcha is incorrect').$cache_key.$code); abort(__('Captcha is incorrect'));
} }
if ($list[$code] + $expris < time()) { if ($list[$code] + $expris < time()) {
unset($list[$code]); unset($list[$code]);
@@ -456,50 +456,62 @@ if (!function_exists('msectime')) {
if (!function_exists('cache_add')) { if (!function_exists('cache_add')) {
function cache_add($key, $value=1, $tag = null) function cache_add($key, $value=1, $tag = null)
{ {
if (substr($key, 0, 20) == 'user_recharge_total_') { if (str_starts_with($key, 'user_recharge_total_')) {
$tag = 'recharge_total'; $tag = 'recharge_total';
} }
if (substr($key, 0, 20) == 'user_recharge_total_') { if (str_starts_with($key, 'user_income_total_')) {
$tag = 'recharge_total';
}
if (substr($key, 0, 17) == 'user_power_total_') {
$tag = 'user_power_total';
}
if (substr($key, 0, 18) == 'user_income_total_') {
$tag = 'income_total'; $tag = 'income_total';
} }
if (substr($key, 0, 16) == 'user_play_count_') { if (str_starts_with($key, 'user_consume_total_')) {
$tag = 'play_count';
}
if (substr($key, 0, 19) == 'user_consume_total_') {
$tag = 'consume_total'; $tag = 'consume_total';
} }
if (substr($key, 0, 18) == 'team_member_total_') { if (str_starts_with($key, 'team_user_total_')) {
$tag = 'team_member_total'; $tag = 'team_user_total';
} }
if (substr($key, 0, 18) == 'team_direct_total_') { if (str_starts_with($key, 'team_direct_total_')) {
$tag = 'team_direct_total'; $tag = 'team_direct_total';
} }
if (substr($key, 0, 20) == 'team_recharge_total_') { if (str_starts_with($key, 'team_vip_total_')) {
$tag = 'team_vip_total';
}
if (str_starts_with($key, 'team_recharge_total_')) {
$tag = 'team_recharge_total'; $tag = 'team_recharge_total';
} }
if (substr($key, 0, 21) == 'team_withdrawl_total_') { if (str_starts_with($key, 'team_withdrawl_total_')) {
$tag = 'team_withdrawl_total'; $tag = 'team_withdrawl_total';
} }
if (substr($key, 0, 18) == 'team_income_total_') { if (str_starts_with($key, 'team_income_total_')) {
$tag = 'team_income_total'; $tag = 'team_income_total';
} }
if (substr($key, 0, 16) == 'team_play_count_') { if (str_starts_with($key, 'team_consume_total_')) {
$tag = 'team_play_count';
}
if (substr($key, 0, 19) == 'team_consume_total_') {
$tag = 'team_consume_total'; $tag = 'team_consume_total';
} }
cache($key, (cache($key) ?? 0) + $value, null, $tag); $old_value = cache_get($key);
cache($key,$old_value + $value, null, $tag);
} }
} }
if(!function_exists('cache_get')){
function cache_get(string $key,bool $force=false):mixed{
$ret= cache($key) ?: 0;
if(!$ret || $force){
if (str_starts_with($key, 'team_user_total_')) {
$user_id = substr($key,strlen('team_user_total_'));
$ret = \support\think\Db::name('user_extend')->where('user_id',$user_id)->column('team_total');
}else if (str_starts_with($key, 'team_direct_total_')) {
$user_id = substr($key,strlen('team_direct_total_'));
$ret = \support\think\Db::name('user_extend')->where('user_id',$user_id)->column('direct_total');
}else if (str_starts_with($key, 'team_vip_total_')) {
$user_id = substr($key,strlen('team_vip_total_'));
$ret = \support\think\Db::name('user_extend')->where('user_id',$user_id)->column('vip_total');
}
cache($key,$ret);
}
return $ret;
}
}
if (!function_exists('build_invite_code')) { if (!function_exists('build_invite_code')) {
function build_invite_code($id = '') function build_invite_code($id = '')
{ {
+45
View File
@@ -0,0 +1,45 @@
<?php
namespace app\model;
use app\model\Base;
/**
* @property integer $id 主键(ID) - 无注释
* @property integer $user_id 用户ID
* @property string $content_type 内容类型 enum('text', 'image', 'file', 'video', 'link')
* @property string $content 收藏内容本体
* @property array $tags 用户自定义标签
* @property bool $is_pinned 是否置顶
* @property integer $status 状态
* @property integer $created_at 创建时间
* @property integer $updated_at 更新时间
*/
class Collection extends Base
{
public function user()
{
return $this->belongsTo('User', 'user_id', 'id');//->setEagerlyType(0);
}
public function setTagsAttr($v)
{
if(is_array($v)){
return implode(',',$v);
}
return $v;
}
public function getTagsAttr($v)
{
if($v && is_string($v)){
return explode(',',$v);
}
return null;
}
function getStatusList(){
return [
'0' => '兑换中',
'1' => '成功',
'-1' => '失败',
];
}
}
+1
View File
@@ -6,6 +6,7 @@ use app\model\Base;
/** /**
* @property integer $user_id 用户ID * @property integer $user_id 用户ID
* @property integer $direct_total 直推数量 * @property integer $direct_total 直推数量
* @property integer $vip_total VIP数量
* @property integer $team_total 团队成员数量 * @property integer $team_total 团队成员数量
* @property float $consume 消费统计 * @property float $consume 消费统计
* @property float $sales 销售额 * @property float $sales 销售额
+78
View File
@@ -0,0 +1,78 @@
<?php
namespace app\queue\single;
use support\think\Db;
use Webman\RedisQueue\Consumer;
/**
* 团队奖励
*/
class Settlement implements Consumer
{
// 要消费的队列名
public $queue = 'Settlement';
// 连接名,对应 plugin/webman/redis-queue/redis.php 里的连接`
public $connection = 'default';
// 消费
public function consume($data)
{
$commission_rates = [0,0.05,0.1,0.15,0.2,0.25];
$amount = $data['amount'];
$user_id = $data['user_id'];
$list = Db::name('user_team')
->alias('ut')
->where('ut.ancestor_id',$user_id)
->join('user as u','u.id=ut.descendant_id')
->field('u.id,u.level')
->where('ut.depth','>',0)
->order('ut.depth','asc')
->column('ut.descendant_id');
$yifen = 0;
$last_level = 0;
foreach($list as $user){
if($user['level']<=$last_level){
$this->log('用户ID:'.$user['id'].'\t,等级:'.$user['level'].'\t,$last_level:'.$last_level.'\t跳过');
continue;
}
$last_level = $user['level'];
$commission_rate = $commission_rates[$user['level']] - $yifen;
if($commission_rate>0){
$yifen += $commission_rate;
$commission_amount = bcmul($amount,$commission_rate,4);
$this->log('用户ID:'.$user['id'].'\t,等级:'.$user['level'].'\t,比例:'.$commission_rate.'\t,金额:'.$commission_amount);
//\app\model\User::score($user['id'],$commission_amount,\app\enum\BalanceType::SEE_POINT_AWARD,'团队奖励:'.$user_id);
}
//已经是最大等级了
if($yifen>=0.25 || $user['level'] == 5){
$this->log('$yifen:'.$yifen.'\t,结束');
break;
}
}
// 无需反序列化
//var_export($data); // 输出 ['to' => 'tom@gmail.com', 'content' => 'hello']
}
function log($data){
\support\Log::info('settlement')->info(json_encode($data));
}
// 消费失败回调
/*
$package = [
'id' => 1357277951, // 消息ID
'time' => 1709170510, // 消息时间
'delay' => 0, // 延迟时间
'attempts' => 2, // 消费次数
'queue' => 'send-mail', // 队列名
'data' => ['to' => 'tom@gmail.com', 'content' => 'hello'], // 消息内容
'max_attempts' => 5, // 最大重试次数
'error' => '错误信息' // 错误信息
]
*/
public function onConsumeFailure(\Throwable $e, $package)
{
if($package['attempts'] >= $package['max_attempts']){
$this->log($package['data']);
}
}
}
+23 -11
View File
@@ -10,9 +10,9 @@
{literal} {literal}
<style> <style>
:root { :root {
--primary: #4361ee; --primary: #27ba57;
--primary-light: #ebf0ff; --primary-light: #f4fff7;
--secondary: #3f37c9; --secondary: #27ba57;
--success: #4cc9f0; --success: #4cc9f0;
--text: #2b2d42; --text: #2b2d42;
--text-light: #8d99ae; --text-light: #8d99ae;
@@ -39,7 +39,7 @@
min-height: 100vh; min-height: 100vh;
line-height: 1.6; line-height: 1.6;
padding: 20px; padding: 20px;
background-image: radial-gradient(at 0 0,rgba(var(--primary-rgb),0.05) 0,transparent 50%),radial-gradient(at 100% 100%,rgba(var(--secondary-rgb),0.05) 0,transparent 50%) _background-image: radial-gradient(at 0 0,rgba(var(--primary-rgb),0.05) 0,transparent 50%),radial-gradient(at 100% 100%,rgba(var(--secondary-rgb),0.05) 0,transparent 50%)
} }
.container { .container {
@@ -56,6 +56,7 @@
.container::before { .container::before {
content: ''; content: '';
position: absolute; position: absolute;
display: none;
top: 0; top: 0;
left: 0; left: 0;
width: 100%; width: 100%;
@@ -93,11 +94,12 @@
font-size: 24px; font-size: 24px;
font-weight: 700; font-weight: 700;
color: var(--white); color: var(--white);
margin-bottom: 8px margin-bottom: 8px;
display: none;
} }
.header p { .header p {
font-size: 15px font-size: 14pt
} }
.card { .card {
@@ -151,6 +153,11 @@
background-color: var(--white); background-color: var(--white);
color: var(--text) color: var(--text)
} }
.form-control[readonly] {
background-color: var(--border);
color: #999;
cursor: not-allowed
}
.form-control:focus { .form-control:focus {
border-color: var(--primary); border-color: var(--primary);
@@ -824,10 +831,10 @@
<input type="tel" name="mobile" lay-reqtext="请输入您的手机号码" id="register-phone" class="form-control" placeholder="请输入您的手机号码" required lay-verify="required|phone"> <input type="tel" name="mobile" lay-reqtext="请输入您的手机号码" id="register-phone" class="form-control" placeholder="请输入您的手机号码" required lay-verify="required|phone">
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group" style="display: none;">
<label for="register-password">我的昵称</label> <label for="register-password">我的昵称</label>
<div class="input-wrapper"> <div class="input-wrapper">
<input type="text" name="nickname" lay-reqtext="请设置您的昵称" id="register-nickname" class="form-control" placeholder="怎么称呼您" required lay-verify="required"> <input type="text" name="nickname" lay-reqtext="请设置您的昵称" id="register-nickname" class="form-control" placeholder="给自己取一个好听的昵称吧" required lay-verify="required">
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
@@ -854,13 +861,18 @@
required lay-verify="required|repassword"> required lay-verify="required|repassword">
</div> </div>
</div> </div>
<div class="form-group">
<label for="register-password">邀请码</label>
<div class="input-wrapper">
<input type="text" readonly class="form-control" lay-reqtext="参数错误" value="{$invite_code}" name="invite_code" required lay-verify="required">
</div>
</div>
<div class="form-group"> <div class="form-group">
<button type="button" class="btn" lay-submit lay-filter="register-form"><span>注册</span></button> <button type="button" class="btn" lay-submit lay-filter="register-form"><span>注册</span></button>
</div> </div>
<input type="hidden" lay-reqtext="参数错误" value="{$invite_code}" name="invite_code" required lay-verify="required">
</form> </form>
<div class="other-options"> <div class="other-options">
已有账号?<a href="/shunliao336.apk" id="switch-to-login">立即下载APP</a> 已有账号?<a href="/" id="switch-to-login">立即下载APP</a>
</div> </div>
</div> </div>
<div class="footer"> <div class="footer">
@@ -882,7 +894,7 @@
<i class="layui-icon layui-icon-username"></i> <i class="layui-icon layui-icon-username"></i>
<span id="username">Cansnow</span> <span id="username">Cansnow</span>
</div> </div>
<a href="/shunliao336.apk" class="btn">下载</a> <a href="/" class="btn">下载</a>
</div> </div>
</div> </div>
</body> </body>
+5 -4
View File
@@ -22,14 +22,15 @@ return [
'user.delete_successed' => [ 'user.delete_successed' => [
[app\event\User::class, 'delete_successed'], [app\event\User::class, 'delete_successed'],
], ],
'user.roleup' => [ 'user.role_up' => [
[app\event\User::class, 'roleup'], [app\event\User::class, 'role_up'],
],
'user.role_buy' => [
[app\event\User::class, 'role_buy'],
], ],
'product.buy' => [ 'product.buy' => [
[app\event\Product::class, 'buy'] [app\event\Product::class, 'buy']
], ],
'role.buy' => [
],
'recharge.success' => [ 'recharge.success' => [
[app\event\Recharge::class, 'success'] [app\event\Recharge::class, 'success']
], ],
+16
View File
@@ -77,5 +77,21 @@ return [
], ],
] ]
], ],
],
'settlement' => [
'handlers' => [
[
'class' => Monolog\Handler\RotatingFileHandler::class,
'constructor' => [
runtime_path() . '/logs/settlement.log',
7,
Monolog\Logger::INFO,
],
'formatter' => [
'class' => Monolog\Formatter\LineFormatter::class,
'constructor' => [null, 'Y-m-d H:i:s', true],
],
]
],
] ]
]; ];
+9 -1
View File
@@ -32,7 +32,8 @@ define(['table', 'upload','form','qrcode'], function (Table,Upload,Form) {
{ {
field: 'userID', field: 'userID',
title: 'userID', title: 'userID',
filter: "string" filter: "string",
sortable: true // 是否排序
}, },
{ {
title: "上级", title: "上级",
@@ -63,6 +64,13 @@ define(['table', 'upload','form','qrcode'], function (Table,Upload,Form) {
}, },
visible: false visible: false
}, },
{
field: 'invite_code',
title: '邀请码',
filter: "string",
sortable: true, // 是否排序
visible: false
},
// { // {
// title: "等级", // title: "等级",
// field: "level", // field: "level",
+38 -15
View File
@@ -11,7 +11,7 @@
<meta http-equiv="Content-Language" content="zh-cn"> <meta http-equiv="Content-Language" content="zh-cn">
<meta name="format-detection" content="telephone=no"> <meta name="format-detection" content="telephone=no">
<link rel="shortcut icon" type="image/x-icon" href="//shunliao.oss-accelerate.aliyuncs.com/web/favicon.ico"> <link rel="shortcut icon" type="image/x-icon" href="//shunliao.oss-accelerate.aliyuncs.com/web/favicon.ico">
<link href="//shunliao.oss-accelerate.aliyuncs.com/web/css/main.7900cfef1d624159734a.css?v=1" rel="stylesheet"> <link href="//shunliao.oss-accelerate.aliyuncs.com/web/css/main.7900cfef1d624159734a.css?v=3" rel="stylesheet">
</head> </head>
@@ -58,17 +58,23 @@
</div> </div>
<div class="download-list"> <div class="download-list">
<div class="download-list__btn-group"> <div class="download-list__btn-group">
<a class="btn android" url="" href="javascript:onClickDownload();" ><img class="btn__img" <a class="btn android" url="" href="javascript:onClickDownload('android');" ><img class="btn__img"
src="//shunliao.oss-accelerate.aliyuncs.com/web/img/icon_icon_brands_android@2x.png" alt=""> src="//shunliao.oss-accelerate.aliyuncs.com/web/img/icon_icon_brands_android@2x.png" alt="">
<p class="text">Android</p> <p class="text">Android</p>
</a> </a>
</div> </div>
<div class="download-list__btn-group"> <div class="download-list__btn-group">
<a class="btn ios" url="" href="javascript:onClickDownload();"> <a class="btn ios" url="" href="javascript:onClickDownload('ios');">
<img class="btn__img" src="//shunliao.oss-accelerate.aliyuncs.com/web/img/icon_icon_brands_apple@2x.png" alt=""> <img class="btn__img" src="//shunliao.oss-accelerate.aliyuncs.com/web/img/icon_icon_brands_apple@2x.png" alt="">
<p class="text">iOS下载</p> <p class="text">iOS下载</p>
</a> </a>
</div> </div>
<div class="download-list__btn-group">
<a class="btn ios" url="" href="javascript:onClickDownload('web');">
<img class="btn__img" src="//shunliao.oss-accelerate.aliyuncs.com/web/img/web.png" alt="">
<p class="text">H5</p>
</a>
</div>
<!-- <div class="download-tutorial-btn" data-position="top"> <!-- <div class="download-tutorial-btn" data-position="top">
<div class="download-tutorial-btn__img"></div> <div class="download-tutorial-btn__img"></div>
<p class="download-tutorial-btn__text">iOS 下载教学</p> <p class="download-tutorial-btn__text">iOS 下载教学</p>
@@ -508,7 +514,22 @@
</div> </div>
<script src="//shunliao.oss-accelerate.aliyuncs.com/web/js/swiper-bundle.min.js"></script> <script src="//shunliao.oss-accelerate.aliyuncs.com/web/js/swiper-bundle.min.js"></script>
<!-- <script src="//shunliao.oss-accelerate.aliyuncs.com/web/img/suoyan/statistics.js.下载"></script> --> <!-- <script src="//shunliao.oss-accelerate.aliyuncs.com/web/img/suoyan/statistics.js.下载"></script> -->
<div class="dialog" id="ios_download_tips">
<div class="container">
<div class="header">
<h1>温馨提示</h1>
<a class="close" href="javascript:closeDownloadTipsDialog();"></a>
</div>
<div class="body">
<p>苹果企业签的用户请注意,安装软件时会提示您重启手机,该重启要求为苹果系统(iOS 18+/iPadOS 18+/visionOS 2+)安全强制规则,非应用本身问题,按提示完成重启即可正常使用。</p>
<img src="//shunliao.oss-accelerate.aliyuncs.com/web/img/ios_tips@2x.png" />
</div>
<div class="footer">
<button id="ios_download_button" onclick="iosDownload()">我已阅读并知晓,去下载</button>
</div>
</div>
</div>
<div class="overlay" id="ios_download_tips_overlay"></div>
<script> <script>
let _swp = null let _swp = null
let swpMode = 'horizontal' let swpMode = 'horizontal'
@@ -525,20 +546,22 @@
// } // }
// } // }
const onClickDownload = () => { const onClickDownload = (platform) => {
const $nav = navigator.userAgent.toLowerCase(); if(platform == 'android'){
if($nav.match(/iphone/i) == "iphone"){
//alert('暂未开放');
//window.location.href = 'https://shunliao.oss-accelerate.aliyuncs.com';
//window.location.href = 'https://static.shun777.com/app/shunliao.ipa';
window.location.href = 'https://h5.shun777.com';
}else{
//window.location.href = 'https://shunliao.oss-accelerate.aliyuncs.com/apk/install.apk';
window.location.href = 'https://static.shun777.com/app/shunliao_v4.apk';
//https://d1facirggd28tx.cloudfront.net/shunliao580.apk //https://d1facirggd28tx.cloudfront.net/shunliao580.apk
//window.location.href = '/shunliao.apk?v=2'; window.location.href = '{:Config('site.android_download_url')}';
}else if(platform == 'ios'){
document.querySelector('#ios_download_tips').classList.add('show');
}else{
window.location.href = '{:Config('site.h5_download_url')}';
} }
} }
const closeDownloadTipsDialog = ()=>{
document.querySelector('#ios_download_tips').classList.remove('show');
}
const iosDownload = ()=>{
window.open('{:Config('site.ios_download_url')}');
}
const showmenus = () => { const showmenus = () => {
document.getElementById("mobile-menus").style.display = "block" document.getElementById("mobile-menus").style.display = "block"
@@ -1,6 +1,7 @@
<?php <?php
return array ( return array (
'Account is incorrect' => '账号不正确', 'Account is incorrect' => '账号不正确',
'Password is incorrect' => '密码不正确',
'Email is incorrect' => '邮箱地址不正确', 'Email is incorrect' => '邮箱地址不正确',
'Mobile is incorrect' => '手机号不正确', 'Mobile is incorrect' => '手机号不正确',
'Invalid trade password' => '交易密码无效', 'Invalid trade password' => '交易密码无效',
+24 -12
View File
@@ -21,12 +21,10 @@ class Jwt
protected static $config = []; protected static $config = [];
protected static $options = []; protected static $options = [];
protected static $disallowFields = [ protected static $disallowFields = [
'password','trade_password','totp_secret',//安全信息 'trade_password','password','empty_password',
'created_at','updated_at','loginfailure',//系统信息 'client','loginfailure',
//'last_time','last_ip','join_time','join_ip',最近登录信息 'currency1','currency2','currency3','currency4','currency5','currency6','currency7','currency8','currency9',
//'score', 'token','prev_time','loginfailure','successions','maxsuccessions',
'level',
]; ];
/** /**
* 注册用户 * 注册用户
@@ -296,7 +294,7 @@ class Jwt
]; ];
$_token = \support\Jwt\JwtToken::generateToken($_user); $_token = \support\Jwt\JwtToken::generateToken($_user);
$user->token = $_token['access_token']; $user->token = $_token['access_token'];
return self::getUserinfo($user); return self::getUserinfo($user,true);
} catch (\Exception $e) { } catch (\Exception $e) {
Db::rollback(); Db::rollback();
self::setError($e->getMessage()); self::setError($e->getMessage());
@@ -356,13 +354,27 @@ class Jwt
/** /**
* 获取会员基本信息 * 获取会员基本信息
*/ */
public static function getUserinfo($user=null) public static function getUserinfo($user=null,$withToken = false)
{ {
$data = self::getUser($user)->toArray(); $data = self::getUser($user);
if(!is_array($data)){
$data = $data->toArray();
}
$data['has_trade_password'] = $data['trade_password'] ? true: false;
$data['has_empty_password'] = $data['empty_password'] ? true: false;
$role_arr = [
'1' => __('普通用户'),
'2' => __('VIP'),
'3' => __('SVIP1'),
'4' => __('SVIP2'),
];
$data['role'] = isset($role_arr[$data['role_id']]) ? $role_arr[$data['role_id']] : __('普通用户');
$disallowFields = self::getDisallowFields(); $disallowFields = self::getDisallowFields();
$userinfo = array_diff_key($data, array_flip($disallowFields)); $data = array_diff_key($data, array_flip($disallowFields));
$userinfo['has_trade_password'] = $data['trade_password'] ? true: false; if($withToken){
return $userinfo; $data['token'] = $user->token;
}
return $data;
} }
/** /**