This commit is contained in:
2026-04-04 08:52:59 +08:00
parent 66bcd8061a
commit d98ac8f146
33 changed files with 2565 additions and 328 deletions
+1
View File
@@ -9,3 +9,4 @@ app/command/Test.php
runtime runtime
vendor vendor
public/shunliao.apk public/shunliao.apk
.user.ini
+1 -1
View File
@@ -1 +1 @@
open_basedir=/www/wwwroot/admin/:/tmp/ open_basedir=/www/wwwroot/im/admin/:/tmp/
+96
View File
@@ -0,0 +1,96 @@
<?php
namespace app\api\controller;
use support\Request;
use support\Response;
use hg\apidoc\annotation as Apidoc;
use app\model\User;
use app\model\Album as AlbumModel;
/**
* 群相册
*/
class AlbumController extends BaseController
{
public $noNeedAuth = ['*'];
public $noNeedLogin = [];
/**
* @Apidoc\Title("群相册列表")
* @Apidoc\Method("POST")
* @Apidoc\Param("group_id", type="string", require=true, desc="群ID")
* @Apidoc\Param("offset", type="int", require=false, desc="偏移量,和页码二选一",default=0)
* @Apidoc\Param("page", type="int", require=false, desc="页码",default=1)
* @Apidoc\Param("limit", type="int", require=true, desc="分页大小",default=10)
*/
function list(Request $request): Response
{
$user = \support\Jwt::getUser();
$limit = $request->post('limit',10);
$offset = $request->post('offset',0);
$group_id = $request->post('groupID') ?:$request->post('group_id');
//$ls = $this->get_user_in_group($group_id);
$query = AlbumModel::where('group_id',$group_id)
->order('id','desc');
if($offset){
$list = $query->where('id','<',$offset)->limit($offset,$limit);
}else{
$list = $query->paginate($limit);
}
return $this->success('ok',$list);
}
/**
* @Apidoc\Title("创建相册")
* @Apidoc\Method("POST")
* @Apidoc\Param("groupID", type="string", require=true, desc="群ID")
* @Apidoc\Param("title", type="string", require=true, desc="标题")
* @Apidoc\Param("image", type="int", require=false, desc="封面ID")
*/
function create(Request $request): Response
{
$user_id = \support\Jwt\JwtToken::getCurrentId();
$data = [
'user_id' => $user_id,
'group_id' => $request->post('groupID'),
'title' => input('title'),
'image' => input('image'),
];
$result = AlbumModel::create($data);
return $this->success('ok',$result);
}
/**
* @Apidoc\Title("更新")
* @Apidoc\Method("POST")
* @Apidoc\Param("id", type="string", require=true, desc="ID")
* @Apidoc\Param("title", type="string", require=true, desc="标题")
* @Apidoc\Param("image", type="int", require=false, desc="封面ID")
*/
function update(Request $request): Response
{
$id = $request->input('id');
$image = $request->input('image');
$title = $request->input('title');
$album = AlbumModel::find($id);
if($title){
$album->title = $title;
}
if($image){
$album->image = $image;
}
$album->save();
return $this->success('ok',$album);
}
/**
* @Apidoc\Title("删除")
* @Apidoc\Method("POST")
* @Apidoc\Param("id", type="string", require=true, desc="ID")
*/
function delete(Request $request): Response
{
$id = Input('id');
$album = AlbumModel::whereIn('id',condition: $id)->find();
$album->delete();
return $this->success('ok');
}
}
@@ -90,4 +90,17 @@ class CollectionController extends BaseController{
return $this->success('发布成功', ['collection' => $collection]); return $this->success('发布成功', ['collection' => $collection]);
} }
/**
* 删除收藏
* @Apidoc\Param("id", type="int",require=true, desc="收藏id")
* @param Request $request
* @return Response
*/
function delete(Request $request): Response
{
$user = \support\Jwt::getUser();
$id = $request->post('id');
CollectionModel::where('id',$id)->where('user_id',$user->id)->delete();
return $this->success('删除成功');
}
} }
+61 -11
View File
@@ -56,6 +56,33 @@ class CommonController extends BaseController{
$config['recharge_status_list'] = \app\enum\RechargeStatus::toArray(); $config['recharge_status_list'] = \app\enum\RechargeStatus::toArray();
$config['withdrawl_status_list'] = \app\enum\WithdrawlStatus::toArray(); $config['withdrawl_status_list'] = \app\enum\WithdrawlStatus::toArray();
$config['server_status_list'] = \app\enum\ServerStatus::toArray(); $config['server_status_list'] = \app\enum\ServerStatus::toArray();
$config['see_point_awards'] = [
[
'name'=>'S1',
'award'=>0.05,
'total'=>50,
],
[
'name'=>'S2',
'award'=>0.1,
'total'=>100,
],
[
'name'=>'S3',
'award'=>0.15,
'total'=>1000,
],
[
'name'=>'S4',
'award'=>0.2,
'total'=>5000,
],
[
'name'=>'S5',
'award'=>0.25,
'total'=>20000,
]
];
//$config['getFriendList'] = $request->IM->friend->getFriendList('100006'); //$config['getFriendList'] = $request->IM->friend->getFriendList('100006');
return $this->success(__('successful'), $config); return $this->success(__('successful'), $config);
} }
@@ -97,7 +124,9 @@ class CommonController extends BaseController{
* 注册会员 * 注册会员
* *
* @Apidoc\Method ("POST") * @Apidoc\Method ("POST")
* @Apidoc\Param("type", type="string",require=true, desc="注册方式:email,mobile")
* @Apidoc\Param("email", type="string",require=true, desc="邮箱") * @Apidoc\Param("email", type="string",require=true, desc="邮箱")
* @Apidoc\Param("mobile", type="string",require=true, desc="手机号码")
* @Apidoc\Param("password", type="string",require=true, desc="密码") * @Apidoc\Param("password", type="string",require=true, desc="密码")
* @Apidoc\Param("trade_password", type="string",require=true, desc="交易密码") * @Apidoc\Param("trade_password", type="string",require=true, desc="交易密码")
* @Apidoc\Param("invite_code", type="string",require=true, desc="推荐码") * @Apidoc\Param("invite_code", type="string",require=true, desc="推荐码")
@@ -121,7 +150,7 @@ class CommonController extends BaseController{
} }
$username = $email; $username = $email;
unset($mobile); unset($mobile);
//captcha_verfiy('email','register',$email,false); captcha_verfiy('email','register',$email,false);
} }
if ($type == 'mobile') { if ($type == 'mobile') {
if(!$mobile || !Validate::regex($mobile, "^1\d{10}$")){ if(!$mobile || !Validate::regex($mobile, "^1\d{10}$")){
@@ -129,10 +158,10 @@ class CommonController extends BaseController{
} }
$username = $mobile; $username = $mobile;
unset($email); unset($email);
//captcha_verfiy('mobile','register',$mobile,false); captcha_verfiy('mobile','register',$mobile,false);
} }
if ($type == 'username') { if ($type == 'username') {
if(!$email || !Validate::is($email, "email")){ if(!$username){
return $this->error(__('Username is incorrect')); return $this->error(__('Username is incorrect'));
} }
} }
@@ -151,9 +180,18 @@ class CommonController extends BaseController{
'role_id' => 1, 'role_id' => 1,
'group_id' => 0, 'group_id' => 0,
'region' => '86', 'region' => '86',
'nickname' => input('nickname','用户_'.substr($username,7)), 'nickname' => input('nickname'),
'avatar' => '/static/avatar/'.rand(0,17).'.png', 'avatar' => '/static/avatar/'.rand(0,17).'.png',
]; ];
if(empty($extends['nickname'])){
if($type == 'mobile'){
$extends['nickname'] = '用户_'.substr($username,7);
}else if($type == 'email'){
$extends['nickname'] = '用户_'.substr(explode('@',$username)[0],7);
}else{
$extends['nickname'] = $username;
}
}
if ($invite_code) { if ($invite_code) {
if(strlen($invite_code) == 12){ if(strlen($invite_code) == 12){
//系统生产的一次性推荐吗 //系统生产的一次性推荐吗
@@ -203,10 +241,12 @@ class CommonController extends BaseController{
* 登录 * 登录
* @Apidoc\Method("POST") * @Apidoc\Method("POST")
* @Apidoc\Param("username", type="string",require=false, desc="用户名登录必填") * @Apidoc\Param("username", type="string",require=false, desc="用户名登录必填")
* @Apidoc\Param("email", type="string",require=false, desc="邮箱登录必填")
* @Apidoc\Param("mobile", type="string",require=false, desc="手机号登录必填") * @Apidoc\Param("mobile", type="string",require=false, desc="手机号登录必填")
* @Apidoc\Param("type", type="string",require=true,default="mobile",desc="登录方式,username,mobile,email") * @Apidoc\Param("type", type="string",require=true,default="mobile",desc="登录方式,username,mobile,email")
* @Apidoc\Param("password", type="string",require=false, desc="密码的登录必填") * @Apidoc\Param("password", type="string",require=false, desc="密码的登录必填")
* @Apidoc\Param("code", type="string",require=false, desc="验证码登录必填") * @Apidoc\Param("code", type="string",require=false, desc="验证码登录必填")
* @Apidoc\Param("platform", type="string",require=false, desc="平台",default="web")
* @Apidoc\Param("region", type="string",require=false,default="86", desc="区域,手机号登录必填") * @Apidoc\Param("region", type="string",require=false,default="86", desc="区域,手机号登录必填")
*/ */
public function login(Request $request){ public function login(Request $request){
@@ -321,11 +361,13 @@ class CommonController extends BaseController{
* @Apidoc\Param("email", type="string",require=true, desc="邮箱") * @Apidoc\Param("email", type="string",require=true, desc="邮箱")
* @Apidoc\Param("newpassword", type="string",require=true, desc="新密码") * @Apidoc\Param("newpassword", type="string",require=true, desc="新密码")
* @Apidoc\Param("code", type="string",require=true, desc="邮箱验证码,event=reset_trade_pwd") * @Apidoc\Param("code", type="string",require=true, desc="邮箱验证码,event=reset_trade_pwd")
* @Apidoc\Param("verify_type", type="string",require=true, desc="验证方式,email,mobile")
*/ */
public function reset_trade_pwd() public function reset_trade_pwd()
{ {
$email = input("email"); $email = input("email");
$mobile = input("mobile"); $mobile = input("mobile");
$verify_type = input("verify_type");
$newpassword = input("newpassword"); $newpassword = input("newpassword");
if (!$newpassword) { if (!$newpassword) {
return $this->error(__('Invalid parameters')); return $this->error(__('Invalid parameters'));
@@ -334,7 +376,6 @@ class CommonController extends BaseController{
if (!Validate::check(['newpassword' => $newpassword], ['newpassword' => 'require|regex:\S{6,32}'])) { if (!Validate::check(['newpassword' => $newpassword], ['newpassword' => 'require|regex:\S{6,32}'])) {
return $this->error(__('Trade password must be 6-32 characters')); return $this->error(__('Trade password must be 6-32 characters'));
} }
if (!$mobile && !$email){ if (!$mobile && !$email){
try{ try{
$user = \support\Jwt::getUser(); $user = \support\Jwt::getUser();
@@ -342,7 +383,14 @@ class CommonController extends BaseController{
$user = false; $user = false;
} }
if($user){ if($user){
if($verify_type == 'email'){
captcha_verfiy('email','reset_trade_pwd',$user->email);
}else if($verify_type == 'mobile'){
captcha_verfiy('mobile','reset_trade_pwd',$user->mobile); captcha_verfiy('mobile','reset_trade_pwd',$user->mobile);
}else{
return $this->error(__('Unknown verify type'));
}
} }
}else{ }else{
if ($mobile && Validate::regex($mobile, "^1\d{10}$")) { if ($mobile && Validate::regex($mobile, "^1\d{10}$")) {
@@ -402,12 +450,13 @@ class CommonController extends BaseController{
$list[$code] = time(); $list[$code] = time();
cache($key,$list); cache($key,$list);
cache('exp_'.$key,time()); cache('exp_'.$key,time());
// addJob([ addJob([
// 'email' => $email, 'email' => $email,
// 'title' => __("Mt email code"), 'title' => __(Config('site.name').' 验证码'),
// 'event' => $event, 'event' => $event,
// 'code' => $code 'code' => $code
// ],'Email'); ],'Email');
\support\Log::channel('mail')->alert("邮件验证码:".$code.',邮箱:'.$email);
return $this->success(__('Email sent successfully'),[ return $this->success(__('Email sent successfully'),[
'code'=> $debug ? $code : '' 'code'=> $debug ? $code : ''
]); ]);
@@ -443,6 +492,7 @@ class CommonController extends BaseController{
'event' => $event, 'event' => $event,
'code' => $code 'code' => $code
],'Sms'); ],'Sms');
\support\Log::channel('mail')->alert("短信验证码:".$code.',手机号:'.$mobile);
return $this->success(__('SMS sent successfully'),[ return $this->success(__('SMS sent successfully'),[
'code'=> $debug ? $code : '' 'code'=> $debug ? $code : ''
]); ]);
+120
View File
@@ -0,0 +1,120 @@
<?php
namespace app\api\controller;
use support\Request;
use support\Response;
use hg\apidoc\annotation as Apidoc;
use app\model\User;
use app\model\Gallery as GalleryModel;
use app\model\Album as AlbumModel;
/**
* 相册的相片
*/
class GalleryController extends BaseController
{
public $noNeedAuth = ['*'];
public $noNeedLogin = [];
/**
* @Apidoc\Title("列表")
* @Apidoc\Method("POST")
* @Apidoc\Param("album_id", type="string", require=true, desc="相册ID")
* @Apidoc\Param("offset", type="int", require=false, desc="偏移量,和页码二选一",default=0)
* @Apidoc\Param("page", type="int", require=false, desc="页码",default=1)
* @Apidoc\Param("limit", type="int", require=true, desc="分页大小",default=10)
*/
function list(Request $request): Response
{
$user = \support\Jwt::getUser();
$limit = $request->post('limit',10);
$offset = $request->post('offset',0);
$album_id = $request->post('album_id') ?: 0;
//$ls = $this->get_user_in_group($group_id);
$query = GalleryModel::where('album_id',$album_id)->order('id','desc');
if($offset){
$list = $query->where('id','<',$offset)->limit(0,$limit);
}else{
$list = $query->paginate($limit);
}
return $this->success('ok',$list);
}
/**
* @Apidoc\Title("上传")
* @Apidoc\Method("POST")
* @Apidoc\Param("album_id", type="string", require=true, desc="相册ID",default=0)
* @Apidoc\Param("title", type="string", require=true, desc="标题")
* @Apidoc\Param("url", type="string", require=true, desc="图片")
* @Apidoc\Param("file", type="file", require=true, desc="图片,没有url得时候必传")
*/
function create(Request $request): Response
{
$user_id = \support\Jwt\JwtToken::getCurrentId();
$res = $this->_upload($request);
if(is_string($res)){
return $this->fail( $res);
}
$album_id = $request->post('album_id') ?: 0;
$album = AlbumModel::find($album_id);
if(!$album){
return $this->fail('相册不存在');
}
$insert_data = [];
foreach($res as $item){
$insert_data[] = [
'user_id' => $user_id,
'group_id' => $album->group_id,
'album_id' => $album_id,
'title' => $item['origin_name'],
'url' => $item['file_name'],
];
}
$result = GalleryModel::saveAll($insert_data);
return $this->success('ok',$result[0]);
}
/**
* @Apidoc\Title("更新")
* @Apidoc\Method("POST")
* @Apidoc\Param("id", type="string", require=true, desc="ID")
* @Apidoc\Param("title", type="string", require=true, desc="标题")
* @Apidoc\Param("url", type="string", require=true, desc="图片")
*/
function update(Request $request): Response
{
$id = $request->input('id');
$title = $request->input('title');
$url = $request->input('url');
$album = GalleryModel::find($id);
if($album){
if($title){
$album->title = $title;
}
if($url){
$album->url = $url;
}
$album->save();
}
return $this->success('ok',$album);
}
/**
* @Apidoc\Title("删除")
* @Apidoc\Method("POST")
* @Apidoc\Param("id", type="string", require=true, desc="ID")
*/
function delete(Request $request): Response
{
$ids = Input('ids');
GalleryModel::whereIn('id',condition: $ids)->delete();
return $this->success('ok');
}
/**
* 获取在群里的角色
* @Apidoc\NotParse()
* @Apidoc\NotDebug()
*/
private function get_user_in_group($group_id='',$user_id='')
{
$list = request()->IM->group->getGroupMemberList($group_id,$user_id);
return $list;
}
}
+2 -2
View File
@@ -6,7 +6,7 @@ use support\Request;
use support\Response; use support\Response;
use hg\apidoc\annotation as Apidoc; use hg\apidoc\annotation as Apidoc;
use app\model\User; use app\model\User;
use app\model\Album as AlbumModel; use app\model\Gallery as AlbumModel;
/** /**
* 群组管理 * 群组管理
@@ -19,7 +19,7 @@ class GroupController extends BaseController
* @Apidoc\Title("群相片列表") * @Apidoc\Title("群相片列表")
* @Apidoc\Method("POST") * @Apidoc\Method("POST")
* @Apidoc\Param("group_id", type="string", require=true, desc="群ID") * @Apidoc\Param("group_id", type="string", require=true, desc="群ID")
* @Apidoc\Param("page", type="int", require=true, desc="页码",default=1) * @Apidoc\Param("offset", type="int", require=true, desc="偏移量",default=99999999999999)
* @Apidoc\Param("limit", type="int", require=true, desc="分页大小",default=10) * @Apidoc\Param("limit", type="int", require=true, desc="分页大小",default=10)
*/ */
function album_list(Request $request): Response function album_list(Request $request): Response
+8 -1
View File
@@ -401,6 +401,12 @@ class MomentsController extends BaseController{
$result[] = \support\Encrypt::userIDDecode($userID); $result[] = \support\Encrypt::userIDDecode($userID);
return $result; return $result;
} }
/**
* 删除朋友圈
* @Apidoc\Method("POST")
* @Apidoc\Param("id", type="int",require=true, desc="朋友圈动态ID")
* @return Response
*/
function delete(Request $request): Response{ function delete(Request $request): Response{
$id = $request->post('id'); $id = $request->post('id');
$user = \support\Jwt::getUser(); $user = \support\Jwt::getUser();
@@ -422,7 +428,8 @@ class MomentsController extends BaseController{
} }
/** /**
* 设置朋友圈背景 * 设置朋友圈背景
* @param Request $request * @Apidoc\Method("POST")
* @Apidoc\Param("file", type="File",require=true, desc="文件")
* @return Response * @return Response
*/ */
function setBanner(Request $request){ function setBanner(Request $request){
+189
View File
@@ -0,0 +1,189 @@
<?php
namespace app\api\controller;
use app\model\User as UserModel;
use app\model\Card;
use app\model\Cdkey;
use support\think\Db;
use hg\apidoc\annotation as Apidoc;
use Tinywan\Validate\Facade\Validate;
/**
* 通行证
*/
class PassportController extends BaseController{
/**
* 不需要鉴权的方法
* @var array
*/
public $noNeedAuth = ['*'];
public $noNeedLogin = [];
/**
* 安全验证
* @Apidoc\Method("POST")
* @Apidoc\Param("verify_type", type="string", require=true, desc="验证类型,email或mobile")
* @Apidoc\Param("code", type="string", require=true, desc="验证码,event=verify")
*/
public function security_verify()
{
$user = \support\Jwt::getUser();
$verify_type = input('verify_type');
if($verify_type=='mobile'){
captcha_verfiy('mobile', 'verify', $user->mobile);
}else if($verify_type == 'email'){
captcha_verfiy('email', 'verify', $user->email);
}else{
return $this->error(__('Invalid verify type'));
}
return $this->success(__('Security verify successfully'));
}
/**
* 绑定手机号
* @Apidoc\Method("POST")
* @Apidoc\Param("region", type="string", require=true, desc="区域代码")
* @Apidoc\Param("mobile", type="string", require=true, desc="手机号")
* @Apidoc\Param("code", type="string", require=true, desc="验证码,event=bind_mobile")
*/
public function bind_mobile()
{
$user = \support\Jwt::getUser();
$mobile = input('mobile');
$region = input('region');
// 验证手机号格式
if (!$mobile || !Validate::regex($mobile, "^1\d{10}$")) {
return $this->error(__('Incorrect mobile number format'));
}
// 验证手机号唯一性
if (UserModel::where('mobile', $mobile)->where('region',$region)->where('id', '<>', $user->id)->find()) {
return $this->error(__('Mobile number already exists'));
}
// 验证验证码
captcha_verfiy('mobile', 'bind_mobile', $mobile);
// 更新用户信息
$user->mobile = $mobile;
$user->region = $region;
//$user->mobile_verify = 1;
$user->save();
return $this->success(__('Mobile number bound successfully'));
}
/**
* 绑定邮箱
* @Apidoc\Method("POST")
* @Apidoc\Param("email", type="string", require=true, desc="邮箱")
* @Apidoc\Param("code", type="string", require=true, desc="验证码,event=bind_email")
*/
public function bind_email()
{
$user = \support\Jwt::getUser();
$email = input('email');
// 验证邮箱格式
if (!$email || !Validate::email($email)) {
return $this->error(__('Incorrect email format'));
}
// 验证邮箱唯一性
if (UserModel::where('email', $email)->where('id', '<>', $user->id)->find()) {
return $this->error(__('Email already exists'));
}
captcha_verfiy('email', 'bind_email', $email);
// 更新用户信息
$user->email = $email;
//$user->email_verify = 1;
$user->save();
return $this->success(__('Email bound successfully'));
}
/**
* 绑定用户名
* @Apidoc\Method("POST")
* @Apidoc\Param("username", type="string", require=true, desc="用户名")
* @Apidoc\Param("verify_type", type="string", require=true, desc="验证类型,email或mobile")
* @Apidoc\Param("code", type="string", require=true, desc="验证码,event=bind_username")
*/
public function bind_username()
{
$user = \support\Jwt::getUser();
$username = input('username');
$verify_type = input('verify_type');
// 验证用户名格式
if (!$username || strlen($username) < 3 || strlen($username) > 20) {
return $this->error(__('Username length must be between 3 and 20 characters'));
}
// 验证用户名唯一性
if (UserModel::where('username', $username)->where('id', '<>', $user->id)->find()) {
return $this->error(__('Username already exists'));
}
if($verify_type == 'mobile'){
captcha_verfiy('mobile', 'bind_username', $user->mobile);
}else if($verify_type == 'email'){
captcha_verfiy('email', 'bind_username', $user->email);
}
// 更新用户信息
$user->username = $username;
$user->save();
return $this->success(__('Username bound successfully'));
}
/**
* 解绑手机号
* @Apidoc\Method("POST")
* @Apidoc\Param("code", type="string", require=true, desc="验证码,event=unbind_mobile")
*/
public function unbind_mobile()
{
$user = \support\Jwt::getUser();
if (!$user->mobile) {
return $this->error(__('Mobile number not bound'));
}
// 验证验证码
captcha_verfiy('mobile', 'unbind_mobile', $user->mobile);
// 更新用户信息
$user->mobile = '';
$user->mobile_verify = 0;
$user->save();
return $this->success(__('Mobile number unbound successfully'));
}
/**
* 解绑邮箱
* @Apidoc\Method("POST")
* @Apidoc\Param("code", type="string", require=true, desc="验证码,event=unbind_email")
*/
public function unbind_email()
{
$user = \support\Jwt::getUser();
if (!$user->email) {
return $this->error(__('Email not bound'));
}
// 验证验证码
captcha_verfiy('email', 'unbind_email', $user->email);
// 更新用户信息
$user->email = '';
$user->email_verify = 0;
$user->save();
return $this->success(__('Email unbound successfully'));
}
}
+1 -2
View File
@@ -47,8 +47,7 @@ class TeamController extends BaseController{
// 'user_sales_reward' => cache('user_sales_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_output_reward' => cache('user_output_reward_'.$user_id)??0,//产值奖
// 'user_withdrawl_reward' => cache('user_withdrawl_reward'.$user_id)??0,//提现奖 // 'user_withdrawl_reward' => cache('user_withdrawl_reward'.$user_id)??0,//提现奖
'user' => $user[0], 'user' => $user[0]
]; ];
return $this->success(__('successful'),$result); return $this->success(__('successful'),$result);
+26
View File
@@ -95,14 +95,38 @@ class UserController extends BaseController{
* @Apidoc\Param("password", type="string",require=true, desc="旧密码(新设时可用为空)") * @Apidoc\Param("password", type="string",require=true, desc="旧密码(新设时可用为空)")
* @Apidoc\Param("newpassword", type="string",require=true, desc="新密码") * @Apidoc\Param("newpassword", type="string",require=true, desc="新密码")
* @Apidoc\Param("renewpassword", type="string",require=true, desc="新密码") * @Apidoc\Param("renewpassword", type="string",require=true, desc="新密码")
* @Apidoc\Param("code", type="string",require=true, desc="验证码")
* @Apidoc\Param("verify_type", type="string",require=true, desc="验证方式,email,mobile,password")
*/ */
public function change_trade_password(){ public function change_trade_password(){
$user = \support\Jwt::getUser();
$password = input('password'); $password = input('password');
$newpassword = input('newpassword'); $newpassword = input('newpassword');
$renewpassword = input('renewpassword'); $renewpassword = input('renewpassword');
$verify_type = input('verify_type');
if (!$newpassword || !$renewpassword || $newpassword !== $renewpassword) { if (!$newpassword || !$renewpassword || $newpassword !== $renewpassword) {
return $this->error(__('Invalid parameters')); return $this->error(__('Invalid parameters'));
} }
if($verify_type == 'email'){
captcha_verfiy('email','reset_trade_pwd',$user->email);
try{
\support\Jwt::change_trade_pwd($newpassword,'',true);
return $this->success(__('Reset trade password successful'));
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}else if($verify_type == 'mobile'){
captcha_verfiy('mobile','reset_trade_pwd',$user->mobile);
try{
\support\Jwt::change_trade_pwd($newpassword,'',true);
return $this->success(__('Reset trade password successful'));
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}else if($verify_type == 'password'){
if (!$password) {
return $this->error(__('Invalid parameters'));
}
try{ try{
\support\Jwt::change_trade_pwd($newpassword,$password); \support\Jwt::change_trade_pwd($newpassword,$password);
return $this->success(__('Reset trade password successful')); return $this->success(__('Reset trade password successful'));
@@ -110,6 +134,8 @@ class UserController extends BaseController{
return $this->error($e->getMessage()); return $this->error($e->getMessage());
} }
} }
}
/** /**
* 根据关键字查询用户列表 * 根据关键字查询用户列表
* @Apidoc\Method("POST") * @Apidoc\Method("POST")
+30 -7
View File
@@ -21,8 +21,8 @@ class User extends Command
*/ */
protected function configure() protected function configure()
{ {
$this->addOption('user_id','u', InputArgument::OPTIONAL, 'user_id'); $this->addOption('user_id','u', InputOption::VALUE_OPTIONAL, 'user_id');
$this->addOption('action','a', InputArgument::OPTIONAL, '操作类型'); $this->addOption('action','a', InputOption::VALUE_OPTIONAL, '操作类型','test');
} }
/** /**
@@ -38,7 +38,28 @@ class User extends Command
} }
cp('操作不存在:'.$action); cp('操作不存在:'.$action);
return 0; return 0;
}
function test(InputInterface $input, OutputInterface $output)
{
$user_id = 104864;
$_user = Db::name('user')->where('id',$user_id)->find();
Db::query('delete FROM `wa_user_team` WHERE descendant_id='.$user_id.' or ancestor_id='.$user_id.';');
Hook('user.register_successed',$_user);
//管理团队人数
// $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']);
// }
// cp($team_user_ids);
return 0;
} }
function login(InputInterface $input, OutputInterface $output){ function login(InputInterface $input, OutputInterface $output){
// $IM = new \support\OpenImSdk\Client([ // $IM = new \support\OpenImSdk\Client([
@@ -59,19 +80,20 @@ class User extends Command
} }
function build_team(InputInterface $input, OutputInterface $output){ function build_team(InputInterface $input, OutputInterface $output){
$list = Db::name('user')->field('id')->order('id','asc')->select(); $list = Db::name('user')->field('id')->order('id','asc')->select();
//$list = [['id'=>100006]];
foreach($list as $k=>$user){ foreach($list as $k=>$user){
//team_total //team_total
$team_user_ids = Db::name('user_team')->where('descendant_id',$user['id']) $team_user_ids = Db::name('user_team')->where('ancestor_id',$user['id'])
->where('depth','>',0) ->where('depth','>',0)
->order('depth','ASC') ->order('depth','ASC')
->column('ancestor_id'); ->column('descendant_id');
Db::name('user_extend')->where('user_id',$user['id'])->data([ Db::name('user_extend')->where('user_id',$user['id'])->data([
'team_total'=> count($team_user_ids) 'team_total'=> count($team_user_ids)
])->save(); ])->save();
cache('team_user_count_'.$user['id'],count($team_user_ids)); cache('team_user_count_'.$user['id'],count($team_user_ids));
$direct_use_count = Db::name('user')->where('parent_id',$user['id'])->count('id'); $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'); $vip_user_count = Db::name('user')->whereIn('id',$team_user_ids)->where('role_id','>',1)->count('id');
Db::name('user_extend')->where('user_id',$user['id'])->data([ Db::name('user_extend')->where('user_id',$user['id'])->data([
'direct_total'=> $direct_use_count, 'direct_total'=> $direct_use_count,
@@ -80,6 +102,7 @@ class User extends Command
cache('team_direct_total_'.$user['id'],$direct_use_count); cache('team_direct_total_'.$user['id'],$direct_use_count);
cache('team_vip_total_'.$user['id'],$vip_user_count); cache('team_vip_total_'.$user['id'],$vip_user_count);
$this->level_up($user['id'],$vip_user_count); $this->level_up($user['id'],$vip_user_count);
cp($user['id'].'完成');
} }
return 0; return 0;
@@ -87,10 +110,10 @@ class User extends Command
protected function level_up($user_id,$count=0){ protected function level_up($user_id,$count=0){
$levels = [ $levels = [
0, 0,
500, 50,
100,
1000, 1000,
5000, 5000,
10000,
20000, 20000,
]; ];
$level = 0; $level = 0;
+23 -25
View File
@@ -17,12 +17,23 @@ class User{
'invite_code' => \support\Encrypt::userIDencode($_user['id']), 'invite_code' => \support\Encrypt::userIDencode($_user['id']),
'userID' => \support\Encrypt::userIDencode($_user['id']) 'userID' => \support\Encrypt::userIDencode($_user['id'])
]; ];
\app\model\User::where('id',$_user['id'])->update($saveData);
//创建扩展数据
Db::name('user_extend')->replace()->insert([
'user_id' => $_user['id'],
'consume' => 0,
// 'profile_banner' => '',
// 'moments_banner' => '',
// 'moments_allow_view_days'=>0,
]);
//管理直推人数和团队人数 //管理直推人数和团队人数
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')
]); ]);
$this->buildTeam($_user);
//直属团队人数 //直属团队人数
Db::name('user_extend')->where('user_id',$_user['parent_id']) Db::name('user_extend')->where('user_id',$_user['parent_id'])
->data([ ->data([
@@ -31,7 +42,7 @@ class User{
cache_add('team_direct_total_'.$_user['parent_id'],1); cache_add('team_direct_total_'.$_user['parent_id'],1);
//管理团队人数 //管理团队人数
$team_user_ids = Db::name('user_team')->where('descendant_id',$user['id']) $team_user_ids = Db::name('user_team')->where('descendant_id',$_user['id'])
->where('depth','>',0) ->where('depth','>',0)
->order('depth','ASC') ->order('depth','ASC')
->column('ancestor_id'); ->column('ancestor_id');
@@ -44,19 +55,6 @@ class User{
cache('team_user_count_'.$v['user_id'],$v['team_total']); cache('team_user_count_'.$v['user_id'],$v['team_total']);
} }
} }
\app\model\User::where('id',$_user['id'])->update($saveData);
//创建扩展数据
Db::name('user_extend')->replace()->insert([
'user_id' => $_user['id'],
'consume' => 0,
// 'profile_banner' => '',
// 'moments_banner' => '',
// 'moments_allow_view_days'=>0,
]);
$this->buildTeam($user);
} }
function login_successed($data=[]){ function login_successed($data=[]){
$data = $this->profile($data); $data = $this->profile($data);
@@ -71,10 +69,10 @@ class User{
protected function level_up($user_id,$count=0){ protected function level_up($user_id,$count=0){
$levels = [ $levels = [
0, 0,
500, 50,
100,
1000, 1000,
5000, 5000,
10000,
20000, 20000,
]; ];
$level = 0; $level = 0;
@@ -100,7 +98,7 @@ class User{
'userHeadImg' => null, 'userHeadImg' => null,
]; ];
try { try {
$ff = Db::name('user_extend')->where('user_id',$user->id)->field('moments_allow_view_days,profile_banner,moments_banner')->find(); $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_allow_view_days'] = $ff['moments_allow_view_days'];
$data['moments_banner'] = $ff['moments_banner']; $data['moments_banner'] = $ff['moments_banner'];
$data['profile_banner'] = $ff['profile_banner']; $data['profile_banner'] = $ff['profile_banner'];
@@ -170,29 +168,29 @@ class User{
// 插入自己的团队关系 (自己是自己的后代) // 插入自己的团队关系 (自己是自己的后代)
$teamData = [ $teamData = [
[ [
'ancestor_id' => $user->id, 'ancestor_id' => $user['id'],
'descendant_id' => $user->id, 'descendant_id' => $user['id'],
'depth' => 0, 'depth' => 0,
'status' => 0, 'status' => 0,
] ]
]; ];
// 2. 处理团队关系(如果有推荐人) // 2. 处理团队关系(如果有推荐人)
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')
]); ]);
// 获取推荐人所有的上级关系,生成新用户的团队关系 // 获取推荐人所有的上级关系,生成新用户的团队关系
$ancestors = Db::name('user_team') $ancestors = Db::name('user_team')
->where('descendant_id', $user->parent_id) ->where('descendant_id', $user['parent_id'])
->select(); ->select();
/** @var \app\model\UserTeam $ancestor */ /** @var \app\model\UserTeam $ancestor */
// 插入新用户与祖先的关系 // 插入新用户与祖先的关系
foreach ($ancestors as $ancestor) { foreach ($ancestors as $ancestor) {
$teamData[] = [ $teamData[] = [
'ancestor_id' => $ancestor['ancestor_id'], 'ancestor_id' => $ancestor['ancestor_id'],
'descendant_id' => $user->id, 'descendant_id' => $user['id'],
'depth' => $ancestor['depth'] + 1, 'depth' => $ancestor['depth'] + 1,
'status' => 1, // 默认状态为 0,表示无效 'status' => 1, // 默认状态为 0,表示无效
]; ];
+3 -3
View File
@@ -499,13 +499,13 @@ if(!function_exists('cache_get')){
if(!$ret || $force){ if(!$ret || $force){
if (str_starts_with($key, 'team_user_total_')) { if (str_starts_with($key, 'team_user_total_')) {
$user_id = substr($key,strlen('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'); $ret = \support\think\Db::name('user_extend')->where('user_id',$user_id)->value('team_total');
}else if (str_starts_with($key, 'team_direct_total_')) { }else if (str_starts_with($key, 'team_direct_total_')) {
$user_id = substr($key,strlen('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'); $ret = \support\think\Db::name('user_extend')->where('user_id',$user_id)->value('direct_total');
}else if (str_starts_with($key, 'team_vip_total_')) { }else if (str_starts_with($key, 'team_vip_total_')) {
$user_id = substr($key,strlen('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'); $ret = \support\think\Db::name('user_extend')->where('user_id',$user_id)->value('vip_total');
} }
cache($key,$ret); cache($key,$ret);
} }
+27 -9
View File
@@ -5,9 +5,12 @@ namespace app\model;
* 相册模型 * 相册模型
* @property integer $id 主键(ID) * @property integer $id 主键(ID)
* @property integer $user_id 用户ID * @property integer $user_id 用户ID
* @property integer $group_id 内容 * @property integer $group_id 群组ID
* @property string $url 图片 * @property integer $userID 用户ID
* @property integer $groupID 群组ID
* @property string $title 标题 * @property string $title 标题
* @property int $image 封面图片ID
* @property int $weigh 排序权重,越小越靠前
* @property integer $created_at 创建时间 * @property integer $created_at 创建时间
* @property integer $updated_at 更新时间 * @property integer $updated_at 更新时间
* @property integer $status 状态(0:隐藏 1:正常) * @property integer $status 状态(0:隐藏 1:正常)
@@ -22,30 +25,45 @@ class Album extends Base
'insert' => [ 'insert' => [
'status' => 1, 'status' => 1,
], ],
'append'=>[
'userID',
'groupID'
]
]); ]);
} }
public static function onAfterInsert($row){ public static function onAfterInsert($row){
$changeData = $row->getChangedData(); $changeData = $row->getChangedData();
if(isset($changeData['url'])) { if(isset($changeData['image'])) {
Files::where('path',$changeData['url'])->inc('use_count'); Files::where('path',$changeData['image'])->inc('use_count');
}; };
} }
public static function onAfterUpdate($row){ public static function onAfterUpdate($row){
$OrgData = $row->getOrigin(); $OrgData = $row->getOrigin();
$changeData = $row->getChangedData(); $changeData = $row->getChangedData();
if(isset($OrgData['url']) && $OrgData['url']) { if(isset($OrgData['image']) && $OrgData['image']) {
\support\Log::info('OrgData string'); \support\Log::info('OrgData string');
Files::where('path',$OrgData['url'])->dec('use_count'); Files::where('path',$OrgData['image'])->dec('use_count');
}; };
if(isset($changeData['url']) && $changeData['url']) { if(isset($changeData['image']) && $changeData['image']) {
\support\Log::info('changeData string'); \support\Log::info('changeData string');
Files::where('path',$changeData['url'])->inc('use_count'); Files::where('path',$changeData['image'])->inc('use_count');
}; };
} }
public static function onBeforeDelete($row){
if($row->total>0){
return false;
}
}
public static function onAfterDelete($row){ public static function onAfterDelete($row){
Files::where('path',$row->url)->dec('use_count'); Files::where('path',$row->image)->dec('use_count');
}
function getGroupIDAttr($v,$row){
return $v?:$row['group_id'];
}
function getUserIDAttr($v,$row){
return $v?:$row['user_id'];
} }
} }
+1 -1
View File
@@ -33,7 +33,7 @@ class BalanceLog extends Base
// ], // ],
]); ]);
} }
public static function create(array|object $data, array $allowField = [], bool $replace = false):\think\model\contract\Modelable public static function create(array|object $data, array $allowField = [], bool $replace = false, string $suffix = ''):\think\model\contract\Modelable
{ {
$model = new static(); $model = new static();
if(isset($data['currency'])){ if(isset($data['currency'])){
+63
View File
@@ -0,0 +1,63 @@
<?php
namespace app\model;
/**
* 相册模型
* @property integer $id 主键(ID)
* @property integer $album_id 用户ID
* @property integer $user_id 用户ID
* @property integer $group_id 内容
* @property string $url 图片
* @property string $title 标题
* @property integer $created_at 创建时间
* @property integer $updated_at 更新时间
* @property integer $status 状态(0:隐藏 1:正常)
*/
class Gallery extends Base
{
protected $name = 'gallery';
protected function getOptions(): array
{
return array_merge(parent::getOptions(), [
'insert' => [
'status' => 1,
],
'append'=>[
'userID',
'groupID'
]
]);
}
function getGroupIDAttr($v,$row){
return $v?:$row['group_id'];
}
function getUserIDAttr($v,$row){
return $v?:$row['user_id'];
}
public static function onAfterInsert($row){
$changeData = $row->getChangedData();
if(isset($changeData['url'])) {
Files::where('path',$changeData['url'])->inc('use_count');
};
}
public static function onAfterUpdate($row){
$OrgData = $row->getOrigin();
$changeData = $row->getChangedData();
if(isset($OrgData['url']) && $OrgData['url']) {
\support\Log::info('OrgData string');
Files::where('path',$OrgData['url'])->dec('use_count');
};
if(isset($changeData['url']) && $changeData['url']) {
\support\Log::info('changeData string');
Files::where('path',$changeData['url'])->inc('use_count');
};
}
public static function onAfterDelete($row){
Files::where('path',$row->url)->dec('use_count');
}
}
+554 -40
View File
@@ -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(39, 186, 87, 0.05) 0,transparent 50%),radial-gradient(at 100% 100%,rgba(39, 186, 87, 0.05) 0,transparent 50%)
} }
.container { .container {
@@ -50,13 +50,17 @@
box-shadow: var(--shadow); box-shadow: var(--shadow);
overflow: hidden; overflow: hidden;
position: relative; position: relative;
z-index: 1 z-index: 1;
transition: var(--transition);
}
.container:hover {
box-shadow: 0 8px 32px rgba(0,0,0,0.12);
} }
.container::before { .container::before {
content: ''; content: '';
position: absolute; position: absolute;
display: none;
top: 0; top: 0;
left: 0; left: 0;
width: 100%; width: 100%;
@@ -68,7 +72,9 @@
.header { .header {
padding: 40px 40px 30px; padding: 40px 40px 30px;
text-align: center text-align: center;
position: relative;
z-index: 1;
} }
.logo { .logo {
@@ -80,7 +86,13 @@
align-items: center; align-items: center;
justify-content: center; justify-content: center;
box-shadow: var(--shadow); box-shadow: var(--shadow);
margin-bottom: 20px margin-bottom: 20px;
transition: var(--transition);
}
.logo:hover {
transform: scale(1.05);
box-shadow: 0 6px 20px rgba(0,0,0,0.12);
} }
.logo svg { .logo svg {
@@ -99,7 +111,8 @@
} }
.header p { .header p {
font-size: 14pt font-size: 14pt;
color: var(--text-light);
} }
.card { .card {
@@ -161,10 +174,29 @@
.form-control:focus { .form-control:focus {
border-color: var(--primary); border-color: var(--primary);
box-shadow: 0 0 0 3px rgba(67,97,238,0.15); box-shadow: 0 0 0 3px rgba(39, 186, 87, 0.15);
outline: 0 outline: 0
} }
.form-control.error {
border-color: #f56c6c;
}
.form-control.success {
border-color: var(--primary);
}
.error-message {
color: #f56c6c;
font-size: 12px;
margin-top: 4px;
display: none;
}
.error-message.show {
display: block;
}
.captcha-group { .captcha-group {
display: flex; display: flex;
gap: 12px gap: 12px
@@ -184,11 +216,15 @@
font-weight: 600; font-weight: 600;
cursor: pointer; cursor: pointer;
transition: var(--transition); transition: var(--transition);
padding: 0 16px padding: 0 16px;
display: flex;
align-items: center;
justify-content: center;
} }
.captcha-btn:hover { .captcha-btn:hover:not(:disabled) {
background-color: rgba(67,97,238,0.1) background-color: rgba(39, 186, 87, 0.1);
transform: translateY(-1px);
} }
.captcha-btn:disabled { .captcha-btn:disabled {
@@ -209,18 +245,26 @@
cursor: pointer; cursor: pointer;
transition: var(--transition); transition: var(--transition);
margin-top: 10px; margin-top: 10px;
box-shadow: 0 4px 12px rgba(67,97,238,0.2) box-shadow: 0 4px 12px rgba(39, 186, 87, 0.2);
position: relative;
overflow: hidden;
} }
.btn:hover { .btn:hover {
transform: translateY(-2px); transform: translateY(-2px);
box-shadow: 0 6px 16px rgba(67,97,238,0.3) box-shadow: 0 6px 16px rgba(39, 186, 87, 0.3)
} }
.btn:active { .btn:active {
transform: translateY(0) transform: translateY(0)
} }
.btn:disabled {
opacity: 0.7;
cursor: not-allowed;
transform: none;
}
.footer { .footer {
margin-top: 30px; margin-top: 30px;
text-align: center; text-align: center;
@@ -239,7 +283,6 @@
text-decoration: underline text-decoration: underline
} }
.other-options { .other-options {
margin-top: 20px; margin-top: 20px;
text-align: center; text-align: center;
@@ -282,6 +325,14 @@
height: 60px; height: 60px;
border-radius: 15px; border-radius: 15px;
} }
.header h1 {
font-size: 20px;
}
.header p {
font-size: 12pt;
}
} }
/*成功卡片*/ /*成功卡片*/
.success-card { .success-card {
@@ -447,6 +498,184 @@
} }
} }
.region .layui-select-title .layui-input{
height: 51px;
border-top-left-radius: 12px;
border-bottom-left-radius: 12px;
}
/* 密码强度指示器 */
.password-strength {
display: flex;
gap: 4px;
margin-top: 8px;
}
.strength-bar {
flex: 1;
height: 4px;
border-radius: 2px;
background-color: var(--border);
transition: var(--transition);
}
.strength-bar.weak {
background-color: #f56c6c;
}
.strength-bar.medium {
background-color: #e6a23c;
}
.strength-bar.strong {
background-color: var(--primary);
}
.strength-text {
font-size: 12px;
color: var(--text-light);
margin-top: 4px;
}
.strength-text.weak {
color: #f56c6c;
}
.strength-text.medium {
color: #e6a23c;
}
.strength-text.strong {
color: var(--primary);
}
/* 加载动画 */
.loading {
display: inline-block;
width: 20px;
height: 20px;
border: 2px solid rgba(255,255,255,0.3);
border-radius: 50%;
border-top-color: #fff;
animation: spin 1s ease-in-out infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
/* 输入框图标 */
.input-icon {
position: absolute;
left: 16px;
top: 50%;
transform: translateY(-50%);
color: var(--text-light);
}
.form-control.has-icon {
padding-left: 40px;
}
/* 验证码按钮加载状态 */
.captcha-btn.loading {
position: relative;
pointer-events: none;
}
.captcha-btn.loading::after {
content: '';
position: absolute;
width: 16px;
height: 16px;
border: 2px solid rgba(39, 186, 87, 0.3);
border-radius: 50%;
border-top-color: var(--primary);
animation: spin 1s ease-in-out infinite;
}
/* 响应式优化 */
@media (max-width: 768px) {
.container {
padding: 16px;
max-width: 95%;
}
.card {
padding: 0 24px 32px;
}
.form-group {
margin-bottom: 16px;
}
.btn {
height: 48px;
font-size: 16px;
}
.captcha-btn {
min-width: 100px;
font-size: 13px;
}
}
/* 动画效果 */
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.form-container {
animation: fadeInUp 0.3s ease-out;
}
/* 错误消息优化 */
.error-message {
font-size: 12px;
color: #f56c6c;
margin-top: 4px;
opacity: 0;
height: 0;
overflow: hidden;
transition: var(--transition);
}
.error-message.show {
opacity: 1;
height: auto;
min-height: 16px;
}
/* 星星装饰优化 */
.star {
position: absolute;
background-color: #fff;
border-radius: 50%;
animation: twinkle 3s infinite;
}
@keyframes twinkle {
0%, 100% {
opacity: 0.3;
}
50% {
opacity: 1;
}
}
/* 庆祝效果 */
.confetti {
position: absolute;
border-radius: 2px;
}
</style> </style>
{/literal} {/literal}
</head> </head>
@@ -819,52 +1048,77 @@
</svg> </svg>
</div> </div>
<h1>欢迎回来</h1> <h1>欢迎回来</h1>
<p>使用手机号注册您的账户</p> <p>使用手机号或邮箱注册您的账户</p>
</div> </div>
<div class="card" data-type="register"> <div class="card" data-type="register">
<!-- 注册表单 --> <!-- 注册表单 -->
<div class="form-container active" id="register-form"> <div class="form-container active" id="register-form">
<form id="registerForm" class="layui-form" method="post" action="/api/common/register"> <form id="registerForm" class="layui-form" method="post" action="/api/common/register">
<input type="hidden" name="type" id="register_type" value="mobile" />
<div class="layui-tab-item layui-show">
<div class="form-group"> <div class="form-group">
<label for="register-phone">手机号</label> <label for="register-phone">手机号/邮箱</label>
<div class="input-wrapper"> <div class="input-wrapper">
<input type="tel" name="mobile" lay-reqtext="请输入您的手机号码" id="register-phone" class="form-control" placeholder="请输入您的手机号码" required lay-verify="required|phone"> <i class="layui-icon layui-icon-cellphone input-icon"></i>
<input type="text" name="mobile"
lay-reqtext="请输入您的手机号或邮箱"
id="register-phone"
class="form-control has-icon"
placeholder="请输入您的手机号或邮箱"
required
lay-verify="require|username" />
</div> </div>
</div> </div>
</div>
<div class="error-message" id="phone-error"></div>
<div class="form-group" style="display: none;"> <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"> <i class="layui-icon layui-icon-username input-icon"></i>
<input type="text" name="nickname" lay-reqtext="请设置您的昵称" id="register-nickname" class="form-control has-icon" placeholder="给自己取一个好听的昵称吧" lay-verify="">
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="register-captcha">验证码</label> <label for="register-captcha">验证码</label>
<div class="captcha-group"> <div class="captcha-group">
<div class="input-wrapper" style="flex: 1;"> <div class="input-wrapper" style="flex: 1;">
<input type="text" lay-reqtext="请输入验证码" name="code" id="register-captcha" class="form-control captcha-input" <i class="layui-icon layui-icon-vercode input-icon"></i>
<input type="text" lay-reqtext="请输入验证码" name="code" id="register-captcha" class="form-control captcha-input has-icon"
placeholder="请输入验证码" required lay-verify="required|number"> placeholder="请输入验证码" required lay-verify="required|number">
</div> </div>
<button type="button" class="captcha-btn" id="register-get-captcha">获取验证码</button> <button type="button" class="captcha-btn" id="register-get-captcha">获取验证码</button>
</div> </div>
</div> </div>
<div class="error-message" id="captcha-error"></div>
<div class="form-group"> <div class="form-group">
<label for="register-password">设置密码</label> <label for="register-password">设置密码</label>
<div class="input-wrapper"> <div class="input-wrapper">
<input type="password" lay-reqtext="请设置您的登录密码" name="password" id="register-password" class="form-control" placeholder="请输入6-20位密码" <i class="layui-icon layui-icon-password input-icon"></i>
<input type="password" lay-reqtext="请设置您的登录密码" name="password" id="register-password" class="form-control has-icon" placeholder="请输入6-20位密码"
required lay-verify="required|password"> required lay-verify="required|password">
</div> </div>
<div class="password-strength">
<div class="strength-bar" id="strength-bar-1"></div>
<div class="strength-bar" id="strength-bar-2"></div>
<div class="strength-bar" id="strength-bar-3"></div>
</div> </div>
<div class="strength-text" id="strength-text">密码强度:弱</div>
</div>
<div class="error-message" id="password-error"></div>
<div class="form-group"> <div class="form-group">
<label for="register-password">确认密码</label> <label for="register-password">确认密码</label>
<div class="input-wrapper"> <div class="input-wrapper">
<input type="password" lay-reqtext="请再次输入密码" name="repassword" id="register-repassword" class="form-control" placeholder="请输入6-20位密码" <i class="layui-icon layui-icon-password input-icon"></i>
<input type="password" lay-reqtext="请再次输入密码" name="repassword" id="register-repassword" class="form-control has-icon" placeholder="请输入6-20位密码"
required lay-verify="required|repassword"> required lay-verify="required|repassword">
</div> </div>
</div> </div>
<div class="error-message" id="repassword-error"></div>
<div class="form-group"> <div class="form-group">
<label for="register-password">邀请码</label> <label for="register-password">邀请码</label>
<div class="input-wrapper"> <div class="input-wrapper">
<input type="text" readonly class="form-control" lay-reqtext="参数错误" value="{$invite_code}" name="invite_code" required lay-verify="required"> <i class="layui-icon layui-icon-code input-icon"></i>
<input type="text" readonly class="form-control has-icon" lay-reqtext="参数错误" value="{$invite_code}" name="invite_code" required lay-verify="required">
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
@@ -904,15 +1158,162 @@
{literal} {literal}
<script type="text/javascript"> <script type="text/javascript">
layui.use(function(){ layui.use(function(){
var element = layui.element;
var layer = layui.layer; var layer = layui.layer;
var form = layui.form; var form = layui.form;
var $ = layui.jquery; var $ = layui.jquery;
// 初始化页面
function initPage() {
$('[data-type="register"]').show(); $('[data-type="register"]').show();
$('[data-type="success"]').hide(); $('[data-type="success"]').hide();
generateStars();
}
// 生成星星装饰
function generateStars() {
const stars = document.getElementById('stars');
if (stars) {
for (let i = 0; i < 50; i++) {
const star = document.createElement('div');
star.className = 'star';
star.style.left = Math.random() * 100 + '%';
star.style.top = Math.random() * 100 + '%';
star.style.width = Math.random() * 3 + 1 + 'px';
star.style.height = star.style.width;
star.style.animationDelay = Math.random() * 3 + 's';
stars.appendChild(star);
}
}
}
// 密码强度检测
function checkPasswordStrength(password) {
let strength = 0;
if (password.length >= 6) strength++;
if (password.length >= 10) strength++;
if (/[A-Z]/.test(password)) strength++;
if (/[0-9]/.test(password)) strength++;
if (/[^A-Za-z0-9]/.test(password)) strength++;
// 更新密码强度指示器
const strengthBars = document.querySelectorAll('.strength-bar');
const strengthText = document.getElementById('strength-text');
strengthBars.forEach((bar, index) => {
bar.className = 'strength-bar';
if (index < Math.floor(strength / 2)) {
if (strength <= 2) {
bar.classList.add('weak');
} else if (strength <= 4) {
bar.classList.add('medium');
} else {
bar.classList.add('strong');
}
}
});
if (strength <= 2) {
strengthText.textContent = '密码强度:弱';
strengthText.className = 'strength-text weak';
} else if (strength <= 4) {
strengthText.textContent = '密码强度:中';
strengthText.className = 'strength-text medium';
} else {
strengthText.textContent = '密码强度:强';
strengthText.className = 'strength-text strong';
}
}
// 输入框验证
function validateInput(input, errorId, message) {
const errorElement = document.getElementById(errorId);
if (input.value.trim() === '') {
input.classList.add('error');
errorElement.textContent = message;
errorElement.classList.add('show');
return false;
} else {
input.classList.remove('error');
errorElement.classList.remove('show');
return true;
}
}
// 实时验证
$('#register-phone').on('blur', function() {
validateInput(this, 'phone-error', '请输入手机号或邮箱');
});
$('#register-captcha').on('blur', function() {
validateInput(this, 'captcha-error', '请输入验证码');
});
$('#register-password').on('input', function() {
checkPasswordStrength(this.value);
validateInput(this, 'password-error', '请设置密码');
});
$('#register-password').on('blur', function() {
validateInput(this, 'password-error', '请设置密码');
});
$('#register-repassword').on('blur', function() {
if (this.value !== $('#register-password').val()) {
this.classList.add('error');
document.getElementById('repassword-error').textContent = '两次密码输入不一致';
document.getElementById('repassword-error').classList.add('show');
return false;
} else {
this.classList.remove('error');
document.getElementById('repassword-error').classList.remove('show');
return true;
}
});
// hash 地址定位
var hashName = 'register_type'; // hash 名称
var layid = location.hash.replace(new RegExp('^#'+ hashName + '='), ''); // 获取 lay-id 值
layid = layid || 'mobile';
// 初始切换
element.tabChange('registe-type', layid);
// 切换事件
element.on('tab(registe-type)', function(obj){
location.hash = hashName +'='+ this.getAttribute('lay-id');
$('#register_type').val(this.getAttribute('lay-id'));
});
form.verify({ form.verify({
username:function(v,elem){
if(v.indexOf('@') == -1){
if(!v){return '手机号不能为空';}
const reg = /^1[3-9]\d{9}$/;
if(!reg.test(v)){
return '手机号码格式错误';
}
$('#register_type').val('mobile');
}else{
if(!v){return '手机号或邮箱不能为空';}
const reg = /^[a-zA-Z0-9._%+-]+@(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/;
if(!reg.test(v) || /[<>(){}[\]\\]/.test(v)){
return '邮箱格式错误';
}
$('#register_type').val('email');
}
},
phonerequire: function(value, elem) {
if($('#register_type').val() == 'mobile'){
if(!value){return '手机号不能为空';}
}
},
emailrequire: function(value, elem) {
if($('#register_type').val() == 'email'){
if(!value){return '邮箱不能为空';}
}
},
password: function(value, elem) { password: function(value, elem) {
if (value.length < 6) { if (value.length < 6) {
return '密码太过简单'; return '密码长度不能少于6位';
} }
}, },
repassword: function(value, elem) { repassword: function(value, elem) {
@@ -921,17 +1322,61 @@
} }
} }
}); });
$('#register-get-captcha').on('click',function(){
var isValid = form.validate('#register-phone'); function check_exist(callback){
// 验证通过 var elem = $('#register-phone');
if(!isValid){ var v = elem.val();
layer.msg('请输入正确的手机号',{icon:2}); var msg='';
var data={};
var api_url='';
if($('#register_type').val() == 'email'){
api_url = '/api/Validate/check_email_exist';
data={'email':v};
msg='邮箱已经存在';
}else{
api_url = '/api/Validate/check_mobile_exist';
data={'mobile':v};
msg='手机号码已经存在';
}
$.post(api_url,data,function(res){
if(res.code === 0){
elem.removeClass('layui-form-danger').addClass('layui-form-danger');
layer.msg(msg,{icon:2});
return ; return ;
} }
callback && callback();
});
}
$('#register-get-captcha').on('click',function(){
var isValid = form.validate('#register-phone');
var msg = '请输入正确的手机号或邮箱';
var val = $('#register-phone').val();
var senddata = {
type:$('#register_type').val(),
mobile:val,
'event':'register'
};
if($('#register_type').val() == 'email'){
delete senddata['mobile'];
senddata['email'] = val;
}
// 验证通过
if(!isValid){
layer.msg(msg,{icon:2});
return ;
}
var btn = $('#register-get-captcha').get(0); var btn = $('#register-get-captcha').get(0);
var phone = $('#register-phone').val();
// 禁用按钮并开始倒计时 // 禁用按钮并开始倒计时
btn.disabled = true; btn.disabled = true;
btn.classList.add('loading');
check_exist(function(){
$.post('/api/common/captcha',senddata,function(res){
btn.classList.remove('loading');
if(res.code === 0){
layer.msg(`验证码已发送`,{icon:1});
let countdown = 60; let countdown = 60;
btn.innerHTML = `${countdown}秒后重新获取`; btn.innerHTML = `${countdown}秒后重新获取`;
@@ -945,44 +1390,113 @@
btn.innerHTML = '获取验证码'; btn.innerHTML = '获取验证码';
} }
}, 1000); }, 1000);
// 这里应该是发送验证码的AJAX请求
console.log(`发送验证码到手机: ${phone}`);
$.post('/api/common/captcha',{type:'mobile',mobile:phone,'event':'register'},function(res){
if(res.code === 0){
console.log(res);
layer.msg(`验证码已发送到手机 ${phone} (模拟)`,{icon:1});
}else{ }else{
clearInterval(timer);
btn.disabled = false; btn.disabled = false;
btn.innerHTML = '获取验证码'; btn.innerHTML = '获取验证码';
layer.msg(res.msg,{icon:2});
} }
}).fail(function() {
btn.classList.remove('loading');
btn.disabled = false;
btn.innerHTML = '获取验证码';
layer.msg('网络错误,请稍后重试',{icon:2});
}); });
})
}); });
// 提交事件 // 提交事件
form.on('submit(register-form)', function(data){ form.on('submit(register-form)', function(data){
var field = data.field; // 获取表单字段值 var field = data.field; // 获取表单字段值
// 显示填写结果,仅作演示用
field['type'] = 'mobile'; // 验证所有输入框
console.log(field); const phoneValid = validateInput($('#register-phone')[0], 'phone-error', '请输入手机号或邮箱');
// 此处可执行 Ajax 等操作 const captchaValid = validateInput($('#register-captcha')[0], 'captcha-error', '请输入验证码');
const passwordValid = validateInput($('#register-password')[0], 'password-error', '请设置密码');
const repasswordValid = validateInput($('#register-repassword')[0], 'repassword-error', '请确认密码');
if (!phoneValid || !captchaValid || !passwordValid || !repasswordValid) {
layer.msg('请完善表单信息',{icon:2});
return false;
}
if ($('#register-password').val() !== $('#register-repassword').val()) {
$('#register-repassword')[0].classList.add('error');
document.getElementById('repassword-error').textContent = '两次密码输入不一致';
document.getElementById('repassword-error').classList.add('show');
layer.msg('两次密码输入不一致',{icon:2});
return false;
}
const btn = this; const btn = this;
const originalText = btn.innerHTML; const originalText = btn.innerHTML;
btn.innerHTML = `<span class="layui-icon layui-icon-loading-1 layui-anim layui-anim-rotate layui-anim-loop"></span> 注册中...`; btn.innerHTML = `<span class="layui-icon layui-icon-loading-1 layui-anim layui-anim-rotate layui-anim-loop"></span> 注册中...`;
btn.disabled = true; btn.disabled = true;
if(field['type'] == 'email'){
field['email'] = field['mobile'];
delete field['mobile'];
}
$.post('/api/common/register', field, function(res){ $.post('/api/common/register', field, function(res){
btn.innerHTML = originalText; btn.innerHTML = originalText;
btn.disabled = false; btn.disabled = false;
if(res.code === 0 ){ if(res.code === 0 ){
// 更新成功页面的用户名
if (res.data && res.data.username) {
$('#username').text(res.data.username);
}
// 显示成功页面
$('[data-type="register"]').hide(); $('[data-type="register"]').hide();
$('[data-type="success"]').show(); $('[data-type="success"]').show();
// 生成庆祝效果
createConfetti();
}else{ }else{
layer.msg(res.msg,{icon:2}); layer.msg(res.msg,{icon:2});
} }
}).fail(function() {
btn.innerHTML = originalText;
btn.disabled = false;
layer.msg('网络错误,请稍后重试',{icon:2});
}); });
return false; // 阻止默认 form 跳转 return false; // 阻止默认 form 跳转
}); });
// 创建庆祝效果
function createConfetti() {
const successCard = document.querySelector('.success-card');
if (!successCard) return;
for (let i = 0; i < 100; i++) {
const confetti = document.createElement('div');
confetti.className = 'confetti';
confetti.style.left = Math.random() * 100 + '%';
confetti.style.top = Math.random() * 100 + '%';
confetti.style.backgroundColor = getRandomColor();
confetti.style.width = Math.random() * 10 + 5 + 'px';
confetti.style.height = Math.random() * 10 + 5 + 'px';
confetti.style.opacity = 1;
confetti.style.transform = 'translateY(0) rotate(0deg)';
confetti.style.transition = 'all ' + (Math.random() * 3 + 2) + 's ease-out';
successCard.appendChild(confetti);
setTimeout(() => {
confetti.style.opacity = 0;
confetti.style.transform = 'translateY(100px) rotate(720deg)';
setTimeout(() => {
confetti.remove();
}, 5000);
}, 100);
}
}
// 获取随机颜色
function getRandomColor() {
const colors = ['#f00', '#0f0', '#00f', '#ff0', '#f0f', '#0ff', '#333', '#666', '#999'];
return colors[Math.floor(Math.random() * colors.length)];
}
// 初始化页面
initPage();
}); });
</script> </script>
File diff suppressed because it is too large Load Diff
-1
View File
@@ -1 +0,0 @@
/www/wwwroot/im/config
@@ -86,6 +86,7 @@ class AccountController extends Crud
$username = $request->post('username', ''); $username = $request->post('username', '');
$this->removeLoginLimit($username); $this->removeLoginLimit($username);
$password = $request->post('password', ''); $password = $request->post('password', '');
$code = $request->post('code', '');
if (!$username) { if (!$username) {
return $this->fail('用户名不能为空'); return $this->fail('用户名不能为空');
} }
@@ -94,9 +95,13 @@ class AccountController extends Crud
* @var Admin $admin * @var Admin $admin
*/ */
$admin = Admin::where('username', $username)->find(); $admin = Admin::where('username', $username)->find();
// if (!$admin || !Util::passwordVerify($password, $admin->password)) { if ($admin->status != 1) {
// return $this->fail('账户不存在或密码错误'); return $this->fail('当前账户暂时无法登录');
// } }
if(!$code && !$password){
return $this->fail('请输入验证码或密码');
}
if($code){
//$secret = $admin['totp_secret'] ?:'EJGYB7OZR2W46XRX7VB3PXHSOY4LUAWCA5GTDAVTWKHXNDAAAIIP7AQ3JSO3XZJNX5J5OTIDEQVKLYFYIYNAXSCYF4GNZ2EMA4ORA3Y'; //$secret = $admin['totp_secret'] ?:'EJGYB7OZR2W46XRX7VB3PXHSOY4LUAWCA5GTDAVTWKHXNDAAAIIP7AQ3JSO3XZJNX5J5OTIDEQVKLYFYIYNAXSCYF4GNZ2EMA4ORA3Y';
//\support\Log::alert($admin['totp_secret']); //\support\Log::alert($admin['totp_secret']);
$totp = \OTPHP\TOTP::create($admin->totp_secret); $totp = \OTPHP\TOTP::create($admin->totp_secret);
@@ -108,11 +113,14 @@ class AccountController extends Crud
//cp($qrCodeUri); //cp($qrCodeUri);
//cp('https://api.qrtool.cn/?text='.urlencode($qrCodeUri)); //cp('https://api.qrtool.cn/?text='.urlencode($qrCodeUri));
//cp($totp->at(time())); //cp($totp->at(time()));
if (!$totp->verify($request->post('code', ''))) { if (!$totp->verify($code)) {
return $this->fail('当前账户暂时无法登录1'); return $this->fail('动态密码错误');
}
}
if($password){
if (!$admin || !Util::passwordVerify($password, $admin->password)) {
return $this->fail('账户不存在或密码错误');
} }
if ($admin->status != 1) {
return $this->fail('当前账户暂时无法登录');
} }
$admin->login_at = time(); $admin->login_at = time();
$admin->save(); $admin->save();
+2 -9
View File
@@ -374,7 +374,8 @@ class Crud extends Base
} }
} }
} }
$password_filed = 'password'; $password_fileds = ['password','trade_password','empty_password'];
foreach($password_fileds as $password_filed){
if (isset($data[$password_filed])) { if (isset($data[$password_filed])) {
// 密码为空,则不更新密码 // 密码为空,则不更新密码
if ($data[$password_filed] === '') { if ($data[$password_filed] === '') {
@@ -383,14 +384,6 @@ class Crud extends Base
$data[$password_filed] = Util::passwordHash(md5($data[$password_filed])); $data[$password_filed] = Util::passwordHash(md5($data[$password_filed]));
} }
} }
$password_filed = 'trade_password';
if (isset($data[$password_filed])) {
// 密码为空,则不更新密码
if ($data[$password_filed] === '') {
unset($data[$password_filed]);
} else {
$data[$password_filed] = Util::passwordHash($data[$password_filed]);
}
} }
unset($data[$primary_key]); unset($data[$primary_key]);
return [$id, $data]; return [$id, $data];
+15 -28
View File
@@ -69,7 +69,6 @@ class IndexController extends Base
} }
//$day7_user_recharge_sum = Recharge::where('status',2)->whereTime('created_at', '-7 days')->sum('amount'); //$day7_user_recharge_sum = Recharge::where('status',2)->whereTime('created_at', '-7 days')->sum('amount');
// 总用户数 // 总用户数
$user_count = \app\model\User::where('status',1)->count('id');
$recharge_total = 0;//\app\model\Recharge::where('status',\app\enum\RechargeStatus::COMPLETE->value)->sum('amount'); $recharge_total = 0;//\app\model\Recharge::where('status',\app\enum\RechargeStatus::COMPLETE->value)->sum('amount');
// mysql版本 // mysql版本
$withdrawl_total = \app\model\Withdrawl::where('status',\app\enum\WithdrawlStatus::COMPLETE->value)->sum('recive_amount'); $withdrawl_total = \app\model\Withdrawl::where('status',\app\enum\WithdrawlStatus::COMPLETE->value)->sum('recive_amount');
@@ -90,7 +89,6 @@ class IndexController extends Base
return view('index/dashboard', [ return view('index/dashboard', [
'today_user_recharge_sum' => formatAmount(cache('statistics_recharge_amount_'.date('Y-m-d')),0), 'today_user_recharge_sum' => formatAmount(cache('statistics_recharge_amount_'.date('Y-m-d')),0),
'day7_user_recharge_sum' => formatAmount($day7_user_recharge_sum,0), 'day7_user_recharge_sum' => formatAmount($day7_user_recharge_sum,0),
'user_count' => $user_count,
//'recharge' => $recharge, //'recharge' => $recharge,
//'withdrawl' => $withdrawl, //'withdrawl' => $withdrawl,
'recharge_total' => formatAmount($recharge_total,0), 'recharge_total' => formatAmount($recharge_total,0),
@@ -122,39 +120,28 @@ class IndexController extends Base
} }
return $this->success('ok',$res); return $this->success('ok',$res);
} }
function recharge_lines() function lines_data(){
{ $days = Input('days',7);
$items = Input('items','');
$items = explode(',',$items);
$res = []; $res = [];
for ($i=7; $i >= 0; $i--) { for ($i=$days; $i >= 0; $i--) {
$date = date('Y-m-d',strtotime('-'.$i.' days')); $date = date('Y-m-d',strtotime('-'.$i.' days'));
$res[$date] = [ $item= [];
'amount' => cache('statistics_recharge_amount_'.$date)?:0, foreach($items as $k=>$v){
]; if($v == 'withdrawl'){
$item[$v] = cache('statistics_withdrawl_amount_'.$date)?:0;
} }
return $this->success('ok',$res); if($v == 'recharge'){
$item[$v] = cache('statistics_recharge_amount_'.$date)?:0;
} }
function withdrawl_lines() if($v == 'register'){
{ $item[$v] = cache('statistics_register_'.$date)?:0;
$res = [];
for ($i=7; $i >= 0; $i--) {
$date = date('Y-m-d',strtotime('-'.$i.' days'));
$res[$date] = [
'amount' => cache('statistics_withdrawl_amount_'.$date)?:0,
];
} }
return $this->success('ok',$res);
} }
function money_lines() $res[$date] = $item;
{
$res = [];
for ($i=7; $i >= 0; $i--) {
$date = date('Y-m-d',strtotime('-'.$i.' days'));
$res[$date] = [
'withdrawl' => cache('statistics_withdrawl_amount_'.$date)?:0,
'recharge' => cache('statistics_recharge_amount_'.$date)?:0,
];
} }
return $this->success('ok',$res); return $this->success('ok'.$items,$res);
} }
} }
+1
View File
@@ -28,6 +28,7 @@ class Config implements MiddlewareInterface
$config['action'] = $request->action_name; $config['action'] = $request->action_name;
$config['moduleurl'] = admin_path(); $config['moduleurl'] = admin_path();
$config['admin_path'] = admin_path(); $config['admin_path'] = admin_path();
$config['development'] = config('site.name') == '瞬聊Test';
$config['domain'] = env_get('server.domain',''); $config['domain'] = env_get('server.domain','');
$request->_view_vars = array_merge((array) $request->_view_vars,[ $request->_view_vars = array_merge((array) $request->_view_vars,[
'user' => session('admin'), 'user' => session('admin'),
+15 -5
View File
@@ -54,18 +54,28 @@
<div class="login-header text-center"> <div class="login-header text-center">
<a href="javascript:;" style="font-size: 22px;display: flex;align-items: center;justify-content: center;"> <img alt="{$title}" src="{$logo}" width="32" class="m-r-5" />{$title}</a> <a href="javascript:;" style="font-size: 22px;display: flex;align-items: center;justify-content: center;"> <img alt="{$title}" src="{$logo}" width="32" class="m-r-5" />{$title}</a>
</div> </div>
<ul class="nav nav-tabs nav-justified">
<li class="active"><a data-toggle="tab" href="#otop_login">OTOP登录</a></li>
<li class="nav-item"><a data-toggle="tab" href="#passowrd_login">密码登录</a></li>
</ul>
<form action="{:url('account/login')}" method="post" id="loginform" valid> <form action="{:url('account/login')}" method="post" id="loginform" valid>
<div class="form-group has-feedback feedback-left"> <div class="form-group has-feedback feedback-left">
<span class="mdi mdi-account form-control-feedback" aria-hidden="true"></span> <span class="mdi mdi-account form-control-feedback" aria-hidden="true"></span>
<input type="text" placeholder="请输入您的用户名" class="form-control" name="username" id="username" data-rule="required" data-msg="请输入您的用户名"/> <input type="text" placeholder="请输入您的用户名" class="form-control" name="username" id="username" data-rule="required" data-msg="请输入您的用户名"/>
</div> </div>
<!-- <div class="form-group has-feedback feedback-left"> <div class="tab-content">
<span class="mdi mdi-lock form-control-feedback" aria-hidden="true"></span> <div class="tab-pane fade active in" id="otop_login">
<input type="password" placeholder="请输入密码" class="form-control" id="password" name="password" data-rule="required;password" />
</div> -->
<div class="form-group has-feedback feedback-left"> <div class="form-group has-feedback feedback-left">
<span class="mdi mdi-lock form-control-feedback" aria-hidden="true"></span> <span class="mdi mdi-lock form-control-feedback" aria-hidden="true"></span>
<input type="code" placeholder="请输入OTOP验证码" class="form-control" id="code" name="code" data-rule="required;length(4)" data-msg="请输入OTOP验证码" /> <input type="code" placeholder="请输入OTOP验证码" class="form-control" id="code" name="code" data-rule="required(#otop_login.active);length(4)" data-msg="请输入OTOP验证码" />
</div>
</div>
<div class="tab-pane fade" id="passowrd_login">
<div class="form-group has-feedback feedback-left">
<span class="mdi mdi-lock form-control-feedback" aria-hidden="true"></span>
<input type="password" placeholder="请输入密码" class="form-control" id="password" name="password" data-rule="required(#passowrd_login.active);password" />
</div>
</div>
</div> </div>
{if Config('site.admin_login_captcha')} {if Config('site.admin_login_captcha')}
<div class="form-group has-feedback feedback-left row"> <div class="form-group has-feedback feedback-left row">
+26 -58
View File
@@ -19,8 +19,7 @@
<p class="h6 text-white m-t-0">7日内充值</p> <p class="h6 text-white m-t-0">7日内充值</p>
<p class="h3 text-white m-b-0 fa-1-5x"><?=$day7_user_recharge_sum?></p> <p class="h3 text-white m-b-0 fa-1-5x"><?=$day7_user_recharge_sum?></p>
</div> </div>
<div class="pull-left"> <span class="img-avatar img-avatar-48 bg-translucent"><i <div class="pull-left"> <span class="img-avatar img-avatar-48 bg-translucent"><i class="mdi mdi-account fa-1-5x"></i></span> </div>
class="mdi mdi-account fa-1-5x"></i></span> </div>
</div> </div>
</div> </div>
</div> </div>
@@ -32,8 +31,7 @@
<p class="h6 text-white m-t-0">总充值</p> <p class="h6 text-white m-t-0">总充值</p>
<p class="h3 text-white m-b-0 fa-1-5x"><?=$recharge_total?></p> <p class="h3 text-white m-b-0 fa-1-5x"><?=$recharge_total?></p>
</div> </div>
<div class="pull-left"> <span class="img-avatar img-avatar-48 bg-translucent"><i <div class="pull-left"> <span class="img-avatar img-avatar-48 bg-translucent"><i class="mdi mdi-arrow-down-bold fa-1-5x"></i></span> </div>
class="mdi mdi-arrow-down-bold fa-1-5x"></i></span> </div>
</div> </div>
</div> </div>
</div> </div>
@@ -45,8 +43,7 @@
<p class="h6 text-white m-t-0">总提现</p> <p class="h6 text-white m-t-0">总提现</p>
<p class="h3 text-white m-b-0 fa-1-5x"><?=$withdrawl_total?></p> <p class="h3 text-white m-b-0 fa-1-5x"><?=$withdrawl_total?></p>
</div> </div>
<div class="pull-left"> <span class="img-avatar img-avatar-48 bg-translucent"><i <div class="pull-left"> <span class="img-avatar img-avatar-48 bg-translucent"><i class="mdi mdi-arrow-up-bold fa-1-5x"></i></span> </div>
class="mdi mdi-arrow-up-bold fa-1-5x"></i></span> </div>
</div> </div>
</div> </div>
</div> </div>
@@ -90,28 +87,24 @@
</colgroup> </colgroup>
<tbody> <tbody>
<tr> <tr>
<td>渠道商</td> <td>用户总</td>
<td> <td><?php echo \app\model\User::count('id');?></td>
<?php
echo \app\model\User::where('group_id',1)->count('id');
?>
</td>
</tr> </tr>
<tr> <tr>
<td>用户余额总和</td> <td>VIP用户数</td>
<td>{$user_money_total}</td> <td><?php echo \app\model\User::where('role_id',2)->count('id');?></td>
</tr>
<tr>
<td>SVIP1用户数</td>
<td><?php echo \app\model\User::where('role_id',3)->count('id');?></td>
</tr>
<tr>
<td>SVIP2用户数</td>
<td><?php echo \app\model\User::where('role_id',4)->count('id');?></td>
</tr> </tr>
<tr> <tr>
<td>用户积分总和</td> <td>用户积分总和</td>
<td>{$user_score_total}</td> <td><?php echo \app\model\User::sum('score');?></td>
</tr>
<tr>
<td>调研币总和</td>
<td><?php echo \app\model\User::sum('currency1');?></td>
</tr>
<tr>
<td>待分配总和</td>
<td><?php echo \app\model\User::sum('currency7');?></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
@@ -132,49 +125,24 @@
</colgroup> </colgroup>
<tbody> <tbody>
<tr> <tr>
<td>问卷成交个数</td> <td>操作系统</td>
<td> <td>{$os}</td>
<?php $system_question_total = cache('system_question_total');echo $system_question_total;?>
</td>
</tr> </tr>
<tr> <tr>
<td>预计支出泡沫</td> <td>workerman</td>
<td> <td>{$workerman_version}</td>
<?php $system_question_cha_total = 0;echo $system_question_cha_total;?>
</td>
</tr> </tr>
<tr> <tr>
<td>总沉淀金额</td> <td>webman</td>
<td> <td>{$webman_version}</td>
<?php
$system_role_buy_amount_total = cache('system_role_buy_amount_total');
$system_role_buy_reward_total = cache('system_role_buy_reward_total');
$system_role_buy_residual_total = $system_role_buy_amount_total - $system_role_buy_reward_total;
echo $system_role_buy_residual_total;
?>
</td>
</tr> </tr>
<tr> <tr>
<td>实际泡沫</td> <td>php</td>
<td> <td>{$php_version}</td>
<?php
$cha = $system_question_cha_total - $system_role_buy_residual_total;
if($cha>0){
echo '<span class="text-danger">'.$cha.'</span>';
}else{
echo '<span class="text-success">'.$cha.'</span>';
}
?>
</td>
</tr> </tr>
<tr> <tr>
<td>&nbsp;</td> <td>MySQL</td>
<td></td> <td>{$mysql_version}</td>
</tr>
<tr>
<td>&nbsp;</td>
<td></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
+28 -68
View File
@@ -83,7 +83,11 @@ define(['lightyear','multitabs', '../libs/Chart','form','bootstrap'], function (
setTheme = function (input_name, data_name) { setTheme = function (input_name, data_name) {
$("input[name='" + input_name + "']").click(function () { $("input[name='" + input_name + "']").click(function () {
$('body').attr(data_name, $(this).val()); $('body').attr(data_name, $(this).val());
localStorage.setItem(data_name,$(this).val());
}); });
if(Config.development){
$('body').attr(data_name, localStorage.getItem(data_name) || 'color_8');
}
} }
setTheme('logo_bg', 'data-logobg'); setTheme('logo_bg', 'data-logobg');
setTheme('header_bg', 'data-headerbg'); setTheme('header_bg', 'data-headerbg');
@@ -167,47 +171,7 @@ define(['lightyear','multitabs', '../libs/Chart','form','bootstrap'], function (
}); });
}, },
dashboard: function () { dashboard: function () {
// new Chart( $.getJSON('index/lines_data?items=withdrawl,recharge&days=7',function(res){
// document.getElementsByClassName('js-chartjs-bars'),
// {
// type: 'bar',
// data: {
// labels: recharge_labels,
// datasets: [
// {
// label: '充值统计',
// data: recharge_values,
// borderColor: '#358ed7',
// backgroundColor: 'rgba(53, 142, 215, 0.175)',
// borderWidth: 1,
// fill: false,
// lineTension: 0.5
// },
// ]
// }
// }
// );
// new Chart(
// document.getElementsByClassName('js-chartjs-lines'),
// {
// type: 'bar',//line
// data: {
// labels: withdrawl_labels,
// datasets: [
// {
// label: '提现统计',
// data: withdrawl_values,
// borderColor: '#358ed7',
// backgroundColor: 'rgba(53, 142, 215, 0.175)',
// borderWidth: 1,
// fill: false,
// lineTension: 0.5
// }
// ]
// }
// }
// );
$.getJSON('index/money_lines',function(res){
res = res.data; res = res.data;
var labels=[]; var labels=[];
var withdrawl_values=[]; var withdrawl_values=[];
@@ -248,17 +212,13 @@ define(['lightyear','multitabs', '../libs/Chart','form','bootstrap'], function (
} }
); );
}); });
$.getJSON('index/role_buy_lines',function(res){ $.getJSON('index/lines_data?items=register&days=7',function(res){
res = res.data; res = res.data;
var amount=[]; var register=[];
var reward=[];
var residual=[];
var labels=[]; var labels=[];
for (let date in res) { for (let date in res) {
labels.push(date); labels.push(date);
amount.push(res[date].amount); register.push(res[date].register);
reward.push(res[date].reward);
residual.push(res[date].residual);
} }
new Chart(document.getElementsByClassName('js-role_buy_lines'),{ new Chart(document.getElementsByClassName('js-role_buy_lines'),{
type: 'line', type: 'line',
@@ -266,32 +226,32 @@ define(['lightyear','multitabs', '../libs/Chart','form','bootstrap'], function (
labels: labels, labels: labels,
datasets: [ datasets: [
{ {
label: '总购买金额', label: '注册统计',
data: amount, data: register,
borderColor: 'rgba(43, 191, 232, 0.7)', borderColor: 'rgba(43, 191, 232, 0.7)',
backgroundColor: 'rgba(43, 191, 232, 0.7)', backgroundColor: 'rgba(43, 191, 232, 0.7)',
borderWidth: 2, borderWidth: 2,
fill: false, fill: false,
lineTension: 0.5 lineTension: 0.5
}, },
{ // {
label: '奖励统计', // label: '奖励统计',
data: reward, // data: reward,
borderColor: 'rgba(166, 53, 215, 0.7)', // borderColor: 'rgba(166, 53, 215, 0.7)',
backgroundColor: 'rgba(166, 53, 215, 0.7)', // backgroundColor: 'rgba(166, 53, 215, 0.7)',
borderWidth: 2, // borderWidth: 2,
fill: false, // fill: false,
lineTension: 0.5 // lineTension: 0.5
}, // },
{ // {
label: '沉淀统计', // label: '沉淀统计',
data: residual, // data: residual,
borderColor: 'rgba(59, 199, 8, 0.7)', // borderColor: 'rgba(59, 199, 8, 0.7)',
backgroundColor: 'rgba(59, 199, 8, 0.7)', // backgroundColor: 'rgba(59, 199, 8, 0.7)',
borderWidth: 2, // borderWidth: 2,
fill: false, // fill: false,
lineTension: 0.5 // lineTension: 0.5
} // }
] ]
} }
}); });
+4 -2
View File
@@ -50,11 +50,13 @@ define(['table', 'upload','form','qrcode'], function (Table,Upload,Form) {
}, },
{ {
title: "用户名", title: "用户名",
field: "username" field: "username",
filter: "string"
}, },
{ {
title: "昵称", title: "昵称",
field: "nickname" field: "nickname",
filter: "string"
}, },
{ {
title: "头像", title: "头像",
+8
View File
@@ -79,3 +79,11 @@ bash bootstrap.sh
mage mage
mage start mage start
docker exec -it mongo mongorestore --uri="mongodb://openIM:n1e5a6s6m7@172.18.0.1:37017/openim_v3" /data/backup/3/openim_v3 docker exec -it mongo mongorestore --uri="mongodb://openIM:n1e5a6s6m7@172.18.0.1:37017/openim_v3" /data/backup/3/openim_v3
docker exec -it mongo mongorestore --uri="mongodb://openIM:n1e5a6s6m7@localhost:27017/openim_v3" /data/backup/0/openim_v3
修复未读<0的问题
cd /www/wwwroot/im/server
go run tools/fix-conversation-maxseq/main.go --config config --dry-run
清理15天以前的消息
cd /www/wwwroot/im/server
go run tools/clean-old-messages/main.go --config config --days 15
+1 -1
View File
@@ -68,7 +68,7 @@ class Jwt
'password' => $password, 'password' => $password,
'email' => $email, 'email' => $email,
'mobile' => $mobile, 'mobile' => $mobile,
'level' => 1, 'level' => 0,
'score' => 0, 'score' => 0,
'avatar' => '', 'avatar' => '',
]; ];
+1 -2
View File
@@ -33,9 +33,8 @@ class Auth
public function getUserToken(string $userID, string $platformID = 'android'): array public function getUserToken(string $userID, string $platformID = 'android'): array
{ {
// 获取管理员token // 获取管理员token
$adminToken = Utils::getAdminToken();
$platformID = Utils::getPlatformId( $platformID ); $platformID = Utils::getPlatformId( $platformID );
return Utils::send(Url::$getUserToken, ['userID' => $userID, 'platformID' => $platformID], '获取用户token错误', $adminToken); return Utils::send(Url::$getUserToken, ['userID' => $userID, 'platformID' => $platformID], '获取用户token错误');
} }
/** /**
+1 -1
View File
@@ -63,7 +63,7 @@ class Friend
* @param string $friendUserID 好友ID * @param string $friendUserID 好友ID
* @return array * @return array
*/ */
public function deleteFriend(string $ownerUserID, string $friendUserID): array public function deleteFriend(string $ownerUserID, string $friendUserID): array|bool
{ {
// 获取管理员token // 获取管理员token
$adminToken = Utils::getAdminToken(); $adminToken = Utils::getAdminToken();
+3
View File
@@ -192,6 +192,9 @@ class Utils
return self::getTokenManager()->clearToken($userID, $isAdmin); return self::getTokenManager()->clearToken($userID, $isAdmin);
} }
public static function getPlatformId($name=''): int{ public static function getPlatformId($name=''): int{
if(ctype_digit($name)){
return (int)$name;
}
$arr = [ $arr = [
'ios' => 1, 'ios' => 1,
'android' => 2, 'android' => 2,