文件
fenleixinxi/application/api/controller/Favorite.php

260 行
9.1 KiB
PHP

此文件含有模棱两可的 Unicode 字符

此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。

<?php
namespace app\api\controller;
use think\Db;
/**
* 用户端 · 收藏与关注(Module-FAVORITE)
* 路由:
* GET /api/v1/favorite/list 收藏的信息分页列表
* POST /api/v1/favorite/toggle 收藏/取消收藏信息
* POST /api/v1/follow/toggle 关注/取消关注用户
* POST /api/v1/favorite/status 批量查询收藏/关注状态
*
* 数据表:fa_fc_favorite(user_id,post_id,createtime) / fa_fc_follow(user_id,target_user_id,createtime)
* 两表均有唯一索引,所有写入「先查后写 + catch 兜底」,保证并发下**幂等**。
*/
class Favorite extends V1
{
/**
* GET /api/v1/favorite/list {page,size}
* → 分页 {list,page,size,total,hasMore}(list 项为 Post 结构,与信息流同构)
* 规则:仅本人收藏;只返回 audit_status=1 且 status=1 且未软删的信息;按收藏时间倒序。
*/
public function list()
{
$user = $this->auth();
// 闭包重建查询:TP5.0 的 count() 会清空 Query::options,不能复用同一对象
list($rows, $total, $page, $size) = $this->pageQuery(function ($forCount) use ($user) {
$q = Db::name('fc_favorite')->alias('f')
->join('fc_post p', 'p.id = f.post_id')
->where('f.user_id', $user['id'])
->where('p.audit_status', 1)
->where('p.status', 1)
->whereNull('p.deletetime');
if ($forCount) {
return $q;
}
return $q->field('p.*')->order('f.createtime desc, f.id desc');
});
$builder = new Post();
$list = [];
foreach ($rows as $row) {
// 复用 Post 的信息结构,保证「我的收藏」与首页信息流字段完全一致
$list[] = $builder->buildPost($row);
}
$this->ok($this->paginate($list, $total, $page, $size));
}
/**
* POST /api/v1/favorite/toggle {postId}
* → {faved,count}
* faved=true 表示本次操作后为「已收藏」;count 为该信息当前收藏总数。
*/
public function toggle()
{
$user = $this->auth();
$postId = (int)$this->input('postId', 0);
$post = $postId > 0 ? Db::name('fc_post')->where('id', $postId)->whereNull('deletetime')->find() : null;
if (!$post) {
$this->fail('信息不存在', 404);
}
$exist = Db::name('fc_favorite')->where('user_id', $user['id'])->where('post_id', $postId)->find();
$faved = false;
if ($exist) {
Db::name('fc_favorite')->where('id', $exist['id'])->delete();
} else {
try {
Db::name('fc_favorite')->insert([
'user_id' => $user['id'],
'post_id' => $postId,
'createtime' => time(),
]);
$faved = true;
} catch (\Exception $e) {
// 并发下唯一索引冲突:视为已收藏(幂等)
$faved = true;
}
}
$count = (int)Db::name('fc_favorite')->where('post_id', $postId)->count();
$this->ok(['faved' => $faved, 'count' => $count]);
}
/**
* GET /api/v1/follow/list {page,size}
* → 分页 {list,page,size,total,hasMore}
* 返回当前登录用户关注的用户/商家列表
*
* @return void
*/
public function followList()
{
$user = $this->auth();
list($rows, $total, $page, $size) = $this->pageQuery(function ($forCount) use ($user) {
$q = Db::name('fc_follow')->where('user_id', $user['id']);
if ($forCount) {
return $q;
}
return $q->order('id desc');
});
$list = [];
foreach ($rows as $row) {
$targetId = (int)$row['target_user_id'];
$targetUser = Db::name('user')->where('id', $targetId)->find();
if (!$targetUser) {
continue;
}
$ext = Db::name('fc_user_ext')->where('user_id', $targetId)->find();
$merchant = Db::name('fc_merchant')->where('user_id', $targetId)->find();
$isMerchant = $merchant && (int)$merchant['audit_status'] === 1;
// 统计该用户发布的有效信息条数
$postCount = (int)Db::name('fc_post')
->where('user_id', $targetId)
->where('status', 1)
->where('audit_status', 1)
->whereNull('deletetime')
->count();
$list[] = [
'id' => (int)$row['id'],
'userId' => $targetId,
'nickname' => (string)($merchant && $merchant['name'] ? $merchant['name'] : $targetUser['nickname']),
'avatar' => (string)($merchant && $merchant['logo'] ? $merchant['logo'] : $targetUser['avatar']),
'isMerchant' => $isMerchant,
'merchantId' => $merchant ? (int)$merchant['id'] : 0,
'category' => (string)($merchant && $merchant['category'] ? $merchant['category'] : ''),
'postCount' => $postCount,
'followed' => true,
'followTime' => $this->iso($row['createtime']),
];
}
$this->ok($this->paginate($list, $total, $page, $size));
}
/**
* POST /api/v1/follow/toggle {userId}
* → {followed}
* 规则:不能关注自己 → fail('不能关注自己');目标用户必须存在。
*/
public function follow()
{
$user = $this->auth();
$targetUserId = (int)$this->input('userId', 0);
if ($targetUserId <= 0) {
$this->fail('用户不存在', 404);
}
if ($targetUserId === (int)$user['id']) {
$this->fail('不能关注自己');
}
$target = Db::name('user')->where('id', $targetUserId)->find();
if (!$target) {
$this->fail('用户不存在', 404);
}
$exist = Db::name('fc_follow')
->where('user_id', $user['id'])
->where('target_user_id', $targetUserId)
->find();
$followed = false;
if ($exist) {
Db::name('fc_follow')->where('id', $exist['id'])->delete();
} else {
try {
Db::name('fc_follow')->insert([
'user_id' => $user['id'],
'target_user_id' => $targetUserId,
'createtime' => time(),
]);
$followed = true;
} catch (\Exception $e) {
// 并发下唯一索引冲突:视为已关注(幂等)
$followed = true;
}
}
$this->ok(['followed' => $followed]);
}
/**
* POST /api/v1/favorite/status {postIds:[],userIds:[]}
* → {faved:{postId:true|false},followed:{userId:true|false}}
* 说明:批量查询(参照「详情页进入时一次性拿状态」);
* 入参为空数组时返回**空对象** {}((object) 强转,避免 PHP 空数组被序列化成 []);
* 查询结果按请求的 id 全量回填(未收藏/未关注为 false)。
*/
public function status()
{
$user = $this->auth();
$faved = [];
foreach ($this->idArray('postIds') as $postId) {
$faved[(string)$postId] = false;
}
if ($faved) {
$rows = Db::name('fc_favorite')
->where('user_id', $user['id'])
->where('post_id', 'in', array_keys($faved))
->column('post_id');
foreach ($rows as $postId) {
$faved[(string)$postId] = true;
}
}
$followed = [];
foreach ($this->idArray('userIds') as $userId) {
$followed[(string)$userId] = false;
}
if ($followed) {
$rows = Db::name('fc_follow')
->where('user_id', $user['id'])
->where('target_user_id', 'in', array_keys($followed))
->column('target_user_id');
foreach ($rows as $userId) {
$followed[(string)$userId] = true;
}
}
$this->ok([
'faved' => (object)$faved,
'followed' => (object)$followed,
]);
}
/**
* 入参 ID 数组归一化(支持数组 / JSON 字符串 / 逗号串)
* @param string $key
* @return array 去重后的正整数 ID 列表(上限 200,避免超长 IN)
*/
protected function idArray($key)
{
$val = $this->input($key, []);
if (is_string($val)) {
$decoded = json_decode($val, true);
$val = is_array($decoded) ? $decoded : explode(',', $val);
}
if (!is_array($val)) {
return [];
}
$ids = [];
foreach ($val as $item) {
$item = (int)$item;
if ($item > 0) {
$ids[] = $item;
}
}
return array_slice(array_values(array_unique($ids)), 0, 200);
}
}