add
This commit is contained in:
203
app/common/controller/BaseController.php
Normal file
203
app/common/controller/BaseController.php
Normal file
@@ -0,0 +1,203 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\controller;
|
||||
|
||||
use app\common\library\Auth;
|
||||
use app\common\library\token\TokenExpirationException;
|
||||
use support\Request;
|
||||
use HttpResponseException;
|
||||
use support\Response;
|
||||
abstract class BaseController
|
||||
{
|
||||
/**
|
||||
* 无需登录的方法
|
||||
* 访问本控制器的此方法,无需会员登录
|
||||
* @var array
|
||||
*/
|
||||
protected array $noNeedLogin = [];
|
||||
|
||||
/**
|
||||
* 默认响应输出类型,支持json/xml/jsonp
|
||||
* @var string
|
||||
*/
|
||||
protected string $responseType = 'application/json';
|
||||
/**
|
||||
* 无需鉴权的方法
|
||||
* @var array
|
||||
*/
|
||||
protected array $noNeedPermission = [];
|
||||
|
||||
|
||||
protected Request $request;
|
||||
/**
|
||||
* 权限类实例
|
||||
* @var Auth
|
||||
*/
|
||||
protected Auth $auth;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->request = request();
|
||||
|
||||
$needLogin = !action_in_arr($this->noNeedLogin);
|
||||
// 初始化会员鉴权实例
|
||||
$this->auth = Auth::instance();
|
||||
$token = request()->header('token');
|
||||
if ($token) {
|
||||
if(!$this->auth->init($token)) {
|
||||
$this->error('Token expiration', [], 409);
|
||||
}
|
||||
};
|
||||
if ($needLogin) {
|
||||
$this->error('Token expiration', [], 409);
|
||||
}
|
||||
if ($needLogin) {
|
||||
if (!$this->auth->isLogin()) {
|
||||
$this->error('请登录', [
|
||||
'type' => $this->auth::NEED_LOGIN
|
||||
], $this->auth::LOGIN_RESPONSE_CODE);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 操作成功
|
||||
* @param string $msg 提示消息
|
||||
* @param mixed $data 返回数据
|
||||
* @param int $code 错误码
|
||||
* @param string|null $type 输出类型
|
||||
* @param array $header 发送的 header 信息
|
||||
* @param array $options Response 输出参数
|
||||
*/
|
||||
protected function success(string $msg = '', mixed $data = null, int $code = 0, string $type = null, array $header = [], array $options = []): void
|
||||
{
|
||||
$this->result($msg, $data, $code, $type, $header, $options);
|
||||
}
|
||||
|
||||
|
||||
protected function successWithData(mixed $data = null, string $msg = '', int $code = 0, string $type = null, array $header = [], array $options = []): void
|
||||
{
|
||||
$this->result($msg, $data, $code, $type, $header, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* 操作失败
|
||||
* @param string $msg 提示消息
|
||||
* @param mixed $data 返回数据
|
||||
* @param int $code 错误码
|
||||
* @param string|null $type 输出类型
|
||||
* @param array $header 发送的 header 信息
|
||||
* @param array $options Response 输出参数
|
||||
*/
|
||||
protected function error(string $msg = '', mixed $data = null, int $code = -1, string $type = null, array $header = [], array $options = []): void
|
||||
{
|
||||
$this->result($msg, $data, $code, $type, $header, $options);
|
||||
}
|
||||
|
||||
protected function errorMsg(string $msg = '', mixed $data = null, int $code = -1, string $type = null, array $header = [], array $options = []): void
|
||||
{
|
||||
$this->result($msg, $data, $code, $type, $header, $options, 'data', 'msg');
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 操作成功
|
||||
* @param string $msg 提示消息
|
||||
* @param mixed $data 返回数据
|
||||
* @param int $code 错误码
|
||||
* @param string|null $type 输出类型
|
||||
* @param array $header 发送的 header 信息
|
||||
* @param array $options Response 输出参数
|
||||
*/
|
||||
protected function n_success(array $data, string $msg = 'ok', int $code = 0, string $type = null, array $header = [], array $options = []): void
|
||||
{
|
||||
$this->resultApi($data, $msg, $code, $type, $header, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* 操作失败
|
||||
* @param string $msg 提示消息
|
||||
* @param mixed $data 返回数据
|
||||
* @param int $code 错误码
|
||||
* @param string|null $type 输出类型
|
||||
* @param array $header 发送的 header 信息
|
||||
* @param array $options Response 输出参数
|
||||
*/
|
||||
protected function n_error(string $msg, array $data = [], int $code = -1, string $type = null, array $header = [], array $options = []): void
|
||||
{
|
||||
$this->resultApi($data, $msg, $code, $type, $header, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* 多种操作结果
|
||||
*/
|
||||
public function resultApi(array $data = [], string $msg = 'ok', int $code = 0, string $type = null, array $header = [], array $options = [])
|
||||
{
|
||||
$data['code'] = $code;
|
||||
$data['message'] = $msg;
|
||||
$data['time'] = $this->request->server('REQUEST_TIME');
|
||||
if(isset($data['data']['records'])) {
|
||||
$data['data']['records'] = apiconvertToCamelCase($data['data']['records']);
|
||||
}
|
||||
if(isset($data['page']['list'])) {
|
||||
$data['page']['list'] = apiconvertToCamelCase($data['page']['list']);
|
||||
}
|
||||
if(isset($data['data']['list'])) {
|
||||
$data['data']['list'] = apiconvertToCamelCase($data['data']['list']);
|
||||
}
|
||||
if(isset($data['data']['userEntity'])) {
|
||||
$data['data']['userEntity'] = apiconvertToCamelCase($data['data']['userEntity']);
|
||||
}
|
||||
$result = $data;
|
||||
|
||||
$type = $type ?: $this->responseType;
|
||||
|
||||
$response = new Response();
|
||||
$response->header('Content-Type', $type);
|
||||
$response->withBody($result);
|
||||
return $response;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 返回 API 数据
|
||||
* @param string $msg 提示消息
|
||||
* @param mixed $data 返回数据
|
||||
* @param int $code 错误码
|
||||
* @param string|null $type 输出类型
|
||||
* @param array $header 发送的 header 信息
|
||||
* @param array $options Response 输出参数
|
||||
*/
|
||||
public function result(string $msg, mixed $data = null, int $code = 0, string $type = null, array $header = [], array $options = [], string $dataKey = 'data', string $msgKey = 'message')
|
||||
{
|
||||
if(isset($data['records'])) {
|
||||
$data['records'] = apiconvertToCamelCase($data['records']);
|
||||
}
|
||||
if(isset($data['page'])) {
|
||||
$data['page'] = apiconvertToCamelCase($data['page']);
|
||||
}
|
||||
if(isset($data['list'])) {
|
||||
$data['list'] = apiconvertToCamelCase($data['list']);
|
||||
}
|
||||
$result = [
|
||||
'code' => $code,
|
||||
'message' => $msg,
|
||||
'time' => time(),
|
||||
$dataKey => $data,
|
||||
];
|
||||
throw new \app\exception\HttpResponseException(json($result));
|
||||
}
|
||||
|
||||
public function ApiDataReturn($data)
|
||||
{
|
||||
return json($data);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
651
app/common/library/Auth.php
Normal file
651
app/common/library/Auth.php
Normal file
@@ -0,0 +1,651 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\library;
|
||||
|
||||
use app\api\model\TbUser;
|
||||
use app\api\model\TbUserBlacklist;
|
||||
use app\api\model\UserInfo;
|
||||
use app\common\model\Common;
|
||||
use app\common\model\SysUser;
|
||||
use app\utils\JwtUtils;
|
||||
use think\facade\Cache;
|
||||
use Throwable;
|
||||
use ba\Random;
|
||||
use think\facade\Db;
|
||||
use think\facade\Event;
|
||||
use think\facade\Config;
|
||||
use app\common\model\User;
|
||||
use think\facade\Validate;
|
||||
use app\common\facade\Token;
|
||||
|
||||
/**
|
||||
* 公共权限类(会员权限类)
|
||||
* @property int $id 会员ID
|
||||
* @property string $username 会员用户名
|
||||
* @property string $nickname 会员昵称
|
||||
* @property string $email 会员邮箱
|
||||
* @property string $mobile 会员手机号
|
||||
* @property string $password 密码密文
|
||||
* @property string $salt 密码盐
|
||||
*/
|
||||
class Auth
|
||||
{
|
||||
/**
|
||||
* 需要登录时/无需登录时的响应状态代码
|
||||
*/
|
||||
public const LOGIN_RESPONSE_CODE = 401;
|
||||
|
||||
/**
|
||||
* 需要登录标记 - 前台应清理 token、记录当前路由 path、跳转到登录页
|
||||
*/
|
||||
public const NEED_LOGIN = 'need login';
|
||||
|
||||
/**
|
||||
* 已经登录标记 - 前台应跳转到基础路由
|
||||
*/
|
||||
public const LOGGED_IN = 'logged in';
|
||||
|
||||
/**
|
||||
* token 入库 type
|
||||
*/
|
||||
public const TOKEN_TYPE = 'user';
|
||||
|
||||
/**
|
||||
* 是否登录
|
||||
* @var bool
|
||||
*/
|
||||
protected bool $loginEd = false;
|
||||
|
||||
/**
|
||||
* 错误消息
|
||||
* @var string
|
||||
*/
|
||||
protected string $error = '';
|
||||
|
||||
/**
|
||||
* Model实例
|
||||
* @var ?User
|
||||
*/
|
||||
protected $model = null;
|
||||
|
||||
protected $user_id_db_connect = null;
|
||||
|
||||
/**
|
||||
* 令牌
|
||||
* @var string
|
||||
*/
|
||||
protected string $token = '';
|
||||
|
||||
/**
|
||||
* 刷新令牌
|
||||
* @var string
|
||||
*/
|
||||
protected string $refreshToken = '';
|
||||
|
||||
/**
|
||||
* 令牌默认有效期
|
||||
* 可在 config/buildadmin.php 内修改默认值
|
||||
* @var int
|
||||
*/
|
||||
protected int $keepTime = 0;
|
||||
|
||||
/**
|
||||
* 刷新令牌有效期
|
||||
* @var int
|
||||
*/
|
||||
protected int $refreshTokenKeepTime = 0;
|
||||
|
||||
/**
|
||||
* 允许输出的字段
|
||||
* @var array
|
||||
*/
|
||||
protected array $allowFields = ['user_id', 'username', 'nickname', 'email', 'mobile', 'avatar', 'gender', 'birthday', 'money', 'score', 'join_time', 'motto', 'last_login_time', 'last_login_ip'];
|
||||
|
||||
public function __construct(array $config = [])
|
||||
{
|
||||
// parent::__construct(array_merge([
|
||||
// 'auth_group' => 'user_group', // 用户组数据表名
|
||||
// 'auth_group_access' => '', // 用户-用户组关系表(关系字段)
|
||||
// 'auth_rule' => 'user_rule', // 权限规则表
|
||||
// ], $config));
|
||||
//
|
||||
// $this->setKeepTime((int)config('buildadmin.user_token_keep_time'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 魔术方法-会员信息字段
|
||||
* @param $name
|
||||
* @return mixed 字段信息
|
||||
*/
|
||||
public function __get($name): mixed
|
||||
{
|
||||
return $this->model[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
* @access public
|
||||
* @param array $options 传递给 /ba/Auth 的参数
|
||||
* @return Auth
|
||||
*/
|
||||
public static function instance(array $options = []): Auth
|
||||
{
|
||||
$request = request();
|
||||
if (!isset($request->userAuth)) {
|
||||
$request->userAuth = new static($options);
|
||||
}
|
||||
return $request->userAuth;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据Token初始化会员登录态
|
||||
* @param $token
|
||||
* @return bool
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function init($token): bool
|
||||
{
|
||||
$jwtUtil = (new JwtUtils());
|
||||
|
||||
$tokenData = $jwtUtil->getClaimByToken($token);
|
||||
if ($tokenData) {
|
||||
|
||||
/**
|
||||
* 过期检查,过期则抛出 @see TokenExpirationException
|
||||
*/
|
||||
|
||||
$userId = $tokenData->sub;
|
||||
if (!isset($tokenData->type) || ($tokenData->type == self::TOKEN_TYPE && $userId > 0)) {
|
||||
|
||||
$where = $sale = ['user_id' => $userId];
|
||||
$user_id_db_name = DatabaseRoute::getConnection('tb_user', $sale);
|
||||
$this->model = Db::connect($user_id_db_name)->name('tb_user')->where($where)->find();
|
||||
if (!$this->model) {
|
||||
$this->setError('Account not exist');
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->model['status'] != 1) {
|
||||
$this->setError('Account disabled');
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->token = $token;
|
||||
$this->newloginSuccessful();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
$this->setError('Token login failed');
|
||||
$this->reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 会员注册,可使用关键词参数方式调用:$auth->register('u18888888888', email: 'test@qq.com')
|
||||
* @param string $username
|
||||
* @param string $password
|
||||
* @param string $mobile
|
||||
* @param string $email
|
||||
* @param int $group 会员分组 ID 号
|
||||
* @param array $extend 扩展数据,如 ['status' => 'disable']
|
||||
* @return bool
|
||||
*/
|
||||
public function register(string $username, string $password = '', string $mobile = '', string $email = '', int $group = 1, array $extend = []): bool
|
||||
{
|
||||
$validate = Validate::rule([
|
||||
'email|' . __('Email') => 'email|unique:user',
|
||||
'mobile|' . __('Mobile') => 'mobile|unique:user',
|
||||
'username|' . __('Username') => 'require|regex:^[a-zA-Z][a-zA-Z0-9_]{2,15}$|unique:user',
|
||||
'password|' . __('Password') => 'regex:^(?!.*[&<>"\'\n\r]).{6,32}$',
|
||||
]);
|
||||
$params = [
|
||||
'username' => $username,
|
||||
'password' => $password,
|
||||
'mobile' => $mobile,
|
||||
'email' => $email,
|
||||
];
|
||||
if (!$validate->check($params)) {
|
||||
$this->setError($validate->getError());
|
||||
return false;
|
||||
}
|
||||
|
||||
// 按需生成随机密码
|
||||
if (!$password) {
|
||||
$password = Random::build();
|
||||
}
|
||||
|
||||
// 用户昵称
|
||||
$nickname = preg_replace_callback('/1[3-9]\d{9}/', function ($matches) {
|
||||
// 对 username 中出现的所有手机号进行脱敏处理
|
||||
$mobile = $matches[0];
|
||||
return substr($mobile, 0, 3) . '****' . substr($mobile, 7);
|
||||
}, $username);
|
||||
|
||||
$ip = request()->ip();
|
||||
$time = time();
|
||||
$data = [
|
||||
'group_id' => $group,
|
||||
'nickname' => $nickname,
|
||||
'join_ip' => $ip,
|
||||
'join_time' => $time,
|
||||
'last_login_ip' => $ip,
|
||||
'last_login_time' => $time,
|
||||
'status' => 'enable', // 状态:enable=启用,disable=禁用,使用 string 存储可以自定义其他状态
|
||||
];
|
||||
$data = array_merge($params, $data);
|
||||
$data = array_merge($data, $extend);
|
||||
|
||||
Db::startTrans();
|
||||
try {
|
||||
$this->model = User::create($data);
|
||||
$this->token = Random::uuid();
|
||||
Token::set($this->token, self::TOKEN_TYPE, $this->model->id, $this->keepTime);
|
||||
Db::commit();
|
||||
|
||||
$this->model->resetPassword($this->model->id, $password);
|
||||
|
||||
Event::trigger('userRegisterSuccess', $this->model);
|
||||
} catch (Throwable $e) {
|
||||
$this->setError($e->getMessage());
|
||||
Db::rollback();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 会员登录
|
||||
* @param string $username 用户名
|
||||
* @param string $password 密码
|
||||
* @param bool $keep 是否保持登录
|
||||
* @return bool
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function login(string $username, string $password, bool $keep): bool
|
||||
{
|
||||
// 判断账户类型
|
||||
$accountType = false;
|
||||
$validate = Validate::rule([
|
||||
'mobile' => 'mobile',
|
||||
'email' => 'email',
|
||||
'username' => 'regex:^[a-zA-Z][a-zA-Z0-9_]{2,15}$',
|
||||
]);
|
||||
if ($validate->check(['mobile' => $username])) $accountType = 'mobile';
|
||||
if ($validate->check(['email' => $username])) $accountType = 'email';
|
||||
if ($validate->check(['username' => $username])) $accountType = 'username';
|
||||
if (!$accountType) {
|
||||
$this->setError('Account not exist');
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->model = User::where($accountType, $username)->find();
|
||||
if (!$this->model) {
|
||||
$this->setError('Account not exist');
|
||||
return false;
|
||||
}
|
||||
if ($this->model->status == 'disable') {
|
||||
$this->setError('Account disabled');
|
||||
return false;
|
||||
}
|
||||
|
||||
// 登录失败重试检查
|
||||
$userLoginRetry = Config::get('buildadmin.user_login_retry');
|
||||
if ($userLoginRetry && $this->model->last_login_time) {
|
||||
// 重置失败次数
|
||||
if ($this->model->login_failure > 0 && time() - $this->model->last_login_time >= 86400) {
|
||||
$this->model->login_failure = 0;
|
||||
$this->model->save();
|
||||
|
||||
// 重获模型实例,避免单实例多次更新
|
||||
$this->model = User::where($accountType, $username)->find();
|
||||
}
|
||||
|
||||
if ($this->model->login_failure >= $userLoginRetry) {
|
||||
$this->setError('Please try again after 1 day');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 密码检查
|
||||
if (!verify_password($password, $this->model->password, ['salt' => $this->model->salt])) {
|
||||
$this->loginFailed();
|
||||
$this->setError('Password is incorrect');
|
||||
return false;
|
||||
}
|
||||
|
||||
// 清理 token
|
||||
if (Config::get('buildadmin.user_sso')) {
|
||||
Token::clear(self::TOKEN_TYPE, $this->model->id);
|
||||
Token::clear(self::TOKEN_TYPE . '-refresh', $this->model->id);
|
||||
}
|
||||
|
||||
if ($keep) {
|
||||
$this->setRefreshToken($this->refreshTokenKeepTime);
|
||||
}
|
||||
$this->loginSuccessful();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 会员登录
|
||||
* @param string $username 用户名
|
||||
* @param string $password 密码
|
||||
* @param bool $keep 是否保持登录
|
||||
* @return bool
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function newlogin(string $phone, string $password, bool $keep): bool
|
||||
{
|
||||
$this->model = TbUser::GetByusername($phone);
|
||||
if (!$this->model) {
|
||||
$this->setError('未注册, 请先注册');
|
||||
return false;
|
||||
}
|
||||
if ($this->model['status'] == 0) {
|
||||
$this->setError('账号已被禁用,请联系客服处理!');
|
||||
return false;
|
||||
}
|
||||
|
||||
if(empty($this->model['password'])) {
|
||||
$this->setError('当前账号未绑定密码,请前往忘记密码中进行重置!');
|
||||
return false;
|
||||
}
|
||||
// 验证密码
|
||||
if(TbUser::CheckPassword($this->model['password'], $password)) {
|
||||
// 实名认证信息
|
||||
$userInfo = new UserInfo();
|
||||
$where = ['user_id' => $this->model['user_id']];
|
||||
$userInfo->setConnection($userInfo::findbefore($userInfo, $where));
|
||||
$userInfo = $userInfo->where($where)->find();
|
||||
if($userInfo && $userInfo->cert_no) {
|
||||
// 继续查黑名单表
|
||||
$userBlack = TbUserBlacklist::where(['id_card_no' => $userInfo->cert_no])->find();
|
||||
if($userBlack) {
|
||||
$this->setError('系统正在维护中,请稍后再试!');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// if ($keep) {
|
||||
// $this->setRefreshToken($this->refreshTokenKeepTime);
|
||||
// }
|
||||
$this->newloginSuccessful();
|
||||
return true;
|
||||
}else {
|
||||
$this->setError('账号或密码不正确!');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 直接登录会员账号
|
||||
* @param int $userId 用户ID
|
||||
* @return bool
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function direct(int $userId): bool
|
||||
{
|
||||
$this->model = User::find($userId);
|
||||
if (!$this->model) return false;
|
||||
if (Config::get('buildadmin.user_sso')) {
|
||||
Token::clear(self::TOKEN_TYPE, $this->model->id);
|
||||
Token::clear(self::TOKEN_TYPE . '-refresh', $this->model->id);
|
||||
}
|
||||
return $this->loginSuccessful();
|
||||
}
|
||||
|
||||
/**
|
||||
* 直接登录会员账号 new
|
||||
* @param int $userId 用户ID
|
||||
* @return bool
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function newdirect(int $userId): bool
|
||||
{
|
||||
if (Config::get('buildadmin.user_sso')) {
|
||||
Token::clear(self::TOKEN_TYPE, $this->model->id);
|
||||
Token::clear(self::TOKEN_TYPE . '-refresh', $this->model->id);
|
||||
}
|
||||
return $this->newloginSuccessful();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 登录成功 new
|
||||
* @return bool
|
||||
*/
|
||||
public function newloginSuccessful(): bool
|
||||
{
|
||||
if (!$this->model) {
|
||||
return false;
|
||||
}
|
||||
// 新增登录时间
|
||||
$db = Db::connect(DatabaseRoute::getConnection('tb_user', ['user_id' => $this->model['user_id']], true));
|
||||
$db->name('tb_user')->where(['user_id' => $this->model['user_id']])->update(['on_line_time' => date('Y-m-d H:i:s')]);
|
||||
$this->loginEd = true;
|
||||
if (!$this->token) {
|
||||
$this->token = (new JwtUtils())->generateToken($this->model['user_id'], 'user');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 检查旧密码是否正确
|
||||
* @param $password
|
||||
* @return bool
|
||||
* @deprecated 请使用 verify_password 公共函数代替
|
||||
*/
|
||||
public function checkPassword($password): bool
|
||||
{
|
||||
return verify_password($password, $this->model->password, ['salt' => $this->model->salt]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录成功
|
||||
* @return bool
|
||||
*/
|
||||
public function loginSuccessful(): bool
|
||||
{
|
||||
if (!$this->model) {
|
||||
return false;
|
||||
}
|
||||
$this->model->startTrans();
|
||||
try {
|
||||
$this->model->login_failure = 0;
|
||||
$this->model->last_login_time = time();
|
||||
$this->model->last_login_ip = request()->ip();
|
||||
$this->model->save();
|
||||
$this->loginEd = true;
|
||||
|
||||
if (!$this->token) {
|
||||
$this->token = Random::uuid();
|
||||
Token::set($this->token, self::TOKEN_TYPE, $this->model->id, $this->keepTime);
|
||||
}
|
||||
$this->model->commit();
|
||||
} catch (Throwable $e) {
|
||||
$this->model->rollback();
|
||||
$this->setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录失败
|
||||
* @return bool
|
||||
*/
|
||||
public function loginFailed(): bool
|
||||
{
|
||||
if (!$this->model) return false;
|
||||
$this->model->startTrans();
|
||||
try {
|
||||
$this->model->login_failure++;
|
||||
$this->model->last_login_time = time();
|
||||
$this->model->last_login_ip = request()->ip();
|
||||
$this->model->save();
|
||||
$this->model->commit();
|
||||
} catch (Throwable $e) {
|
||||
$this->model->rollback();
|
||||
$this->setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
return $this->reset();
|
||||
}
|
||||
|
||||
public function get_user_id_db_connect()
|
||||
{
|
||||
return $this->user_id_db_connect;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 退出登录
|
||||
* @return bool
|
||||
*/
|
||||
public function logout(): bool
|
||||
{
|
||||
if (!$this->loginEd) {
|
||||
$this->setError('You are not logged in');
|
||||
return false;
|
||||
}
|
||||
return $this->reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否登录
|
||||
* @return bool
|
||||
*/
|
||||
public function isLogin(): bool
|
||||
{
|
||||
return $this->loginEd;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会员模型
|
||||
* @return User
|
||||
*/
|
||||
public function getUser()
|
||||
{
|
||||
return $this->model;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会员Token
|
||||
* @return string
|
||||
*/
|
||||
public function getToken(): string
|
||||
{
|
||||
return $this->token;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置刷新Token
|
||||
* @param int $keepTime
|
||||
* @return void
|
||||
*/
|
||||
public function setRefreshToken(int $keepTime = 0): void
|
||||
{
|
||||
$this->refreshToken = Random::uuid();
|
||||
Token::set($this->refreshToken, self::TOKEN_TYPE . '-refresh', $this->model->id, $keepTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会员刷新Token
|
||||
* @return string
|
||||
*/
|
||||
public function getRefreshToken(): string
|
||||
{
|
||||
return $this->refreshToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会员信息 - 只输出允许输出的字段
|
||||
* @return array
|
||||
*/
|
||||
public function getUserInfo(): array
|
||||
{
|
||||
if (!$this->model) return [];
|
||||
$info = $this->model;
|
||||
$info = array_intersect_key($info, array_flip($this->getAllowFields()));
|
||||
$info['token'] = $this->getToken();
|
||||
$info['refresh_token'] = $this->getRefreshToken();
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取允许输出字段
|
||||
* @return array
|
||||
*/
|
||||
public function getAllowFields(): array
|
||||
{
|
||||
return $this->allowFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置允许输出字段
|
||||
* @param $fields
|
||||
* @return void
|
||||
*/
|
||||
public function setAllowFields($fields): void
|
||||
{
|
||||
$this->allowFields = $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置Token有效期
|
||||
* @param int $keepTime
|
||||
* @return void
|
||||
*/
|
||||
public function setKeepTime(int $keepTime = 0): void
|
||||
{
|
||||
$this->keepTime = $keepTime;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 设置错误消息
|
||||
* @param string $error
|
||||
* @return Auth
|
||||
*/
|
||||
public function setError(string $error): Auth
|
||||
{
|
||||
$this->error = $error;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取错误消息
|
||||
* @return string
|
||||
*/
|
||||
public function getError(): string
|
||||
{
|
||||
return $this->error ? __($this->error) : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 属性重置(注销、登录失败、重新初始化等将单例数据销毁)
|
||||
*/
|
||||
protected function reset(bool $deleteToken = true): bool
|
||||
{
|
||||
if ($deleteToken && $this->token) {
|
||||
// Token::delete($this->token);
|
||||
}
|
||||
|
||||
$this->token = '';
|
||||
$this->loginEd = false;
|
||||
$this->model = null;
|
||||
$this->refreshToken = '';
|
||||
$this->setError('');
|
||||
$this->setKeepTime((int)Config('buildadmin.user_token_keep_time'));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
71
app/common/library/Email.php
Normal file
71
app/common/library/Email.php
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\library;
|
||||
|
||||
use Throwable;
|
||||
use think\facade\Lang;
|
||||
use PHPMailer\PHPMailer\PHPMailer;
|
||||
|
||||
/**
|
||||
* 邮件类
|
||||
* 继承PHPMailer并初始化好了站点系统配置中的邮件配置信息
|
||||
*/
|
||||
class Email extends PHPMailer
|
||||
{
|
||||
/**
|
||||
* 是否已在管理后台配置好邮件服务
|
||||
* @var bool
|
||||
*/
|
||||
public bool $configured = false;
|
||||
|
||||
/**
|
||||
* 默认配置
|
||||
* @var array
|
||||
*/
|
||||
public array $options = [
|
||||
'charset' => 'utf-8', //编码格式
|
||||
'debug' => true, //调式模式
|
||||
'lang' => 'zh_cn',
|
||||
];
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @param array $options
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function __construct(array $options = [])
|
||||
{
|
||||
$this->options = array_merge($this->options, $options);
|
||||
|
||||
parent::__construct($this->options['debug']);
|
||||
$langSet = Lang::getLangSet();
|
||||
if ($langSet == 'zh-cn' || !$langSet) $langSet = 'zh_cn';
|
||||
$this->options['lang'] = $this->options['lang'] ?: $langSet;
|
||||
|
||||
$this->setLanguage($this->options['lang'], root_path() . 'vendor' . DIRECTORY_SEPARATOR . 'phpmailer' . DIRECTORY_SEPARATOR . 'phpmailer' . DIRECTORY_SEPARATOR . 'language' . DIRECTORY_SEPARATOR);
|
||||
$this->CharSet = $this->options['charset'];
|
||||
|
||||
$sysMailConfig = get_sys_config('', 'mail');
|
||||
$this->configured = true;
|
||||
foreach ($sysMailConfig as $item) {
|
||||
if (!$item) {
|
||||
$this->configured = false;
|
||||
}
|
||||
}
|
||||
if ($this->configured) {
|
||||
$this->Host = $sysMailConfig['smtp_server'];
|
||||
$this->SMTPAuth = true;
|
||||
$this->Username = $sysMailConfig['smtp_user'];
|
||||
$this->Password = $sysMailConfig['smtp_pass'];
|
||||
$this->SMTPSecure = $sysMailConfig['smtp_verification'] == 'SSL' ? self::ENCRYPTION_SMTPS : self::ENCRYPTION_STARTTLS;
|
||||
$this->Port = $sysMailConfig['smtp_port'];
|
||||
|
||||
$this->setFrom($sysMailConfig['smtp_sender_mail'], $sysMailConfig['smtp_user']);
|
||||
}
|
||||
}
|
||||
|
||||
public function setSubject($subject): void
|
||||
{
|
||||
$this->Subject = "=?utf-8?B?" . base64_encode($subject) . "?=";
|
||||
}
|
||||
}
|
||||
156
app/common/library/Menu.php
Normal file
156
app/common/library/Menu.php
Normal file
@@ -0,0 +1,156 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\library;
|
||||
|
||||
use Throwable;
|
||||
use app\admin\model\AdminRule;
|
||||
use app\admin\model\UserRule;
|
||||
|
||||
/**
|
||||
* 菜单规则管理类
|
||||
*/
|
||||
class Menu
|
||||
{
|
||||
/**
|
||||
* @param array $menu
|
||||
* @param int|string $parent 父级规则name或id
|
||||
* @param string $mode 添加模式(规则重复时):cover=覆盖旧菜单,rename=重命名新菜单,ignore=忽略
|
||||
* @param string $position 位置:backend=后台,frontend=前台
|
||||
* @return void
|
||||
* @throws Throwable
|
||||
*/
|
||||
public static function create(array $menu, int|string $parent = 0, string $mode = 'cover', string $position = 'backend'): void
|
||||
{
|
||||
$pid = 0;
|
||||
$model = $position == 'backend' ? new AdminRule() : new UserRule();
|
||||
$parentRule = $model->where((is_numeric($parent) ? 'id' : 'name'), $parent)->find();
|
||||
if ($parentRule) {
|
||||
$pid = $parentRule['id'];
|
||||
}
|
||||
foreach ($menu as $item) {
|
||||
if (!self::requiredAttrCheck($item)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 属性
|
||||
$item['status'] = 1;
|
||||
if (!isset($item['pid'])) {
|
||||
$item['pid'] = $pid;
|
||||
}
|
||||
|
||||
$sameOldMenu = $model->where('name', $item['name'])->find();
|
||||
if ($sameOldMenu) {
|
||||
// 存在相同名称的菜单规则
|
||||
if ($mode == 'cover') {
|
||||
$sameOldMenu->save($item);
|
||||
} elseif ($mode == 'rename') {
|
||||
$count = $model->where('name', $item['name'])->count();
|
||||
$item['name'] = $item['name'] . '-CONFLICT-' . $count;
|
||||
$item['path'] = $item['path'] . '-CONFLICT-' . $count;
|
||||
$item['title'] = $item['title'] . '-CONFLICT-' . $count;
|
||||
$sameOldMenu = $model->create($item);
|
||||
} elseif ($mode == 'ignore') {
|
||||
// 忽略同名菜单时,当前 pid 下没有同名菜单,则创建同名新菜单,以保证所有新增菜单的上下级结构
|
||||
$sameOldMenu = $model
|
||||
->where('name', $item['name'])
|
||||
->where('pid', $item['pid'])
|
||||
->find();
|
||||
|
||||
if (!$sameOldMenu) {
|
||||
$sameOldMenu = $model->create($item);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$sameOldMenu = $model->create($item);
|
||||
}
|
||||
if (!empty($item['children'])) {
|
||||
self::create($item['children'], $sameOldMenu['id'], $mode, $position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删菜单
|
||||
* @param string|int $id 规则name或id
|
||||
* @param bool $recursion 是否递归删除子级菜单、是否删除自身,是否删除上级空菜单
|
||||
* @param string $position 位置:backend=后台,frontend=前台
|
||||
* @return bool
|
||||
* @throws Throwable
|
||||
*/
|
||||
public static function delete(string|int $id, bool $recursion = false, string $position = 'backend'): bool
|
||||
{
|
||||
if (!$id) {
|
||||
return true;
|
||||
}
|
||||
$model = $position == 'backend' ? new AdminRule() : new UserRule();
|
||||
$menuRule = $model->where((is_numeric($id) ? 'id' : 'name'), $id)->find();
|
||||
if (!$menuRule) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$children = $model->where('pid', $menuRule['id'])->select()->toArray();
|
||||
if ($recursion && $children) {
|
||||
foreach ($children as $child) {
|
||||
self::delete($child['id'], true, $position);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$children || $recursion) {
|
||||
$menuRule->delete();
|
||||
self::delete($menuRule->pid, false, $position);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 启用菜单
|
||||
* @param string|int $id 规则name或id
|
||||
* @param string $position 位置:backend=后台,frontend=前台
|
||||
* @return bool
|
||||
* @throws Throwable
|
||||
*/
|
||||
public static function enable(string|int $id, string $position = 'backend'): bool
|
||||
{
|
||||
$model = $position == 'backend' ? new AdminRule() : new UserRule();
|
||||
$menuRule = $model->where((is_numeric($id) ? 'id' : 'name'), $id)->find();
|
||||
if (!$menuRule) {
|
||||
return false;
|
||||
}
|
||||
$menuRule->status = 1;
|
||||
$menuRule->save();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 禁用菜单
|
||||
* @param string|int $id 规则name或id
|
||||
* @param string $position 位置:backend=后台,frontend=前台
|
||||
* @return bool
|
||||
* @throws Throwable
|
||||
*/
|
||||
public static function disable(string|int $id, string $position = 'backend'): bool
|
||||
{
|
||||
$model = $position == 'backend' ? new AdminRule() : new UserRule();
|
||||
$menuRule = $model->where((is_numeric($id) ? 'id' : 'name'), $id)->find();
|
||||
if (!$menuRule) {
|
||||
return false;
|
||||
}
|
||||
$menuRule->status = 0;
|
||||
$menuRule->save();
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function requiredAttrCheck($menu): bool
|
||||
{
|
||||
$attrs = ['type', 'title', 'name'];
|
||||
foreach ($attrs as $attr) {
|
||||
if (!array_key_exists($attr, $menu)) {
|
||||
return false;
|
||||
}
|
||||
if (!$menu[$attr]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
87
app/common/library/SnowFlake.php
Normal file
87
app/common/library/SnowFlake.php
Normal file
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\library;
|
||||
|
||||
/**
|
||||
* 雪花ID生成类
|
||||
*/
|
||||
class SnowFlake
|
||||
{
|
||||
/**
|
||||
* 起始时间戳
|
||||
* @var int
|
||||
*/
|
||||
private const EPOCH = 1672502400000;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private const max41bit = 1099511627775;
|
||||
|
||||
/**
|
||||
* 机器节点 10bit
|
||||
* @var int
|
||||
*/
|
||||
protected static int $machineId = 1;
|
||||
|
||||
/**
|
||||
* 序列号
|
||||
* @var int
|
||||
*/
|
||||
protected static int $count = 0;
|
||||
|
||||
/**
|
||||
* 最后一次生成ID的时间偏移量
|
||||
* @var int
|
||||
*/
|
||||
protected static int $last = 0;
|
||||
|
||||
/**
|
||||
* 设置机器节点
|
||||
* @param int $mId 机器节点id
|
||||
* @return void
|
||||
*/
|
||||
public static function setMachineId(int $mId): void
|
||||
{
|
||||
self::$machineId = $mId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成雪花ID
|
||||
* @return float|int
|
||||
*/
|
||||
public static function generateParticle(): float|int
|
||||
{
|
||||
// 当前时间 42bit
|
||||
$time = (int)floor(microtime(true) * 1000);
|
||||
|
||||
// 时间偏移量
|
||||
$time -= self::EPOCH;
|
||||
|
||||
// 起始时间戳加上时间偏移量并转为二进制
|
||||
$base = decbin(self::max41bit + $time);
|
||||
|
||||
// 追加节点机器id
|
||||
if (!is_null(self::$machineId)) {
|
||||
$machineId = str_pad(decbin(self::$machineId), 10, "0", STR_PAD_LEFT);
|
||||
$base .= $machineId;
|
||||
}
|
||||
|
||||
// 序列号
|
||||
if ($time == self::$last) {
|
||||
self::$count++;
|
||||
} else {
|
||||
self::$count = 0;
|
||||
}
|
||||
|
||||
// 追加序列号部分
|
||||
$sequence = str_pad(decbin(self::$count), 12, "0", STR_PAD_LEFT);
|
||||
$base .= $sequence;
|
||||
|
||||
// 保存生成ID的时间偏移量
|
||||
self::$last = $time;
|
||||
|
||||
// 返回64bit二进制数的十进制标识
|
||||
return bindec($base);
|
||||
}
|
||||
}
|
||||
232
app/common/library/Token.php
Normal file
232
app/common/library/Token.php
Normal file
@@ -0,0 +1,232 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\library;
|
||||
|
||||
use think\helper\Arr;
|
||||
use think\helper\Str;
|
||||
use think\facade\Config;
|
||||
use InvalidArgumentException;
|
||||
use app\common\library\token\TokenExpirationException;
|
||||
|
||||
/**
|
||||
* Token 管理类
|
||||
*/
|
||||
class Token
|
||||
{
|
||||
/**
|
||||
* Token 实例
|
||||
* @var array
|
||||
* @uses Token 数组项
|
||||
*/
|
||||
public array $instance = [];
|
||||
|
||||
/**
|
||||
* token驱动类句柄
|
||||
* @var ?object
|
||||
*/
|
||||
public ?object $handler = null;
|
||||
|
||||
/**
|
||||
* 驱动类命名空间
|
||||
* @var string
|
||||
*/
|
||||
protected string $namespace = '\\app\\common\\library\\token\\driver\\';
|
||||
|
||||
/**
|
||||
* 获取驱动句柄
|
||||
* @param string|null $name
|
||||
* @return object
|
||||
*/
|
||||
public function getDriver(string $name = null): object
|
||||
{
|
||||
if (!is_null($this->handler)) {
|
||||
return $this->handler;
|
||||
}
|
||||
$name = $name ?: $this->getDefaultDriver();
|
||||
|
||||
if (is_null($name)) {
|
||||
throw new InvalidArgumentException(
|
||||
sprintf(
|
||||
'Unable to resolve NULL driver for [%s].',
|
||||
static::class
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return $this->createDriver($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建驱动句柄
|
||||
* @param string $name
|
||||
* @return object
|
||||
*/
|
||||
protected function createDriver(string $name): object
|
||||
{
|
||||
$type = $this->resolveType($name);
|
||||
|
||||
$method = 'create' . Str::studly($type) . 'Driver';
|
||||
|
||||
$params = $this->resolveParams($name);
|
||||
|
||||
if (method_exists($this, $method)) {
|
||||
return $this->$method(...$params);
|
||||
}
|
||||
|
||||
$class = $this->resolveClass($type);
|
||||
|
||||
if (isset($this->instance[$type])) {
|
||||
return $this->instance[$type];
|
||||
}
|
||||
|
||||
return new $class(...$params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认驱动
|
||||
* @return string
|
||||
*/
|
||||
protected function getDefaultDriver(): string
|
||||
{
|
||||
return $this->getConfig('default');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取驱动配置
|
||||
* @param string|null $name 要获取的配置项,不传递获取完整token配置
|
||||
* @param null $default
|
||||
* @return array|string
|
||||
*/
|
||||
protected function getConfig(string $name = null, $default = null): array|string
|
||||
{
|
||||
if (!is_null($name)) {
|
||||
return Config::get('buildadmin.token.' . $name, $default);
|
||||
}
|
||||
|
||||
return Config::get('buildadmin.token');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取驱动配置参数
|
||||
* @param $name
|
||||
* @return array
|
||||
*/
|
||||
protected function resolveParams($name): array
|
||||
{
|
||||
$config = $this->getStoreConfig($name);
|
||||
return [$config];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取驱动类
|
||||
* @param string $type
|
||||
* @return string
|
||||
*/
|
||||
protected function resolveClass(string $type): string
|
||||
{
|
||||
if ($this->namespace || str_contains($type, '\\')) {
|
||||
$class = str_contains($type, '\\') ? $type : $this->namespace . Str::studly($type);
|
||||
|
||||
if (class_exists($class)) {
|
||||
return $class;
|
||||
}
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException("Driver [$type] not supported.");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取驱动配置
|
||||
* @param string $store
|
||||
* @param string|null $name
|
||||
* @param null $default
|
||||
* @return array|string
|
||||
*/
|
||||
protected function getStoreConfig(string $store, string $name = null, $default = null): array|string
|
||||
{
|
||||
if ($config = $this->getConfig("stores.$store")) {
|
||||
return Arr::get($config, $name, $default);
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException("Store [$store] not found.");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取驱动类型
|
||||
* @param string $name
|
||||
* @return string
|
||||
*/
|
||||
protected function resolveType(string $name): string
|
||||
{
|
||||
return $this->getStoreConfig($name, 'type', 'Mysql');
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置token
|
||||
* @param string $token
|
||||
* @param string $type
|
||||
* @param int $user_id
|
||||
* @param int|null $expire
|
||||
* @return bool
|
||||
*/
|
||||
public function set(string $token, string $type, int $user_id, int $expire = null): bool
|
||||
{
|
||||
return $this->getDriver()->set($token, $type, $user_id, $expire);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取token
|
||||
* @param string $token
|
||||
* @param bool $expirationException
|
||||
* @return array
|
||||
*/
|
||||
public function get(string $token, bool $expirationException = true): array
|
||||
{
|
||||
return $this->getDriver()->get($token, $expirationException);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查token
|
||||
* @param string $token
|
||||
* @param string $type
|
||||
* @param int $user_id
|
||||
* @param bool $expirationException
|
||||
* @return bool
|
||||
*/
|
||||
public function check(string $token, string $type, int $user_id, bool $expirationException = true): bool
|
||||
{
|
||||
return $this->getDriver()->check($token, $type, $user_id, $expirationException);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除token
|
||||
* @param string $token
|
||||
* @return bool
|
||||
*/
|
||||
public function delete(string $token): bool
|
||||
{
|
||||
return $this->getDriver()->delete($token);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理指定用户token
|
||||
* @param string $type
|
||||
* @param int $user_id
|
||||
* @return bool
|
||||
*/
|
||||
public function clear(string $type, int $user_id): bool
|
||||
{
|
||||
return $this->getDriver()->clear($type, $user_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Token过期检查
|
||||
* @throws TokenExpirationException
|
||||
*/
|
||||
public function tokenExpirationCheck(array $token): void
|
||||
{
|
||||
if (isset($token['expire_time']) && $token['expire_time'] <= time()) {
|
||||
throw new TokenExpirationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
341
app/common/library/Upload.php
Normal file
341
app/common/library/Upload.php
Normal file
@@ -0,0 +1,341 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\library;
|
||||
|
||||
use Throwable;
|
||||
use ba\Random;
|
||||
use ba\Filesystem;
|
||||
use think\Exception;
|
||||
use think\helper\Str;
|
||||
use think\facade\Config;
|
||||
use think\facade\Validate;
|
||||
use think\file\UploadedFile;
|
||||
use InvalidArgumentException;
|
||||
use think\validate\ValidateRule;
|
||||
use app\common\model\Attachment;
|
||||
use app\common\library\upload\Driver;
|
||||
|
||||
/**
|
||||
* 上传
|
||||
*/
|
||||
class Upload
|
||||
{
|
||||
/**
|
||||
* 上传配置
|
||||
*/
|
||||
protected array $config = [];
|
||||
|
||||
/**
|
||||
* 被上传文件
|
||||
*/
|
||||
protected ?UploadedFile $file = null;
|
||||
|
||||
/**
|
||||
* 是否是图片
|
||||
*/
|
||||
protected bool $isImage = false;
|
||||
|
||||
/**
|
||||
* 文件信息
|
||||
*/
|
||||
protected array $fileInfo;
|
||||
|
||||
/**
|
||||
* 上传驱动
|
||||
*/
|
||||
protected array $driver = [
|
||||
'name' => 'local', // 默认驱动:local=本地
|
||||
'handler' => [], // 驱动句柄
|
||||
'namespace' => '\\app\\common\\library\\upload\\driver\\', // 驱动类的命名空间
|
||||
];
|
||||
|
||||
/**
|
||||
* 存储子目录
|
||||
*/
|
||||
protected string $topic = 'default';
|
||||
|
||||
/**
|
||||
* 构造方法
|
||||
* @param ?UploadedFile $file 上传的文件
|
||||
* @param array $config 配置
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function __construct(?UploadedFile $file = null, array $config = [])
|
||||
{
|
||||
$upload = Config::get('upload');
|
||||
$this->config = array_merge($upload, $config);
|
||||
|
||||
if ($file) {
|
||||
$this->setFile($file);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置上传文件
|
||||
* @param ?UploadedFile $file
|
||||
* @return Upload
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function setFile(?UploadedFile $file): Upload
|
||||
{
|
||||
if (empty($file)) {
|
||||
throw new Exception(__('No files were uploaded'));
|
||||
}
|
||||
|
||||
$suffix = strtolower($file->extension());
|
||||
$suffix = $suffix && preg_match("/^[a-zA-Z0-9]+$/", $suffix) ? $suffix : 'file';
|
||||
$fileInfo['suffix'] = $suffix;
|
||||
$fileInfo['type'] = $file->getMime();
|
||||
$fileInfo['size'] = $file->getSize();
|
||||
$fileInfo['name'] = $file->getOriginalName();
|
||||
$fileInfo['sha1'] = $file->sha1();
|
||||
|
||||
$this->file = $file;
|
||||
$this->fileInfo = $fileInfo;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置上传驱动
|
||||
*/
|
||||
public function setDriver(string $driver): Upload
|
||||
{
|
||||
$this->driver['name'] = $driver;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取上传驱动句柄
|
||||
* @param ?string $driver 驱动名称
|
||||
* @param bool $noDriveException 找不到驱动是否抛出异常
|
||||
* @return bool|Driver
|
||||
*/
|
||||
public function getDriver(?string $driver = null, bool $noDriveException = true): bool|Driver
|
||||
{
|
||||
if (is_null($driver)) {
|
||||
$driver = $this->driver['name'];
|
||||
}
|
||||
if (!isset($this->driver['handler'][$driver])) {
|
||||
$class = $this->resolveDriverClass($driver);
|
||||
if ($class) {
|
||||
$this->driver['handler'][$driver] = new $class();
|
||||
} elseif ($noDriveException) {
|
||||
throw new InvalidArgumentException(__('Driver %s not supported', [$driver]));
|
||||
}
|
||||
}
|
||||
return $this->driver['handler'][$driver] ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取驱动类
|
||||
*/
|
||||
protected function resolveDriverClass(string $driver): bool|string
|
||||
{
|
||||
if ($this->driver['namespace'] || str_contains($driver, '\\')) {
|
||||
$class = str_contains($driver, '\\') ? $driver : $this->driver['namespace'] . Str::studly($driver);
|
||||
if (class_exists($class)) {
|
||||
return $class;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置存储子目录
|
||||
*/
|
||||
public function setTopic(string $topic): Upload
|
||||
{
|
||||
$this->topic = $topic;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否是图片并设置好相关属性
|
||||
* @return bool
|
||||
* @throws Throwable
|
||||
*/
|
||||
protected function checkIsImage(): bool
|
||||
{
|
||||
if (in_array($this->fileInfo['type'], ['image/gif', 'image/jpg', 'image/jpeg', 'image/bmp', 'image/png', 'image/webp']) || in_array($this->fileInfo['suffix'], ['gif', 'jpg', 'jpeg', 'bmp', 'png', 'webp'])) {
|
||||
$imgInfo = getimagesize($this->file->getPathname());
|
||||
if (!$imgInfo || !isset($imgInfo[0]) || !isset($imgInfo[1])) {
|
||||
throw new Exception(__('The uploaded image file is not a valid image'));
|
||||
}
|
||||
$this->fileInfo['width'] = $imgInfo[0];
|
||||
$this->fileInfo['height'] = $imgInfo[1];
|
||||
$this->isImage = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传的文件是否为图片
|
||||
* @return bool
|
||||
*/
|
||||
public function isImage(): bool
|
||||
{
|
||||
return $this->isImage;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件后缀
|
||||
* @return string
|
||||
*/
|
||||
public function getSuffix(): string
|
||||
{
|
||||
return $this->fileInfo['suffix'] ?: 'file';
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件保存路径和名称
|
||||
* @param ?string $saveName
|
||||
* @param ?string $filename
|
||||
* @param ?string $sha1
|
||||
* @return string
|
||||
*/
|
||||
public function getSaveName(?string $saveName = null, ?string $filename = null, ?string $sha1 = null): string
|
||||
{
|
||||
if ($filename) {
|
||||
$suffix = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
|
||||
$suffix = $suffix && preg_match("/^[a-zA-Z0-9]+$/", $suffix) ? $suffix : 'file';
|
||||
} else {
|
||||
$suffix = $this->fileInfo['suffix'];
|
||||
}
|
||||
$filename = $filename ?: $this->fileInfo['name'];
|
||||
$sha1 = $sha1 ?: $this->fileInfo['sha1'];
|
||||
$replaceArr = [
|
||||
'{topic}' => $this->topic,
|
||||
'{year}' => date("Y"),
|
||||
'{mon}' => date("m"),
|
||||
'{day}' => date("d"),
|
||||
'{hour}' => date("H"),
|
||||
'{min}' => date("i"),
|
||||
'{sec}' => date("s"),
|
||||
'{random}' => Random::build(),
|
||||
'{random32}' => Random::build('alnum', 32),
|
||||
'{fileName}' => $this->getFileNameSubstr($filename, $suffix),
|
||||
'{suffix}' => $suffix,
|
||||
'{.suffix}' => $suffix ? '.' . $suffix : '',
|
||||
'{fileSha1}' => $sha1,
|
||||
];
|
||||
$saveName = $saveName ?: $this->config['save_name'];
|
||||
return Filesystem::fsFit(str_replace(array_keys($replaceArr), array_values($replaceArr), $saveName));
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证文件是否符合上传配置要求
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function validates(): void
|
||||
{
|
||||
if (empty($this->file)) {
|
||||
throw new Exception(__('No files have been uploaded or the file size exceeds the upload limit of the server'));
|
||||
}
|
||||
|
||||
$size = Filesystem::fileUnitToByte($this->config['max_size']);
|
||||
$mime = $this->checkConfig($this->config['allowed_mime_types']);
|
||||
$suffix = $this->checkConfig($this->config['allowed_suffixes']);
|
||||
|
||||
// 文件大小
|
||||
$fileValidateRule = ValidateRule::fileSize($size, __('The uploaded file is too large (%sMiB), Maximum file size:%sMiB', [
|
||||
round($this->fileInfo['size'] / pow(1024, 2), 2),
|
||||
round($size / pow(1024, 2), 2)
|
||||
]));
|
||||
|
||||
// 文件后缀
|
||||
if ($suffix) {
|
||||
$fileValidateRule->fileExt($suffix, __('The uploaded file format is not allowed'));
|
||||
}
|
||||
// 文件 MIME 类型
|
||||
if ($mime) {
|
||||
$fileValidateRule->fileMime($mime, __('The uploaded file format is not allowed'));
|
||||
}
|
||||
|
||||
// 图片文件利用tp内置规则做一些额外检查
|
||||
if ($this->checkIsImage()) {
|
||||
$fileValidateRule->image("{$this->fileInfo['width']},{$this->fileInfo['height']}", __('The uploaded image file is not a valid image'));
|
||||
}
|
||||
|
||||
Validate::failException()
|
||||
->rule([
|
||||
'file' => $fileValidateRule,
|
||||
'topic' => ValidateRule::is('alphaDash', __('Topic format error')),
|
||||
'driver' => ValidateRule::is('alphaDash', __('Driver %s not supported', [$this->driver['name']])),
|
||||
])
|
||||
->check([
|
||||
'file' => $this->file,
|
||||
'topic' => $this->topic,
|
||||
'driver' => $this->driver['name'],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
* @param ?string $saveName
|
||||
* @param int $adminId
|
||||
* @param int $userId
|
||||
* @return array
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function upload(?string $saveName = null, int $adminId = 0, int $userId = 0): array
|
||||
{
|
||||
$this->validates();
|
||||
|
||||
$driver = $this->getDriver();
|
||||
$saveName = $saveName ?: $this->getSaveName();
|
||||
$params = [
|
||||
'topic' => $this->topic,
|
||||
'admin_id' => $adminId,
|
||||
'user_id' => $userId,
|
||||
'url' => $driver->url($saveName, false),
|
||||
'width' => $this->fileInfo['width'] ?? 0,
|
||||
'height' => $this->fileInfo['height'] ?? 0,
|
||||
'name' => $this->getFileNameSubstr($this->fileInfo['name'], $this->fileInfo['suffix'], 100) . ".{$this->fileInfo['suffix']}",
|
||||
'size' => $this->fileInfo['size'],
|
||||
'mimetype' => $this->fileInfo['type'],
|
||||
'storage' => $this->driver['name'],
|
||||
'sha1' => $this->fileInfo['sha1']
|
||||
];
|
||||
|
||||
// 附件数据入库 - 不依赖模型新增前事件,确保入库前文件已经移动完成
|
||||
$attachment = Attachment::where('sha1', $params['sha1'])
|
||||
->where('topic', $params['topic'])
|
||||
->where('storage', $params['storage'])
|
||||
->find();
|
||||
if ($attachment && $driver->exists($attachment->url)) {
|
||||
$attachment->quote++;
|
||||
$attachment->last_upload_time = time();
|
||||
} else {
|
||||
$driver->save($this->file, $saveName);
|
||||
$attachment = new Attachment();
|
||||
$attachment->data(array_filter($params));
|
||||
}
|
||||
$attachment->save();
|
||||
return $attachment->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件名称字符串的子串
|
||||
*/
|
||||
public function getFileNameSubstr(string $fileName, string $suffix, int $length = 15): string
|
||||
{
|
||||
// 对 $fileName 中不利于传输的字符串进行过滤
|
||||
$pattern = "/[\s:@#?&\/=',+]+/u";
|
||||
$fileName = str_replace(".$suffix", '', $fileName);
|
||||
$fileName = preg_replace($pattern, '', $fileName);
|
||||
return mb_substr(htmlspecialchars(strip_tags($fileName)), 0, $length);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查配置项,将 string 类型的配置转换为 array,并且将所有字母转换为小写
|
||||
*/
|
||||
protected function checkConfig($configItem): array
|
||||
{
|
||||
if (is_array($configItem)) {
|
||||
return array_map('strtolower', $configItem);
|
||||
} else {
|
||||
return explode(',', strtolower($configItem));
|
||||
}
|
||||
}
|
||||
}
|
||||
92
app/common/library/token/Driver.php
Normal file
92
app/common/library/token/Driver.php
Normal file
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\library\token;
|
||||
|
||||
use think\facade\Config;
|
||||
|
||||
/**
|
||||
* Token 驱动抽象类
|
||||
*/
|
||||
abstract class Driver
|
||||
{
|
||||
/**
|
||||
* 具体驱动的句柄 Mysql|Redis
|
||||
* @var object
|
||||
*/
|
||||
protected object $handler;
|
||||
|
||||
/**
|
||||
* @var array 配置数据
|
||||
*/
|
||||
protected array $options = [];
|
||||
|
||||
/**
|
||||
* 设置 token
|
||||
* @param string $token Token
|
||||
* @param string $type Type
|
||||
* @param int $userId 用户ID
|
||||
* @param ?int $expire 过期时间
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function set(string $token, string $type, int $userId, int $expire = null): bool;
|
||||
|
||||
/**
|
||||
* 获取 token 的数据
|
||||
* @param string $token Token
|
||||
* @return array
|
||||
*/
|
||||
abstract public function get(string $token): array;
|
||||
|
||||
/**
|
||||
* 检查token是否有效
|
||||
* @param string $token
|
||||
* @param string $type
|
||||
* @param int $userId
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function check(string $token, string $type, int $userId): bool;
|
||||
|
||||
/**
|
||||
* 删除一个token
|
||||
* @param string $token
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function delete(string $token): bool;
|
||||
|
||||
/**
|
||||
* 清理一个用户的所有token
|
||||
* @param string $type
|
||||
* @param int $userId
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function clear(string $type, int $userId): bool;
|
||||
|
||||
/**
|
||||
* 返回句柄对象
|
||||
* @access public
|
||||
* @return object|null
|
||||
*/
|
||||
public function handler(): ?object
|
||||
{
|
||||
return $this->handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $token
|
||||
* @return string
|
||||
*/
|
||||
protected function getEncryptedToken(string $token): string
|
||||
{
|
||||
$config = Config::get('buildadmin.token');
|
||||
return hash_hmac($config['algo'], $token, $config['key']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $expireTime
|
||||
* @return int
|
||||
*/
|
||||
protected function getExpiredIn(int $expireTime): int
|
||||
{
|
||||
return $expireTime ? max(0, $expireTime - time()) : 365 * 86400;
|
||||
}
|
||||
}
|
||||
16
app/common/library/token/TokenExpirationException.php
Normal file
16
app/common/library/token/TokenExpirationException.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\library\token;
|
||||
|
||||
use think\Exception;
|
||||
|
||||
/**
|
||||
* Token过期异常
|
||||
*/
|
||||
class TokenExpirationException extends Exception
|
||||
{
|
||||
public function __construct(protected $message = '', protected $code = 409, protected $data = [])
|
||||
{
|
||||
parent::__construct($message, $code);
|
||||
}
|
||||
}
|
||||
109
app/common/library/token/driver/Mysql.php
Normal file
109
app/common/library/token/driver/Mysql.php
Normal file
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\library\token\driver;
|
||||
|
||||
use Throwable;
|
||||
use think\facade\Db;
|
||||
use think\facade\Cache;
|
||||
use app\common\library\token\Driver;
|
||||
|
||||
/**
|
||||
* @see Driver
|
||||
*/
|
||||
class Mysql extends Driver
|
||||
{
|
||||
/**
|
||||
* 默认配置
|
||||
* @var array
|
||||
*/
|
||||
protected array $options = [];
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @access public
|
||||
* @param array $options 参数
|
||||
*/
|
||||
public function __construct(array $options = [])
|
||||
{
|
||||
if (!empty($options)) {
|
||||
$this->options = array_merge($this->options, $options);
|
||||
}
|
||||
|
||||
if ($this->options['name']) {
|
||||
$this->handler = Db::connect($this->options['name'])->name($this->options['table']);
|
||||
} else {
|
||||
$this->handler = Db::name($this->options['table']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function set(string $token, string $type, int $userId, int $expire = null): bool
|
||||
{
|
||||
if (is_null($expire)) {
|
||||
$expire = $this->options['expire'];
|
||||
}
|
||||
$expireTime = $expire !== 0 ? time() + $expire : 0;
|
||||
$token = $this->getEncryptedToken($token);
|
||||
$this->handler->insert([
|
||||
'token' => $token,
|
||||
'type' => $type,
|
||||
'user_id' => $userId,
|
||||
'create_time' => time(),
|
||||
'expire_time' => $expireTime,
|
||||
]);
|
||||
|
||||
// 每隔48小时清理一次过期Token
|
||||
$time = time();
|
||||
$lastCacheCleanupTime = Cache::get('last_cache_cleanup_time');
|
||||
if (!$lastCacheCleanupTime || $lastCacheCleanupTime < $time - 172800) {
|
||||
Cache::set('last_cache_cleanup_time', $time);
|
||||
$this->handler->where('expire_time', '<', time())->where('expire_time', '>', 0)->delete();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function get(string $token): array
|
||||
{
|
||||
$data = $this->handler->where('token', $this->getEncryptedToken($token))->find();
|
||||
if (!$data) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$data['token'] = $token; // 返回未加密的token给客户端使用
|
||||
$data['expires_in'] = $this->getExpiredIn($data['expire_time'] ?? 0); // 返回剩余有效时间
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function check(string $token, string $type, int $userId): bool
|
||||
{
|
||||
$data = $this->get($token);
|
||||
if (!$data || ($data['expire_time'] && $data['expire_time'] <= time())) return false;
|
||||
return $data['type'] == $type && $data['user_id'] == $userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function delete(string $token): bool
|
||||
{
|
||||
$this->handler->where('token', $this->getEncryptedToken($token))->delete();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function clear(string $type, int $userId): bool
|
||||
{
|
||||
$this->handler->where('type', $type)->where('user_id', $userId)->delete();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
146
app/common/library/token/driver/Redis.php
Normal file
146
app/common/library/token/driver/Redis.php
Normal file
@@ -0,0 +1,146 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\library\token\driver;
|
||||
|
||||
use Throwable;
|
||||
use BadFunctionCallException;
|
||||
use app\common\library\token\Driver;
|
||||
|
||||
/**
|
||||
* @see Driver
|
||||
*/
|
||||
class Redis extends Driver
|
||||
{
|
||||
/**
|
||||
* 默认配置
|
||||
* @var array
|
||||
*/
|
||||
protected array $options = [];
|
||||
|
||||
/**
|
||||
* Token 过期后缓存继续保留的时间(s)
|
||||
*/
|
||||
protected int $expiredHold = 60 * 60 * 24 * 2;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
* @access public
|
||||
* @param array $options 参数
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function __construct(array $options = [])
|
||||
{
|
||||
if (!extension_loaded('redis')) {
|
||||
throw new BadFunctionCallException('未安装redis扩展');
|
||||
}
|
||||
if (!empty($options)) {
|
||||
$this->options = array_merge($this->options, $options);
|
||||
}
|
||||
$this->handler = new \Redis();
|
||||
if ($this->options['persistent']) {
|
||||
$this->handler->pconnect($this->options['host'], $this->options['port'], $this->options['timeout'], 'persistent_id_' . $this->options['select']);
|
||||
} else {
|
||||
$this->handler->connect($this->options['host'], $this->options['port'], $this->options['timeout']);
|
||||
}
|
||||
|
||||
if ('' != $this->options['password']) {
|
||||
$this->handler->auth($this->options['password']);
|
||||
}
|
||||
|
||||
if (false !== $this->options['select']) {
|
||||
$this->handler->select($this->options['select']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function set(string $token, string $type, int $userId, int $expire = null): bool
|
||||
{
|
||||
if (is_null($expire)) {
|
||||
$expire = $this->options['expire'];
|
||||
}
|
||||
$expireTime = $expire !== 0 ? time() + $expire : 0;
|
||||
$token = $this->getEncryptedToken($token);
|
||||
$tokenInfo = [
|
||||
'token' => $token,
|
||||
'type' => $type,
|
||||
'user_id' => $userId,
|
||||
'create_time' => time(),
|
||||
'expire_time' => $expireTime,
|
||||
];
|
||||
$tokenInfo = json_encode($tokenInfo, JSON_UNESCAPED_UNICODE);
|
||||
if ($expire) {
|
||||
$expire += $this->expiredHold;
|
||||
$result = $this->handler->setex($token, $expire, $tokenInfo);
|
||||
} else {
|
||||
$result = $this->handler->set($token, $tokenInfo);
|
||||
}
|
||||
$this->handler->sAdd($this->getUserKey($type, $userId), $token);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function get(string $token): array
|
||||
{
|
||||
$key = $this->getEncryptedToken($token);
|
||||
$data = $this->handler->get($key);
|
||||
if (is_null($data) || false === $data) {
|
||||
return [];
|
||||
}
|
||||
$data = json_decode($data, true);
|
||||
|
||||
$data['token'] = $token; // 返回未加密的token给客户端使用
|
||||
$data['expires_in'] = $this->getExpiredIn($data['expire_time'] ?? 0); // 过期时间
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function check(string $token, string $type, int $userId): bool
|
||||
{
|
||||
$data = $this->get($token);
|
||||
if (!$data || ($data['expire_time'] && $data['expire_time'] <= time())) return false;
|
||||
return $data['type'] == $type && $data['user_id'] == $userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function delete(string $token): bool
|
||||
{
|
||||
$data = $this->get($token);
|
||||
if ($data) {
|
||||
$key = $this->getEncryptedToken($token);
|
||||
$this->handler->del($key);
|
||||
$this->handler->sRem($this->getUserKey($data['type'], $data['user_id']), $key);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function clear(string $type, int $userId): bool
|
||||
{
|
||||
$userKey = $this->getUserKey($type, $userId);
|
||||
$keys = $this->handler->sMembers($userKey);
|
||||
$this->handler->del($userKey);
|
||||
$this->handler->del($keys);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会员的key
|
||||
* @param $type
|
||||
* @param $userId
|
||||
* @return string
|
||||
*/
|
||||
protected function getUserKey($type, $userId): string
|
||||
{
|
||||
return $this->options['prefix'] . $type . '-' . $userId;
|
||||
}
|
||||
}
|
||||
47
app/common/library/upload/Driver.php
Normal file
47
app/common/library/upload/Driver.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\library\upload;
|
||||
|
||||
use think\file\UploadedFile;
|
||||
|
||||
/**
|
||||
* 上传驱动抽象类
|
||||
*/
|
||||
abstract class Driver
|
||||
{
|
||||
/**
|
||||
* @var array 配置数据
|
||||
*/
|
||||
protected array $options = [];
|
||||
|
||||
/**
|
||||
* 保存文件
|
||||
* @param UploadedFile $file
|
||||
* @param string $saveName
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function save(UploadedFile $file, string $saveName): bool;
|
||||
|
||||
/**
|
||||
* 删除文件
|
||||
* @param string $saveName
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function delete(string $saveName): bool;
|
||||
|
||||
/**
|
||||
* 获取资源 URL 地址;
|
||||
* @param string $saveName 资源保存名称
|
||||
* @param string|bool $domain 是否携带域名 或者直接传入域名
|
||||
* @param string $default 默认值
|
||||
* @return string
|
||||
*/
|
||||
abstract public function url(string $saveName, string|bool $domain = true, string $default = ''): string;
|
||||
|
||||
/**
|
||||
* 文件是否存在
|
||||
* @param string $saveName
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function exists(string $saveName): bool;
|
||||
}
|
||||
148
app/common/library/upload/driver/Local.php
Normal file
148
app/common/library/upload/driver/Local.php
Normal file
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\library\upload\driver;
|
||||
|
||||
use ba\Filesystem;
|
||||
use think\facade\Config;
|
||||
use think\file\UploadedFile;
|
||||
use think\exception\FileException;
|
||||
use app\common\library\upload\Driver;
|
||||
|
||||
/**
|
||||
* 上传到本地磁盘的驱动
|
||||
* @see Driver
|
||||
*/
|
||||
class Local extends Driver
|
||||
{
|
||||
protected array $options = [];
|
||||
|
||||
public function __construct(array $options = [])
|
||||
{
|
||||
$this->options = Config::get('filesystem.disks.public');
|
||||
if (!empty($options)) {
|
||||
$this->options = array_merge($this->options, $options);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存文件
|
||||
* @param UploadedFile $file
|
||||
* @param string $saveName
|
||||
* @return bool
|
||||
*/
|
||||
public function save(UploadedFile $file, string $saveName): bool
|
||||
{
|
||||
$savePathInfo = pathinfo($saveName);
|
||||
$saveFullPath = $this->getFullPath($saveName);
|
||||
|
||||
// cgi 直接 move
|
||||
if (request()->isCgi()) {
|
||||
$file->move($saveFullPath, $savePathInfo['basename']);
|
||||
return true;
|
||||
}
|
||||
|
||||
set_error_handler(function ($type, $msg) use (&$error) {
|
||||
$error = $msg;
|
||||
});
|
||||
|
||||
// 建立文件夹
|
||||
if (!is_dir($saveFullPath) && !mkdir($saveFullPath, 0755, true)) {
|
||||
restore_error_handler();
|
||||
throw new FileException(sprintf('Unable to create the "%s" directory (%s)', $saveFullPath, strip_tags($error)));
|
||||
}
|
||||
|
||||
// cli 使用 rename
|
||||
$saveName = $this->getFullPath($saveName, true);
|
||||
if (!rename($file->getPathname(), $saveName)) {
|
||||
restore_error_handler();
|
||||
throw new FileException(sprintf('Could not move the file "%s" to "%s" (%s)', $file->getPathname(), $saveName, strip_tags($error)));
|
||||
}
|
||||
|
||||
restore_error_handler();
|
||||
@chmod($saveName, 0666 & ~umask());
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除文件
|
||||
* @param string $saveName
|
||||
* @return bool
|
||||
*/
|
||||
public function delete(string $saveName): bool
|
||||
{
|
||||
$saveFullName = $this->getFullPath($saveName, true);
|
||||
if ($this->exists($saveFullName)) {
|
||||
@unlink($saveFullName);
|
||||
}
|
||||
Filesystem::delEmptyDir(dirname($saveFullName));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取资源 URL 地址
|
||||
* @param string $saveName 资源保存名称
|
||||
* @param string|bool $domain 是否携带域名 或者直接传入域名
|
||||
* @param string $default 默认值
|
||||
* @return string
|
||||
*/
|
||||
public function url(string $saveName, string|bool $domain = true, string $default = ''): string
|
||||
{
|
||||
$saveName = $this->clearRootPath($saveName);
|
||||
|
||||
if ($domain === true) {
|
||||
$domain = '//' . request()->host();
|
||||
} elseif ($domain === false) {
|
||||
$domain = '';
|
||||
}
|
||||
|
||||
$saveName = $saveName ?: $default;
|
||||
if (!$saveName) return $domain;
|
||||
|
||||
$regex = "/^((?:[a-z]+:)?\/\/|data:image\/)(.*)/i";
|
||||
if (preg_match('/^http(s)?:\/\//', $saveName) || preg_match($regex, $saveName) || $domain === false) {
|
||||
return $saveName;
|
||||
}
|
||||
return str_replace('\\', '/', $domain . $saveName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件是否存在
|
||||
* @param string $saveName
|
||||
* @return bool
|
||||
*/
|
||||
public function exists(string $saveName): bool
|
||||
{
|
||||
$saveFullName = $this->getFullPath($saveName, true);
|
||||
return file_exists($saveFullName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件的完整存储路径
|
||||
* @param string $saveName
|
||||
* @param bool $baseName 是否包含文件名
|
||||
* @return string
|
||||
*/
|
||||
public function getFullPath(string $saveName, bool $baseName = false): string
|
||||
{
|
||||
$savePathInfo = pathinfo($saveName);
|
||||
$root = $this->getRootPath();
|
||||
$dirName = $savePathInfo['dirname'] . '/';
|
||||
|
||||
// 以 root 路径开始时单独返回,避免重复调用此方法时造成 $dirName 的错误拼接
|
||||
if (str_starts_with($saveName, $root)) {
|
||||
return Filesystem::fsFit($baseName || !isset($savePathInfo['extension']) ? $saveName : $dirName);
|
||||
}
|
||||
|
||||
return Filesystem::fsFit($root . $dirName . ($baseName ? $savePathInfo['basename'] : ''));
|
||||
}
|
||||
|
||||
public function clearRootPath(string $saveName): string
|
||||
{
|
||||
return str_replace($this->getRootPath(), '', Filesystem::fsFit($saveName));
|
||||
}
|
||||
|
||||
public function getRootPath(): string
|
||||
{
|
||||
return Filesystem::fsFit(str_replace($this->options['url'], '', $this->options['root']));
|
||||
}
|
||||
}
|
||||
115
app/common/model/Attachment.php
Normal file
115
app/common/model/Attachment.php
Normal file
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\model;
|
||||
|
||||
use Throwable;
|
||||
use think\Model;
|
||||
use think\facade\Event;
|
||||
use app\admin\model\Admin;
|
||||
use app\common\library\Upload;
|
||||
use think\model\relation\BelongsTo;
|
||||
|
||||
/**
|
||||
* Attachment模型
|
||||
* @property string url 文件物理路径
|
||||
* @property int quote 上传(引用)次数
|
||||
* @property int last_upload_time 最后上传时间
|
||||
*/
|
||||
class Attachment extends Model
|
||||
{
|
||||
protected $autoWriteTimestamp = true;
|
||||
protected $updateTime = false;
|
||||
|
||||
protected $append = [
|
||||
'suffix',
|
||||
'full_url'
|
||||
];
|
||||
|
||||
/**
|
||||
* 上传类实例,可以通过它调用上传文件驱动,且驱动类具有静态缓存
|
||||
*/
|
||||
protected static Upload $upload;
|
||||
|
||||
protected static function init(): void
|
||||
{
|
||||
self::$upload = new Upload();
|
||||
}
|
||||
|
||||
public function getSuffixAttr($value, $row): string
|
||||
{
|
||||
if ($row['name']) {
|
||||
$suffix = strtolower(pathinfo($row['name'], PATHINFO_EXTENSION));
|
||||
return $suffix && preg_match("/^[a-zA-Z0-9]+$/", $suffix) ? $suffix : 'file';
|
||||
}
|
||||
return 'file';
|
||||
}
|
||||
|
||||
public function getFullUrlAttr($value, $row): string
|
||||
{
|
||||
$driver = self::$upload->getDriver($row['storage'], false);
|
||||
return $driver ? $driver->url($row['url']) : full_url($row['url']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增前
|
||||
* @throws Throwable
|
||||
*/
|
||||
protected static function onBeforeInsert($model): bool
|
||||
{
|
||||
$repeat = $model->where([
|
||||
['sha1', '=', $model->sha1],
|
||||
['topic', '=', $model->topic],
|
||||
['storage', '=', $model->storage],
|
||||
])->find();
|
||||
if ($repeat) {
|
||||
$driver = self::$upload->getDriver($repeat->storage, false);
|
||||
if ($driver && !$driver->exists($repeat->url)) {
|
||||
$repeat->delete();
|
||||
return true;
|
||||
} else {
|
||||
$repeat->quote++;
|
||||
$repeat->last_upload_time = time();
|
||||
$repeat->save();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增后
|
||||
*/
|
||||
protected static function onAfterInsert($model): void
|
||||
{
|
||||
Event::trigger('AttachmentInsert', $model);
|
||||
|
||||
if (!$model->last_upload_time) {
|
||||
$model->quote = 1;
|
||||
$model->last_upload_time = time();
|
||||
$model->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除后
|
||||
*/
|
||||
protected static function onAfterDelete($model): void
|
||||
{
|
||||
Event::trigger('AttachmentDel', $model);
|
||||
|
||||
$driver = self::$upload->getDriver($model->storage, false);
|
||||
if ($driver && $driver->exists($model->url)) {
|
||||
$driver->delete($model->url);
|
||||
}
|
||||
}
|
||||
|
||||
public function admin(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Admin::class);
|
||||
}
|
||||
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
}
|
||||
150
app/common/model/BaseModel.php
Normal file
150
app/common/model/BaseModel.php
Normal file
@@ -0,0 +1,150 @@
|
||||
<?php
|
||||
namespace app\common\model;
|
||||
|
||||
use app\common\library\DatabaseRoute;
|
||||
use think\db\BaseQuery;
|
||||
use think\facade\Db;
|
||||
use think\facade\Log;
|
||||
use think\Model;
|
||||
use think\db\Query;
|
||||
|
||||
class BaseModel extends Model
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
* 查询前处理数据库连接
|
||||
*/
|
||||
public static function findbefore($model, $where):string
|
||||
{
|
||||
return DatabaseRoute::getConnection($model->getTable(), $where, false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 写入数据前处理数据库连接
|
||||
* @param array $data 写入的数据
|
||||
* @return void
|
||||
*/
|
||||
protected static function onBeforeInsert($data)
|
||||
{
|
||||
$connection = DatabaseRoute::getConnection($data->getTable(), $data, true);
|
||||
print_r($connection);
|
||||
$data->setConnection($connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新数据前处理数据库连接
|
||||
* @param array $data 更新的数据
|
||||
* @return void
|
||||
*/
|
||||
protected static function onBeforeUpdate($data)
|
||||
{
|
||||
$connection = DatabaseRoute::getConnection($data->getTable(), $data->getWhere(), true);
|
||||
$data->setConnection($connection);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 从查询选项中提取分库键的值(深度解析版)
|
||||
*/
|
||||
private static function extractKeyFromOptions(array $options, string $keyField)
|
||||
{
|
||||
$dbKey = null;
|
||||
|
||||
// 1. 处理顶级 where 条件
|
||||
if (isset($options['where'])) {
|
||||
$dbKey = static::parseWhereCondition($options['where'], $keyField);
|
||||
if ($dbKey !== null) return $dbKey;
|
||||
}
|
||||
|
||||
// 2. 处理 whereOr 条件
|
||||
if (isset($options['whereOr'])) {
|
||||
$dbKey = static::parseWhereCondition($options['whereOr'], $keyField);
|
||||
if ($dbKey !== null) return $dbKey;
|
||||
}
|
||||
|
||||
// 3. 处理软删除条件
|
||||
if (isset($options['soft_delete']) && is_array($options['soft_delete'])) {
|
||||
if ($options['soft_delete'][0] === $keyField) {
|
||||
$dbKey = $options['soft_delete'][2] ?? null;
|
||||
Log::info("条件类型3: 软删除条件中匹配到 {$keyField}={$dbKey}");
|
||||
return $dbKey;
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 处理字段绑定(bind)
|
||||
if (isset($options['bind'][$keyField])) {
|
||||
$dbKey = $options['bind'][$keyField];
|
||||
Log::info("条件类型4: 绑定参数中匹配到 {$keyField}={$dbKey}");
|
||||
return $dbKey;
|
||||
}
|
||||
|
||||
// 5. 处理查询参数(param)
|
||||
if (isset($options['param'][$keyField])) {
|
||||
$dbKey = $options['param'][$keyField];
|
||||
Log::info("条件类型5: 查询参数中匹配到 {$keyField}={$dbKey}");
|
||||
return $dbKey;
|
||||
}
|
||||
|
||||
// 6. 处理数据(data)- 适用于更新操作
|
||||
if (isset($options['data'][$keyField])) {
|
||||
$dbKey = $options['data'][$keyField];
|
||||
Log::info("条件类型6: 数据中匹配到 {$keyField}={$dbKey}");
|
||||
return $dbKey;
|
||||
}
|
||||
|
||||
// 未找到分库键
|
||||
Log::warning("条件解析失败: 未找到 {$keyField} 的值");
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析 where 条件(处理 AND/OR 逻辑)
|
||||
*/
|
||||
private static function parseWhereCondition($where, string $keyField)
|
||||
{
|
||||
$dbKey = null;
|
||||
|
||||
// 处理数组形式的 where 条件
|
||||
if (is_array($where)) {
|
||||
foreach ($where as $index => $whereItem) {
|
||||
// 1. 处理 AND/OR 逻辑分组
|
||||
if (is_string($index) && in_array(strtoupper($index), ['AND', 'OR', 'NOT'])) {
|
||||
Log::info("条件类型1: 发现 {$index} 逻辑分组,开始递归解析");
|
||||
|
||||
// 递归解析逻辑分组中的条件
|
||||
$dbKey = static::parseWhereCondition($whereItem, $keyField);
|
||||
if ($dbKey !== null) {
|
||||
Log::info("条件类型1: {$index} 分组中匹配到 {$keyField}={$dbKey}");
|
||||
return $dbKey;
|
||||
}
|
||||
} // 2. 处理 ['user_id', '=', 123] 形式
|
||||
elseif (is_array($whereItem) && count($whereItem) >= 3) {
|
||||
if ($whereItem[0] === $keyField) {
|
||||
$dbKey = $whereItem[2];
|
||||
Log::info("条件类型2: 直接匹配 {$keyField}={$dbKey}");
|
||||
return $dbKey;
|
||||
}
|
||||
} // 3. 处理闭包条件: ['user_id', function($query){...}]
|
||||
elseif (is_array($whereItem) && isset($whereItem[1]) && $whereItem[1] instanceof \Closure) {
|
||||
Log::info("条件类型3: 发现闭包条件,开始递归解析");
|
||||
|
||||
// 创建临时查询对象执行闭包
|
||||
$subQuery = new Query();
|
||||
$whereItem[1]($subQuery);
|
||||
|
||||
// 递归解析闭包中的条件
|
||||
$dbKey = static::extractKeyFromOptions($subQuery->getOptions(), $keyField);
|
||||
if ($dbKey !== null) {
|
||||
Log::info("条件类型3: 闭包中匹配到 {$keyField}={$dbKey}");
|
||||
return $dbKey;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
125
app/common/model/Common.php
Normal file
125
app/common/model/Common.php
Normal file
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\model;
|
||||
|
||||
|
||||
use app\api\model\CommonInfo;
|
||||
use app\common\library\DatabaseRoute;
|
||||
use app\czg\model\SysCaptcha;
|
||||
use think\cache\driver\Redis;
|
||||
use think\facade\Db;
|
||||
use think\Model;
|
||||
use support\think\Cache;
|
||||
|
||||
class Common extends Model
|
||||
{
|
||||
|
||||
|
||||
// 统一处理分表表新增编辑查询(主表中没有的表)
|
||||
public static function saveDbData($table, $sale, $data, $operate = 'insertGetId', $where = [])
|
||||
{
|
||||
if(in_array($operate, ['insertGetId', 'insert']) || $operate == 'update') {
|
||||
$connect_name = DatabaseRoute::getConnection($table, $sale, true);
|
||||
}else {
|
||||
$connect_name = DatabaseRoute::getConnection($table, $sale);
|
||||
}
|
||||
$db = Db::connect($connect_name);
|
||||
if($operate == 'insert' || $operate == 'insertGetId') {
|
||||
return $db->name($table)->insertGetId($data);
|
||||
}elseif($operate == 'update') {
|
||||
return $db->name($table)->where($where)->update($data);
|
||||
}elseif ($operate == 'select') {
|
||||
return $db->name($table)->where($where)->select();
|
||||
}elseif ($operate == 'find') {
|
||||
return $db->name($table)->where($where)->find();
|
||||
}
|
||||
}
|
||||
|
||||
public static function db_count($table, $sale, $where)
|
||||
{
|
||||
$connect_name = DatabaseRoute::getConnection($table, $sale);
|
||||
$db = Db::connect($connect_name);
|
||||
return $db->name($table)->where($where)->count();
|
||||
|
||||
}
|
||||
|
||||
public static function getAppUseKv()
|
||||
{
|
||||
$info = Db::connect(config('database.search_library'))->name('common_info')->where(['is_app_use' => 1])->field('id, value')->select();
|
||||
$data = [];
|
||||
foreach ($info as $k => $v) {
|
||||
$data[$v['id']] = $v['value'];
|
||||
}
|
||||
return returnSuccessData($data);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 检查是否允许访问(基于Redis的频率限制)
|
||||
* @param string $id 唯一标识(如用户ID、IP等)
|
||||
* @param string $key 操作类型(如"updateWeekCourseView")
|
||||
* @param int $count 允许的访问次数
|
||||
* @param int $seconds 时间窗口(秒)
|
||||
* @return bool 是否允许访问
|
||||
*/
|
||||
public static function isAccessAllowed($id, $key, $count, $seconds, $sys_data = false)
|
||||
{
|
||||
if($sys_data) {
|
||||
$redisKey = 'sys:data:' . $key . ':' . $id;
|
||||
}else {
|
||||
$redisKey = generateRedisKey($key, $id);
|
||||
}
|
||||
// 获取当前访问次数
|
||||
$currentCount = Cache::get($redisKey);
|
||||
|
||||
if ($currentCount === null) {
|
||||
// 首次访问:初始化计数器并设置过期时间
|
||||
Cache::set($redisKey, $seconds, 1);
|
||||
return true;
|
||||
}
|
||||
if ((int)$currentCount < $count) {
|
||||
// 未超过限制:增加计数
|
||||
Cache::set($redisKey,$currentCount + 1);
|
||||
return true;
|
||||
}
|
||||
// 已超过限制
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @param int $courseId 课程ID
|
||||
* @return int 周播放量
|
||||
*/
|
||||
public static function getCourseWeekViewCount($courseId)
|
||||
{
|
||||
$key = "course:viewCount:{$courseId}";
|
||||
// 从Redis获取周播放量
|
||||
$viewCount = Cache::get($key);
|
||||
if (empty($viewCount)) {
|
||||
// 计算下周一的时间戳
|
||||
$now = time();
|
||||
$dayOfWeek = date('N', $now); // 1-7,1表示周一
|
||||
$daysToMonday = $dayOfWeek === 1 ? 7 : (1 - $dayOfWeek + 7) % 7;
|
||||
$nextMonday = $now + $daysToMonday * 86400;
|
||||
|
||||
// 计算剩余秒数并设置缓存
|
||||
$seconds = $nextMonday - $now;
|
||||
Cache::set($key, 1, $seconds);
|
||||
return 1;
|
||||
}
|
||||
// 播放量递增并返回
|
||||
$newCount = Cache::set($key, $viewCount + 1, -1);
|
||||
return $newCount;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
83
app/common/model/Config.php
Normal file
83
app/common/model/Config.php
Normal file
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\model;
|
||||
|
||||
use Throwable;
|
||||
use think\Model;
|
||||
use app\admin\model\Config as adminConfigModel;
|
||||
|
||||
class Config extends Model
|
||||
{
|
||||
/**
|
||||
* 添加系统配置分组
|
||||
* @throws Throwable
|
||||
*/
|
||||
public static function addConfigGroup(string $key, string $value): bool
|
||||
{
|
||||
return self::addArrayItem('config_group', $key, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除系统配置分组
|
||||
* @throws Throwable
|
||||
*/
|
||||
public static function removeConfigGroup(string $key): bool
|
||||
{
|
||||
if (adminConfigModel::where('group', $key)->find()) return false;
|
||||
return self::removeArrayItem('config_group', $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加系统快捷配置入口
|
||||
* @throws Throwable
|
||||
*/
|
||||
public static function addQuickEntrance(string $key, string $value): bool
|
||||
{
|
||||
return self::addArrayItem('config_quick_entrance', $key, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除系统快捷配置入口
|
||||
* @throws Throwable
|
||||
*/
|
||||
public static function removeQuickEntrance(string $key): bool
|
||||
{
|
||||
return self::removeArrayItem('config_quick_entrance', $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 为Array类型的配置项添加元素
|
||||
* @throws Throwable
|
||||
*/
|
||||
public static function addArrayItem(string $name, string $key, string $value): bool
|
||||
{
|
||||
$configRow = adminConfigModel::where('name', $name)->find();
|
||||
foreach ($configRow->value as $item) {
|
||||
if ($item['key'] == $key) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
$configRow->value = array_merge($configRow->value, [['key' => $key, 'value' => $value]]);
|
||||
$configRow->save();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除Array类型配置项的一个元素
|
||||
* @throws Throwable
|
||||
*/
|
||||
public static function removeArrayItem(string $name, string $key): bool
|
||||
{
|
||||
$configRow = adminConfigModel::where('name', $name)->find();
|
||||
$configRowValue = $configRow->value;
|
||||
foreach ($configRowValue as $iKey => $item) {
|
||||
if ($item['key'] == $key) {
|
||||
unset($configRowValue[$iKey]);
|
||||
}
|
||||
}
|
||||
$configRow->value = $configRowValue;
|
||||
$configRow->save();
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
14
app/common/model/CourseCollect.php
Normal file
14
app/common/model/CourseCollect.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\model;
|
||||
|
||||
|
||||
use app\czg\model\SysCaptcha;
|
||||
use think\facade\Cache;
|
||||
|
||||
class CourseCollect extends BaseModel
|
||||
{
|
||||
|
||||
|
||||
|
||||
}
|
||||
80
app/common/model/SysUser.php
Normal file
80
app/common/model/SysUser.php
Normal file
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\model;
|
||||
|
||||
|
||||
use app\common\library\DatabaseRoute;
|
||||
use app\czg\model\SysCaptcha;
|
||||
use ba\Random;
|
||||
use think\facade\Cache;
|
||||
use think\facade\Db;
|
||||
|
||||
class SysUser extends BaseModel
|
||||
{
|
||||
|
||||
public static function adda()
|
||||
{
|
||||
SysUser::create([
|
||||
'user_id' => rand(000000, 999999),
|
||||
'username' => '哎呀' . rand(000000, 999999),
|
||||
]);
|
||||
}
|
||||
|
||||
// 查询username
|
||||
public static function GetByusername($username)
|
||||
{
|
||||
// 全表扫描username
|
||||
$dbmap = config('database.db_map');
|
||||
foreach ($dbmap as $dbname) {
|
||||
if(!in_array($dbname, config('database.unset_db_map'))) {
|
||||
$connect = Db::connect($dbname);
|
||||
$data = $connect->name('sys_user')->where(['username' => $username])->find();
|
||||
if($data) {
|
||||
// 如果查到直接跳出循环并保存缓存
|
||||
Cache::set('admin_info_' . $username, $data['user_id']);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
public static function GetByQrcode($qd_rcode)
|
||||
{
|
||||
// 全表扫描username
|
||||
return DatabaseRoute::getAllDbData('sys_user', function ($query) use ($qd_rcode) {
|
||||
return $query->where([
|
||||
'qd_code' => $qd_rcode
|
||||
]);
|
||||
})->find();
|
||||
}
|
||||
|
||||
public static function updateSysMoney($userId, $money, $type)
|
||||
{
|
||||
$count = Db::name('sys_user_money')->where([
|
||||
'user_id' => $userId
|
||||
])->count();
|
||||
if (!$count) {
|
||||
Db::name('sys_user_money')->where([
|
||||
'user_id' => $userId
|
||||
])->insert([
|
||||
'id' => Random::generateRandomPrefixedId(19),
|
||||
'user_id' => $userId,
|
||||
'money' => 0
|
||||
]);
|
||||
}
|
||||
$model = Db::name('sys_user_money');
|
||||
if ($type) {
|
||||
$model->inc('money', floatval($money))->where([
|
||||
'user_id' => $userId
|
||||
])->update();
|
||||
}else{
|
||||
$model->dec('money', floatval($money))->where([
|
||||
'user_id' => $userId
|
||||
])->update();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
51
app/common/model/User.php
Normal file
51
app/common/model/User.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\model;
|
||||
|
||||
use think\Model;
|
||||
|
||||
/**
|
||||
* 会员公共模型
|
||||
* @property int $id 会员ID
|
||||
* @property string $password 密码密文
|
||||
* @property string $salt 密码盐(废弃待删)
|
||||
* @property int $login_failure 登录失败次数
|
||||
* @property string $last_login_time 上次登录时间
|
||||
* @property string $last_login_ip 上次登录IP
|
||||
* @property string $email 会员邮箱
|
||||
* @property string $mobile 会员手机号
|
||||
* @property string $status 状态:enable=启用,disable=禁用,...(string存储,可自定义其他)
|
||||
*/
|
||||
class User extends Model
|
||||
{
|
||||
protected $autoWriteTimestamp = true;
|
||||
|
||||
public function getAvatarAttr($value): string
|
||||
{
|
||||
return full_url($value, false, config('buildadmin.default_avatar'));
|
||||
}
|
||||
|
||||
public function setAvatarAttr($value): string
|
||||
{
|
||||
return $value == full_url('', false, config('buildadmin.default_avatar')) ? '' : $value;
|
||||
}
|
||||
|
||||
public function resetPassword($uid, $newPassword): int|User
|
||||
{
|
||||
return $this->where(['id' => $uid])->update(['password' => hash_password($newPassword), 'salt' => '']);
|
||||
}
|
||||
|
||||
public function getMoneyAttr($value): string
|
||||
{
|
||||
return bcdiv($value, 100, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户的余额是不可以直接进行修改的,请通过 UserMoneyLog 模型插入记录来实现自动修改余额
|
||||
* 此处定义上 money 的修改器仅为防止直接对余额的修改造成数据错乱
|
||||
*/
|
||||
public function setMoneyAttr($value): string
|
||||
{
|
||||
return bcmul($value, 100, 2);
|
||||
}
|
||||
}
|
||||
41
app/common/model/UserMoneyLog.php
Normal file
41
app/common/model/UserMoneyLog.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\model;
|
||||
|
||||
use think\model;
|
||||
|
||||
class UserMoneyLog extends model
|
||||
{
|
||||
protected $autoWriteTimestamp = true;
|
||||
protected $updateTime = false;
|
||||
|
||||
public function getMoneyAttr($value): string
|
||||
{
|
||||
return bcdiv($value, 100, 2);
|
||||
}
|
||||
|
||||
public function setMoneyAttr($value): string
|
||||
{
|
||||
return bcmul($value, 100, 2);
|
||||
}
|
||||
|
||||
public function getBeforeAttr($value): string
|
||||
{
|
||||
return bcdiv($value, 100, 2);
|
||||
}
|
||||
|
||||
public function setBeforeAttr($value): string
|
||||
{
|
||||
return bcmul($value, 100, 2);
|
||||
}
|
||||
|
||||
public function getAfterAttr($value): string
|
||||
{
|
||||
return bcdiv($value, 100, 2);
|
||||
}
|
||||
|
||||
public function setAfterAttr($value): string
|
||||
{
|
||||
return bcmul($value, 100, 2);
|
||||
}
|
||||
}
|
||||
11
app/common/model/UserScoreLog.php
Normal file
11
app/common/model/UserScoreLog.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace app\common\model;
|
||||
|
||||
use think\model;
|
||||
|
||||
class UserScoreLog extends model
|
||||
{
|
||||
protected $autoWriteTimestamp = true;
|
||||
protected $updateTime = false;
|
||||
}
|
||||
@@ -14,189 +14,14 @@ class IndexController
|
||||
{
|
||||
public function index(Request $request)
|
||||
{
|
||||
\support\Log::info('来了' . date('Y-m-d H:i:s'));
|
||||
$get['courseId'] = $course_id = '1877654905222135809';
|
||||
$user['user_id'] = $user_id = '14240';
|
||||
$user = DatabaseRoute::getDb('tb_user', $user_id)->find();
|
||||
try {
|
||||
if(empty($get['courseId'])) {
|
||||
return json('参数不完整');
|
||||
}
|
||||
$courseId = $get['courseId'];
|
||||
// 获取短剧详情
|
||||
$dd_b = Db::connect('duanju_slave');
|
||||
$db_name = $dd_b->name('course');
|
||||
$bean = $db_name->where(['course_id' => $courseId])->find();
|
||||
if(!$bean) {
|
||||
return json('短剧不存在');
|
||||
}
|
||||
|
||||
|
||||
$courseCollect = DatabaseRoute::getDb('course_collect', $user_id)
|
||||
->where(['course_id' => $course_id])
|
||||
->where(['user_id' => $user_id])
|
||||
->where(['classify' => 3])
|
||||
->limit(1)
|
||||
->find();
|
||||
|
||||
|
||||
|
||||
// 是否追剧
|
||||
$collect = DatabaseRoute::getDb('course_collect', $user_id)
|
||||
->where(['course_id' => $course_id])
|
||||
->where(['classify' => 1])
|
||||
->limit(1)
|
||||
->find();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$db = Db::connect(config('think-orm.search_library'));
|
||||
$userVip = $db->name('user_vip')->where(['user_id' => $user['user_id']])->find();
|
||||
|
||||
if ($userVip) {
|
||||
$user['member'] = $userVip['is_vip'];
|
||||
$user['end_time'] = $userVip['end_time'];
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
$userInfo = $user;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if (!empty($userInfo['member']) && $userInfo['member'] == 2) {
|
||||
$isVip = true;
|
||||
}else{
|
||||
$isVip = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// 查询用户是否购买了整集
|
||||
$courseUser = DatabaseRoute::getDb('course_user', $user_id)
|
||||
->where(['course_id' => $course_id])
|
||||
->where(['classify' => 1])
|
||||
->find();
|
||||
|
||||
|
||||
|
||||
// 每天购买超过上限,获得免费时间段资格
|
||||
$freeWatch = Test::checkFreeWatchPayCount($user['user_id']);
|
||||
|
||||
$startSort = 0;
|
||||
$endSort = 5;
|
||||
$dn_course_details = DatabaseRoute::getDb('course_details', ['course_id' => $courseId]);
|
||||
$sort = null;
|
||||
if (is_null($sort)) {
|
||||
|
||||
if (!empty($courseCollect)) {
|
||||
$courseDetails = $dn_course_details->field('sort')
|
||||
->where('course_details_id', $courseCollect['course_details_id'])
|
||||
->limit(1)
|
||||
->find();
|
||||
$sort = $courseDetails['sort'];
|
||||
}
|
||||
}
|
||||
|
||||
if ($freeWatch || !empty($courseUser)) {
|
||||
$courseDetailsSetVos = Test::courseSets($courseId, 2, null);
|
||||
} else {
|
||||
$courseDetailsSetVos = Test::courseSets($courseId, 1, $bean['wholesale_price']);
|
||||
}
|
||||
|
||||
// 调整集数范围
|
||||
if (!is_null($sort) && $sort > 2) {
|
||||
$startSort = $sort - 3;
|
||||
$endSort = $sort + 3;
|
||||
if (count($courseDetailsSetVos) < $endSort) {
|
||||
$startSort = count($courseDetailsSetVos) - 5;
|
||||
$endSort = count($courseDetailsSetVos) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// 已购买剧集ID集合
|
||||
$detailsId = [];
|
||||
if (!$freeWatch) {
|
||||
$det_db = Db::connect(DatabaseRoute::getConnection('course_user', ['user_id' => $user['user_id']]));
|
||||
$detailsId = $det_db->name('course_user')->where(['course_id' => $courseId, 'classify' => 2])->column('course_details_id');
|
||||
$det_db->close();
|
||||
$detailsId = array_flip(array_flip($detailsId)); // 去重
|
||||
\support\Log::info('啦啦啦' . date('Y-m-d H:i:s'));
|
||||
}
|
||||
// 处理剧集列表
|
||||
$current = null;
|
||||
foreach ($courseDetailsSetVos as &$s) {
|
||||
$s['wholesalePrice'] = (int) $s['wholesalePrice'];
|
||||
// 当前播放集
|
||||
if (!empty($courseCollect) && $s['courseDetailsId'] == $courseCollect['course_details_id']) {
|
||||
$s['current'] = 1;
|
||||
$current = &$s;
|
||||
}
|
||||
|
||||
// 非免费用户的权限控制
|
||||
if (
|
||||
!$freeWatch &&
|
||||
$s['sort'] > 3 &&
|
||||
(empty($detailsId) || !in_array($s['courseDetailsId'], $detailsId)) &&
|
||||
empty($courseUser) &&
|
||||
!$isVip
|
||||
) {
|
||||
$s['videoUrl'] = null;
|
||||
}
|
||||
|
||||
// 检查是否已点赞
|
||||
if ($s['sort'] > $startSort && $s['sort'] < $endSort) {
|
||||
$isGood_db = Db::connect(DatabaseRoute::getConnection('course_collect', ['user_id' => $user['user_id']]));
|
||||
$isGood = $isGood_db->name('course_collect')
|
||||
->where('course_details_id', $s['courseDetailsId'])
|
||||
->where('classify', 2)
|
||||
->limit(1)
|
||||
->count();
|
||||
$s['isGood'] = empty($isGood) || $isGood == 0 ? 0 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有当前播放集,默认第一集
|
||||
if (empty($current) && !empty($courseDetailsSetVos)) {
|
||||
$courseDetailsSetVos[0]['current'] = 1;
|
||||
$current = &$courseDetailsSetVos[0];
|
||||
}
|
||||
Test::setCourseView($bean);
|
||||
|
||||
$price = ($freeWatch ? 0 : ($bean['price'] ?? 0));
|
||||
$price = bccomp($price, '0', 2) <= 0 ? 0 : $price;
|
||||
// 返回结果
|
||||
$map = [
|
||||
'current' => $current,
|
||||
'price' => $price,
|
||||
'title' => $bean['title'],
|
||||
'collect' => empty($collect) || $collect == 0 ? 0 : 1,
|
||||
'list' => $courseDetailsSetVos
|
||||
];
|
||||
\support\Log::info('即将返回' . date('Y-m-d H:i:s'));
|
||||
return json($map);
|
||||
} catch (\Exception $e) {
|
||||
return json($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function view(Request $request)
|
||||
{
|
||||
return view('index/view', ['name' => 'webman']);
|
||||
}
|
||||
|
||||
public function json(Request $request)
|
||||
{
|
||||
return json(['code' => 0, 'msg' => 'ok']);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
22
app/czg/app/controller/CourseController.php
Normal file
22
app/czg/app/controller/CourseController.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
namespace app\czg\app\controller;
|
||||
|
||||
use app\common\controller\BaseController;
|
||||
use app\common\library\DatabaseRoute;
|
||||
use app\czg\app\model\Course;
|
||||
use app\model\Test;
|
||||
use support\Request;
|
||||
use think\facade\Db;
|
||||
|
||||
class CourseController extends BaseController
|
||||
{
|
||||
|
||||
|
||||
public function courseSets()
|
||||
{
|
||||
$get = $this->request->get();
|
||||
$res = Course::courseSets($get, $this->auth->getUser());
|
||||
return $this->ApiDataReturn($res);
|
||||
}
|
||||
|
||||
}
|
||||
93
app/czg/app/model/AlibabaSms.php
Normal file
93
app/czg/app/model/AlibabaSms.php
Normal file
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
namespace app\api\model;
|
||||
|
||||
use app\common\model\Sms;
|
||||
use fast\Random;
|
||||
use think\Exception;
|
||||
use think\Hook;
|
||||
use think\Log;
|
||||
use think\Model;
|
||||
use AlibabaCloud\SDK\Dysmsapi\V20170525\Dysmsapi;
|
||||
use AlibabaCloud\Tea\Exception\TeaError;
|
||||
use AlibabaCloud\Tea\Utils\Utils;
|
||||
|
||||
use Darabonba\OpenApi\Models\Config;
|
||||
use AlibabaCloud\SDK\Dysmsapi\V20170525\Models\SendSmsRequest;
|
||||
use AlibabaCloud\Tea\Utils\Utils\RuntimeOptions;
|
||||
|
||||
class AlibabaSms extends Model
|
||||
{
|
||||
|
||||
/**
|
||||
* 使用AK&SK初始化账号Client
|
||||
* @param string $accessKeyId
|
||||
* @param string $accessKeySecret
|
||||
* @return Dysmsapi Client
|
||||
*/
|
||||
public static function createClient($accessKeyId, $accessKeySecret){
|
||||
$config = new Config([
|
||||
"accessKeyId" => $accessKeyId,
|
||||
"accessKeySecret" => $accessKeySecret
|
||||
]);
|
||||
$config->endpoint = "dysmsapi.aliyuncs.com";
|
||||
return new Dysmsapi($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $args
|
||||
* @return void
|
||||
*/
|
||||
public static function main($args, $access_key_id, $access_key_secret){
|
||||
|
||||
$client = self::createClient($access_key_id, $access_key_secret);
|
||||
$sendSmsRequest = new SendSmsRequest($args);
|
||||
$runtime = new RuntimeOptions([]);
|
||||
try {
|
||||
// 复制代码运行请自行打印 API 的返回值
|
||||
$res = $client->sendSmsWithOptions($sendSmsRequest, $runtime);
|
||||
if($res->body->code == 'OK') {
|
||||
return true;
|
||||
}else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (Exception $error) {
|
||||
if (!($error instanceof TeaError)) {
|
||||
$error = new TeaError([], $error->getMessage(), $error->getCode(), $error);
|
||||
}
|
||||
// 如有需要,请打印 error
|
||||
Utils::assertAsString($error->message);
|
||||
Log::write('短信发送错误--' . $error->message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static function sms($mobile, $event, $code = null)
|
||||
{
|
||||
$code = $code?:Random::numeric(config('captcha.length'));
|
||||
$time = time();
|
||||
$ip = request()->ip();
|
||||
$sms = Sms::create(['event' => $event, 'mobile' => $mobile, 'code' => $code, 'ip' => $ip, 'createtime' => $time]);
|
||||
if (!$sms) {
|
||||
return false;
|
||||
}
|
||||
$ret = AlibabaSms::main([
|
||||
'templateCode' => config('alibaba.registerCode'),
|
||||
'templateParam' => json_encode(['code' => $code]),
|
||||
'phoneNumbers' => $mobile,
|
||||
'signName' => config('alibaba.sign'),
|
||||
]);
|
||||
if($ret) {
|
||||
return true;
|
||||
}else {
|
||||
$sms->delete();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
21
app/czg/app/model/Announcement.php
Normal file
21
app/czg/app/model/Announcement.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace app\api\model;
|
||||
|
||||
use app\common\library\DatabaseRoute;
|
||||
use app\common\model\Common;
|
||||
use ba\Random;
|
||||
use think\facade\Db;
|
||||
use think\Model;
|
||||
|
||||
class Announcement extends Model
|
||||
{
|
||||
|
||||
public static function list($type)
|
||||
{
|
||||
$res_data = Db::connect(config('database.search_library'))->name('announcement')->where(['type' => $type, 'state' => 1])->select()->toArray();
|
||||
$res_data = convertToCamelCase($res_data);
|
||||
return returnSuccessData($res_data);
|
||||
}
|
||||
|
||||
}
|
||||
13
app/czg/app/model/Banner.php
Normal file
13
app/czg/app/model/Banner.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace app\api\model;
|
||||
|
||||
use ba\Random;
|
||||
use think\Model;
|
||||
|
||||
class Banner extends Model
|
||||
{
|
||||
|
||||
|
||||
|
||||
}
|
||||
118
app/czg/app/model/Cash.php
Normal file
118
app/czg/app/model/Cash.php
Normal file
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
namespace app\api\model;
|
||||
|
||||
use app\common\library\DatabaseRoute;
|
||||
use ba\Random;
|
||||
use think\facade\Db;
|
||||
use think\Model;
|
||||
|
||||
class Cash extends Model
|
||||
{
|
||||
|
||||
public static function selectPayDetails($cashOut, $get, $isApp, $user)
|
||||
{
|
||||
$db_name_cash_out = Db::connect(DatabaseRoute::getConnection('cash_out', ['user_id' => $cashOut['user_id']]))->name('cash_out');
|
||||
// 构建查询条件
|
||||
if ($isApp) {
|
||||
// APP端:只查询用户自己的提现记录(用户类型为1)
|
||||
$query = $db_name_cash_out->where('user_id', $cashOut['user_id'])
|
||||
->where('user_type', 1);
|
||||
} else {
|
||||
// 管理端:根据用户ID或系统用户ID查询
|
||||
if (!empty($cashOut['user_id'])) {
|
||||
$query = $db_name_cash_out->where('user_id', $cashOut['user_id']);
|
||||
} else {
|
||||
if (empty($cashOut['sys_user_id'])) {
|
||||
// 无有效用户ID,返回空结果
|
||||
return returnErrorData('无效用户');
|
||||
} else {
|
||||
$query = $db_name_cash_out->where('user_id', $cashOut['sys_user_id'])
|
||||
->where('user_type', 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
$count = $query->count();
|
||||
// 执行分页查询
|
||||
$cashOutList = $query->limit(page($get['page'], $get['limit']), $get['limit'])->order('create_at', 'desc')->select()->toArray();
|
||||
if (!$isApp) {
|
||||
// 管理端:补充用户信息和统计数据
|
||||
$userIdList = array_column($cashOutList, 'user_id');
|
||||
if (!empty($userIdList)) {
|
||||
// 查询用户提现总数和总金额
|
||||
$cashoutSumMap = self::selectSumByUserIdList($userIdList, 1);
|
||||
$cashoutVerifySumMap = self::selectSumByUserIdList($userIdList, 3);
|
||||
// 查询用户名称
|
||||
$userinfoMap = $user;
|
||||
|
||||
// 合并数据到结果集
|
||||
foreach ($cashOutList as &$item) {
|
||||
$userId = $item['user_id'];
|
||||
// 设置用户名
|
||||
$item['userName'] = $userinfoMap[$userId] ?? '';
|
||||
// 设置提现统计
|
||||
if (isset($cashoutSumMap[$userId])) {
|
||||
$item['count'] = $cashoutSumMap[$userId]['count'];
|
||||
$item['total'] = $cashoutSumMap[$userId]['total'];
|
||||
}
|
||||
// 设置审核通过的提现统计
|
||||
if (isset($cashoutVerifySumMap[$userId])) {
|
||||
$item['verifyCount'] = $cashoutVerifySumMap[$userId]['count'];
|
||||
$item['verifyTotal'] = $cashoutVerifySumMap[$userId]['total'];
|
||||
}
|
||||
}
|
||||
unset($item);
|
||||
}
|
||||
}
|
||||
|
||||
if ($isApp) {
|
||||
// APP端:对敏感信息进行脱敏处理
|
||||
foreach ($cashOutList as &$item) {
|
||||
if (!empty($item['bank_name'])) {
|
||||
// 银行卡号脱敏
|
||||
$item['zhifubao'] = bankCard($item['zhifubao']);
|
||||
} elseif (filter_var($item['zhifubao'], FILTER_VALIDATE_EMAIL)) {
|
||||
// 邮箱脱敏
|
||||
$item['zhifubao'] = email($item['zhifubao']);
|
||||
} elseif (preg_match('/^1[3-9]\d{9}$/', $item['zhifubao'])) {
|
||||
// 手机号脱敏
|
||||
$item['zhifubao'] = maskPhoneNumber($item['zhifubao']);
|
||||
}
|
||||
}
|
||||
unset($item);
|
||||
}
|
||||
|
||||
return returnSuccessData([
|
||||
'currPage' => $get['page'],
|
||||
'pageSize' => $get['limit'],
|
||||
'list' => convertToCamelCase($cashOutList),
|
||||
'totalCount' => $count,
|
||||
'totalPage' => ceil($count / $get['limit']),
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
public static function selectSumByUserIdList($userIdList, $state)
|
||||
{
|
||||
|
||||
$result = DatabaseRoute::getAllDbData('cash_out', function($query) use ($userIdList, $state) {
|
||||
return $query
|
||||
->field([
|
||||
'user_id',
|
||||
'ROUND(SUM(money), 2) AS total',
|
||||
'COUNT(*) AS count'
|
||||
])
|
||||
->where('state', $state)
|
||||
->whereIn('user_id', $userIdList)
|
||||
->group('user_id');
|
||||
})->select()->toArray();
|
||||
$resultMap = [];
|
||||
foreach ($result as $item) {
|
||||
$resultMap[$item['user_id']] = $item;
|
||||
}
|
||||
|
||||
return $resultMap;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
32
app/czg/app/model/CommonInfo.php
Normal file
32
app/czg/app/model/CommonInfo.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace app\czg\app\model;
|
||||
|
||||
use app\common\model\BaseModel;
|
||||
use app\exception\SysException;
|
||||
|
||||
class CommonInfo extends BaseModel
|
||||
{
|
||||
public function getByCode(int $code)
|
||||
{
|
||||
$val = cache('common_info:'.$code);
|
||||
if ($val) {
|
||||
return $val;
|
||||
}
|
||||
$val = $this->where([
|
||||
'type' => $code
|
||||
])->find()->toArray();
|
||||
cache('common_info:'.$code, $val, 60 * 60 * 24);
|
||||
return $val;
|
||||
}
|
||||
|
||||
public function getByCodeToInt(int $code) {
|
||||
$val = $this->getByCode($code);
|
||||
if (!$val || empty($val['value'])) {
|
||||
throw new SysException('代码获取失败, code: {}', $code);
|
||||
}
|
||||
|
||||
return intval($val['value']);
|
||||
}
|
||||
|
||||
}
|
||||
443
app/czg/app/model/Course.php
Normal file
443
app/czg/app/model/Course.php
Normal file
@@ -0,0 +1,443 @@
|
||||
<?php
|
||||
|
||||
namespace app\czg\app\model;
|
||||
|
||||
use app\common\library\DatabaseRoute;
|
||||
use app\common\model\Common;
|
||||
use ba\Random;
|
||||
use think\cache\driver\Redis;
|
||||
use support\think\Cache;
|
||||
use think\facade\Db;
|
||||
use support\Log;
|
||||
use think\Model;
|
||||
|
||||
class Course extends Model
|
||||
{
|
||||
|
||||
public static function selectCourse($data)
|
||||
{
|
||||
|
||||
if(empty($data['page']) || empty($data['limit'])) {
|
||||
return returnErrorData('参数不完整');
|
||||
}
|
||||
// 先查看有没有缓存
|
||||
$cache = Cache::get('index_data_' . $data['page']);
|
||||
|
||||
if($cache) {
|
||||
$cache = json_decode($cache, true);
|
||||
if(!empty($data['sort'])) {
|
||||
$cache['list'] = shuffleMultiArray($cache['list']);
|
||||
$cache['list'] = array_values($cache['list']);
|
||||
}
|
||||
return returnSuccessData($cache);
|
||||
}
|
||||
$page = ($data['page'] - 1) * $data['limit'];
|
||||
$db = $data_db = Db::connect(config('database.search_library'))->name('course');
|
||||
$db = $db->where(['status' => 1]);
|
||||
$count = $db->count();
|
||||
$data_db = $data_db->limit($page, $data['limit']);
|
||||
if(!empty($data['sort'])) {
|
||||
if($data['sort'] == 1) {
|
||||
$data_db = $data_db->order('week_pay','desc');
|
||||
}elseif ($data['sort'] == 2) {
|
||||
$data_db = $data_db->order('week_view','desc');
|
||||
}
|
||||
}else {
|
||||
$data_db = $data_db->order('create_time','desc');
|
||||
}
|
||||
$list = $data_db->select()->toArray();
|
||||
foreach ($list as $k => &$v) {
|
||||
$v['course_id'] = (string)$v['course_id'];
|
||||
// 如果没有剧集了,给下架
|
||||
$crous_detail_db_count = DatabaseRoute::getDb('course_details', ['course_id' => $v['course_id']])->where(['course_id' => $v['course_id']])->count();
|
||||
if(!$crous_detail_db_count) {
|
||||
Course::where(['course_id' => $v['course_id']])->update(['status' => 2]);
|
||||
}
|
||||
}
|
||||
|
||||
$return = [
|
||||
'currPage' => 1,
|
||||
'list' => convertToCamelCase($list),
|
||||
'pageSize' => $data['limit'],
|
||||
'totalCount' => $count,
|
||||
'totalPage' => ceil($count / $data['limit']),
|
||||
];
|
||||
if($list) {
|
||||
Cach::set('index_data_' . $data['page'], json_encode($return, true));
|
||||
}
|
||||
return returnSuccessData($return);
|
||||
}
|
||||
|
||||
public static function selectByUserId($get, $user_id)
|
||||
{
|
||||
|
||||
if(empty($get['classify'])) {
|
||||
return returnErrorData('参数不完整');
|
||||
}
|
||||
$db = Db::connect(DatabaseRoute::getConnection('course_collect', ['user_id' => $user_id]));
|
||||
if($get['classify'] == 1) {
|
||||
// 追剧
|
||||
$result = $result2 = $db->name('course_collect')
|
||||
->alias('c1')
|
||||
->join('course_collect c2', 'c1.course_id = c2.course_id AND c2.user_id = ' . $user_id .' AND c2.classify = ' . $get['classify'])
|
||||
->where('c1.classify', 3)
|
||||
->where('c1.user_id', $user_id);
|
||||
$count = $result2->count();
|
||||
$result = $result->field([
|
||||
'c1.course_id' => 'courseId',
|
||||
'c1.course_details_id' => 'courseDetailsId',
|
||||
'c1.update_time' => 'updateTime'
|
||||
])
|
||||
->limit(page($get['page'], $get['limit']), $get['limit'])
|
||||
->select();
|
||||
}elseif($get['classify'] == 2 || $get['classify'] == 3) {
|
||||
// 点赞 观看历史
|
||||
$result = $result2 = $db->name('course_collect')
|
||||
->alias('c1')
|
||||
->where('c1.classify', $get['classify'])
|
||||
->where('c1.user_id', $user_id);
|
||||
$count = $result2->count();
|
||||
$result = $result->field([
|
||||
'c1.course_id' => 'courseId',
|
||||
'c1.course_details_id' => 'courseDetailsId',
|
||||
'c1.update_time' => 'updateTime'
|
||||
])->limit(page($get['page'], $get['limit']), $get['limit'])->select();
|
||||
}
|
||||
$is_arr = self::sacouresdata($result);
|
||||
$db = Db::connect(config('database.search_library'));
|
||||
$course = $db->name('course')->whereIn('course_id', $is_arr['course_id'])->select();
|
||||
$course_arr = self::sacouresdata($result, $course);
|
||||
// 拿详情
|
||||
$course_data = self::sacouresjidata($course_arr['course_list']);
|
||||
return returnSuccessData([
|
||||
'currPage' => $get['page'],
|
||||
'pageSize' => $get['limit'],
|
||||
'totalCount' => $count,
|
||||
'totalPage' => ceil($count / $get['limit']),
|
||||
'records' => $course_data,
|
||||
]);
|
||||
|
||||
}
|
||||
|
||||
|
||||
// 拿短剧详情
|
||||
public static function sacouresdata($result, $course = [])
|
||||
{
|
||||
$data['course_id'] = [];
|
||||
$data['course_details_id'] = [];
|
||||
$data['course_list'] = [];
|
||||
foreach ($result as $k => $v) {
|
||||
if(empty($course)) {
|
||||
$data['course_id'][] = (string)$v['courseId'];
|
||||
$data['course_details_id'][] = (string)$v['courseDetailsId'];
|
||||
}else {
|
||||
foreach ($course as $courseKey => $courseValue) {
|
||||
if($courseValue['course_id'] == $v['courseId']) {
|
||||
$data['course_list'][] = [
|
||||
'courseId' => (string)$courseValue['course_id'],
|
||||
'courseDetailsId' => (string)$v['courseDetailsId'],
|
||||
'courseLabel' => $courseValue['course_label'],
|
||||
'courseLabelIds' => $courseValue['course_label_ids'],
|
||||
'courseType' => $courseValue['course_type'],
|
||||
'createTime' => $courseValue['create_time'],
|
||||
'payNum' => $courseValue['pay_num'],
|
||||
'title' => $courseValue['title'],
|
||||
'titleImg' => $courseValue['title_img'],
|
||||
'updateTime' => $courseValue['update_time'],
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
// 拿剧集详情
|
||||
public static function sacouresjidata($result)
|
||||
{
|
||||
foreach ($result as $k => $v) {
|
||||
$db_name = DatabaseRoute::getConnection('course_details', ['course_id' => $v['courseId']]);
|
||||
$db = Db::connect($db_name);
|
||||
$course_details = $db->name('course_details')->where(['course_details_id' => $v['courseDetailsId']])->find();
|
||||
$result[$k]['courseDetailsCount'] = $db->name('course_details')->where(['course_id' => $v['courseId']])->count();
|
||||
if($course_details) {
|
||||
$result[$k]['courseDetailsName'] = $course_details['course_details_name'];
|
||||
}else {
|
||||
$result[$k]['courseDetailsName'] = '';
|
||||
}
|
||||
$result[$k]['db_name'] = $db_name;
|
||||
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public static function collectVideoSummary($user_id)
|
||||
{
|
||||
$db_name = DatabaseRoute::getDb('course_collect', $user_id)->where('user_id', $user_id)
|
||||
->field([
|
||||
'COUNT(CASE WHEN classify = 1 THEN 1 END)' => 'collectCount',
|
||||
'COUNT(CASE WHEN classify = 2 THEN 1 END)' => 'likeCount'
|
||||
])
|
||||
->find();
|
||||
return returnSuccessData($db_name);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取推荐视频
|
||||
*/
|
||||
public static function selectCourseDetailsList($get, $user_id)
|
||||
{
|
||||
if(empty($get['page']) || empty($get['limit']) || empty($get['randomNum'])) {
|
||||
return returnErrorData('参数不完整');
|
||||
}
|
||||
$sale = config('database.db_map');
|
||||
foreach ($sale as $k => $v) {
|
||||
if(in_array($v, config('database.unset_db_map'))) {
|
||||
unset($sale[$k]);
|
||||
}
|
||||
}
|
||||
$dbname = $sale[array_rand($sale)];
|
||||
$db = Db::connect($dbname)->name('course_details');
|
||||
$where = ['good' => 1, 'is_price' => 2];
|
||||
$course_detail_count = $db->where($where)->count();
|
||||
if(!$course_detail_count) {
|
||||
return returnErrorData('暂无视频');
|
||||
}
|
||||
$page = rand(1, $course_detail_count);
|
||||
$size = 5;
|
||||
$currPage = ceil($page / $size);
|
||||
$res = $db->where($where)->limit($page, $size)->select()->toArray();
|
||||
|
||||
if($user_id) {
|
||||
$db_name = DatabaseRoute::getConnection('course_collect', ['user_id' => $user_id]);
|
||||
$user_db = Db::connect($db_name)->name('course_collect');
|
||||
foreach ($res as $k => $v) {
|
||||
$res[$k]['isCollect'] = $user_db->where(['classify' => 1, 'user_id' => $user_id])->count();
|
||||
$res[$k]['isGood'] = $user_db->where(['classify' => 2, 'user_id' => $user_id])->count();
|
||||
}
|
||||
}
|
||||
$res = apiconvertToCamelCase($res);
|
||||
return returnSuccessData([
|
||||
'currPage' => $currPage,
|
||||
'pageSize' => $size,
|
||||
'list' => $res,
|
||||
'records' => null,
|
||||
'totalCount' => $course_detail_count,
|
||||
'totalPage' => ceil($course_detail_count / $size),
|
||||
]);
|
||||
}
|
||||
|
||||
public static function getRedEnvelopeTips($user_id)
|
||||
{
|
||||
$db = DatabaseRoute::getDb('orders', $user_id);
|
||||
$count = $db->where('create_time', '>', date('Y-m-d 00:00:00'))->where(['user_id' => $user_id, 'status' => 1, 'pay_way' => 9])->count();
|
||||
$totalCount = CommonInfo::where(['type' => 901])->find()->value;
|
||||
$string = '每日前' . $totalCount . '次付款均可获取抽奖机会,抽奖保底抽中付款金额等额红包,红包可直接提现。当前为第' . $count + 1 . '次付款';
|
||||
return returnSuccessData($string);
|
||||
|
||||
}
|
||||
|
||||
// 查看短剧
|
||||
public static function viewCourse($get)
|
||||
{
|
||||
if(empty($get['courseId']) || empty($get['courseDetailsId']) || empty($get['type'])) {
|
||||
return returnErrorData('参数不完整');
|
||||
}
|
||||
|
||||
// 获取短剧详情
|
||||
$course = Course::where(['course_id' => $get['courseId']])->find();
|
||||
if(!$course) {
|
||||
return returnErrorData('短剧不存在');
|
||||
}
|
||||
|
||||
// 获取短剧集详情
|
||||
$course_detail_db = DatabaseRoute::getDb('course_details', ['course_id' => $get['courseId']])
|
||||
->where(['course_id' => $get['courseId']])
|
||||
->where(['course_details_id' => $get['courseDetailsId']])
|
||||
->find();
|
||||
if(!$course_detail_db) {
|
||||
return returnErrorData('短剧集不存在');
|
||||
}
|
||||
|
||||
$db = Db::connect(DatabaseRoute::getConnection('course_details', ['course_id' => $get['courseId']], true));
|
||||
if($get['type'] == 'start') {
|
||||
$db->name('course_details')
|
||||
->where(['course_id' => $get['courseId']])
|
||||
->where(['course_details_id' => $get['courseDetailsId']])
|
||||
->inc('view_count')
|
||||
->update();
|
||||
}elseif($get['type'] == 'end') {
|
||||
$db->name('course_details')
|
||||
->where(['course_id' => $get['courseId']])
|
||||
->where(['course_details_id' => $get['courseDetailsId']])
|
||||
->inc('play_complete_count')
|
||||
->update();
|
||||
}
|
||||
|
||||
return returnSuccessData();
|
||||
}
|
||||
|
||||
// 根据id查询短剧集数列表
|
||||
public static function courseSets($get, $user, $sort = null)
|
||||
{
|
||||
try {
|
||||
if(empty($get['courseId'])) {
|
||||
return returnErrorData('参数不完整');
|
||||
}
|
||||
$courseId = $get['courseId'];
|
||||
// 获取短剧详情
|
||||
$dd_b = Db::connect(config('database.search_library'));
|
||||
$db_name = $dd_b->name('course');
|
||||
$bean = $db_name->where(['course_id' => $courseId])->find();
|
||||
$dd_b->close();
|
||||
if(!$bean) {
|
||||
return returnErrorData('短剧不存在');
|
||||
}
|
||||
$courseCollect = CourseCollect::Watchhistory($user['user_id'], $courseId);
|
||||
Db::connect()->close();
|
||||
// 是否追剧
|
||||
$collect = CourseCollect::isfollowthedrama($user['user_id'], $courseId);
|
||||
Db::connect()->close();
|
||||
$userInfo = TbUser::selectUserByIdNew($user);
|
||||
if (!empty($userInfo['member']) && $userInfo['member'] == 2) {
|
||||
$isVip = true;
|
||||
}else{
|
||||
$isVip = false;
|
||||
}
|
||||
// 查询用户是否购买了整集
|
||||
$courseUser = CourseCollect::selectCourseUser($user['user_id'], $courseId);
|
||||
Db::connect()->close();
|
||||
// 每天购买超过上限,获得免费时间段资格
|
||||
$freeWatch = CourseCollect::checkFreeWatchPayCount($user['user_id']);
|
||||
$startSort = 0;
|
||||
$endSort = 5;
|
||||
$dn_course_details = DatabaseRoute::getDb('course_details', ['course_id' => $courseId]);
|
||||
if (is_null($sort)) {
|
||||
if (!empty($courseCollect)) {
|
||||
$courseDetails = $dn_course_details->field('sort')
|
||||
->where('course_details_id', $courseCollect['course_details_id'])
|
||||
->limit(1)
|
||||
->find();
|
||||
$sort = $courseDetails['sort'];
|
||||
}
|
||||
}
|
||||
Db::connect()->close();
|
||||
if ($freeWatch || !empty($courseUser)) {
|
||||
$courseDetailsSetVos = CourseDetails::courseSets($courseId, 2, null);
|
||||
} else {
|
||||
$courseDetailsSetVos = CourseDetails::courseSets($courseId, 1, $bean['wholesale_price']);
|
||||
}
|
||||
// 调整集数范围
|
||||
if (!is_null($sort) && $sort > 2) {
|
||||
$startSort = $sort - 3;
|
||||
$endSort = $sort + 3;
|
||||
if (count($courseDetailsSetVos) < $endSort) {
|
||||
$startSort = count($courseDetailsSetVos) - 5;
|
||||
$endSort = count($courseDetailsSetVos) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// 已购买剧集ID集合
|
||||
$detailsId = [];
|
||||
if (!$freeWatch) {
|
||||
$det_db = Db::connect(DatabaseRoute::getConnection('course_user', ['user_id' => $user['user_id']]));
|
||||
$detailsId = $det_db->name('course_user')->where(['course_id' => $courseId, 'classify' => 2])->column('course_details_id');
|
||||
$det_db->close();
|
||||
$detailsId = array_flip(array_flip($detailsId)); // 去重
|
||||
}
|
||||
// 处理剧集列表
|
||||
$current = null;
|
||||
foreach ($courseDetailsSetVos as &$s) {
|
||||
$s['wholesalePrice'] = (int) $s['wholesalePrice'];
|
||||
// 当前播放集
|
||||
if (!empty($courseCollect) && $s['courseDetailsId'] == $courseCollect['course_details_id']) {
|
||||
$s['current'] = 1;
|
||||
$current = &$s;
|
||||
}
|
||||
|
||||
// 非免费用户的权限控制
|
||||
if (
|
||||
!$freeWatch &&
|
||||
$s['sort'] > 3 &&
|
||||
(empty($detailsId) || !in_array($s['courseDetailsId'], $detailsId)) &&
|
||||
empty($courseUser) &&
|
||||
!$isVip
|
||||
) {
|
||||
$s['videoUrl'] = null;
|
||||
}
|
||||
|
||||
// 检查是否已点赞
|
||||
if ($s['sort'] > $startSort && $s['sort'] < $endSort) {
|
||||
$isGood_db = Db::connect(DatabaseRoute::getConnection('course_collect', ['user_id' => $user['user_id']]));
|
||||
$isGood = $isGood_db->name('course_collect')
|
||||
->where('course_details_id', $s['courseDetailsId'])
|
||||
->where('classify', 2)
|
||||
->limit(1)
|
||||
->count();
|
||||
$isGood_db->close();
|
||||
$s['isGood'] = empty($isGood) || $isGood == 0 ? 0 : 1;
|
||||
}
|
||||
}
|
||||
// 如果没有当前播放集,默认第一集
|
||||
if (empty($current) && !empty($courseDetailsSetVos)) {
|
||||
$courseDetailsSetVos[0]['current'] = 1;
|
||||
$current = &$courseDetailsSetVos[0];
|
||||
}
|
||||
self::setCourseView($bean);
|
||||
|
||||
$price = ($freeWatch ? 0 : ($bean['price'] ?? 0));
|
||||
$price = bccomp($price, '0', 2) <= 0 ? 0 : $price;
|
||||
// 返回结果
|
||||
$map = [
|
||||
'current' => $current,
|
||||
'price' => $price,
|
||||
'title' => $bean['title'],
|
||||
'collect' => empty($collect) || $collect == 0 ? 0 : 1,
|
||||
'list' => $courseDetailsSetVos
|
||||
];
|
||||
return returnSuccessData($map);
|
||||
} catch (\Exception $e) {
|
||||
Log::info("请求剧集异常: " . $e->getMessage() . '/' . $e->getLine() . '/');
|
||||
return returnErrorData($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static function setCourseView($course)
|
||||
{
|
||||
// 1. 更新总播放量
|
||||
if (empty($course['view_counts'])) {
|
||||
$viewCounts = 1;
|
||||
} else {
|
||||
$viewCounts = $course['view_counts'] + 1;
|
||||
}
|
||||
|
||||
// 2. 检查是否允许更新周播放量(假设ApiAccessLimitUtil为自定义工具类)
|
||||
$allowUpdateWeekView = Common::isAccessAllowed(
|
||||
(string)$course['course_id'],
|
||||
"updateWeekCourseView",
|
||||
1,
|
||||
600
|
||||
);
|
||||
|
||||
// 3. 获取并更新周播放量
|
||||
$weekView = $course['week_view'] ?? 0;
|
||||
if ($allowUpdateWeekView) {
|
||||
// 从Redis获取周播放量(假设redisServiceImpl为自定义服务类)
|
||||
$weekView = Common::getCourseWeekViewCount($course['course_id']);
|
||||
}
|
||||
|
||||
$db_name = Db::connect(config('database.z_library'))->name('course');
|
||||
// 4. 执行数据库更新
|
||||
$db_name->where(['course_id' => $course['course_id']])->update([
|
||||
'view_counts' => $viewCounts,
|
||||
'week_view' => $weekView
|
||||
]);
|
||||
Db::connect()->close();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
84
app/czg/app/model/CourseCollect.php
Normal file
84
app/czg/app/model/CourseCollect.php
Normal file
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
namespace app\czg\app\model;
|
||||
|
||||
use app\common\library\DatabaseRoute;
|
||||
use app\common\model\Common;
|
||||
use app\utils\RedisUtils;
|
||||
use ba\Random;
|
||||
use think\facade\Cache;
|
||||
use think\facade\Db;
|
||||
use think\facade\Log;
|
||||
use think\Model;
|
||||
|
||||
class CourseCollect extends Model
|
||||
{
|
||||
|
||||
// 观看记录
|
||||
public static function Watchhistory($user_id, $course_id)
|
||||
{
|
||||
return DatabaseRoute::getDb('course_collect', $user_id)
|
||||
->where(['course_id' => $course_id])
|
||||
->where(['user_id' => $user_id])
|
||||
->where(['classify' => 3])
|
||||
->limit(1)
|
||||
->find();
|
||||
}
|
||||
|
||||
|
||||
// 是否追剧
|
||||
public static function isfollowthedrama($user_id, $course_id)
|
||||
{
|
||||
return DatabaseRoute::getDb('course_collect', $user_id)
|
||||
->where(['course_id' => $course_id])
|
||||
->where(['classify' => 1])
|
||||
->limit(1)
|
||||
->find();
|
||||
}
|
||||
|
||||
// 查询用户是否购买了整集
|
||||
public static function selectCourseUser($user_id, $course_id)
|
||||
{
|
||||
return DatabaseRoute::getDb('course_user', $user_id)
|
||||
->where(['course_id' => $course_id])
|
||||
->where(['classify' => 1])
|
||||
->find();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 校验用户是否达到免费播放购买次数
|
||||
*/
|
||||
public static function checkFreeWatchPayCount($userId)
|
||||
{
|
||||
$isExpire = RedisUtils::getFreeWatchTimeIsExpire($userId);
|
||||
|
||||
if (!$isExpire) {
|
||||
$count = DatabaseRoute::getDb('orders', $userId)->where([
|
||||
'status' => 1,
|
||||
'pay_way' => 9,
|
||||
['create_time', '>', date('Y-m-d 00:00:00')],
|
||||
])->count();
|
||||
$needCount = (new CommonInfo())->getByCode(916);
|
||||
$freeTime = (new CommonInfo())->getByCode(917);
|
||||
Db::connect()->close();
|
||||
if (!$needCount || !$freeTime) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($count >= intval($needCount['value'])) {
|
||||
RedisUtils::setFreeWatchTime($userId, intval($freeTime['value']) * 60, false);
|
||||
RedisUtils::getFreeWatchTimeIsExpire($userId);
|
||||
$isExpire = false;
|
||||
}else{
|
||||
$isExpire = true;
|
||||
}
|
||||
}
|
||||
|
||||
return !$isExpire;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
53
app/czg/app/model/CourseDetails.php
Normal file
53
app/czg/app/model/CourseDetails.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace app\czg\app\model;
|
||||
|
||||
use app\common\library\DatabaseRoute;
|
||||
use app\common\model\Common;
|
||||
use ba\Random;
|
||||
use think\facade\Cache;
|
||||
use think\facade\Db;
|
||||
use think\Model;
|
||||
|
||||
class CourseDetails extends Model
|
||||
{
|
||||
|
||||
|
||||
public static function courseSets($courseId, $isPrice, $wholesalePrice)
|
||||
{
|
||||
|
||||
$db = Db::connect(DatabaseRoute::getConnection('course_details', ['course_id' => $courseId]));
|
||||
$courseDetailsSetVos = $db->name('course_details')
|
||||
->alias('c')
|
||||
->field([
|
||||
'c.course_id' => 'courseId',
|
||||
'c.course_details_id' => 'courseDetailsId',
|
||||
'c.course_details_name' => 'courseDetailsName',
|
||||
'c.video_url' => 'videoUrl',
|
||||
'c.price' => 'price',
|
||||
'c.sort' => 'sort',
|
||||
'c.is_price' => 'isPrice',
|
||||
'c.title_img' => 'titleImg',
|
||||
'c.good_num' => 'goodNum',
|
||||
])
|
||||
->where('c.course_id', $courseId)
|
||||
->order('c.sort', 'asc')
|
||||
->select()
|
||||
->toArray();
|
||||
$db->close();
|
||||
foreach ($courseDetailsSetVos as $k => &$v) {
|
||||
$v['courseId'] = (string) $v['courseId'];
|
||||
$v['courseDetailsId'] = (string) $v['courseDetailsId'];
|
||||
if(empty($wholesalePrice)) {
|
||||
$v['wholesalePrice'] = 0;
|
||||
}else {
|
||||
$v['wholesalePrice'] = $wholesalePrice;
|
||||
}
|
||||
if($isPrice != 1) {
|
||||
$v['isPrice'] = 2;
|
||||
}
|
||||
}
|
||||
return $courseDetailsSetVos;
|
||||
}
|
||||
|
||||
}
|
||||
128
app/czg/app/model/DiscSpinningRecord.php
Normal file
128
app/czg/app/model/DiscSpinningRecord.php
Normal file
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
|
||||
namespace app\api\model;
|
||||
|
||||
use app\common\library\DatabaseRoute;
|
||||
use think\facade\Cache;
|
||||
use think\facade\Db;
|
||||
|
||||
class DiscSpinningRecord
|
||||
{
|
||||
|
||||
public static function countDraw($userId)
|
||||
{
|
||||
return DatabaseRoute::getDb('disc_spinning_record', $userId)->where([
|
||||
'source' => 'order',
|
||||
'draw_day' => date('Y-m-d')
|
||||
])->count();
|
||||
}
|
||||
|
||||
|
||||
public static function countTaskDisc($userId, $type)
|
||||
{
|
||||
$countTaskDisc = 0;
|
||||
$signCount = null;
|
||||
|
||||
// 检查类型参数
|
||||
if (empty($type) || $type === "1") {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$db_name = \think\facade\Db::connect(config('database.search_library'));
|
||||
$task = $db_name->name('task_center')->where(['type' => 2]);
|
||||
// 构建查询条件
|
||||
$sourceType = null;
|
||||
if ($type === "2") {
|
||||
$task =$task->where('number', '>', 1);
|
||||
$task =$task->where('number', '<', 8);
|
||||
$sourceType = "taskW";
|
||||
} elseif ($type === "3") {
|
||||
$task =$task->where('number', '>', 7);
|
||||
$task =$task->where('number', '<', 32);
|
||||
$sourceType = "taskM";
|
||||
}
|
||||
|
||||
// 检查是否已有抽奖记录
|
||||
$spCount = DatabaseRoute::getDb('disc_spinning_record', $userId)->where(['source' => $sourceType])->count();
|
||||
if (!empty($spCount) && $spCount > 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 获取任务列表
|
||||
$taskCenters = $task->select();
|
||||
foreach ($taskCenters as $k => $taskCenter) {
|
||||
// 获取任务奖励配置
|
||||
$rewardMap_arr = $db_name->name('task_center_reward')->field('type,number')->where(['task_id' => $taskCenter['id']])->select()->toArray();
|
||||
if (empty($rewardMap_arr)) {
|
||||
continue;
|
||||
}
|
||||
$number = 0;
|
||||
$rewardMap = [];
|
||||
foreach ($rewardMap_arr as $tk => $tv) {
|
||||
$number += $tv['number'];
|
||||
$t_type = $tv['type'];
|
||||
}
|
||||
$rewardMap[$t_type] = $number;
|
||||
$taskWRedisMap = [];
|
||||
if ($type === "2") {
|
||||
// 周任务处理逻辑
|
||||
$taskWCount = UserSignRecord::getTaskWCount($userId, $rewardMap[9]);
|
||||
if (!empty($taskWCount)) {
|
||||
foreach ($taskWCount as $key => $value) {
|
||||
if ($value > 0) {
|
||||
$countTaskDisc += $value;
|
||||
$taskWRedisMap[$key] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!empty($taskWRedisMap)) {
|
||||
Cache::set("date:spinning:draw:taskW" . $userId, json_encode($taskWRedisMap), todayAfterSecond());
|
||||
}
|
||||
} elseif ($type === "3") {
|
||||
// 月任务处理逻辑
|
||||
if ($signCount === null) {
|
||||
$signCount = UserSignRecord::getUserSignCount($userId);
|
||||
}
|
||||
|
||||
if ($signCount >= $taskCenter['number']) {
|
||||
if (isset($rewardMap[9])) {
|
||||
$spinningCount = DatabaseRoute::getDb('disc_spinning_record', $userId)->where(['source' => 'taskM', 'source_id' => $taskCenter['id']])->count();
|
||||
$countTaskDisc = $rewardMap[9] - ($spinningCount ?? 0);
|
||||
|
||||
if ($countTaskDisc > 0) {
|
||||
$taskWRedisMap[$taskCenter['id']] = $countTaskDisc;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!empty($taskWRedisMap)) {
|
||||
Cache::set("date:spinning:draw:taskM" . $userId, json_encode($taskWRedisMap), todayAfterSecond());
|
||||
}
|
||||
}
|
||||
}
|
||||
return $countTaskDisc;
|
||||
}
|
||||
|
||||
|
||||
public static function selectOrdersCountStatisticsByDay($user_id, $limit)
|
||||
{
|
||||
$db = Db::connect(DatabaseRoute::getConnection('orders', ['user_id' => $user_id]));
|
||||
$count = $db->name('orders')
|
||||
->alias('o')
|
||||
->leftJoin('disc_spinning_record record', 'o.orders_id = record.source_id AND record.source = "order"')
|
||||
->where('o.user_id', $user_id)
|
||||
->where('o.status', 1)
|
||||
->where('o.pay_way', 9)
|
||||
->whereraw('o.create_time > DATE_FORMAT(NOW(), "%Y-%m-%d 00:00:00")')
|
||||
->whereNull('record.source_id')
|
||||
->order('o.create_time')
|
||||
->count();
|
||||
if ($count == null) {
|
||||
return 0;
|
||||
}
|
||||
if ($count <= $limit) {
|
||||
return $count;
|
||||
}
|
||||
return $limit;
|
||||
}
|
||||
|
||||
}
|
||||
20
app/czg/app/model/HelpClassify.php
Normal file
20
app/czg/app/model/HelpClassify.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace app\api\model;
|
||||
|
||||
use app\common\library\DatabaseRoute;
|
||||
use app\common\model\BaseModel;
|
||||
use app\common\model\SysUser;
|
||||
use ba\Exception;
|
||||
use ba\Random;
|
||||
use think\facade\Db;
|
||||
|
||||
class HelpClassify extends BaseModel
|
||||
{
|
||||
|
||||
public function helpword()
|
||||
{
|
||||
return $this->hasMany('HelpWord', 'help_classify_id', 'help_classify_id');
|
||||
}
|
||||
|
||||
}
|
||||
16
app/czg/app/model/HelpWord.php
Normal file
16
app/czg/app/model/HelpWord.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace app\api\model;
|
||||
|
||||
use app\common\library\DatabaseRoute;
|
||||
use app\common\model\BaseModel;
|
||||
use app\common\model\SysUser;
|
||||
use ba\Exception;
|
||||
use ba\Random;
|
||||
use think\facade\Db;
|
||||
|
||||
class HelpWord extends BaseModel
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
179
app/czg/app/model/Invite.php
Normal file
179
app/czg/app/model/Invite.php
Normal file
@@ -0,0 +1,179 @@
|
||||
<?php
|
||||
|
||||
namespace app\api\model;
|
||||
|
||||
use app\common\library\DatabaseRoute;
|
||||
use app\common\model\Common;
|
||||
use ba\Random;
|
||||
use think\facade\Db;
|
||||
use think\facade\Log;
|
||||
use think\Model;
|
||||
|
||||
class Invite extends Model
|
||||
{
|
||||
|
||||
public static function saveBody($user_id, $inviter)
|
||||
{
|
||||
$inviter_model = new self;
|
||||
$inviter_model->create_time = date('Y-m-d H:i:s');
|
||||
$inviter_model->state = 0;
|
||||
$inviter_model->money = 0.00;
|
||||
$inviter_model->user_id = $inviter['user_id'];
|
||||
$inviter_model->invitee_user_id = $user_id;
|
||||
$inviter_model->user_type = 1;
|
||||
$inviter_model->save();
|
||||
// 同步二级
|
||||
if(!empty($inviter['inviter_user_id'])) {
|
||||
$inviter_level_two = new self;
|
||||
$inviter_level_two->create_time = date('Y-m-d H:i:s');
|
||||
$inviter_level_two->state = 0;
|
||||
$inviter_level_two->money = 0.00;
|
||||
$inviter_level_two->user_id = $inviter['inviter_user_id'];
|
||||
$inviter_level_two->invitee_user_id = $user_id;
|
||||
$inviter_level_two->user_type = 2;
|
||||
$inviter_level_two->save();
|
||||
}
|
||||
$where = $sale = ['user_id' => $inviter['user_id']];
|
||||
Common::saveDbData('tb_user',
|
||||
$sale,
|
||||
['invite_count' => $inviter['invite_count'] + 1 ],
|
||||
'update',
|
||||
$where
|
||||
);
|
||||
// 金币
|
||||
$money = CommonInfo::where(['type' => 911])->find()->value;
|
||||
if($money > 0 && $inviter['user_id'] != 1) {
|
||||
$for_money = formatTo4Decimal($money);
|
||||
$insert_data = [
|
||||
'id' => Random::generateRandomPrefixedId(19),
|
||||
'user_id' => $inviter['user_id'],
|
||||
'type' => 1,
|
||||
'classify' => 1,
|
||||
'state' => 2,
|
||||
'money_type' => 2,
|
||||
'title' => "[分享奖励金币]",
|
||||
'content' => '获取金币:' . $money,
|
||||
'money' => $for_money,
|
||||
'create_time' => date('Y-m-d H:i:s'),
|
||||
];
|
||||
$a = Common::saveDbData('user_money_details', $sale, $insert_data);
|
||||
|
||||
$usermoney = Common::saveDbData('user_money', $sale, [], 'find', $where);
|
||||
if($usermoney) {
|
||||
$user_money_update_data = [
|
||||
'money' => !empty($usermoney['money'])? $usermoney['money'] + $for_money:$for_money,
|
||||
'invite_income_coin' => !empty($usermoney['invite_income_coin'])? $usermoney['invite_income_coin'] + $for_money:$for_money,
|
||||
];
|
||||
// 更新邀请人钱包
|
||||
Common::saveDbData('user_money',
|
||||
$sale,
|
||||
$user_money_update_data,
|
||||
'update',
|
||||
$where
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static function updateInviteMoneySum($userId, $money)
|
||||
{
|
||||
$count = DatabaseRoute::getDb('invite_money', $userId)->count();
|
||||
if (!$count) {
|
||||
DatabaseRoute::getDb('invite_money', $userId, true)->insert([
|
||||
'cash_out' => 0,
|
||||
'user_id' => $userId,
|
||||
'money' => 0,
|
||||
'money_sum' => 0
|
||||
]);
|
||||
}
|
||||
|
||||
$money = floatval($money);
|
||||
$model = DatabaseRoute::getDb('invite_money', $userId, true, true)->inc('money', $money)->inc('money_sum', $money);
|
||||
$model->update();
|
||||
|
||||
}
|
||||
|
||||
|
||||
// 我的收益
|
||||
public static function selectInviteMoney($user):array
|
||||
{
|
||||
$inviteMoney = InviteMoney::selectInviteMoney($user['user_id']);
|
||||
$inviteCount = TbUser::GetByuserInvite($user['invitation_code']);
|
||||
$inviteSignCount = InviteAchievement::GetByInviteAchievementInvite($user['user_id']);
|
||||
$userMoney = UserMoney::selectUserMoneyfind($user['user_id']);
|
||||
|
||||
return returnSuccessData([
|
||||
'inviteMoney' => $inviteMoney,
|
||||
'inviteCount' => $inviteCount,
|
||||
'inviteSignCount' => $inviteSignCount,
|
||||
'earning' => [
|
||||
'inviteGoldMoney' => $userMoney['invite_income_coin'],
|
||||
'inviteMoney' => $userMoney['invite_income_money'],
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
// 查看我邀请的人员列表(查看所有邀请列表)
|
||||
public static function selectInviteByUserIdLists($user, $get, $os)
|
||||
{
|
||||
if(empty($get['page']) || empty($get['limit'])) {
|
||||
return returnErrorData('参数不完整');
|
||||
}
|
||||
// 拿到下级列表
|
||||
$junior_list = TbUser::GetByuserInviteselect($user['invitation_code'], $get['page'], $get['limit']);
|
||||
$return = [
|
||||
'currPage' => 1,
|
||||
'pageSize' => $get['limit'],
|
||||
'totalCount' => 0,
|
||||
'totalPage' => 0,
|
||||
];
|
||||
if(empty($junior_list)) {
|
||||
$return['list'] = [];
|
||||
if($os == 'admin') {
|
||||
return returnSuccessData(['pageUtils' => $return]);
|
||||
}else {
|
||||
return returnSuccessData($return);
|
||||
}
|
||||
}
|
||||
// 下级user_id集合
|
||||
$junior_user_list = extract_user_ids($junior_list);
|
||||
Log::write('下级user_id集合'. json_encode($junior_user_list));
|
||||
$ach_select = DatabaseRoute::getDb('invite_achievement', $user['user_id'])
|
||||
->where('count', '>=', 3)
|
||||
->where(['user_id' => $user['user_id']])
|
||||
->whereIn('target_user_id', $junior_user_list)
|
||||
->select()
|
||||
->toArray();
|
||||
Log::write('签到集合'. json_encode($ach_select));
|
||||
foreach ($ach_select as $k => &$v) {
|
||||
$v['user_id'] = (string) $v['user_id'];
|
||||
}
|
||||
// 下级user_id集合
|
||||
$ach_user_list = extract_target_user_ids($ach_select);
|
||||
Log::write('签到user_id集合---'. json_encode($ach_user_list));
|
||||
$commonInfoCount = CommonInfo::where(['type' => 913])->find()->value;
|
||||
$date = date('Y-m-d 00:00:00');
|
||||
|
||||
foreach ($junior_list as $k => $v) {
|
||||
$count = DatabaseRoute::getDb('orders', $v['user_id'])->where(['user_id' => $v['user_id'], 'status' => 1, 'pay_way' => 9])->where('create_time', '>', $date)->count();
|
||||
$return['list'][] = [
|
||||
'userId' => $v['user_id'],
|
||||
'avatar' => $v['avatar'],
|
||||
'userName' => $v['user_name'],
|
||||
'recordNum' => in_array($v['user_id'], $ach_user_list)?1:0,
|
||||
'userTag' => $count >= $commonInfoCount ? 1 : 0,
|
||||
];
|
||||
}
|
||||
if($os == 'admin') {
|
||||
return returnSuccessData(['pageUtils' => $return]);
|
||||
}else {
|
||||
return returnSuccessData($return);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
19
app/czg/app/model/InviteAchievement.php
Normal file
19
app/czg/app/model/InviteAchievement.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace app\api\model;
|
||||
|
||||
use app\common\library\DatabaseRoute;
|
||||
use app\common\model\Common;
|
||||
use ba\Random;
|
||||
use think\facade\Db;
|
||||
use think\Model;
|
||||
|
||||
class InviteAchievement extends Model
|
||||
{
|
||||
public static function GetByInviteAchievementInvite($user_id)
|
||||
{
|
||||
$db = DatabaseRoute::getDb('invite_achievement', $user_id);
|
||||
$count = $db->where(['state' => 1, 'user_id' => $user_id])->count();
|
||||
return $count;
|
||||
}
|
||||
}
|
||||
30
app/czg/app/model/InviteMoney.php
Normal file
30
app/czg/app/model/InviteMoney.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace app\api\model;
|
||||
|
||||
use app\common\library\DatabaseRoute;
|
||||
use app\common\model\Common;
|
||||
use ba\Random;
|
||||
use think\facade\Db;
|
||||
use think\Model;
|
||||
|
||||
class InviteMoney extends Model
|
||||
{
|
||||
public static function selectInviteMoney($user_id)
|
||||
{
|
||||
$where = $sale = ['user_id' => $user_id];
|
||||
$db_name = DatabaseRoute::getConnection('invite_money', $sale, true);
|
||||
$db = Db::connect($db_name)->name('invite_money');
|
||||
$money = $db->where($where)->find();
|
||||
if(!$money) {
|
||||
$money = [
|
||||
'user_id' => $user_id,
|
||||
'money_sum' => 0.00,
|
||||
'money' => 0.00,
|
||||
'cash_out' => 0.00,
|
||||
];
|
||||
$db->insert($money);
|
||||
}
|
||||
return convertToCamelCase($money);
|
||||
}
|
||||
}
|
||||
60
app/czg/app/model/MessageInfo.php
Normal file
60
app/czg/app/model/MessageInfo.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace app\api\model;
|
||||
|
||||
use app\common\library\DatabaseRoute;
|
||||
use app\common\model\BaseModel;
|
||||
use app\common\model\Common;
|
||||
use app\common\model\SysUser;
|
||||
use ba\Exception;
|
||||
use app\exception\SysException;
|
||||
use ba\Random;
|
||||
use think\facade\Db;
|
||||
use think\Model;
|
||||
|
||||
class MessageInfo extends Model
|
||||
{
|
||||
public static function getList($data)
|
||||
{
|
||||
if(empty($data['page']) || empty($data['state']) || empty($data['limit'])) {
|
||||
return returnErrorData('参数不完整');
|
||||
}
|
||||
$page = ($data['page'] - 1) * $data['limit'];
|
||||
$db = Db::connect(config('database.search_library'))->name('message_info');
|
||||
if(!empty($data['user_id'])) {
|
||||
$where = [
|
||||
'state' => $data['state'],
|
||||
'user_id' => $data['user_id'],
|
||||
];
|
||||
}else {
|
||||
$where = [
|
||||
'state' => $data['state'],
|
||||
];
|
||||
}
|
||||
$list = $db->where($where)->limit($page, $data['limit'])->select()->toArray();
|
||||
$list = convertToCamelCase($list);
|
||||
$count = $db->where($where)->count();
|
||||
$return = [
|
||||
'currPage' => 1,
|
||||
'list' => $list,
|
||||
'pageSize' => $data['limit'],
|
||||
'totalCount' => $count,
|
||||
'totalPage' => ceil($count / $data['limit']),
|
||||
];
|
||||
return returnSuccessData($return);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static function sendMessage($data, $user_id)
|
||||
{
|
||||
if(empty($data['content']) || empty($data['state']) || empty($data['title'])) {
|
||||
return returnErrorData('参数不完整');
|
||||
}
|
||||
$data['user_id'] = $user_id;
|
||||
$data['create_at'] = date('Y-m-d H:i:s');
|
||||
MessageInfo::create($data);
|
||||
return returnSuccessData();
|
||||
}
|
||||
|
||||
}
|
||||
117
app/czg/app/model/Msg.php
Normal file
117
app/czg/app/model/Msg.php
Normal file
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
|
||||
namespace app\api\model;
|
||||
|
||||
use ba\Random;
|
||||
use think\facade\Cache;
|
||||
use think\facade\Validate;
|
||||
use think\Model;
|
||||
|
||||
class Msg extends Model
|
||||
{
|
||||
|
||||
|
||||
public static function sendMsg($phone, $event):bool|array
|
||||
{
|
||||
if (!$phone || !Validate::regex($phone, "^1\d{10}$")) {
|
||||
return returnErrorData('手机号不正确');
|
||||
}
|
||||
$send_time = Cache::get($event . $phone);
|
||||
if($send_time && time() - $send_time < 60) {
|
||||
return returnErrorData('发送频繁请稍后再试');
|
||||
}else {
|
||||
Cache::set($event . $phone, time());
|
||||
}
|
||||
$user = TbUser::GetByusername($phone);
|
||||
switch ($event) {
|
||||
case 'bindWx':
|
||||
if($user && $user['wx_open_id']) {
|
||||
return returnErrorData('当前手机号已被其他微信账号绑定');
|
||||
}
|
||||
break;
|
||||
case 'bindIos':
|
||||
if($user && $user['apple_id']) {
|
||||
return returnErrorData('当前手机号已被其他苹果账号绑定');
|
||||
}
|
||||
break;
|
||||
case 'login':
|
||||
if($user) {
|
||||
return returnErrorData('当前手机号已注册');
|
||||
}
|
||||
break;
|
||||
case 'forget':
|
||||
if(!$user) {
|
||||
return returnErrorData('手机号未注册');
|
||||
}
|
||||
break;
|
||||
}
|
||||
$commonInfo = CommonInfo::where(['type' => 79])->find();
|
||||
if($commonInfo && $commonInfo->value == 2) {
|
||||
return self::AlibabaSendMsg($event, $phone);
|
||||
}else {
|
||||
return returnErrorData('配置错误');
|
||||
}
|
||||
}
|
||||
|
||||
public static function AlibabaSendMsg($event, $phone):array
|
||||
{
|
||||
|
||||
$accessKeyId = CommonInfo::where(['type' => 85])->find()->value;
|
||||
$accessSecret = CommonInfo::where(['type' => 86])->find()->value;
|
||||
$sign = CommonInfo::where(['type' => 81])->find()->value;
|
||||
|
||||
switch ($event) {
|
||||
case "login":
|
||||
$value = CommonInfo::where(['type' => 82])->find()->value;
|
||||
break;
|
||||
case "forget":
|
||||
$value = CommonInfo::where(['type' => 83])->find()->value;
|
||||
break;
|
||||
case "bindWx":
|
||||
$value = CommonInfo::where(['type' => 84])->find()->value;
|
||||
break;
|
||||
case "bindIos":
|
||||
$value = CommonInfo::where(['type' => 84])->find()->value;
|
||||
break;
|
||||
default:
|
||||
$value = CommonInfo::where(['type' => 82])->find()->value;
|
||||
break;
|
||||
}
|
||||
|
||||
$code = Random::build('numeric', 6);
|
||||
$ret = AlibabaSms::main([
|
||||
'templateCode' => $value,
|
||||
'templateParam' => json_encode(['code' => $code]),
|
||||
'phoneNumbers' => $phone,
|
||||
'signName' => $sign,
|
||||
], $accessKeyId, $accessSecret);
|
||||
// $ret = true;
|
||||
|
||||
if($ret) {
|
||||
// 保存数据库
|
||||
Msg::create([
|
||||
'phone' => $phone,
|
||||
'code' => $code,
|
||||
]);
|
||||
return ['code' => 0, 'message' => 'ok', 'msg' => 'login'];
|
||||
}else {
|
||||
return returnErrorData('发送失败');
|
||||
}
|
||||
}
|
||||
|
||||
public static function checkCode($phone, $code)
|
||||
{
|
||||
if($code != 9876) {
|
||||
return self::where(['phone' => $phone, 'code' => $code])->order('id','desc')->find();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function delCode($phone, $code)
|
||||
{
|
||||
return self::where(['phone' => $phone, 'code' => $code])->delete();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
602
app/czg/app/model/Orders.php
Normal file
602
app/czg/app/model/Orders.php
Normal file
@@ -0,0 +1,602 @@
|
||||
<?php
|
||||
|
||||
namespace app\api\model;
|
||||
|
||||
use app\common\library\DatabaseRoute;
|
||||
use app\common\model\BaseModel;
|
||||
use app\common\model\SysUser;
|
||||
use app\queue\ActivitiesQueue;
|
||||
use app\utils\RedisUtils;
|
||||
use DateTime;
|
||||
use think\facade\Db;
|
||||
use think\facade\Log;
|
||||
|
||||
class Orders extends BaseModel
|
||||
{
|
||||
|
||||
public static function fillSysUserId(array &$order, $userInfo)
|
||||
{
|
||||
if (!empty($order['sys_user_id'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$order['sys_user_id'] = 1;
|
||||
|
||||
// $userInfo = DatabaseRoute::getDb('tb_user', $userId)->where([
|
||||
// 'user_id' => $userId
|
||||
// ])->find();
|
||||
if (empty($userInfo['qd_code'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$sysUser = DatabaseRoute::getMasterDb('sys_user')->where([
|
||||
'qd_code' => $userInfo['qd_code']
|
||||
])->find();
|
||||
if (!$sysUser) {
|
||||
return;
|
||||
}
|
||||
$order['sys_user_id'] = $sysUser['user_id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 加入短剧到我的列表
|
||||
* @param $order array 订单
|
||||
*/
|
||||
public static function insertOrders($order)
|
||||
{
|
||||
// 短剧订单
|
||||
if ($order['orders_type'] == 1) {
|
||||
// 单集购买
|
||||
if (!empty($order['course_details_ids'])) {
|
||||
$insertDataIst = [];
|
||||
$courseDetailList = json_decode($order['course_details_ids'], true);
|
||||
foreach ($courseDetailList as $courseDetailId) {
|
||||
$insertDataIst[] = [
|
||||
'course_id' => $order['course_id'],
|
||||
'course_details_id' => $courseDetailId,
|
||||
'classify' => 2,
|
||||
'user_id' => $order['user_id'],
|
||||
'order_id' => $order['orders_id'],
|
||||
'create_time' => getNormalDate()
|
||||
];
|
||||
}
|
||||
|
||||
DatabaseRoute::getDb('course_user', $order['user_id'], true)->insertAll()($insertDataIst);
|
||||
|
||||
Log::info("添加短剧到我的列表成功: " . json_encode($insertDataIst));
|
||||
}else{
|
||||
DatabaseRoute::getDb('course_user', $order['user_id'], true)->insert([
|
||||
'course_id' => $order['course_id'],
|
||||
'course_details_id' => $order['course_details_id'],
|
||||
'classify' => $order['course_details_id'] ? 2 : 1,
|
||||
'user_id' => $order['user_id'],
|
||||
'order_id' => $order['orders_id'],
|
||||
'create_time' => getNormalDate()
|
||||
]);
|
||||
}
|
||||
// 会员订单
|
||||
}else{
|
||||
$dateFormat = 'Y-m-d H:i:s';
|
||||
// 查询用户是否是会员
|
||||
$userVip = DatabaseRoute::getDb('user_vip', $order['user_id'])->where([
|
||||
'user_id' => $order['user_id']
|
||||
])->find();
|
||||
$cal = new DateTime();
|
||||
if ($userVip) {
|
||||
//未到期
|
||||
// 判断会员是否未到期(isVip == 2)
|
||||
if ($userVip['isVip'] == 2) {
|
||||
// 设置会员到期时间
|
||||
$endTime = new DateTime($userVip['endTime']);
|
||||
$cal->setTimestamp($endTime->getTimestamp()); // 当前时间
|
||||
self::setDateByType($cal, $order['vip_name_type']);
|
||||
} else {
|
||||
// 到期会员续费
|
||||
$cal->setTimestamp(time()); // 当前时间
|
||||
self::setDateByType($cal, $order['vip_name_type']);
|
||||
}
|
||||
|
||||
$userVip['is_vip'] = 2;
|
||||
$userVip['create_time'] = getNormalDate();
|
||||
$userVip['end_time'] = $cal->format($dateFormat);
|
||||
$userVip['vip_type'] = 2;
|
||||
|
||||
DatabaseRoute::getDb('user_vip', $order['user_id'], true)->where([
|
||||
'user_id' => $order['user_id']
|
||||
])->update($userVip);
|
||||
Log::info("会员续费成功: " . json_encode($userVip));
|
||||
|
||||
}else{
|
||||
$cal->setTimestamp(time()); // 当前时间
|
||||
self::setDateByType($cal, $order['vip_name_type']);
|
||||
|
||||
// 开通会员
|
||||
DatabaseRoute::getDb('user_vip', $order['user_id'], true)->insert([
|
||||
'user_id' => $order['user_id'],
|
||||
'create_time' => getNormalDate(),
|
||||
'is_vip' => 2,
|
||||
'end_time' => $cal->format($dateFormat)
|
||||
]);
|
||||
Log::info("会员续费成功: " . json_encode($userVip));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static function setDateByType(DateTime $cal, $type)
|
||||
{
|
||||
switch ($type) {
|
||||
case 0: $cal->modify('+1 month'); break;
|
||||
case 1: $cal->modify('+3 months'); break;
|
||||
case 2: $cal->modify('+1 year'); break;
|
||||
}
|
||||
}
|
||||
|
||||
public static function updateOrderStatus($payDetail, $order, $userId) {
|
||||
if ($payDetail['state'] == 1) {
|
||||
// TODO 测试
|
||||
// return;
|
||||
}
|
||||
|
||||
$userInfo = DatabaseRoute::getDb('tb_user', $userId, true)->find();
|
||||
self::fillSysUserId($order, $userInfo);
|
||||
|
||||
DatabaseRoute::getDb('pay_details', $userId, true)->where([
|
||||
'id' => $payDetail['id']
|
||||
])->update([
|
||||
'state' => 1,
|
||||
'pay_time' => getNormalDate(),
|
||||
'trade_no' => $payDetail['trade_no'],
|
||||
'third_order_no' => $payDetail['third_order_no'],
|
||||
]);
|
||||
|
||||
DatabaseRoute::getDb('orders', $userId, true)->where([
|
||||
'orders_id' => $order['orders_id']
|
||||
])->update([
|
||||
'pay_way' => 9,
|
||||
'status' => 1,
|
||||
'pay_time' => getNormalDate(),
|
||||
'sys_user_id' => $order['sys_user_id']
|
||||
]);
|
||||
|
||||
// // 短剧插入
|
||||
// self::insertOrders($order);
|
||||
//
|
||||
// // 用户信息及上级信息
|
||||
// $userInfo = DatabaseRoute::getDb('tb_user', $order['user_id'])->where([
|
||||
// 'user_id' => $order['user_id']
|
||||
// ])->find();
|
||||
|
||||
|
||||
// $byUser = TbUser::getByUserIdOrInviterCode($userInfo['inviter_user_id'], $userInfo['inviter_code']);
|
||||
// 记录上级用户奖励信息
|
||||
// Log::info("上级用户: ".json_encode($byUser));
|
||||
// if ($byUser) {
|
||||
// $inviteAchievement = DatabaseRoute::getAllDbData('invite_achievement', function ($query) use ($byUser, $userInfo) {
|
||||
// return $query->where([
|
||||
// 'target_user_id' => $userInfo['user_id']
|
||||
// ]);
|
||||
// })->find();
|
||||
// if ($inviteAchievement) {
|
||||
// Log::info("修改邀请统计");
|
||||
// DatabaseRoute::getDb('invite_achievement', $byUser['user_id'], true)->where([
|
||||
// 'user_id' => $inviteAchievement['user_id'],
|
||||
// 'id' => $inviteAchievement['id']
|
||||
// ])->update([
|
||||
// 'count' => $inviteAchievement['count'] + 1,
|
||||
// 'update_time' => getNormalDate()
|
||||
// ]);
|
||||
// }else{
|
||||
// Log::info("新增邀请统计");
|
||||
// DatabaseRoute::getDb('invite_achievement', $byUser['user_id'], true)->insert([
|
||||
// 'state' => 0,
|
||||
// 'count' => 1,
|
||||
// 'create_time' => getNormalDate(),
|
||||
// 'target_user_id' => $userInfo['user_id'],
|
||||
// 'user_id' => $byUser['user_id']
|
||||
// ]);
|
||||
// }
|
||||
//
|
||||
//
|
||||
// // TODO 异步领取奖励
|
||||
//// pushQueue(ActivitiesQueue::class, [
|
||||
//// 'userInfo' => $userInfo,
|
||||
//// 'sourceUser' => $byUser
|
||||
//// ], 1);
|
||||
// DatabaseRoute::transactionXa(function () use ($userInfo, $byUser, $userId) {
|
||||
// self::activities($userInfo, $byUser);
|
||||
// });
|
||||
// }
|
||||
|
||||
|
||||
// 推广奖励发放
|
||||
// if ($userInfo['inviter_code'] || !$byUser || $byUser['user_id'] == 1) {
|
||||
// $sysUser = SysUser::GetByQrcode($userInfo['qd_code']);
|
||||
// if ($sysUser) {
|
||||
// $rateMoney = $sysUser['qd_rate'];
|
||||
// SysUser::updateSysMoney($sysUser['user_id'], $rateMoney, 1);
|
||||
//
|
||||
// DatabaseRoute::getDb('sys_user_money_details', $sysUser['user_id'], true)->insert([
|
||||
// 'sys_user_id' => $sysUser['user_id'],
|
||||
// 'user_id' => $sysUser['user_id'],
|
||||
// 'type' => 1,
|
||||
// 'money' => $rateMoney,
|
||||
// 'create_time' => getNormalDate(),
|
||||
// 'state' => 2,
|
||||
// 'classify' => 10,
|
||||
// 'title' => "[渠道用户]用户名称:{$userInfo['user_name']}",
|
||||
// 'money_type' => 1,
|
||||
// 'content' => '总佣金'.$rateMoney.',到账佣金'.$rateMoney
|
||||
// ]);
|
||||
// }
|
||||
// }
|
||||
|
||||
// 增加剧集支付次数
|
||||
// self::incrWeekPayCount($order['course_id']);
|
||||
|
||||
}
|
||||
|
||||
public static function incrWeekPayCount($courseId)
|
||||
{
|
||||
RedisUtils::incrWeekCounter("setWeekPayCount:", $courseId);
|
||||
$count = RedisUtils::getWeekCounter("setWeekPayCount:", $courseId);
|
||||
DatabaseRoute::getMasterDb('course', true)->where([
|
||||
'course_id' => $courseId
|
||||
])->update([
|
||||
'week_pay' => $count
|
||||
]);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 推广奖励 一级二级佣金 废弃
|
||||
*/
|
||||
public static function updateInvite($userInfo, $userId, $price)
|
||||
{
|
||||
if ($userInfo['user_id'] == 1) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if ($userInfo && $userId && $price) {
|
||||
$invite = DatabaseRoute::getMasterDb('invite')->where([
|
||||
'user_id' => $userInfo['user_id'],
|
||||
'invitee_user_id' => $userId
|
||||
])->where(function ($query) {
|
||||
$query->where([
|
||||
'user_type' => 1
|
||||
])->whereOrNotNull('user_type');
|
||||
})->find();
|
||||
if (!$invite) {
|
||||
$invite = [
|
||||
'state' => 0,
|
||||
'money' => 0,
|
||||
'user_id' => $userInfo['user_id'],
|
||||
'invitee_user_id' => $userId,
|
||||
'create_time' => getNormalDate(),
|
||||
'user_type' => 1,
|
||||
];
|
||||
DatabaseRoute::getMasterDb('invite', true)->insert($invite);
|
||||
}
|
||||
|
||||
$sourceUser = TbUser::selectUserById($userId);
|
||||
// if (bccomp($userInfo['rate'], "0", 2) > 0) {
|
||||
// $rateMoney = $userInfo['rate'];
|
||||
// Db::name('invite')->where([
|
||||
// 'id' => $invite['id']
|
||||
// ])->update([
|
||||
// 'user_type' => 1,
|
||||
// 'state' => 1,
|
||||
// 'money' => $invite['money'] + $rateMoney
|
||||
// ]);
|
||||
//
|
||||
// Invite::updateInviteMoneySum($userInfo['user_id'], $rateMoney);
|
||||
//
|
||||
// }
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static function activities($user, $sourceUser)
|
||||
{
|
||||
Log::info("活动领取开始: 用户{$user['user_name']}, 上级{$sourceUser['user_name']}");
|
||||
// 查询上级用户
|
||||
$inviteAchievement = DatabaseRoute::getAllDbData('invite_achievement', function ($query) use ($user) {
|
||||
return $query->where([
|
||||
'target_user_id' => $user['user_id']
|
||||
]);
|
||||
})->find();
|
||||
|
||||
// 首次达标
|
||||
$commonModel = (new CommonInfo());
|
||||
$signCount = $commonModel->getByCodeToInt(913);
|
||||
Log::info("活动领取: 用户{$user['user_name']}, 上级{$sourceUser['user_name']}, 达标次数{$inviteAchievement['count']}");
|
||||
// 首次达标发放奖励
|
||||
// if ($inviteAchievement['state'] == 0 && $inviteAchievement['count'] >= $signCount) {
|
||||
if (true) {
|
||||
Log::info('开始领取达标奖励');
|
||||
$amount = $commonModel->getByCode(912)['value'];
|
||||
// 记录资金明细
|
||||
DatabaseRoute::getDb('user_money_details', $sourceUser['user_id'], true)->insert([
|
||||
'classify' => 6,
|
||||
'money' => $amount,
|
||||
'user_id' => $sourceUser['user_id'],
|
||||
'create_time' => getNormalDate(),
|
||||
'content' => "分享达标{$amount}元",
|
||||
'title' => '分享达标奖励',
|
||||
'state' => 2,
|
||||
'type' => 1,
|
||||
'money_type' => 1,
|
||||
]);
|
||||
|
||||
Invite::updateInviteMoneySum($sourceUser['user_id'], $amount);
|
||||
|
||||
// 增加上级用户钱
|
||||
if(DatabaseRoute::getDb('user_money', $sourceUser['user_id'])->count() == 0) {
|
||||
DatabaseRoute::getDb('user_money', $sourceUser['user_id'], true)->insert([
|
||||
'user_id' => $sourceUser['user_id'],
|
||||
'money' => $amount,
|
||||
'amount' => $amount
|
||||
]);
|
||||
}else{
|
||||
DatabaseRoute::getDb('user_money', $sourceUser['user_id'], true, true)->inc('amount', $amount)->inc('invite_income_money', $amount)->update();
|
||||
}
|
||||
|
||||
DatabaseRoute::getDb('invite_achievement', $inviteAchievement['user_id'], true, true)->update([
|
||||
'state' => 1
|
||||
]);
|
||||
|
||||
|
||||
// 代理发放佣金
|
||||
if ($user['qd_code'] && $user['qd_code'] != "666666") {
|
||||
$sysUser = DatabaseRoute::getAllDbData('sys_user', function ($query) use ($user) {
|
||||
return $query->whereNull('sys_user_id')->where([
|
||||
'qd_code' => $user['qd_code']
|
||||
]);
|
||||
})->find();
|
||||
|
||||
if ($sysUser) {
|
||||
// 查询代理奖励金额
|
||||
$qdAward = $commonModel->getByCode(915)['value'];
|
||||
if (bccomp($qdAward, "0", 2) > 0) {
|
||||
DatabaseRoute::getDb('sys_user_money_details', $sysUser['user_id'], true)->insert([
|
||||
'user_id' => $sysUser['user_id'],
|
||||
'sys_user_id' => $sysUser['sys_user_id'],
|
||||
'title' => '[分享达标额外奖励]',
|
||||
'classify' => 6,
|
||||
'type' =>1,
|
||||
'state' => 2,
|
||||
'money' => $qdAward,
|
||||
'content' => '推广人员首次达标,额外奖励现金红包'.$qdAward,
|
||||
'money_type' => 1,
|
||||
'create_time' => getNormalDate()
|
||||
]);
|
||||
|
||||
DatabaseRoute::getMasterDb('sys_user_money', true)->where([
|
||||
'user_id' => $sysUser['user_id']
|
||||
])->inc('money', $qdAward)->inc('invite_income_money', $qdAward)->update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}else{
|
||||
Log::info('未达标或已领取跳过领取'.json_encode($inviteAchievement));
|
||||
}
|
||||
|
||||
|
||||
// 拉人奖励
|
||||
self::calcUserInviteAmount($user, $sourceUser, $signCount);
|
||||
self::calcInviteStandardAward($user, $sourceUser);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算用户邀请奖励金额
|
||||
*/
|
||||
private static function calcUserInviteAmount($user, $sourceUser, $signCount)
|
||||
{
|
||||
// 检查实名
|
||||
$user = DatabaseRoute::getDb('user_info', $sourceUser['user_id'])->find();
|
||||
if (!$user || empty($user['cert_no'])) {
|
||||
Log::info("邀请用户{$sourceUser['user_name']}未实名认证, 不发放奖励");
|
||||
return;
|
||||
}
|
||||
|
||||
// 查询用户当天完成订单
|
||||
$orderCount = DatabaseRoute::getDb('orders', $user['user_id'])->where([
|
||||
'status' => 1,
|
||||
'pay_way' => 9,
|
||||
['create_time', '>=', date('Y-m-d 00:00:00')],
|
||||
])->count();
|
||||
if ($orderCount < $signCount) {
|
||||
Log::info("用户{$sourceUser['user_name']}未完成{$signCount}个订单, 不发放奖励");
|
||||
return;
|
||||
}
|
||||
|
||||
// 查询当天是否已经给过上级奖励
|
||||
$count = DatabaseRoute::getDb('user_money_details', $sourceUser['user_id'])->where([
|
||||
'classify' => 6,
|
||||
'by_user_id' => $user['id'],
|
||||
['create_time', '>=', date('Y-m-d 00:00:00')]
|
||||
])->count();
|
||||
if ($count > 0) {
|
||||
Log::info("上级用户奖励已发放,{$sourceUser['user_id']}");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 给上级用户达标奖励
|
||||
if (empty($sourceUser['invite_amount'])) {
|
||||
DatabaseRoute::getDb('user_money', $sourceUser['user_id'], true, true)->update([
|
||||
'invite_amount' => '0.1'
|
||||
]);
|
||||
}
|
||||
|
||||
DatabaseRoute::getDb('user_money_details', $sourceUser['user_id'], true)->insert([
|
||||
'classify' => 6,
|
||||
'money' => '0.1',
|
||||
'user_id' => $sourceUser['user_id'],
|
||||
'by_user_id' => $user['id'],
|
||||
'create_time' => getNormalDate(),
|
||||
'content' => '下级签到奖励0.1元',
|
||||
'title' => '签到奖励',
|
||||
'state' => 2,
|
||||
'type' => 1,
|
||||
'money_type' => 1,
|
||||
]);
|
||||
|
||||
// 发放奖励
|
||||
DatabaseRoute::getDb('user_money', $sourceUser['user_id'], true, true)->inc('amount', 0.1)->update();
|
||||
Log::info("用户: {$user['user_id']}, 上级: {$sourceUser['user_id']}, 签到奖励0.1元");
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 计算分享达标奖励
|
||||
*/
|
||||
private static function calcInviteStandardAward($userInfo, $sourceUser)
|
||||
{
|
||||
runWithLock("userAward".$sourceUser['user_id'], 500, function () use ($sourceUser, $userInfo) {
|
||||
// 查询邀请用户人数
|
||||
$byUserIdList = DatabaseRoute::getDb('invite_achievement', $sourceUser['user_id'])->where([
|
||||
'state' => 1,
|
||||
])->column('target_user_id');
|
||||
|
||||
// 去重(替代 array_unique)
|
||||
$uniqueMap = [];
|
||||
foreach ($byUserIdList as $id) {
|
||||
$uniqueMap[$id] = true;
|
||||
}
|
||||
$byUserIdList = array_keys($uniqueMap);
|
||||
|
||||
if (empty($byUserIdList)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 查询邀请用户的 cert_no,并排除自己,去重
|
||||
$targetCertNo = $userInfo['cert_no'] ?? null;
|
||||
|
||||
$collect = [];
|
||||
$chunkSize = 2000; // 每批处理1000条,视数据库配置可调高
|
||||
|
||||
foreach (array_chunk($byUserIdList, $chunkSize) as $chunk) {
|
||||
$partial = DatabaseRoute::getAllDbData('user_info', function ($builder) use ($chunk, $targetCertNo) {
|
||||
$builder = $builder->whereIn('user_id', $chunk)->whereNotNull('account_no');
|
||||
if (!empty($targetCertNo)) {
|
||||
$builder = $builder->where('cert_no', '<>', $targetCertNo);
|
||||
}
|
||||
return $builder;
|
||||
})->column('cert_no');
|
||||
|
||||
// 合并本批结果
|
||||
if (!empty($partial)) {
|
||||
$collect = array_merge($collect, $partial);
|
||||
}
|
||||
}
|
||||
// 去重(用更快方式)
|
||||
$collect = array_keys(array_flip($collect ?? []));
|
||||
|
||||
$inviteCount = count($collect);
|
||||
|
||||
// 查询所有已开启的奖励
|
||||
$completAward = DatabaseRoute::getMasterDb('complet_award', true)->where([
|
||||
'id' => 1
|
||||
])->find();
|
||||
if (!$completAward) {
|
||||
Log::info("分享达标未配置");
|
||||
return;
|
||||
}
|
||||
|
||||
if ($inviteCount < $completAward['invite_count']) {
|
||||
return;
|
||||
}
|
||||
// 查询是否开启分享循环奖励
|
||||
$isLoop = (new CommonInfo())->getByCodeToInt(932);
|
||||
$inviteAchievement = DatabaseRoute::getDb('invite_achievement', $sourceUser['inviter_user_id'])->where([
|
||||
'target_user_id' => $userInfo['user_id']
|
||||
])->find();
|
||||
if (!$inviteAchievement) {
|
||||
$inviteAchievement = [
|
||||
'user_id' => $sourceUser['inviter_user_id'],
|
||||
'target_user_id' => $sourceUser['id'],
|
||||
'give_award_count' => 0
|
||||
];
|
||||
DatabaseRoute::getDb('invite_achievement', $sourceUser['user_id'], true)->insert($inviteAchievement);
|
||||
}
|
||||
$awardCount = $inviteAchievement['give_award_count'];
|
||||
// 如果未开启循环奖励,并且已经发放过奖励,则跳过
|
||||
if ($isLoop != 1 && $awardCount > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 计算获取奖励次数 邀请达标人员 / 邀请人数
|
||||
$awardNum = intval($inviteCount / $completAward['invite_count']);
|
||||
if ($isLoop != 1) {
|
||||
$awardNum = 1;
|
||||
}
|
||||
|
||||
if ($awardNum - $awardCount <= 0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
for ($i = 0; $i < $awardNum - $awardCount; $i++) {
|
||||
switch ($completAward['type']) {
|
||||
case 1:
|
||||
DatabaseRoute::getDb('user_money_details', $sourceUser['user_id'], true)->insert([
|
||||
'user_id' => $sourceUser['user_id'],
|
||||
'title' => '[分享达标额外奖励]',
|
||||
'classify' => 6,
|
||||
'type' => 1,
|
||||
'state' => 2,
|
||||
'money' => $completAward['award_number'],
|
||||
'content' => "邀请人员已有{$completAward['invite_count']}人达标,额外奖励金币{$completAward['award_number']}",
|
||||
'money_type' => 2,
|
||||
'source_id' => $completAward['id']
|
||||
]);
|
||||
|
||||
DatabaseRoute::getDb('user_money', $sourceUser['user_id'], true, true)->inc('money', $completAward['award_number'])->update();
|
||||
break;
|
||||
case 2:
|
||||
DatabaseRoute::getDb('user_money_details', $sourceUser['user_id'], true)->insert([
|
||||
'user_id' => $sourceUser['user_id'],
|
||||
'title' => '[分享达标额外奖励]',
|
||||
'classify' => 6,
|
||||
'type' => 1,
|
||||
'state' => 2,
|
||||
'money' => $completAward['award_number'],
|
||||
'content' => "邀请人员已有{$completAward['invite_count']}人达标,额外奖励现金红包{$completAward['award_number']}",
|
||||
'money_type' => 1,
|
||||
'source_id' => $completAward['id']
|
||||
]);
|
||||
|
||||
DatabaseRoute::getDb('user_money', $sourceUser['user_id'], true, true)->inc('amount', $completAward['award_number'])->update();
|
||||
}
|
||||
|
||||
//更新邀请达标奖励次数
|
||||
DatabaseRoute::getDb('invite_achievement', $inviteAchievement['user_id'], true, true)->where([
|
||||
'id' => $inviteAchievement['id'],
|
||||
])->update([
|
||||
'user_id' => $inviteAchievement['user_id'],
|
||||
'give_award_count' => $inviteAchievement['give_award_count'] + ($awardNum - $awardCount)
|
||||
]);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
public static function selectOrdersByDay(int $userId)
|
||||
{
|
||||
return DatabaseRoute::getDb('orders', $userId, false, false, false)->alias('o')
|
||||
->leftJoin('disc_spinning_record r', 'o.orders_id = r.source_id')
|
||||
->where('o.user_id', $userId)
|
||||
->where('o.status', 1)
|
||||
->where('o.pay_way', 9)
|
||||
->whereraw('o.create_time > DATE_FORMAT(NOW(), "%Y-%m-%d 00:00:00")')
|
||||
->whereNull('r.source_id')
|
||||
->order('o.create_time')
|
||||
->find(); // LIMIT 1
|
||||
}
|
||||
|
||||
}
|
||||
17
app/czg/app/model/TaskCenter.php
Normal file
17
app/czg/app/model/TaskCenter.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace app\api\model;
|
||||
|
||||
use app\common\library\DatabaseRoute;
|
||||
use app\common\model\Common;
|
||||
use ba\Random;
|
||||
use think\facade\Cache;
|
||||
use think\facade\Db;
|
||||
use think\Model;
|
||||
|
||||
class TaskCenter extends Model
|
||||
{
|
||||
|
||||
|
||||
|
||||
}
|
||||
420
app/czg/app/model/TaskCenterRecord.php
Normal file
420
app/czg/app/model/TaskCenterRecord.php
Normal file
@@ -0,0 +1,420 @@
|
||||
<?php
|
||||
|
||||
namespace app\api\model;
|
||||
|
||||
use app\admin\model\User;
|
||||
use app\common\library\DatabaseRoute;
|
||||
use app\common\model\Common;
|
||||
use ba\Random;
|
||||
use think\facade\Cache;
|
||||
use think\facade\Db;
|
||||
use think\facade\Log;
|
||||
use think\Model;
|
||||
|
||||
class TaskCenterRecord extends Model
|
||||
{
|
||||
|
||||
|
||||
public static function selectTaskCenter($user_id, $target_user_id)
|
||||
{
|
||||
// 查找任务
|
||||
$db = Db::connect(config('database.search_library'));
|
||||
$rask_arr = $db->name('task_center')->where(['shows' => 1])->order('type', 'asc')->order('sort', 'asc')->select()->toArray();
|
||||
$data = [];
|
||||
$day_date = date('Y-m-d 00:00:00');
|
||||
foreach ($rask_arr as $k => $task) {
|
||||
$task_reward_arr = $db->name('task_center_reward')->field('type,number')->where(['task_id' => $task['id']])->select()->toArray();
|
||||
$todaySign = true;
|
||||
$signCount = null;
|
||||
// 默认值
|
||||
$rask_arr[$k]['disabled'] = true;
|
||||
$rask_arr[$k]['discNumber'] = 0;
|
||||
if(empty($task_reward_arr)) {
|
||||
continue;
|
||||
}
|
||||
$number = 0;
|
||||
$task_reward = [];
|
||||
foreach ($task_reward_arr as $tk => $tv) {
|
||||
$number += $tv['number'];
|
||||
$t_type = $tv['type'];
|
||||
}
|
||||
$task_reward[$t_type] = $number;
|
||||
|
||||
switch ($task['type']) {
|
||||
//签到任务
|
||||
case 2:
|
||||
|
||||
if($task['number'] == 1) {
|
||||
$order_db = DatabaseRoute::getDb('orders', $user_id);
|
||||
$dayOrderNum = $order_db->where(['status' => 1, 'pay_way' => 9])->where('create_time', '>', $day_date)->count();
|
||||
if($dayOrderNum < 3) {
|
||||
$rask_arr[$k]['discNumber'] = $dayOrderNum;
|
||||
$rask_arr[$k]['number'] = 3;
|
||||
$todaySign = false;
|
||||
}elseif (UserSignRecord::getTaskCenterRecordCount($user_id, $task['id'], $day_date) > 0) {
|
||||
$rask_arr[$k]['buttonTitle'] = '已领取';
|
||||
$rask_arr[$k]['number'] = null;
|
||||
}else {
|
||||
$rask_arr[$k]['discNumber'] = 0;
|
||||
$rask_arr[$k]['number'] = null;
|
||||
$rask_arr[$k]['jumpType'] = 0;
|
||||
}
|
||||
}else {
|
||||
// 周任务
|
||||
if($task['number'] > 1 && $task['number'] < 8) {
|
||||
if(!empty($task_reward[9])) {
|
||||
$disc_spi_count = DatabaseRoute::getDb('disc_spinning_record', $user_id)->where(['source' => 'taskW'])->count();
|
||||
if($disc_spi_count > 0) {
|
||||
continue 2;
|
||||
}
|
||||
$isBreak = false;
|
||||
// 抽奖次数
|
||||
$taskWCount = UserSignRecord::getTaskWCount($user_id, $task_reward[9]);
|
||||
if($taskWCount) {
|
||||
foreach ($taskWCount as $taskWCount_K => $taskWCount_v) {
|
||||
if($taskWCount_v > 0) {
|
||||
$isBreak = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if($isBreak) {
|
||||
$rask_arr[$k]['discNumber'] = null;
|
||||
$rask_arr[$k]['number'] = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
$wSignCount = UserSignRecord::getWSignCount($user_id);
|
||||
$wSignCount_s = $todaySign ? 1 : 0;
|
||||
if(!$wSignCount || ($wSignCount + $wSignCount_s) < $rask_arr[$k]['number']) {
|
||||
$rask_arr[$k]['discNumber'] = !$wSignCount?0:$wSignCount;
|
||||
$rask_arr[$k]['disabled'] = false;
|
||||
}else {
|
||||
continue 2;
|
||||
}
|
||||
}elseif ($task['number'] > 7 && $task['number'] < 32) {
|
||||
if(!$signCount) {
|
||||
$signCount = UserSignRecord::getUserSignCount($user_id);
|
||||
}
|
||||
if($signCount + ($todaySign ? 1 : 0) < $rask_arr[$k]['number']) {
|
||||
$rask_arr[$k]['discNumber'] = $signCount;
|
||||
$rask_arr[$k]['disabled'] = false;
|
||||
}else {
|
||||
if(!empty($task_reward[9])) {
|
||||
$spinningCount = DatabaseRoute::getDb('disc_spinning_record', $user_id)->where(['source' => 'taskW', 'source_id' => $tv['id']])->count();
|
||||
if ($spinningCount == null || $task_reward[9] - $spinningCount > 0) {
|
||||
$rask_arr[$k]['discNumber'] = null;
|
||||
$rask_arr[$k]['number'] = null;
|
||||
break;
|
||||
} else {
|
||||
continue 2;
|
||||
}
|
||||
}else {
|
||||
if(UserSignRecord::getTaskCenterRecordCount($user_id, $task['id'], $day_date) > 0) {
|
||||
$rask_arr[$k]['buttonTitle'] = '已领取';
|
||||
$rask_arr[$k]['disabled'] = false;
|
||||
$rask_arr[$k]['number'] = null;
|
||||
$rask_arr[$k]['discNumber'] = null;
|
||||
}else {
|
||||
$rask_arr[$k]['number'] = null;
|
||||
$rask_arr[$k]['discNumber'] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
// 一次性任务
|
||||
case 3:
|
||||
if($task['id'] == 1) {
|
||||
// $inviteAchievement = Db::connect(DatabaseRoute::getConnection('invite_achievement', ['user_id' => $user_id]))->name('invite_achievement')
|
||||
// ->where(['target_user_id' => $user_id])->find();
|
||||
$inviteAchievement = DatabaseRoute::getDb('invite_achievement', $target_user_id)
|
||||
->where('target_user_id', $user_id)
|
||||
->find();
|
||||
|
||||
if($inviteAchievement && !empty($inviteAchievement['tasks'])) {
|
||||
$splitTasks = explode(',', $inviteAchievement['tasks']);
|
||||
$isOver = false;
|
||||
foreach ($splitTasks as $tasks) {
|
||||
$isEqual = trim($tasks) === '1';
|
||||
if($isEqual) {
|
||||
$isOver = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if($isOver) {
|
||||
unset($rask_arr[$k]);
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
$userinfo = DatabaseRoute::getDb('user_info', $user_id)->find();
|
||||
if($userinfo && !empty($userinfo['cert_name']) && !empty($userinfo['cert_no'])) {
|
||||
$users = UserInfo::getUsersByNameAndCertNo($userinfo['cert_name'], $userinfo['cert_no']);
|
||||
if(UserSignRecord::getTaskCenterRecordCountUserIdAll($users, $task['id']) > 0) {
|
||||
if($inviteAchievement) {
|
||||
// Db::connect(DatabaseRoute::getConnection('invite_achievement', ['user_id' => $user_id], true))->name('invite_achievement')
|
||||
// ->where(['id' => $inviteAchievement['id']])
|
||||
// ->update(['tasks' => empty($inviteAchievement['tasks']) ? '1' : $inviteAchievement['tasks'] . ',1']);
|
||||
DatabaseRoute::getDb('invite_achievement', $user_id, true, true)
|
||||
->where(['id' => $inviteAchievement['id']])
|
||||
->update(['tasks' => empty($inviteAchievement['tasks']) ? '1' : $inviteAchievement['tasks'] . ',1']);
|
||||
}
|
||||
unset($rask_arr[$k]);
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
if(UserSignRecord::getTaskCenterRecordCount($user_id, $task['id'], null) > 0) {
|
||||
unset($rask_arr[$k]);
|
||||
continue 2;
|
||||
}
|
||||
$sumOrderNum = 0;
|
||||
if($inviteAchievement) {
|
||||
$sumOrderNum = $inviteAchievement['count'];
|
||||
}
|
||||
if($sumOrderNum != null && $sumOrderNum < $rask_arr[$k]['number']) {
|
||||
$rask_arr[$k]['discNumber'] = $sumOrderNum;
|
||||
}else {
|
||||
$rask_arr[$k]['discNumber'] = null;
|
||||
$rask_arr[$k]['number'] = null;
|
||||
$rask_arr[$k]['jumpType'] = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return returnSuccessData(convertToCamelCase($rask_arr));
|
||||
}
|
||||
|
||||
public static function addBlackUser(int $userId, string $behavior)
|
||||
{
|
||||
Log::info("异常用户id, 异常操作: {$userId},{$behavior}");
|
||||
$db = Db::connect(DatabaseRoute::getConnection('tb_user', ['user_id' => $userId], true));
|
||||
$userInfo = $db->name('user_info')->where('user_id', $userId)->find();
|
||||
|
||||
if (!empty($userInfo) && !empty($userInfo['cert_no'])) {
|
||||
Db::name('tb_user_blacklist')->insert([
|
||||
'real_name' => $userInfo['cert_name'],
|
||||
'id_card_no' => $userInfo['cert_no']
|
||||
]);
|
||||
}
|
||||
$db->name('tb_user')->where('user_id', $userId)
|
||||
->update([
|
||||
'status' => 0,
|
||||
'platform' => $behavior,
|
||||
'update_time' => date('Y-m-d H:i:s')
|
||||
]);
|
||||
}
|
||||
|
||||
// 任务领取
|
||||
public static function taskReceive($userId, $id, $target_id)
|
||||
{
|
||||
|
||||
$user_id_slave_db = Db::connect(DatabaseRoute::getConnection('tb_user', ['user_id' => $userId]));
|
||||
$user_id_master_db = Db::connect(DatabaseRoute::getConnection('tb_user', ['user_id' => $userId], true));
|
||||
|
||||
// 查询任务中心记录
|
||||
$taskCenter = Db::name('task_center')->find($id);
|
||||
if (empty($taskCenter) || $taskCenter['shows'] != 1) {
|
||||
return ['code' => -1, 'msg' => '领取失败'];
|
||||
}
|
||||
|
||||
// 查询邀请成就记录
|
||||
$inviteAchievement = DatabaseRoute::getDb('invite_achievement', $target_id)
|
||||
->where('target_user_id', $userId)
|
||||
->find();
|
||||
|
||||
// 处理类型为2的任务
|
||||
if ($taskCenter['type'] == 2) {
|
||||
// 统计今日订单数量
|
||||
$todayStart = date('Y-m-d') . ' 00:00:00';
|
||||
$dayOrderNum = $user_id_slave_db->name('orders')
|
||||
->where('user_id', $userId)
|
||||
->where('create_time', '>=', $todayStart)
|
||||
->count();
|
||||
|
||||
if ($taskCenter['number'] == 1) {
|
||||
// 查询昨日签到记录
|
||||
$yesterday = date('Y-m-d', strtotime('-1 day'));
|
||||
$yesterdaySign = $user_id_slave_db->name('user_sign_record')
|
||||
->where('user_id', $userId)
|
||||
->where('sign_day', $yesterday)
|
||||
->find();
|
||||
|
||||
// 构建今日签到记录
|
||||
$signRecord = [
|
||||
'user_id' => $userId,
|
||||
'sign_day' => date('Y-m-d'),
|
||||
'create_time' => date('Y-m-d H:i:s'),
|
||||
'day' => 1 // 默认连续1天
|
||||
];
|
||||
|
||||
// 计算连续签到天数
|
||||
if (!empty($yesterdaySign) && $yesterdaySign['day'] != 7) {
|
||||
$signRecord['day'] = $yesterdaySign['day'] + 1;
|
||||
}
|
||||
|
||||
// 检查领取条件
|
||||
if ($dayOrderNum < 3) {
|
||||
return ['code' => -1, 'msg' => '领取失败,未达成领取条件'];
|
||||
}
|
||||
|
||||
// 检查是否已领取
|
||||
$recordCount = $user_id_slave_db->name('task_center_record')
|
||||
->where('user_id', $userId)
|
||||
->where('task_id', $taskCenter['id'])
|
||||
->where('create_time', '>=', $todayStart)
|
||||
->count();
|
||||
|
||||
if ($recordCount > 0) {
|
||||
return ['code' => -1, 'msg' => '不可重复领取'];
|
||||
}
|
||||
|
||||
// 保存签到记录
|
||||
$user_id_master_db->name('user_sign_record')->insert($signRecord);
|
||||
} else {
|
||||
return ['code' => -1, 'msg' => '异常领取,已记录'];
|
||||
|
||||
}
|
||||
}
|
||||
// 处理类型为3且id=1的任务
|
||||
elseif ($taskCenter['type'] == 3 && $taskCenter['id'] == 1) {
|
||||
$sumOrderNum = 0;
|
||||
|
||||
// 检查是否已领取过该任务
|
||||
if (!empty($inviteAchievement) && !empty($inviteAchievement['tasks'])) {
|
||||
$tasks = explode(',', $inviteAchievement['tasks']);
|
||||
if (in_array('1', array_map('trim', $tasks))) {
|
||||
return ['code' => -1, 'msg' => '不可重复领取'];
|
||||
}
|
||||
}
|
||||
|
||||
// 获取订单总数
|
||||
if (!empty($inviteAchievement)) {
|
||||
$sumOrderNum = $inviteAchievement['count'];
|
||||
}
|
||||
|
||||
// 检查订单数量是否达标
|
||||
if ($sumOrderNum !== null && $sumOrderNum < $taskCenter['number']) {
|
||||
return ['code' => -1, 'msg' => '领取失败,未达成领取条件'];
|
||||
} else {
|
||||
// 检查用户实名信息
|
||||
$userInfo = $user_id_slave_db->name('user_info')
|
||||
->where('user_id', $userId)
|
||||
->find();
|
||||
|
||||
if (empty($userInfo) || empty($userInfo['cert_no']) || empty($userInfo['cert_name'])) {
|
||||
return ['code' => -1, 'msg' => '请实名后领取'];
|
||||
}
|
||||
|
||||
// 检查同一实名是否已领取
|
||||
$users = $user_id_slave_db->name('user_info')
|
||||
->where('cert_name', $userInfo['cert_name'])
|
||||
->where('cert_no', $userInfo['cert_no'])
|
||||
->select()
|
||||
->toArray();
|
||||
|
||||
$courseIds = array_column($users, 'user_id');
|
||||
$recordCount = $user_id_slave_db->name('task_center_record')
|
||||
->whereIn('user_id', $courseIds)
|
||||
->where('task_id', $taskCenter['id'])
|
||||
->count();
|
||||
|
||||
if ($recordCount > 0) {
|
||||
return ['code' => -1, 'msg' => '同一实名算一个新用户,不可重复领取'];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return ['code' => -1, 'msg' => '异常领取,已记录'];
|
||||
}
|
||||
|
||||
// 处理奖励发放
|
||||
$records = [];
|
||||
$targetId = null;
|
||||
|
||||
// 查询任务奖励列表
|
||||
$rewards = Db::name('task_center_reward')
|
||||
->where('task_id', $id)
|
||||
->select()
|
||||
->toArray();
|
||||
|
||||
foreach ($rewards as $reward) {
|
||||
switch ($reward['type']) {
|
||||
// 金币奖励
|
||||
case 1:
|
||||
$moneyDetail = [
|
||||
'user_id' => $userId,
|
||||
'title' => '[任务中心]',
|
||||
'classify' => 7,
|
||||
'type' => 1,
|
||||
'state' => 2,
|
||||
'money' => $reward['number'],
|
||||
'content' => $taskCenter['title'] . "任务完成,金币奖励" . $reward['number'],
|
||||
'money_type' => 2,
|
||||
'source_id' => $reward['task_id'],
|
||||
'create_time' => date('Y-m-d H:i:s')
|
||||
];
|
||||
|
||||
// 更新用户金币
|
||||
UserMoney::updateMoney($userId, $reward['number']);
|
||||
// 保存金币明细
|
||||
$targetId = $user_id_master_db->name('user_money_details')->insertGetId($moneyDetail);
|
||||
break;
|
||||
|
||||
// 现金奖励
|
||||
case 2:
|
||||
$cashDetail = [
|
||||
'user_id' => $userId,
|
||||
'title' => '[任务中心]',
|
||||
'classify' => 7,
|
||||
'type' => 1,
|
||||
'state' => 2,
|
||||
'money' => $reward['number'],
|
||||
'content' => $taskCenter['title'] . "任务完成,现金奖励" . $reward['number'],
|
||||
'money_type' => 1,
|
||||
'source_id' => $reward['task_id'],
|
||||
'create_time' => date('Y-m-d H:i:s')
|
||||
];
|
||||
|
||||
// 更新用户现金
|
||||
$user_id_master_db->name('user_money')->where('user_id', $userId)->inc('amount', $reward['number'])->update();
|
||||
UserMoney::updateAmount($userId, $reward['number']);
|
||||
// 保存现金明细
|
||||
$targetId = $user_id_master_db->name('user_money_details')->insertGetId($cashDetail);
|
||||
break;
|
||||
}
|
||||
|
||||
// 构建任务记录
|
||||
$records[] = [
|
||||
'user_id' => $userId,
|
||||
'task_id' => $id,
|
||||
'type' => $reward['type'],
|
||||
'number' => $reward['number'],
|
||||
'name' => $taskCenter['title'],
|
||||
'target_id' => $targetId,
|
||||
'create_time' => date('Y-m-d H:i:s'),
|
||||
'update_time' => date('Y-m-d H:i:s')
|
||||
];
|
||||
}
|
||||
|
||||
// 批量保存任务记录
|
||||
if (!empty($records)) {
|
||||
$user_id_master_db->name('task_center_record')->insertAll($records);
|
||||
}
|
||||
|
||||
// 更新邀请成就任务记录
|
||||
if (!empty($inviteAchievement) && $id == 1) {
|
||||
$tasks = $inviteAchievement['tasks'] ?? '';
|
||||
$newTasks = empty($tasks) ? '1' : $tasks . ',1';
|
||||
|
||||
$user_id_master_db->name('invite_achievement')
|
||||
->where('user_id', $inviteAchievement['user_id'])
|
||||
->where('id', $inviteAchievement['id'])
|
||||
->update(['tasks' => $newTasks]);
|
||||
}
|
||||
return returnSuccessData();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
309
app/czg/app/model/TbUser.php
Normal file
309
app/czg/app/model/TbUser.php
Normal file
@@ -0,0 +1,309 @@
|
||||
<?php
|
||||
|
||||
namespace app\czg\app\model;
|
||||
|
||||
use app\common\library\DatabaseRoute;
|
||||
use app\common\model\BaseModel;
|
||||
use app\common\model\Common;
|
||||
use app\common\model\SysUser;
|
||||
use ba\Exception;
|
||||
use app\exception\SysException;
|
||||
use ba\Random;
|
||||
use think\facade\Db;
|
||||
use think\facade\Log;
|
||||
|
||||
class TbUser extends BaseModel
|
||||
{
|
||||
// 查询username
|
||||
public static function GetByusername($username, $field = 'phone')
|
||||
{
|
||||
// 全表扫描username
|
||||
$dbmap = config('database.db_map');
|
||||
foreach ($dbmap as $dbname) {
|
||||
if(!in_array($dbname, config('database.unset_db_map'))) {
|
||||
$connect = Db::connect($dbname);
|
||||
$data = $connect->name('tb_user')->where([$field => $username])->find();
|
||||
if($data) {
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public static function GetByuserInvite($inviter_code)
|
||||
{
|
||||
// // 全表扫描username
|
||||
// $dbmap = config('database.db_map');
|
||||
// $count = 0;
|
||||
// foreach ($dbmap as $dbname) {
|
||||
// if(!in_array($dbname, config('database.unset_db_map'))) {
|
||||
// $connect = Db::connect($dbname);
|
||||
// $data = $connect->name('tb_user')->where(['inviter_code' => $inviter_code])->count();
|
||||
// $count += $data;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
$count = DatabaseRoute::getAllDbData('tb_user', function ($query) use ($inviter_code) {
|
||||
return $query->where(['inviter_code' => $inviter_code]);
|
||||
})->count();
|
||||
return $count;
|
||||
}
|
||||
|
||||
public static function GetByuserInviteselect($invitation_code, $page, $limit)
|
||||
{
|
||||
$data_list = [];
|
||||
$data_arr = DatabaseRoute::paginateAllDb('tb_user', function ($query) use ($invitation_code) {
|
||||
return $query->where(['inviter_code' => $invitation_code])->field('user_id,avatar,user_name,user_id');
|
||||
}, $page, $limit);
|
||||
if(!empty($data_arr['list'])) {
|
||||
foreach ($data_arr['list'] as $k => $v) {
|
||||
$data_list[] = [
|
||||
'user_id' => (string) $v['user_id'],
|
||||
'avatar' => $v['avatar'],
|
||||
'user_name' => $v['user_name'],
|
||||
];
|
||||
}
|
||||
}
|
||||
return $data_list;
|
||||
|
||||
|
||||
// $dbmap = config('database.db_map');
|
||||
// $data_list = [];
|
||||
// foreach ($dbmap as $dbname) {
|
||||
//
|
||||
// if(!in_array($dbname, config('database.unset_db_map'))) {
|
||||
// $connect = Db::connect($dbname);
|
||||
// $data_arr = $connect->name('tb_user')->where(['inviter_code' => $invitation_code])->field('user_id,avatar,user_name,user_id')->limit(page($page, $limit), $limit)->select()->toArray();
|
||||
// if($data_arr) {
|
||||
// foreach ($data_arr as $k => $v) {
|
||||
// $data_list[] = [
|
||||
// 'user_id' => $v['user_id'],
|
||||
// 'avatar' => $v['avatar'],
|
||||
// 'user_name' => $v['user_name'],
|
||||
// ];
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// Log::write('下级列表:' . json_encode($data_arr));
|
||||
// return $data_list;
|
||||
|
||||
|
||||
}
|
||||
|
||||
public static function getByUserIdOrInviterCode($userId, $inviterCode)
|
||||
{
|
||||
$user = $userId ? DatabaseRoute::getDb('tb_user', $userId)->where([
|
||||
'user_id' => $userId
|
||||
])->find() : null;
|
||||
return $user ?? self::GetByinvitationCode($inviterCode);
|
||||
}
|
||||
|
||||
public static function GetByinvitationCode($invitation_code)
|
||||
{
|
||||
// 全表扫描username
|
||||
$dbmap = config('database.db_map');
|
||||
foreach ($dbmap as $dbname) {
|
||||
if(!in_array($dbname, config('database.unset_db_map'))) {
|
||||
$connect = Db::connect($dbname);
|
||||
$data = $connect->name('tb_user')->where(['invitation_code' => $invitation_code])->find();
|
||||
if($data) {
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public static function GetByinviterCodeCount($inviter_code)
|
||||
{
|
||||
// 全表扫描username
|
||||
$dbmap = config('database.db_map');
|
||||
foreach ($dbmap as $dbname) {
|
||||
if(!in_array($dbname, config('database.unset_db_map'))) {
|
||||
$connect = Db::connect($dbname);
|
||||
$data = $connect->name('tb_user')->where(['inviter_code' => $inviter_code])->count();
|
||||
if($data) {
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function register($data):array
|
||||
{
|
||||
if(empty($data['password']) || empty($data['phone']) || empty($data['msg']) || empty($data['platform'])) {
|
||||
return returnErrorData('参数不完整');
|
||||
}
|
||||
$toUser = self::GetByusername($data['phone']);
|
||||
if($toUser) {
|
||||
return returnErrorData('此号码已注册');
|
||||
}
|
||||
if(!Msg::checkCode($data['phone'], $data['msg'])) {
|
||||
return returnErrorData('验证码错误');
|
||||
}
|
||||
if(!empty($data['inviterCode'])) {
|
||||
$inviter = self::GetByinvitationCode($data['inviterCode']);
|
||||
if(!$inviter) {
|
||||
return returnErrorData('邀请码不正确');
|
||||
}
|
||||
}else {
|
||||
$data['inviterCode'] = CommonInfo::where(['type' => 88])->find()->value;
|
||||
$inviter = self::GetByinvitationCode($data['inviterCode']);
|
||||
}
|
||||
|
||||
if(empty($data['qdCode'])) {
|
||||
$data['qdCode'] = $inviter['qd_code'];
|
||||
}else {
|
||||
$sys_user = SysUser::GetByQrcode($data['qdCode']);
|
||||
if(!$sys_user) {
|
||||
return returnErrorData('渠道码错误');
|
||||
}
|
||||
}
|
||||
$user_id = Random::generateRandomPrefixedId(19);
|
||||
Db::startTrans();
|
||||
try {
|
||||
$insert = [
|
||||
'user_id' => $user_id,
|
||||
'user_name' => maskPhoneNumber($data['phone']),
|
||||
'qd_code' => $data['qdCode'],
|
||||
'phone' => $data['phone'],
|
||||
'platform' => $data['platform'],
|
||||
'create_time' => date('Y-m-d H:i:s'),
|
||||
'sys_phone' => !empty($data['sys_phone'])?:'',
|
||||
'password' => sha256Hex($data['password']),
|
||||
'status' => 1,
|
||||
'update_time' => date('Y-m-d H:i:s'),
|
||||
'rate' => CommonInfo::where(['type' => 420])->find()->value,
|
||||
'two_rate' => CommonInfo::where(['type' => 421])->find()->value,
|
||||
'invitation_code' => toSerialCode($user_id),
|
||||
'inviter_code' => $data['inviterCode'],
|
||||
'inviter_user_id' => $inviter['user_id'],
|
||||
];
|
||||
$connect_name = DatabaseRoute::getConnection('tb_user', ['user_id' => $user_id], true);
|
||||
$db = Db::connect($connect_name);
|
||||
$db->name('tb_user')->insertGetId($insert);
|
||||
$user = $db->name('tb_user')->where(['user_id' => $user_id])->find();
|
||||
// 删除验证码
|
||||
if(Msg::delCode($data['phone'], $data['msg'])) {
|
||||
if($inviter) {
|
||||
// 关于上级的处理
|
||||
Invite::saveBody($user_id, $inviter);
|
||||
}
|
||||
Db::commit();
|
||||
return returnSuccessData([], ['user' => apiconvertToCamelCase($user)]);
|
||||
}
|
||||
|
||||
}catch (Exception $exception) {
|
||||
Db::rollback();
|
||||
return returnErrorData($exception->getMessage());
|
||||
}
|
||||
return returnErrorData('error');
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static function CheckPassword($userpasswoed, $formpassword):bool
|
||||
{
|
||||
$hash = hash('sha256', $formpassword);
|
||||
return $hash === $userpasswoed;
|
||||
}
|
||||
|
||||
public static function checkEnable($user)
|
||||
{
|
||||
// $user = DatabaseRoute::getDb('tb_user', $userId)->where([
|
||||
// 'user_id' => $userId
|
||||
// ])->find();
|
||||
if (!$user) {
|
||||
throw new SysException("用户不存在");
|
||||
}
|
||||
|
||||
if ($user['status'] != 1) {
|
||||
throw new SysException("用户已禁用");
|
||||
}
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
// 忘记密码
|
||||
public static function forgetPwd($phone, $pwd, $msg):array
|
||||
{
|
||||
if(empty($phone) || empty($pwd) || empty($msg)) {
|
||||
return returnErrorData('参数不完整');
|
||||
}
|
||||
$user = TbUser::GetByusername($phone);
|
||||
if($user) {
|
||||
if(Msg::checkCode($phone, $msg)) {
|
||||
$pwd = sha256Hex($pwd);
|
||||
$where = $sale = ['user_id' => $user['user_id']];
|
||||
Db::startTrans();
|
||||
try {
|
||||
Common::saveDbData('tb_user', $sale, ['password' => $pwd], 'update', $where);
|
||||
Msg::delCode($phone, $msg);
|
||||
Db::commit();
|
||||
return returnSuccessData();
|
||||
}catch (Exception $e) {
|
||||
Db::rollback();
|
||||
return returnErrorData($e->getMessage());
|
||||
}
|
||||
|
||||
}else {
|
||||
return returnErrorData('验证码错误');
|
||||
}
|
||||
}
|
||||
return returnErrorData('error');
|
||||
}
|
||||
|
||||
public static function selectUserById($userId)
|
||||
{
|
||||
$user = DatabaseRoute::getDb('tb_user', $userId)->find();
|
||||
if ($user) {
|
||||
$userVip = Db::name('user_vip')->where([
|
||||
'user_id' => $userId
|
||||
])->find();
|
||||
if ($userVip) {
|
||||
$user['member'] = $userVip['is_vip'];
|
||||
$user['end_time'] = $userVip['end_time'];
|
||||
}
|
||||
}
|
||||
|
||||
return $user;
|
||||
|
||||
}
|
||||
|
||||
public static function selectUserByIdNew($user)
|
||||
{
|
||||
$db = Db::connect(config('database.search_library'));
|
||||
$userVip = $db->name('user_vip')->where(['user_id' => $user['user_id']])->find();
|
||||
$db->close();
|
||||
if ($userVip) {
|
||||
$user['member'] = $userVip['is_vip'];
|
||||
$user['end_time'] = $userVip['end_time'];
|
||||
}
|
||||
return $user;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验用户实名
|
||||
*/
|
||||
public static function checkReal($userId)
|
||||
{
|
||||
$count = DatabaseRoute::getDb('user_info', $userId)->whereNotNull('cert_name')->whereNotNull('cert_no')->count();
|
||||
return $count > 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
34
app/czg/app/model/TbUserBlacklist.php
Normal file
34
app/czg/app/model/TbUserBlacklist.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace app\api\model;
|
||||
|
||||
|
||||
use app\common\library\DatabaseRoute;
|
||||
use think\facade\Log;
|
||||
use think\Model;
|
||||
|
||||
class TbUserBlacklist extends Model
|
||||
{
|
||||
|
||||
public function addBlackUser(int $userId, string $string)
|
||||
{
|
||||
|
||||
Log::info("用户{$userId}加入黑名单,原因:{$string}");
|
||||
$user = DatabaseRoute::getDb('tb_user', $userId)->where([
|
||||
'user_id' => $userId
|
||||
])->find();
|
||||
if ($user && $user['cert_no']) {
|
||||
$this->insert([
|
||||
'real_name' => $user['real_name'],
|
||||
'cert_no' => $user['cert_no'],
|
||||
]);
|
||||
}
|
||||
|
||||
DatabaseRoute::getDb('tb_user', $userId, true)->where([
|
||||
'user_id' => $userId
|
||||
])->update([
|
||||
'status' => 0
|
||||
]);
|
||||
|
||||
}
|
||||
}
|
||||
180
app/czg/app/model/UniAdCallbackRecord.php
Normal file
180
app/czg/app/model/UniAdCallbackRecord.php
Normal file
@@ -0,0 +1,180 @@
|
||||
<?php
|
||||
|
||||
namespace app\api\model;
|
||||
|
||||
use app\common\library\DatabaseRoute;
|
||||
use ba\Random;
|
||||
use think\cache\driver\Redis;
|
||||
use think\facade\Cache;
|
||||
use think\facade\Db;
|
||||
use think\facade\Log;
|
||||
use think\Model;
|
||||
|
||||
class UniAdCallbackRecord extends Model
|
||||
{
|
||||
|
||||
public static function getFreeWatchRemainTime($user_id, $isPermanent):int
|
||||
{
|
||||
// 构造Redis键名(根据是否永久免费区分)
|
||||
$redisKey = $isPermanent
|
||||
? "user:free_watch:permanent:{$user_id}"
|
||||
: "user:free_watch:normal:{$user_id}";
|
||||
|
||||
$redis = new Redis;
|
||||
// 从Redis获取值,默认返回0
|
||||
$remainTime = $redis->get($redisKey, 0);
|
||||
// 确保返回整数类型
|
||||
return (int)$remainTime;
|
||||
}
|
||||
|
||||
|
||||
public static function adCallBack(array $callBackDTO)
|
||||
{
|
||||
$respData = [];
|
||||
|
||||
$db = Db::connect(config('database.search_library'));
|
||||
// 检查是否重复回调
|
||||
$record = $db->name('uni_ad_callback_record')
|
||||
->where('trans_id', $callBackDTO['trans_id'])
|
||||
->find();
|
||||
|
||||
if ($record) {
|
||||
Log::write("回调重复, trans_id: {$record['trans_id']}");
|
||||
$respData['isValid'] = false;
|
||||
return $respData;
|
||||
}
|
||||
|
||||
// 准备记录数据
|
||||
$recordData = [
|
||||
'user_id' => (int)$callBackDTO['user_id'],
|
||||
'platform' => $callBackDTO['platform'],
|
||||
'trans_id' => $callBackDTO['trans_id'],
|
||||
'adpid' => $callBackDTO['adpid'],
|
||||
'provider' => $callBackDTO['provider'],
|
||||
'sign' => $callBackDTO['sign'],
|
||||
'extra' => $callBackDTO['extra'],
|
||||
'create_time' => date('Y-m-d H:i:s'),
|
||||
'is_ended' => 1
|
||||
];
|
||||
$security = 'cbc34e14ee6d64738557c96623dd6da89f77cac8ae519c6a5c87191d614d386a';
|
||||
// 签名验证
|
||||
$flag = self::validateSign($security, $callBackDTO['trans_id'], $callBackDTO['sign']);
|
||||
if (!$flag) {
|
||||
$recordData['err_msg'] = "签名验证失败";
|
||||
Log::write(json_encode($recordData));
|
||||
Db::name('uni_ad_callback_record')->insert($recordData);
|
||||
$respData['isValid'] = false;
|
||||
return $respData;
|
||||
}
|
||||
|
||||
// 检查用户是否存在
|
||||
$userEntity = Db::name('user_entity')
|
||||
->where('id', $callBackDTO['user_id'])
|
||||
->find();
|
||||
|
||||
if (!$userEntity) {
|
||||
$recordData['err_msg'] = "用户不存在";
|
||||
Log::warning(self::getBaseErrInfo($recordData));
|
||||
Db::name('uni_ad_callback_record')->insert($recordData);
|
||||
$respData['isValid'] = false;
|
||||
return $respData;
|
||||
}
|
||||
|
||||
// 根据extra字段处理不同逻辑
|
||||
if (!str_contains($callBackDTO['extra'], "cash")) {
|
||||
// 获取配置信息
|
||||
$info = Db::name('common_info')
|
||||
->where('id', 921)
|
||||
->find();
|
||||
|
||||
if (!$info || empty($info['value'])) {
|
||||
$recordData['err_msg'] = "CommonInfo时长时间未配置";
|
||||
Log::warning(self::getBaseErrInfo($recordData));
|
||||
Db::name('uni_ad_callback_record')->insert($recordData);
|
||||
$respData['isValid'] = false;
|
||||
return $respData;
|
||||
}
|
||||
|
||||
// 设置免费观看时间(分钟转秒)
|
||||
self::setFreeWatchTime($recordData['user_id'], (int)$info['value'] * 60, true);
|
||||
} else {
|
||||
// 设置可提现标志
|
||||
$recordId = Db::name('uni_ad_callback_record')->insertGetId($recordData);
|
||||
self::setCanCashFlag($userEntity['id'], $recordId);
|
||||
return ['isValid' => true]; // 提前返回,避免重复插入
|
||||
}
|
||||
|
||||
// 保存记录
|
||||
Db::name('uni_ad_callback_record')->insert($recordData);
|
||||
|
||||
// 返回成功响应
|
||||
$respData['isValid'] = true;
|
||||
return $respData;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证签名
|
||||
* @param string $securityKey 安全密钥
|
||||
* @param string $transId 交易ID
|
||||
* @param string $sign 签名
|
||||
* @return bool 验证结果
|
||||
*/
|
||||
public static function validateSign(string $securityKey, string $transId, string $sign): bool
|
||||
{
|
||||
// 实际签名验证逻辑(根据业务需求实现)
|
||||
$expectedSign = self::generateSign($securityKey, $transId);
|
||||
return $expectedSign === $sign;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置免费观看时间
|
||||
* @param int $userId 用户ID
|
||||
* @param int $duration 时长(秒)
|
||||
* @param bool $isPermanent 是否永久
|
||||
*/
|
||||
public static function setFreeWatchTime(int $userId, int $duration, bool $isPermanent)
|
||||
{
|
||||
$key = $isPermanent ? "free_watch:permanent:{$userId}" : "free_watch:normal:{$userId}";
|
||||
Cache::set($key, $duration, $duration); // 缓存时间等于有效时长
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置可提现标志
|
||||
* @param int $userId 用户ID
|
||||
* @param int $recordId 记录ID
|
||||
*/
|
||||
public static function setCanCashFlag(int $userId, int $recordId)
|
||||
{
|
||||
Cache::set("can_cash:{$userId}", $recordId, 86400); // 缓存24小时
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取基础错误信息
|
||||
* @param array $record 记录数据
|
||||
* @return string 错误信息
|
||||
*/
|
||||
public static function getBaseErrInfo(array $record): string
|
||||
{
|
||||
return "广告回调错误: [user_id={$record['user_id']}, trans_id={$record['trans_id']}, err_msg={$record['err_msg']}]";
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成签名
|
||||
* @param string $secret 安全密钥
|
||||
* @param string $transId 交易ID
|
||||
* @return string 生成的签名(十六进制字符串)
|
||||
*/
|
||||
public static function generateSign(string $secret, string $transId): string
|
||||
{
|
||||
// 生成待加密的字符串
|
||||
$data = $secret . ':' . $transId;
|
||||
|
||||
// 使用SHA-256生成签名(返回十六进制字符串)
|
||||
return hash('sha256', $data);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
53
app/czg/app/model/UserInfo.php
Normal file
53
app/czg/app/model/UserInfo.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace app\api\model;
|
||||
|
||||
use app\common\library\DatabaseRoute;
|
||||
use app\common\model\BaseModel;
|
||||
use think\facade\Db;
|
||||
|
||||
class UserInfo extends BaseModel
|
||||
{
|
||||
public static function getUsersByNameAndCertNo($cert_name, $cert_no)
|
||||
{
|
||||
// 全表扫描username
|
||||
// $dbmap = config('database.db_map');
|
||||
$data = [];
|
||||
// foreach ($dbmap as $dbname) {
|
||||
// if(!in_array($dbname, config('database.unset_db_map'))) {
|
||||
// $connect = Db::connect($dbname);
|
||||
// $data_arr = $connect->name('user_info')->where(['cert_name' => $cert_name, 'cert_no' => $cert_no])->field('user_id')->select()->toArray();
|
||||
// if($data_arr) {
|
||||
// foreach ($data_arr as $k => $v) {
|
||||
// $data[] = $v['user_id'];
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
$data_arr = DatabaseRoute::getAllDbData('user_info', function ($query) use ($cert_no, $cert_name) {
|
||||
return $query->where(['cert_name' => $cert_name, 'cert_no' => $cert_no])->field('user_id');
|
||||
})->select()->toArray();
|
||||
if($data_arr) {
|
||||
foreach ($data_arr as $k => $v) {
|
||||
$data[] = $v['user_id'];
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
|
||||
}
|
||||
|
||||
public static function getByUserIdOrSave(int $userId)
|
||||
{
|
||||
$userInfo = DatabaseRoute::getDb('user_info', $userId)->find();
|
||||
if (!$userInfo) {
|
||||
$id = DatabaseRoute::getDb('user_info', $userId, true)->insertGetId([
|
||||
'user_id' => $userId
|
||||
]);
|
||||
$userInfo['id'] = $id;
|
||||
$userInfo['user_id'] = $userId;
|
||||
}
|
||||
return $userInfo;
|
||||
}
|
||||
}
|
||||
121
app/czg/app/model/UserMoney.php
Normal file
121
app/czg/app/model/UserMoney.php
Normal file
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
|
||||
namespace app\api\model;
|
||||
|
||||
use app\common\library\DatabaseRoute;
|
||||
use app\common\model\BaseModel;
|
||||
use app\common\model\SysUser;
|
||||
use ba\Exception;
|
||||
use ba\Random;
|
||||
use think\facade\Db;
|
||||
|
||||
class UserMoney extends BaseModel
|
||||
{
|
||||
|
||||
public static function selectUserMoney($user_id)
|
||||
{
|
||||
$where = $sale = ['user_id' => $user_id];
|
||||
$money = DatabaseRoute::getDb('user_money', $user_id)->where($where)->find();
|
||||
if(!$money) {
|
||||
$database_name = DatabaseRoute::getConnection('user_money', $sale, true);
|
||||
$money = [
|
||||
'user_id' => $user_id,
|
||||
'money' => 0.00,
|
||||
'amount' => 0.00,
|
||||
];
|
||||
Db::connect($database_name)->name('user_money')->insert($money);
|
||||
}
|
||||
if($money['money'] == 0) {
|
||||
$money['money'] = 0;
|
||||
}
|
||||
if($money['amount'] == 0) {
|
||||
$money['amount'] = 0;
|
||||
}
|
||||
$money['amount'] = sprintf('%.2f', $money['amount']);
|
||||
$money['money'] = sprintf('%.2f', $money['money']);
|
||||
$money = apiconvertToCamelCase($money);
|
||||
return returnSuccessData($money);
|
||||
}
|
||||
public static function selectUserMoneyfind($user_id)
|
||||
{
|
||||
$db = DatabaseRoute::getDb('user_money', $user_id);
|
||||
$data = $db->where([ 'user_id' => $user_id])->find();
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
public static function queryUserMoneyDetails($user_id, $get)
|
||||
{
|
||||
$user_where = $sale = ['user_id' => $user_id];
|
||||
$where = [];
|
||||
if(!empty($get['classify'])) {
|
||||
$where['classify'] = $get['classify'];
|
||||
}
|
||||
if(!empty($get['type'])) {
|
||||
$where['type'] = $get['type'];
|
||||
}
|
||||
if(!empty($get['moneyType'])) {
|
||||
$where['money_type'] = $get['moneyType'];
|
||||
}
|
||||
if(!empty($get['classify'])) {
|
||||
$where['classify'] = $get['classify'];
|
||||
}
|
||||
$money = DatabaseRoute::getDb('user_money_details', $user_id)->where($user_where)->where($where);
|
||||
if(!empty($get['viewType']) && $get['viewType'] == 1) {
|
||||
$money = $money->whereIn('classify', [1,6]);
|
||||
}
|
||||
$count = $money->count();
|
||||
$money = $money->order('create_time','desc')->limit(page($get['page'], $get['limit']), $get['limit'])->select()->toArray();
|
||||
foreach ($money as $k => &$v) {
|
||||
$v['money'] = sprintf('%.2f', $v['money']);
|
||||
}
|
||||
return returnSuccessData([
|
||||
'currPage' => $get['page'],
|
||||
'pageSize' => $get['limit'],
|
||||
'records' => apiconvertToCamelCase($money),
|
||||
'totalCount' => $count,
|
||||
'totalPage' => ceil($count / $get['limit']),
|
||||
]);
|
||||
}
|
||||
|
||||
public static function updateAmount($userId, $money, $isIncr=true)
|
||||
{
|
||||
$userMoney = self::selectUserMoneyfind($userId);
|
||||
if (!$userMoney) {
|
||||
DatabaseRoute::getDb('user_money', $userId, true)->insert([
|
||||
'user_id' => $userId,
|
||||
'money' => 0,
|
||||
'amount' => 0
|
||||
]);
|
||||
}
|
||||
$money = floatval($money);
|
||||
$model = DatabaseRoute::getDb('user_money', $userId, true, true);
|
||||
if ($isIncr) {
|
||||
$model->inc('amount', $money);
|
||||
}else{
|
||||
$model->dec('amount', $money);
|
||||
}
|
||||
$model->update();
|
||||
}
|
||||
|
||||
public static function updateMoney($userId, $money, $isIncr=true)
|
||||
{
|
||||
$userMoney = self::selectUserMoneyfind($userId);
|
||||
if (!$userMoney) {
|
||||
DatabaseRoute::getDb('user_money', $userId, true)->insert([
|
||||
'user_id' => $userId,
|
||||
'money' => 0,
|
||||
'amount' => 0
|
||||
]);
|
||||
}
|
||||
$money = floatval($money);
|
||||
$model = DatabaseRoute::getDb('user_money', $userId, true, true);
|
||||
if ($isIncr) {
|
||||
$model->inc('money', $money);
|
||||
}else{
|
||||
$model->dec('money', $money);
|
||||
}
|
||||
$model->update();
|
||||
}
|
||||
|
||||
}
|
||||
15
app/czg/app/model/UserMoneyDetails.php
Normal file
15
app/czg/app/model/UserMoneyDetails.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace app\api\model;
|
||||
|
||||
use app\common\library\DatabaseRoute;
|
||||
use app\common\model\BaseModel;
|
||||
use app\common\model\SysUser;
|
||||
use ba\Exception;
|
||||
use ba\Random;
|
||||
use think\facade\Db;
|
||||
|
||||
class UserMoneyDetails extends BaseModel
|
||||
{
|
||||
|
||||
}
|
||||
85
app/czg/app/model/UserPrizeExchange.php
Normal file
85
app/czg/app/model/UserPrizeExchange.php
Normal file
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
namespace app\api\model;
|
||||
|
||||
use think\facade\Db;
|
||||
use think\Model;
|
||||
|
||||
class UserPrizeExchange extends Model
|
||||
{
|
||||
public static function pages($params, $userId)
|
||||
{
|
||||
// 提取查询参数
|
||||
$foreignId = isset($params['foreignId']) ? (int)$params['foreignId'] : null;
|
||||
$foreignType = $params['foreignType'] ?? '';
|
||||
$userName = $params['userName'] ?? '';
|
||||
$prizeName = $params['prizeName'] ?? '';
|
||||
$status = isset($params['status']) ? (int)$params['status'] : null;
|
||||
$phone = $params['phone'] ?? '';
|
||||
$remark = $params['remark'] ?? '';
|
||||
$beginDate = $params['beginDate'] ?? '';
|
||||
$endDate = $params['endDate'] ?? '';
|
||||
|
||||
$db = Db::connect(config('database.search_library'));
|
||||
// 构建查询条件
|
||||
$query = $db->name('user_prize_exchange');
|
||||
|
||||
if (!is_null($foreignId)) {
|
||||
$query = $query->where('foreign_id', $foreignId);
|
||||
}
|
||||
|
||||
if (!empty($foreignType)) {
|
||||
$query = $query->where('foreign_type', $foreignType);
|
||||
}
|
||||
|
||||
if (!is_null($userId)) {
|
||||
$query = $query->where('user_id', $userId);
|
||||
}
|
||||
|
||||
if (!empty($userName)) {
|
||||
$query = $query->where('user_name', 'like', "%{$userName}%");
|
||||
}
|
||||
|
||||
if (!empty($prizeName)) {
|
||||
$query = $query->where('prize_name', 'like', "%{$prizeName}%");
|
||||
}
|
||||
|
||||
if (!is_null($status)) {
|
||||
$query = $query->where('status', $status);
|
||||
}
|
||||
|
||||
if (!empty($phone)) {
|
||||
$query = $query->where('phone', 'like', "%{$phone}%");
|
||||
}
|
||||
|
||||
if (!empty($remark)) {
|
||||
$query = $query->where('remark', 'like', "%{$remark}%");
|
||||
}
|
||||
|
||||
if (!empty($beginDate)) {
|
||||
$query = $query->where('create_time', '>=', "{$beginDate} 00:00:00");
|
||||
}
|
||||
|
||||
if (!empty($endDate)) {
|
||||
$query = $query->where('create_time', '<=', "{$endDate} 23:59:59");
|
||||
}
|
||||
$count = $query->count();
|
||||
// 设置排序
|
||||
$query = $query->order('id', 'desc');
|
||||
|
||||
// 分页参数
|
||||
$pageNum = isset($params['page']) ? (int)$params['page'] : 1;
|
||||
$pageSize = isset($params['limit']) ? (int)$params['limit'] : 10;
|
||||
|
||||
// 执行分页查询
|
||||
$list = $query->limit(page($pageNum, $pageSize), $pageSize)->select()->toArray();
|
||||
|
||||
return returnSuccessData([
|
||||
'currPage' => $pageNum,
|
||||
'pageSize' => $pageSize,
|
||||
'list' => ($list),
|
||||
'totalCount' => $count,
|
||||
'totalPage' => ceil($count / $pageSize),
|
||||
]);
|
||||
}
|
||||
}
|
||||
233
app/czg/app/model/UserSignRecord.php
Normal file
233
app/czg/app/model/UserSignRecord.php
Normal file
@@ -0,0 +1,233 @@
|
||||
<?php
|
||||
|
||||
namespace app\api\model;
|
||||
|
||||
use app\common\library\DatabaseRoute;
|
||||
use ba\Random;
|
||||
use think\facade\Db;
|
||||
use think\Model;
|
||||
|
||||
class UserSignRecord extends Model
|
||||
{
|
||||
|
||||
|
||||
public static function month($month = '-30 days')
|
||||
{
|
||||
return date('Y-m-d 00:00:00', strtotime($month));
|
||||
}
|
||||
|
||||
|
||||
public static function getTaskWCount($user_id, $wCount)
|
||||
{
|
||||
$user_sign_record_db = DatabaseRoute::getDb('user_sign_record', $user_id);
|
||||
$day = self::month();
|
||||
$noRecordTasks = $user_sign_record_db->where(['day' => 7])->where('create_time', '>', $day)->order('create_time', 'acs')
|
||||
->field('id')
|
||||
->select()
|
||||
->toArray();
|
||||
$taskWCount = Db::connect(DatabaseRoute::getConnection('user_sign_record', ['user_id' => $user_id]));
|
||||
$taskWCount =
|
||||
$taskWCount
|
||||
->name('user_sign_record')
|
||||
->alias('sign')
|
||||
->field([
|
||||
'sign.id as id',
|
||||
"{$wCount} - COUNT(CASE WHEN spRecord.source_id IS NOT NULL THEN 1 END) AS `day`"
|
||||
])
|
||||
->leftJoin('disc_spinning_record spRecord', 'sign.id = spRecord.source_id')
|
||||
->where('sign.day', 7)
|
||||
->where('sign.user_id', $user_id)
|
||||
->where('sign.create_time', '>', $day)
|
||||
->whereIn('sign.id', $noRecordTasks)
|
||||
->group('sign.id')
|
||||
->order('sign.create_time', 'asc')
|
||||
->select();
|
||||
if($taskWCount) {
|
||||
$days = 0;
|
||||
$task_reward = [];
|
||||
foreach ($taskWCount as $tk => $tv) {
|
||||
$task_reward[][$tv['id']] = $days += $tv['day'];
|
||||
}
|
||||
return $task_reward;
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static function getWSignCount($user_id)
|
||||
{
|
||||
$user_sign_record_db = DatabaseRoute::getDb('user_sign_record', $user_id);
|
||||
$noRecordTasks = $user_sign_record_db->where('create_time', '>', date('Y-m-d 00:00:00', strtotime('-1 day')))->order('create_time', 'desc')
|
||||
->field('day')
|
||||
->find();
|
||||
return $noRecordTasks?$noRecordTasks['day']:null;
|
||||
}
|
||||
|
||||
public static function getUserSignCount($user_id)
|
||||
{
|
||||
$user_sign_record_db = DatabaseRoute::getDb('user_sign_record', $user_id);
|
||||
$noRecordTasks = $user_sign_record_db->where('sign_day', '>', date('Y-m') . '-00')->order('create_time', 'asc')
|
||||
->count();
|
||||
return $noRecordTasks;
|
||||
}
|
||||
|
||||
public static function getTaskCenterRecordCount($user_id, $task_id, $day_date)
|
||||
{
|
||||
$data = DatabaseRoute::getDb('task_center_record', $user_id)->where(['task_id' => $task_id]);
|
||||
if($day_date) {
|
||||
$data = $data->where('create_time', '>', $day_date);
|
||||
}
|
||||
return $data->count();
|
||||
}
|
||||
public static function getTaskCenterRecordCountUserIdAll($user_id_s, $task_id):string|int
|
||||
{
|
||||
$count = 0;
|
||||
foreach ($user_id_s as $k => $user_id) {
|
||||
$count += DatabaseRoute::getDb('task_center_record', $user_id)->where(['task_id' => $task_id])->count();
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
public static function getUserSignData($user_id, $user)
|
||||
{
|
||||
$dto = [
|
||||
'userId' => $user_id,
|
||||
'username' => $user['user_name'],
|
||||
'mobile' => $user['phone'],
|
||||
'signDays' => 0,
|
||||
'enable' => 1,
|
||||
'isReceived' => 0,
|
||||
];
|
||||
$config = CommonInfo::where(['type' => 918])->find()->value;
|
||||
if(!$config) {
|
||||
return returnErrorData('签到活动配置不存在');
|
||||
}
|
||||
// 活动天数
|
||||
$parts = explode(',', $config);
|
||||
$activeDays = isset($parts[0]) ? (int)$parts[0] : 0;
|
||||
// 当前日期
|
||||
$beginDay = date('Y-m-d');
|
||||
// 签到记录
|
||||
$recordList = [];
|
||||
// 连续签到日期
|
||||
$flowDays = buildFlowDays($beginDay, $activeDays);
|
||||
$list = DatabaseRoute::getDb('user_sign_record', $user_id)->order('sign_day', 'asc')->order('create_time', 'asc')->order('id', 'asc')->select()->toArray();
|
||||
// 第x天
|
||||
$index = 1;
|
||||
// 连续签到天数
|
||||
$signDays = 0;
|
||||
if(!$list) {
|
||||
foreach ($flowDays as $k => $day) {
|
||||
$recordList[] = [
|
||||
'showText' => '第' . $index . '天',
|
||||
'status' => 0,
|
||||
'signDay' => $day,
|
||||
'signDate' => '',
|
||||
];
|
||||
$index ++;
|
||||
}
|
||||
$dto['recordList'] = $recordList;
|
||||
return returnSuccessData($dto);
|
||||
}
|
||||
$beginSignDay = $list[0]['sign_day'];
|
||||
$enDay = date('Y-m-d', strtotime('+' . $activeDays - 1 . 'days', strtotime(date('Y-m-d'))));
|
||||
|
||||
$flowDays = buildFlowDaysTwo($beginSignDay, $enDay);
|
||||
// 需要移除的记录
|
||||
$removeList = [];
|
||||
$signMap = [];
|
||||
foreach ($list as $record) {
|
||||
$signMap[$record['sign_day']] = $record['create_time'];
|
||||
}
|
||||
|
||||
foreach ($flowDays as $k => $day) {
|
||||
$date = !empty($signMap[$day])?$signMap[$day]:'';
|
||||
if($date) {
|
||||
$recordList[$k] = [
|
||||
'signDay' => $day,
|
||||
'status' => 1,
|
||||
'signDate' => $date,
|
||||
'showText' => '已签到',
|
||||
];
|
||||
$signDays ++;
|
||||
}else {
|
||||
$recordList[$k] = [
|
||||
'signDay' => $day,
|
||||
'status' => 0,
|
||||
'signDate' => '',
|
||||
];
|
||||
$daysBetween = daysBetween($day);
|
||||
if($daysBetween > 0) {
|
||||
$signDays = 0;
|
||||
$recordList[$k]['showText'] = '未签到';
|
||||
}elseif($daysBetween == 0) {
|
||||
$recordList[$k]['showText'] = '待签到';
|
||||
}else {
|
||||
$recordList[$k]['showText'] = '第' . $k + 1 .'天';
|
||||
}
|
||||
}
|
||||
if($signDays == $activeDays) {
|
||||
break;
|
||||
}
|
||||
|
||||
// 1. 检查记录日期是否等于结束日期
|
||||
|
||||
if ($day === $enDay) {
|
||||
// 2. 反转记录列表
|
||||
$tempList = array_reverse($recordList);
|
||||
// 3. 计算今天且状态为"1"的记录数
|
||||
$today = date('Y-m-d');
|
||||
$isInclude = 0;
|
||||
|
||||
foreach ($tempList as $item) {
|
||||
if ($item['signDay'] === $today && $item['status'] == 1) {
|
||||
$isInclude++;
|
||||
}
|
||||
}
|
||||
// 4. 确定要移除的记录数量
|
||||
$removeSize = $signDays;
|
||||
if ($isInclude > 0) {
|
||||
$removeSize = $signDays - 1;
|
||||
}
|
||||
// 5. 添加要移除的记录到 removeList
|
||||
for ($i = 0; $i < $removeSize; $i++) {
|
||||
if (isset($tempList[$i])) { // 确保索引存在
|
||||
$removeList[] = $tempList[$i];
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 1. 移除元素
|
||||
$recordList = array_udiff($recordList, $removeList, function($a, $b) {
|
||||
return ($a === $b) ? 0 : 1;
|
||||
});
|
||||
// 2. 第一次反转
|
||||
$recordList = array_reverse($recordList);
|
||||
// 3. 截取前 activeDays 条记录
|
||||
$recordList = array_slice($recordList, 0, $activeDays, true);
|
||||
// 4. 第二次反转
|
||||
$recordList = array_reverse($recordList);
|
||||
$index = 1;
|
||||
foreach ($recordList as $k => $v) {
|
||||
$recordList[$k]['showText'] = sprintf($v['showText'], '第' . $index . '天');
|
||||
$index ++;
|
||||
}
|
||||
$dto['recordList'] = $recordList;
|
||||
$dto['signDays'] = $signDays;
|
||||
if($signDays >= $activeDays) {
|
||||
$dto['enable'] = 0;
|
||||
}
|
||||
|
||||
$count = DatabaseRoute::getDb('user_money_details', $user_id)->where('type', 1)
|
||||
->where('classify', 7)
|
||||
->where('money_type', 1)
|
||||
->where('title', 'like', '[连续签到%')
|
||||
->where('title', 'like', '%天]')
|
||||
->count();
|
||||
$dto['isReceived'] = $count > 0 ? 1 : 0;
|
||||
return returnSuccessData($dto);
|
||||
}
|
||||
|
||||
}
|
||||
243
app/czg/app/model/WithDraw.php
Normal file
243
app/czg/app/model/WithDraw.php
Normal file
@@ -0,0 +1,243 @@
|
||||
<?php
|
||||
|
||||
namespace app\api\model;
|
||||
|
||||
use app\common\library\DatabaseRoute;
|
||||
use app\exception\SysException;
|
||||
use app\utils\WuYouPayUtils;
|
||||
use think\facade\Db;
|
||||
|
||||
class WithDraw
|
||||
{
|
||||
public static function goWithDraw($userId, $amount, $msg='', bool $isSys=false, bool $isAlipay=false)
|
||||
{
|
||||
if (bccomp($amount, "0", 2) <= 0) {
|
||||
throw new SysException('请不要输入小于0的数字,请输入正确的提现金额!');
|
||||
}
|
||||
|
||||
$cashInfo = [
|
||||
'is_out' => 0,
|
||||
'money' => $amount,
|
||||
'user_id' => $userId,
|
||||
'user_type' => $isSys ? 2 : 1,
|
||||
'state' => 0,
|
||||
'rate' => 0,
|
||||
'create_at' => getNormalDate(),
|
||||
'withdraw_type' => 1,
|
||||
'order_number' => uuid(),
|
||||
];
|
||||
|
||||
$moneyDetails = [
|
||||
'user_id' => $userId,
|
||||
'sys_user_id' => $userId,
|
||||
'title' => '[提现]',
|
||||
'content' => "提现:{$amount}元",
|
||||
'type' => 2,
|
||||
'state' => 2,
|
||||
'classify' => 4,
|
||||
'money' => $amount,
|
||||
'create_time' => getNormalDate(),
|
||||
'money_type' => 1,
|
||||
];
|
||||
|
||||
$userMoney = [];
|
||||
$flag = true;
|
||||
|
||||
// 系统用户提现
|
||||
if ($isSys) {
|
||||
$user = DatabaseRoute::getDb('sys_user', $userId)->find();
|
||||
$msgCount = Db::name('msg')->where([
|
||||
'phone' => $user['mobile'],
|
||||
'code' => $msg
|
||||
])->count();
|
||||
if (!$msgCount) {
|
||||
throw new SysException("验证码不正确");
|
||||
}
|
||||
|
||||
if (empty($user['zhi_fu_bao']) || empty($user['zhi_fu_bao_name'])) {
|
||||
throw new SysException([
|
||||
'code' => 9999,
|
||||
'msg' => '请先绑定支付宝账号'
|
||||
]);
|
||||
}
|
||||
$cashInfo['zhifubao'] = $user['zhi_fu_bao'];
|
||||
$cashInfo['zhifubao_name'] = $user['zhi_fu_bao_name'];
|
||||
$cashInfo['bank_name'] = '';
|
||||
|
||||
// 校验余额
|
||||
$userMoney = Db::name('sys_user_money')->where([
|
||||
'user_id' => $userId
|
||||
])->find();
|
||||
}else{
|
||||
$userMoney = DatabaseRoute::getDb('user_money', $userId)->find();
|
||||
$user = TbUser::selectUserById($userId);
|
||||
if ($user['status'] != 1) {
|
||||
throw new SysException([
|
||||
'code' => 9999,
|
||||
'msg' => $user['status'] == 0 ? '账号不存在': '账号已被禁用,请联系客服处理'
|
||||
]);
|
||||
}
|
||||
|
||||
$userInfo = DatabaseRoute::getDb('user_info', $userId)->find();
|
||||
if (!$userInfo || empty($userInfo['cert_name'])) {
|
||||
throw new SysException([
|
||||
'code' => 9991,
|
||||
'msg' => '请先实名认证'
|
||||
]);
|
||||
}
|
||||
|
||||
if (empty($userInfo['account_no']) || empty($userInfo['mobile'])) {
|
||||
throw new SysException([
|
||||
'code' => 9991,
|
||||
'msg' => '需重新完成实名认证后才可提现'
|
||||
]);
|
||||
}
|
||||
|
||||
if (empty($userInfo['bank_name'] && !empty($userInfo['resp_json']))) {
|
||||
DatabaseRoute::getDb('user_info', $userId, true, true)->update([
|
||||
'bank_name' => self::getBankName($userInfo['resp_json'])
|
||||
]);
|
||||
}
|
||||
|
||||
if (empty($userInfo['bank_name'])) {
|
||||
DatabaseRoute::getDb('user_info', $userId, true, true)->delete();
|
||||
throw new SysException([
|
||||
'code' => 9991,
|
||||
'msg' => '需重新完成实名认证后才可提现'
|
||||
]);
|
||||
}
|
||||
|
||||
if ($isAlipay) {
|
||||
$cashInfo['zhifubao'] = $user['zhi_fu_bao'];
|
||||
$cashInfo['zhifubao_name'] = $user['zhi_fu_bao_name'];
|
||||
|
||||
if ($userInfo['cert_name'] != $user['zhi_fu_bao_name']) {
|
||||
throw new SysException([
|
||||
'code' => 9991,
|
||||
'msg' => '认证名称不一致,请重新确认!'
|
||||
]);
|
||||
}
|
||||
}else{
|
||||
$cashInfo['zhifubao'] = $userInfo['account_no'];
|
||||
$cashInfo['zhifubao_name'] = $userInfo['cert_name'];
|
||||
}
|
||||
|
||||
$cashInfo['bank_name'] = $userInfo['bank_name'];
|
||||
$cashInfo['id_card_no'] = $userInfo['cert_no'];
|
||||
$cashInfo['province'] = $userInfo['province'];
|
||||
$cashInfo['city'] = $userInfo['city'];
|
||||
$cashInfo['bank_branch'] = $userInfo['bank_branch'];
|
||||
|
||||
// 校验黑名单用户
|
||||
$count = Db::name('tb_withdraw_blacklist')->where([
|
||||
'real_name' => $cashInfo['zhifubao_name']
|
||||
])->count();
|
||||
|
||||
$blackCount = Db::name('tb_user_blacklist')->where([
|
||||
'id_card_no' => trim($userInfo['cert_no'])
|
||||
])->count();
|
||||
if ($blackCount) {
|
||||
$cashInfo['state'] = 2;
|
||||
$cashInfo['out_at'] = getNormalDate();
|
||||
$moneyDetails['content'] = "刷单用户禁止提现:$amount";
|
||||
$flag = false;
|
||||
}else if($count) {
|
||||
$cashInfo['state'] = 3;
|
||||
$cashInfo['content'] = "提现=$amount";
|
||||
$moneyDetails['relation_id'] = '提现黑名单用户,请谨慎审核!';
|
||||
$flag = false;
|
||||
}
|
||||
}
|
||||
|
||||
$info = (new CommonInfo())->getByCode(112);
|
||||
if (!$info || bccomp($amount, $info['value'], 2) < 0) {
|
||||
throw new SysException("不满足最低提现金额,请重新输入!");
|
||||
}
|
||||
|
||||
// 校验余额
|
||||
if (bccomp($userMoney['amount'], $amount, 2) < 0) {
|
||||
throw new SysException("可提现余额不足");
|
||||
}
|
||||
|
||||
self::checkCanCash($userId, 1, $amount);
|
||||
|
||||
|
||||
if($flag) {
|
||||
$cashInfo['state'] = 4;
|
||||
$resp = WuYouPayUtils::extractOrder($isAlipay, $cashInfo['order_number'], $userId, $amount, $isSys, $cashInfo['zhifubao'], $cashInfo['zhifubao_name'],
|
||||
$cashInfo['bank_name'], !empty($cashInfo['province'])?$cashInfo['province']:'', !empty($cashInfo['city'])?$cashInfo['city']:'', !empty($cashInfo['bank_branch'])?$cashInfo['bank_branch']:'');
|
||||
if (isset($resp['status']) && ($resp['status'] == 2 || $resp['status'] == 10000)) {
|
||||
$cashInfo['content'] = "成功提现:$amount";
|
||||
$cashInfo['state'] = 1;
|
||||
$cashInfo['out_at'] = getNormalDate();
|
||||
}
|
||||
|
||||
if (!empty($resp['error_msg'])){
|
||||
throw new SysException($resp['error_msg']);
|
||||
}
|
||||
}
|
||||
// 插入提现记录
|
||||
unset($cashInfo['id_card_no']);
|
||||
unset($cashInfo['content']);
|
||||
DatabaseRoute::getDb('cash_out', $userId, true)->insert($cashInfo);
|
||||
|
||||
DatabaseRoute::getDb($isSys ? 'sys_user_money_details' : 'user_money_details', $userId, true)->insert($moneyDetails);
|
||||
if ($isSys) {
|
||||
Db::name('sys_user_money')->where([
|
||||
'user_id' => $userId
|
||||
])->dec('amount', floatval($amount))->update();
|
||||
}else{
|
||||
DatabaseRoute::getDb('user_money', $userId, true)->where([
|
||||
'user_id' => $userId
|
||||
])->dec('amount', floatval($amount))->update();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private static function checkCanCash($userId, $type, $money)
|
||||
{
|
||||
|
||||
if ($type == 1) {
|
||||
// 查询当日体现次数
|
||||
$count = DatabaseRoute::getDb('cash_out', $userId)->where([
|
||||
['state', 'in', [1, 3]],
|
||||
'withdraw_type' => 1,
|
||||
['create_at', '>=', date('Y-m-d 00:00:00')],
|
||||
])->count();
|
||||
|
||||
$commonInfo = (new CommonInfo())->getByCode(922);
|
||||
if (!$commonInfo) {
|
||||
throw new SysException("【922】每日提现次数上限未配置");
|
||||
}
|
||||
|
||||
if ($count >= intval($commonInfo['value'])) {
|
||||
throw new SysException("超过当日提现限制次数{$commonInfo['value']}次,请明天再试!");
|
||||
}
|
||||
|
||||
$commonInfo = (new CommonInfo())->getByCode(923);
|
||||
if (!$commonInfo) {
|
||||
throw new SysException("【923】单次提现超额未配置");
|
||||
}
|
||||
if (bccomp($money, $commonInfo['value']) > 0) {
|
||||
throw new SysException("单次提现超额");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static function getBankName($json)
|
||||
{
|
||||
|
||||
$resp = json_decode($json);
|
||||
$result = $resp['result'];
|
||||
$errorCode = $resp['error_code'];
|
||||
$respCode = $resp['respCode'];
|
||||
if ($errorCode == 0 && $respCode == '0') {
|
||||
return $result['bancardInfor'] ? $result['bancardInfor']['bankName'] : '';
|
||||
}
|
||||
return '';
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
16
app/enums/ErrEnums.php
Normal file
16
app/enums/ErrEnums.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace app\enums;
|
||||
|
||||
enum ErrEnums: string
|
||||
{
|
||||
case MANY_REQUEST = '请求频繁请稍候再试';
|
||||
case FAIL = '请求失败';
|
||||
public function code(): int
|
||||
{
|
||||
return match($this) {
|
||||
default=>500
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
35
app/exception/CzgException.php
Normal file
35
app/exception/CzgException.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace app\exception;
|
||||
|
||||
use app\enums\ErrEnums;
|
||||
use Exception;
|
||||
|
||||
class CzgException extends \RuntimeException
|
||||
{
|
||||
public function __construct($data = [
|
||||
'msg' => '',
|
||||
'args' => [],
|
||||
'code' => 500
|
||||
], ...$args)
|
||||
{
|
||||
if ($data instanceof ErrEnums) {
|
||||
parent::__construct($data->value, $data->code());
|
||||
|
||||
}else if (is_string($data)) {
|
||||
if (!empty($args)) {
|
||||
$data = format($data, $args);
|
||||
|
||||
}
|
||||
parent::__construct($data, 500);
|
||||
}
|
||||
else{
|
||||
$val = $data['msg'];
|
||||
if (isset($data['args'])) {
|
||||
$val = format($data['msg'], $data['args']);
|
||||
}
|
||||
parent::__construct($val, isset($data['code']) ?? 500);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
22
app/exception/HttpResponseException.php
Normal file
22
app/exception/HttpResponseException.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
namespace app\exception;
|
||||
|
||||
use RuntimeException;
|
||||
use support\Response;
|
||||
|
||||
class HttpResponseException extends RuntimeException
|
||||
{
|
||||
protected $response;
|
||||
|
||||
// 接收 Response 实例作为参数
|
||||
public function __construct(Response $response)
|
||||
{
|
||||
$this->response = $response;
|
||||
}
|
||||
|
||||
// 获取响应对象
|
||||
public function getResponse()
|
||||
{
|
||||
return $this->response;
|
||||
}
|
||||
}
|
||||
35
app/exception/SysException.php
Normal file
35
app/exception/SysException.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace app\exception;
|
||||
|
||||
use app\enums\ErrEnums;
|
||||
use Exception;
|
||||
|
||||
class SysException extends \RuntimeException
|
||||
{
|
||||
public function __construct($data = [
|
||||
'msg' => '',
|
||||
'args' => [],
|
||||
'code' => 500
|
||||
], ...$args)
|
||||
{
|
||||
if ($data instanceof ErrEnums) {
|
||||
parent::__construct($data->value, $data->code());
|
||||
|
||||
}else if (is_string($data)) {
|
||||
if (!empty($args)) {
|
||||
$data = format($data, $args);
|
||||
|
||||
}
|
||||
parent::__construct($data, 500);
|
||||
}
|
||||
else{
|
||||
$val = $data['msg'];
|
||||
if (isset($data['args'])) {
|
||||
$val = format($data['msg'], $data['args']);
|
||||
}
|
||||
parent::__construct($val, isset($data['code']) ?? 500);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
94
app/utils/AliUtils.php
Normal file
94
app/utils/AliUtils.php
Normal file
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
namespace app\utils;
|
||||
|
||||
use app\exception\SysException;
|
||||
use think\facade\Log;
|
||||
|
||||
class AliUtils
|
||||
{
|
||||
public static function verify($name, $idCard, $accountNo, $bankPreMobile)
|
||||
{
|
||||
Log::info("阿里云四要素认证请求参数: {$name}, {$idCard}, {$accountNo}, {$bankPreMobile}");
|
||||
|
||||
// 参数校验
|
||||
if (empty($name)) {
|
||||
throw new SysException("持卡人姓名不能为空");
|
||||
}
|
||||
if (empty($idCard)) {
|
||||
throw new SysException("身份证号码不能为空");
|
||||
}
|
||||
if (empty($accountNo)) {
|
||||
throw new SysException("银行卡卡号不能为空");
|
||||
}
|
||||
if (empty($bankPreMobile)) {
|
||||
throw new SysException("银行预留手机号码不能为空");
|
||||
}
|
||||
if (!preg_match('/^[\x{4e00}-\x{9fa5}]{2,}$/u', $name)) {
|
||||
throw new SysException("持卡人姓名不合法");
|
||||
}
|
||||
if (!self::isValidIdCard($idCard)) {
|
||||
throw new SysException("身份证号码不合法");
|
||||
}
|
||||
if (!preg_match('/^1[3-9]\d{9}$/', $bankPreMobile)) {
|
||||
throw new SysException("银行预留手机号码格式不正确");
|
||||
}
|
||||
|
||||
$appcode = 'f98606b602564d209f37fc02b0bd590c';
|
||||
$headers = [
|
||||
"Authorization: APPCODE {$appcode}",
|
||||
"Content-Type: application/x-www-form-urlencoded; charset=UTF-8"
|
||||
];
|
||||
|
||||
$url = 'https://bkvip.market.alicloudapi.com/v3/bcheck';
|
||||
$body = http_build_query([
|
||||
'accountNo' => $accountNo,
|
||||
'name' => $name,
|
||||
'idCardCode' => $idCard,
|
||||
'bankPreMobile' => $bankPreMobile
|
||||
]);
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_URL => $url,
|
||||
CURLOPT_HTTPHEADER => $headers,
|
||||
CURLOPT_POST => true,
|
||||
CURLOPT_POSTFIELDS => $body,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_TIMEOUT => 15
|
||||
]);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
|
||||
Log::info("阿里云四要素: {$idCard}, {$name}, {$accountNo}, {$bankPreMobile}, 结果: {$response}");
|
||||
|
||||
$ret = json_decode($response, true);
|
||||
if (!isset($ret['error_code']) || !isset($ret['result'])) {
|
||||
throw new SysException("阿里云接口返回格式错误".$response);
|
||||
}
|
||||
|
||||
$errorCode = $ret['error_code'];
|
||||
$result = $ret['result'];
|
||||
$respCode = $result['respCode'] ?? '';
|
||||
|
||||
if ($errorCode == 0 && $respCode === "0") {
|
||||
// 验证通过
|
||||
} else {
|
||||
throw new SysException($result['respMsg'] ?? '认证失败');
|
||||
}
|
||||
|
||||
$bankName = $result['bancardInfor']['bankName'] ?? null;
|
||||
|
||||
return [
|
||||
'bankName' => $bankName,
|
||||
'respJson' => $response
|
||||
];
|
||||
}
|
||||
|
||||
private static function isValidIdCard($idCard)
|
||||
{
|
||||
// 简单校验身份证号
|
||||
return preg_match('/^\d{17}[\dXx]$/', $idCard);
|
||||
}
|
||||
}
|
||||
86
app/utils/JwtUtils.php
Normal file
86
app/utils/JwtUtils.php
Normal file
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
namespace app\utils;
|
||||
use Firebase\JWT\JWT;
|
||||
use Firebase\JWT\Key;
|
||||
|
||||
class JwtUtils
|
||||
{
|
||||
private string $secret = "f4e2e52034348f86b67cde581c0f9eb5";
|
||||
private int $expire = 604800; // 单位:秒
|
||||
private string $header = "token";
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->secret = base64_decode($this->secret);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 生成 JWT Token
|
||||
*/
|
||||
public function generateToken($userId, string $type): string
|
||||
{
|
||||
$now = time();
|
||||
$payload = [
|
||||
'sub' => (string)$userId,
|
||||
'type' => $type,
|
||||
'iat' => $now,
|
||||
'exp' => $now + $this->expire
|
||||
];
|
||||
|
||||
return JWT::encode($payload, $this->secret, 'HS512');
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 token 中解析出 Claims
|
||||
*/
|
||||
public function getClaimByToken(string $token): ?object
|
||||
{
|
||||
try {
|
||||
return JWT::decode($token, new Key($this->secret, 'HS512'));
|
||||
} catch (\Exception $e) {
|
||||
error_log('Token 验证失败: ' . $e->getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断 token 是否过期(通过传入的 exp 字段)
|
||||
*/
|
||||
public function isTokenExpired(int $exp): bool
|
||||
{
|
||||
return $exp < time();
|
||||
}
|
||||
|
||||
// Getter/Setter
|
||||
public function getSecret(): string
|
||||
{
|
||||
return $this->secret;
|
||||
}
|
||||
|
||||
public function getExpire(): int
|
||||
{
|
||||
return $this->expire;
|
||||
}
|
||||
|
||||
public function getHeader(): string
|
||||
{
|
||||
return $this->header;
|
||||
}
|
||||
|
||||
public function setSecret(string $secret): void
|
||||
{
|
||||
$this->secret = $secret;
|
||||
}
|
||||
|
||||
public function setExpire(int $expire): void
|
||||
{
|
||||
$this->expire = $expire;
|
||||
}
|
||||
|
||||
public function setHeader(string $header): void
|
||||
{
|
||||
$this->header = $header;
|
||||
}
|
||||
}
|
||||
230
app/utils/RedisUtils.php
Normal file
230
app/utils/RedisUtils.php
Normal file
@@ -0,0 +1,230 @@
|
||||
<?php
|
||||
|
||||
namespace app\utils;
|
||||
|
||||
use app\exception\SysException;
|
||||
use support\think\Cache;
|
||||
|
||||
class RedisUtils
|
||||
{
|
||||
const CAN_CREATE_ORDER = 'can_create_order:';
|
||||
const CREATE_ORDER_LIMIT = 'createOrder:';
|
||||
const CAN_CASH = 'cash:canCash:';
|
||||
|
||||
public static function checkCanCreateOrder($userId)
|
||||
{
|
||||
$val = cache(self::CAN_CREATE_ORDER . $userId);
|
||||
return empty($val);
|
||||
}
|
||||
|
||||
public static function setUserCanCreateOrder($userId, $seconds)
|
||||
{
|
||||
cache(self::CAN_CREATE_ORDER . $userId, 1, $seconds);
|
||||
}
|
||||
|
||||
public static function setCreateOrderFlagAndCheckLimit($userId, $orderId): bool
|
||||
{
|
||||
// 构造 key
|
||||
$key = "createOrder:{$userId}:{$orderId}";
|
||||
|
||||
// 设置 Redis key,值为 orderId,过期时间 60 秒
|
||||
Cache::store('redis')->set($key, (string)$orderId, 60);
|
||||
|
||||
// 获取 Redis 原生实例
|
||||
$redis = Cache::store('redis')->handler();
|
||||
|
||||
// 使用 scan 非阻塞统计 key 数量
|
||||
$pattern = "createOrder:{$userId}:*";
|
||||
$iterator = null;
|
||||
$count = 0;
|
||||
|
||||
while ($keys = $redis->scan($iterator, $pattern, 100)) {
|
||||
$count += count($keys);
|
||||
}
|
||||
|
||||
// 超过 22 返回 true
|
||||
return $count > 22;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用的“按周统计”计数器(自动按课程 ID、业务类型区分)
|
||||
*
|
||||
* @param string $prefix 前缀标识,如 'course:week_play:'
|
||||
* @param int|string $id 主体 ID(如课程 ID)
|
||||
* @param int $expireSeconds 默认过期时间(秒),默认 7 天
|
||||
* @return int 返回当前统计值
|
||||
*/
|
||||
public static function incrWeekCounter(string $prefix, $id, int $expireSeconds = 604800): int
|
||||
{
|
||||
$key = $prefix . date('oW') . ':' . $id; // oW 为当前“ISO 年 + 周数”,确保每周一个 key
|
||||
$count = Cache::store('redis')->inc($key);
|
||||
|
||||
// 第一次设置时设定过期时间
|
||||
if ($count === 1) {
|
||||
Cache::store('redis')->expire($key, $expireSeconds);
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
|
||||
public static function getconfig()
|
||||
{
|
||||
return Cache::getConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定“周统计”计数值
|
||||
*
|
||||
* @param string $prefix 前缀
|
||||
* @param int|string $id 主体 ID
|
||||
* @return int
|
||||
*/
|
||||
public static function getWeekCounter(string $prefix, $id): int
|
||||
{
|
||||
$key = $prefix . date('oW') . ':' . $id;
|
||||
return (int)Cache::store('redis')->get($key, 0);
|
||||
}
|
||||
|
||||
public static function isCanCash($userId)
|
||||
{
|
||||
$val = cache(self::CAN_CASH . $userId);
|
||||
return !empty($val);
|
||||
}
|
||||
|
||||
public static function checkRealCount(int $userId)
|
||||
{
|
||||
$key = 'updateAuthCertInfo:' . $userId;
|
||||
$val = cache($key);
|
||||
if (!empty($val)) {
|
||||
throw new SysException("实名修改失败: 每月可修改次数已用完,请联系管理员");
|
||||
}
|
||||
}
|
||||
|
||||
public static function setRealCount(int $userId)
|
||||
{
|
||||
$key = 'updateAuthCertInfo:' . $userId;
|
||||
cache($key, 2628000);
|
||||
}
|
||||
|
||||
private static function getFreeWatchKey($userId, $permanently = false)
|
||||
{
|
||||
if ($permanently) {
|
||||
return "free:watch:" . $userId;
|
||||
}
|
||||
|
||||
return "free:watch:" . date('Y-m-d') . ':' . $userId;
|
||||
}
|
||||
|
||||
public static function isExpiredSet($key)
|
||||
{
|
||||
$redis = Cache::store('redis')->handler();
|
||||
|
||||
// 获取 key 的剩余过期时间(单位:秒)
|
||||
$ttl = $redis->ttl($key);
|
||||
|
||||
if ($ttl == -1) {
|
||||
return false;
|
||||
}
|
||||
return $ttl !== -2;
|
||||
}
|
||||
|
||||
public static function getFreeWatchTimeIsExpire($userId)
|
||||
{
|
||||
$freeWatchKey = self::getFreeWatchKey($userId, true);
|
||||
$permanentlyFreeWatch = cache($freeWatchKey);
|
||||
|
||||
$watchKey = self::getFreeWatchKey($userId, false);
|
||||
$payFreeWatchInfo = cache($watchKey);
|
||||
|
||||
$expireTime = -1;
|
||||
$jsonObject = null;
|
||||
|
||||
if (!empty($payFreeWatchInfo)) {
|
||||
$jsonObject = json_decode($payFreeWatchInfo, true);
|
||||
$expireTime = isset($jsonObject['expireTime']) ? $jsonObject['expireTime'] : -1;
|
||||
}
|
||||
|
||||
$now = time(); // 当前时间戳
|
||||
|
||||
if ((!empty($permanentlyFreeWatch) && RedisUtils::isExpiredSet($freeWatchKey)) ||
|
||||
(!empty($permanentlyFreeWatch) && $now >= $expireTime)) {
|
||||
|
||||
if (empty($permanentlyFreeWatch)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!RedisUtils::isExpiredSet($freeWatchKey)) {
|
||||
cache($freeWatchKey, intval($permanentlyFreeWatch));
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
if (empty($payFreeWatchInfo)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$second = isset($jsonObject['second']) ? intval($jsonObject['second']) : 0;
|
||||
|
||||
if ($expireTime == -1) {
|
||||
$jsonObject['expireTime'] = $now + $second;
|
||||
|
||||
$tomorrowZero = strtotime(date('Y-m-d', strtotime('+1 day')));
|
||||
$expire = $tomorrowZero - $now;
|
||||
|
||||
cache($watchKey, json_encode($jsonObject), $expire);
|
||||
return false;
|
||||
} else {
|
||||
return $now > $expireTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static function setFreeWatchTime($userId, $second, $isPermanently = false)
|
||||
{
|
||||
$now = time();
|
||||
$tomorrow = strtotime(date('Y-m-d', strtotime('+1 day')));
|
||||
$freeWatchKey = self::getFreeWatchKey($userId, $isPermanently);
|
||||
|
||||
if ($isPermanently) {
|
||||
$data = cache($freeWatchKey);
|
||||
if (empty($data)) {
|
||||
// 永久的,但不设置过期时间(-1)
|
||||
cache($freeWatchKey, $second);
|
||||
} else {
|
||||
$expire = Cache::store('redis')->handler()->ttl($freeWatchKey);
|
||||
if ($expire === -1 || $expire === false || $expire === null) {
|
||||
$expire = -1;
|
||||
} else {
|
||||
$expire += intval($second);
|
||||
}
|
||||
|
||||
$newValue = intval($data) + intval($second);
|
||||
if ($expire > 0) {
|
||||
cache($freeWatchKey, $newValue, $expire);
|
||||
} else {
|
||||
cache($freeWatchKey, $newValue);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 非永久,临时有效期到明天零点
|
||||
$expire = $tomorrow - $now;
|
||||
|
||||
$jsonObject = [
|
||||
'expireTime' => -1,
|
||||
'second' => intval($second)
|
||||
];
|
||||
|
||||
// 如果不存在才设置
|
||||
if (!Cache::store('redis')->has($freeWatchKey)) {
|
||||
cache($freeWatchKey, json_encode($jsonObject), $expire);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function setCanCashFlag(mixed $userId, int $param)
|
||||
{
|
||||
cache("cash:canCash:".$userId, $param,300);
|
||||
}
|
||||
|
||||
}
|
||||
192
app/utils/WuYouPayUtils.php
Normal file
192
app/utils/WuYouPayUtils.php
Normal file
@@ -0,0 +1,192 @@
|
||||
<?php
|
||||
|
||||
namespace app\utils;
|
||||
|
||||
use app\api\model\CommonInfo;
|
||||
use app\exception\SysException;
|
||||
use think\facade\Log;
|
||||
|
||||
class WuYouPayUtils
|
||||
{
|
||||
|
||||
private static string $mchId;
|
||||
private static string $payUrl;
|
||||
private static string $notifyUrl;
|
||||
private static string $h5BaseUrl;
|
||||
private static string $secret;
|
||||
private static string $extractUrl;
|
||||
private static string $extractNotifyUrl;
|
||||
private static string $queryUrl;
|
||||
|
||||
private static function boot(): void
|
||||
{
|
||||
static $booted = false;
|
||||
if ($booted) {
|
||||
return;
|
||||
}
|
||||
$booted = true;
|
||||
|
||||
self::$mchId = config('wuyou.merchant_id', '');
|
||||
self::$payUrl = config('wuyou.url', '');
|
||||
self::$notifyUrl = config('wuyou.notify_url', '');
|
||||
self::$h5BaseUrl = config('wuyou.h5_base_url', '');
|
||||
self::$secret = config('wuyou.secret', '');
|
||||
self::$extractUrl = config('wuyou.extract_url', '');
|
||||
self::$extractNotifyUrl = config('wuyou.extract_notify_url', '');
|
||||
self::$queryUrl = config('wuyou.query_url', '');
|
||||
|
||||
if (hasEmpty(self::$mchId, self::$payUrl, self::$notifyUrl, self::$h5BaseUrl, self::$secret)) {
|
||||
throw new SysException('缺少必要参数');
|
||||
}
|
||||
}
|
||||
|
||||
public static function getParamsSign(array $params): string
|
||||
{
|
||||
self::boot();
|
||||
ksort($params);
|
||||
|
||||
$signStr = '';
|
||||
foreach ($params as $key => $value) {
|
||||
$signStr .= $key . '=' . $value . '&';
|
||||
}
|
||||
|
||||
$signStr .= 'key=' . self::$secret;
|
||||
|
||||
return strtoupper(md5($signStr));
|
||||
}
|
||||
|
||||
private static function getBaseParams()
|
||||
{
|
||||
self::boot();
|
||||
return [
|
||||
'mch_id' => self::$mchId,
|
||||
'timestamp' => time(),
|
||||
];
|
||||
}
|
||||
|
||||
public static function payOrderTest($orderNo, $userId, $amount, $userAgent, $allId, $payType) {
|
||||
return http_post('192.168.1.21:8080/api/delay', [
|
||||
'callbacks' => 'CODE_SUCCESS',
|
||||
'order_sn' => uuid(),
|
||||
'out_trade_no' => "{$orderNo}-{$userId}",
|
||||
'pay_time' => time(),
|
||||
'sign' => '',
|
||||
'total' => $amount
|
||||
|
||||
]);
|
||||
}
|
||||
|
||||
public static function payOrder($orderNo, $userId, $amount, $userAgent, $allId, $payType)
|
||||
{
|
||||
$payConfig = (new CommonInfo())->getByCode(926)['value'] ?? '0';
|
||||
if ($payConfig !== '1') {
|
||||
throw new SysException('暂无支付渠道');
|
||||
}
|
||||
|
||||
$params = self::getBaseParams();
|
||||
$params['type'] = '6001';
|
||||
$params['is_code'] = '1';
|
||||
$params['out_trade_no'] = "{$orderNo}-{$userId}";
|
||||
$params['total'] = $amount;
|
||||
$params['notify_url'] = self::$notifyUrl;
|
||||
|
||||
$params['sign'] = self::getParamsSign($params);
|
||||
if ($payType === 'h5') {
|
||||
$params['return_url'] = self::$h5BaseUrl . $allId;
|
||||
}
|
||||
|
||||
|
||||
return self::requestPost(self::$payUrl, $params, $userAgent);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发起 POST 请求
|
||||
* @param string $url 请求地址
|
||||
* @param array $params 请求参数
|
||||
* @param string $userAgent User-Agent
|
||||
* @return array|null
|
||||
*/
|
||||
private static function requestPost(string $url, array $params, string $userAgent): ?array
|
||||
{
|
||||
Log::info("[支付]请求地址:{$url},请求参数:".json_encode($params).",User-Agent:{$userAgent}");
|
||||
$curl = curl_init();
|
||||
|
||||
curl_setopt_array($curl, [
|
||||
CURLOPT_URL => $url,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_POST => true,
|
||||
CURLOPT_POSTFIELDS => http_build_query($params),
|
||||
CURLOPT_HTTPHEADER => [
|
||||
'User-Agent: ' . $userAgent,
|
||||
'Content-Type: application/x-www-form-urlencoded',
|
||||
// 'Content-Type: application/json',
|
||||
]
|
||||
]);
|
||||
|
||||
$response = curl_exec($curl);
|
||||
curl_close($curl);
|
||||
Log::info("[支付]返回结果:{$response}");
|
||||
|
||||
return json_decode($response, true);
|
||||
}
|
||||
|
||||
public static function queryExtractOrder($outOrderNo, $userId, $isUser, $amount)
|
||||
{
|
||||
$params = self::getBaseParams();
|
||||
$params['out_trade_no'] = sprintf('%s-%s:%s', $outOrderNo, $userId, $isUser ? 'us' : 'dl');
|
||||
$params['total'] = $amount;
|
||||
$params['sign'] = self::getParamsSign($params);
|
||||
|
||||
return self::requestPost(self::$extractUrl, $params, 'WuYouPay');
|
||||
}
|
||||
|
||||
public static function extractOrder($isAliPay, $outOrderNo, $userId, $amount, $isUser, $account, $userName, $bankName, $province, $city, $bankBranch)
|
||||
{
|
||||
|
||||
$params = self::getBaseParams();
|
||||
|
||||
$params['out_trade_no'] = sprintf('%s-%s:%s', $outOrderNo, $userId, $isUser ? 'us' : 'dl');
|
||||
$params['total'] = $amount;
|
||||
$params['bank_card'] = $account;
|
||||
$params['bank_account_name'] = $userName;
|
||||
|
||||
if ($isAliPay) {
|
||||
$params['bank_name'] = '1';
|
||||
$params['bank_branch'] = '1';
|
||||
$params['province'] = '1';
|
||||
$params['city'] = '1';
|
||||
} else {
|
||||
$params['bank_name'] = $bankName;
|
||||
$params['bank_branch'] = !empty($bankBranch) ? $bankBranch : '1';
|
||||
$params['province'] = !empty($province) ? $province : '1';
|
||||
$params['city'] = !empty($city) ? $city : '1';
|
||||
}
|
||||
|
||||
$params['notify_url'] = self::$extractNotifyUrl;
|
||||
|
||||
$params['sign'] = self::getParamsSign($params);
|
||||
|
||||
$params['business_type'] = 0;
|
||||
if ($isAliPay) {
|
||||
$params['business_attr'] = 'alipay';
|
||||
} else {
|
||||
$params['business_attr'] = 'unionpay';
|
||||
}
|
||||
|
||||
// 发送请求(POST)
|
||||
return self::requestPost(self::$extractUrl, $params, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36');
|
||||
|
||||
}
|
||||
|
||||
public static function queryOrder($trade_no, $user_id, string $amount, string $string)
|
||||
{
|
||||
$params = self::getBaseParams();
|
||||
$params['out_trade_no'] = "{$trade_no}-{$user_id}";
|
||||
$params['total'] = $amount;
|
||||
$params['sign'] = self::getParamsSign($params);
|
||||
|
||||
return self::requestPost(self::$queryUrl, $params, 'WuYouPay');
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user