258 lines
8.3 KiB
PHP
258 lines
8.3 KiB
PHP
<?php
|
||
namespace app\api\controller\asset;
|
||
|
||
use app\api\controller\BaseApiController;
|
||
use app\common\model\AssetUser;
|
||
use app\common\model\AssetResource;
|
||
use app\common\model\AssetUserResource;
|
||
use think\facade\Db;
|
||
|
||
class AssetAppController extends BaseApiController
|
||
{
|
||
// 所有接口都免框架登录验证(使用独立 asset_token 体系,在方法内自行校验)
|
||
public array $notNeedLogin = ['login', 'getResourceList', 'changePassword', 'recordDownload'];
|
||
|
||
/** token 有效期:7 天 */
|
||
const TOKEN_EXPIRE = 86400 * 7;
|
||
|
||
/**
|
||
* @notes 通过 token 获取当前用户(DB 优先,file cache 兜底)
|
||
*/
|
||
private function getAssetUser(): ?AssetUser
|
||
{
|
||
$token = $this->request->header('token');
|
||
if (empty($token)) {
|
||
return null;
|
||
}
|
||
|
||
// 方式1: 从数据库 token 字段查找(需要已执行 asset_user_token.sql)
|
||
try {
|
||
$user = AssetUser::where('token', $token)
|
||
->where('token_expire_time', '>', time())
|
||
->where('status', 1)
|
||
->find();
|
||
if ($user) {
|
||
return $user;
|
||
}
|
||
} catch (\Throwable $e) {
|
||
// token 字段不存在时忽略,走 cache 兜底
|
||
}
|
||
|
||
// 方式2: 从 file cache 查找(兼容旧登录 / 未执行 SQL 迁移)
|
||
$userId = cache('asset_token_' . $token);
|
||
if ($userId) {
|
||
$user = AssetUser::where('id', $userId)->where('status', 1)->find();
|
||
return $user ?: null;
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
* @notes 小程序端登录
|
||
*/
|
||
public function login()
|
||
{
|
||
$phone = $this->request->post('phone');
|
||
$password = $this->request->post('password');
|
||
|
||
if (empty($phone) || empty($password)) {
|
||
return $this->fail('手机号或密码不能为空');
|
||
}
|
||
|
||
$user = AssetUser::where('phone', $phone)->find();
|
||
if (!$user) {
|
||
return $this->fail('账号不存在');
|
||
}
|
||
|
||
if ($user->status != 1) {
|
||
return $this->fail('账号已被禁用');
|
||
}
|
||
|
||
if (!password_verify($password, $user->password)) {
|
||
return $this->fail('密码错误');
|
||
}
|
||
|
||
// 生成 token 并双写(DB + cache 兜底)
|
||
$token = md5($user->id . time() . uniqid('asset', true));
|
||
|
||
// 写入 file cache(始终可用)
|
||
cache('asset_token_' . $token, $user->id, self::TOKEN_EXPIRE);
|
||
|
||
// 尝试写入数据库(需要已执行 SQL 迁移)
|
||
try {
|
||
$user->token = $token;
|
||
$user->token_expire_time = time() + self::TOKEN_EXPIRE;
|
||
$user->save();
|
||
} catch (\Throwable $e) {
|
||
// token 字段不存在时忽略,cache 已经写入
|
||
}
|
||
|
||
return $this->success('登录成功', [
|
||
'token' => $token,
|
||
'user' => [
|
||
'id' => $user->id,
|
||
'phone' => $user->phone
|
||
]
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* @notes 获取用户关联的资源列表 (或公开资源)
|
||
*/
|
||
public function getResourceList()
|
||
{
|
||
$token = $this->request->header('token');
|
||
$user = $this->getAssetUser();
|
||
$type = $this->request->get('type', 1);
|
||
$pageNo = $this->request->get('page_no', 1);
|
||
$pageSize = $this->request->get('page_size', 20);
|
||
$days = (int)$this->request->get('days', 0);
|
||
$usageStatus = (int)$this->request->get('usage_status', 0); // 0=全部, 1=未使用, 2=已使用
|
||
|
||
// 携带了 token 但用户无效(被禁用/过期),返回 -1 触发前端重新登录
|
||
if (!empty($token) && !$user) {
|
||
return $this->fail('账号已被禁用或登录已过期,请重新登录', [], -1);
|
||
}
|
||
|
||
$query = AssetResource::where('type', $type);
|
||
$usedResourceIds = [];
|
||
|
||
if ($user) {
|
||
// 登录用户:只看自己的专属资源
|
||
$resourceIds = AssetUserResource::where('user_id', $user->id)->column('resource_id');
|
||
if (empty($resourceIds)) {
|
||
return $this->data(['lists' => [], 'count' => 0, 'page_no' => $pageNo, 'page_size' => $pageSize]);
|
||
}
|
||
$query = $query->whereIn('id', $resourceIds);
|
||
|
||
// 获取该用户的使用记录
|
||
$usedResourceIds = \think\facade\Db::name('asset_resource_usage')
|
||
->where('user_id', $user->id)
|
||
->column('resource_id');
|
||
|
||
// 使用状态过滤
|
||
if ($usageStatus === 1) { // 未使用
|
||
if (!empty($usedResourceIds)) {
|
||
$query = $query->whereNotIn('id', $usedResourceIds);
|
||
}
|
||
} elseif ($usageStatus === 2) { // 已使用
|
||
if (empty($usedResourceIds)) {
|
||
return $this->data(['lists' => [], 'count' => 0, 'page_no' => $pageNo, 'page_size' => $pageSize]);
|
||
}
|
||
$query = $query->whereIn('id', $usedResourceIds);
|
||
}
|
||
} else {
|
||
// 游客(未登录):只看公开资源(没关联给任何用户的资源)
|
||
$associatedResourceIds = AssetUserResource::column('resource_id');
|
||
if (!empty($associatedResourceIds)) {
|
||
$query = $query->whereNotIn('id', $associatedResourceIds);
|
||
}
|
||
}
|
||
|
||
// 时间过滤
|
||
if ($days > 0) {
|
||
$startTime = strtotime("-{$days} days", strtotime(date('Y-m-d')));
|
||
$query = $query->where('create_time', '>=', $startTime);
|
||
}
|
||
|
||
$count = (clone $query)->count();
|
||
$lists = (clone $query)
|
||
->order('id', 'desc')
|
||
->page($pageNo, $pageSize)
|
||
->select()
|
||
->toArray();
|
||
|
||
// 附加 is_used 字段
|
||
foreach ($lists as &$item) {
|
||
$item['is_used'] = in_array($item['id'], $usedResourceIds);
|
||
}
|
||
|
||
return $this->data([
|
||
'lists' => $lists,
|
||
'count' => $count,
|
||
'page_no' => $pageNo,
|
||
'page_size' => $pageSize,
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* @notes 记录资源下载/使用
|
||
*/
|
||
public function recordDownload()
|
||
{
|
||
$user = $this->getAssetUser();
|
||
if (!$user) {
|
||
return $this->fail('请先登录', [], -1);
|
||
}
|
||
|
||
$resourceId = $this->request->post('resource_id');
|
||
if (empty($resourceId)) {
|
||
return $this->fail('参数缺失');
|
||
}
|
||
|
||
// 检查资源是否存在
|
||
$resource = AssetResource::find($resourceId);
|
||
if (!$resource) {
|
||
return $this->fail('资源不存在');
|
||
}
|
||
|
||
// 检查是否有关联权限 (或者是公开资源)
|
||
$isAssociated = AssetUserResource::where('user_id', $user->id)->where('resource_id', $resourceId)->find();
|
||
$isPublic = !AssetUserResource::where('resource_id', $resourceId)->find();
|
||
|
||
if (!$isAssociated && !$isPublic) {
|
||
return $this->fail('无权操作此资源');
|
||
}
|
||
|
||
// 记录下载
|
||
$exists = \think\facade\Db::name('asset_resource_usage')
|
||
->where('user_id', $user->id)
|
||
->where('resource_id', $resourceId)
|
||
->find();
|
||
|
||
if (!$exists) {
|
||
\think\facade\Db::name('asset_resource_usage')->insert([
|
||
'user_id' => $user->id,
|
||
'resource_id' => $resourceId,
|
||
'create_time' => time()
|
||
]);
|
||
}
|
||
|
||
return $this->success('记录成功');
|
||
}
|
||
|
||
/**
|
||
* @notes 修改密码
|
||
*/
|
||
public function changePassword()
|
||
{
|
||
$user = $this->getAssetUser();
|
||
if (!$user) {
|
||
return $this->fail('登录已过期,请重新登录', [], -1);
|
||
}
|
||
|
||
$oldPassword = $this->request->post('old_password');
|
||
$password = $this->request->post('password');
|
||
|
||
if (empty($oldPassword)) {
|
||
return $this->fail('请输入原密码');
|
||
}
|
||
if (empty($password)) {
|
||
return $this->fail('请输入新密码');
|
||
}
|
||
if (strlen($password) < 6) {
|
||
return $this->fail('新密码至少6位');
|
||
}
|
||
|
||
if (!password_verify($oldPassword, $user->password)) {
|
||
return $this->fail('原密码不正确');
|
||
}
|
||
|
||
$user->password = password_hash($password, PASSWORD_DEFAULT);
|
||
$user->save();
|
||
|
||
return $this->success('密码修改成功');
|
||
}
|
||
}
|