249 行
8.9 KiB
PHP
249 行
8.9 KiB
PHP
<?php
|
||
|
||
namespace app\api\controller;
|
||
|
||
use think\Db;
|
||
|
||
/**
|
||
* 用户端 · 商家门店与主页(Module-MERCHANT)
|
||
* 路由:
|
||
* GET /api/v1/merchant/:id M1 商家公开主页/门店详情
|
||
* GET /api/v1/merchant/:id/posts M2 商家名下发布的信息流(分页)
|
||
*/
|
||
class Merchant extends V1
|
||
{
|
||
/**
|
||
* M1 获取商家门店详情及公开档案
|
||
* GET /api/v1/merchant/:id
|
||
* 规则:允许游客;支持通过 merchant.id 或 user_id 查询;若提供客户端坐标则计算到店距离
|
||
*
|
||
* @return void
|
||
*/
|
||
public function detail()
|
||
{
|
||
$id = (int)$this->routeId();
|
||
if ($id <= 0) {
|
||
$id = (int)$this->input('id', 0) ?: (int)$this->input('userId', 0);
|
||
}
|
||
if ($id <= 0) {
|
||
$this->fail('缺少商家参数', 400);
|
||
}
|
||
|
||
// 优先按 merchant.id 查,若未命中再按 user_id 查
|
||
$m = Db::name('fc_merchant')->where('id', $id)->find();
|
||
if (!$m) {
|
||
$m = Db::name('fc_merchant')->where('user_id', $id)->find();
|
||
}
|
||
if (!$m) {
|
||
$this->fail('商家门店不存在', 404);
|
||
}
|
||
|
||
// 关联发布者基础信息
|
||
$user = Db::name('user')->where('id', $m['user_id'])->find();
|
||
$userNickname = $user ? (string)$user['nickname'] : '';
|
||
$userAvatar = $user ? (string)$user['avatar'] : '';
|
||
|
||
// 统计真实有效发布数量
|
||
$postCount = (int)Db::name('fc_post')
|
||
->where('user_id', $m['user_id'])
|
||
->where('status', 1)
|
||
->where('audit_status', 1)
|
||
->whereNull('deletetime')
|
||
->count();
|
||
|
||
// 计算用户与门店之间的物理距离(km)
|
||
$clientLat = $this->coord('lat', 90);
|
||
$clientLng = $this->coord('lng', 180);
|
||
$distance = null;
|
||
if ($clientLat !== null && $clientLng !== null && !empty($m['lat']) && !empty($m['lng'])) {
|
||
$distance = $this->calcDistance($clientLat, $clientLng, (float)$m['lat'], (float)$m['lng']);
|
||
}
|
||
|
||
// 首屏拉取前 6 条该商家的有效发帖
|
||
$posts = $this->fetchMerchantPosts($m['user_id'], 1, 6);
|
||
|
||
// 严格保护商家联系电话:未付费解锁或非会员状态下脱敏,杜绝从信息详情页跳转商家主页白嫖绕过
|
||
$rawContact = (string)(isset($m['contact']) ? $m['contact'] : '');
|
||
$unlocked = false;
|
||
$me = $this->user();
|
||
if ($me) {
|
||
$now = time();
|
||
$ext = $this->ext($me['id']);
|
||
$isMember = (int)$ext['is_member'] === 1
|
||
&& !empty($ext['member_expiretime']) && (int)$ext['member_expiretime'] > $now;
|
||
$isOwner = (int)$m['user_id'] === (int)$me['id'];
|
||
if ($isOwner || $isMember) {
|
||
$unlocked = true;
|
||
} else {
|
||
// 检查是否从已解锁的关联信息跳转而来
|
||
$postId = (int)$this->input('postId', 0);
|
||
if ($postId > 0) {
|
||
$paidView = Db::name('fc_pay_view')
|
||
->where('user_id', $me['id'])
|
||
->where('post_id', $postId)
|
||
->find();
|
||
if ($paidView) {
|
||
$unlocked = true;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
$displayContact = $unlocked ? $rawContact : $this->maskPhone($rawContact);
|
||
|
||
$data = [
|
||
'id' => (int)$m['id'],
|
||
'userId' => (int)$m['user_id'],
|
||
'name' => (string)$m['name'],
|
||
'logo' => (string)(isset($m['logo']) ? $m['logo'] : ''),
|
||
'banner' => (string)(isset($m['banner']) ? $m['banner'] : ''),
|
||
'address' => (string)(isset($m['address']) ? $m['address'] : ''),
|
||
'lat' => (isset($m['lat']) && $m['lat'] !== null && $m['lat'] !== '') ? (float)$m['lat'] : null,
|
||
'lng' => (isset($m['lng']) && $m['lng'] !== null && $m['lng'] !== '') ? (float)$m['lng'] : null,
|
||
'contact' => $displayContact,
|
||
'unlocked' => $unlocked,
|
||
'category' => (string)(isset($m['category']) ? $m['category'] : ''),
|
||
'notice' => (string)(isset($m['notice']) ? $m['notice'] : ''),
|
||
'auditStatus' => (int)$m['audit_status'],
|
||
'verified' => (int)$m['audit_status'] === 1,
|
||
'licenseVerified' => !empty($m['license']) && (int)$m['audit_status'] === 1,
|
||
'postCount' => $postCount,
|
||
'distance' => $distance,
|
||
'userNickname' => $userNickname,
|
||
'userAvatar' => $userAvatar,
|
||
'createdAt' => isset($m['createtime']) ? $this->iso($m['createtime']) : '',
|
||
'posts' => $posts['list'],
|
||
'hasMorePosts' => $posts['hasMore'],
|
||
];
|
||
|
||
$this->ok($data);
|
||
}
|
||
|
||
/**
|
||
* M2 获取商家名下的信息列表(支持分页)
|
||
* GET /api/v1/merchant/:id/posts
|
||
*
|
||
* @return void
|
||
*/
|
||
public function posts()
|
||
{
|
||
$id = (int)$this->routeId();
|
||
if ($id <= 0) {
|
||
$id = (int)$this->input('id', 0) ?: (int)$this->input('userId', 0);
|
||
}
|
||
if ($id <= 0) {
|
||
$this->fail('缺少商家参数', 400);
|
||
}
|
||
|
||
$userId = $id;
|
||
$m = Db::name('fc_merchant')->where('id', $id)->find();
|
||
if ($m) {
|
||
$userId = (int)$m['user_id'];
|
||
}
|
||
|
||
list($page, $size) = $this->pager(10, 30);
|
||
$res = $this->fetchMerchantPosts($userId, $page, $size);
|
||
$this->ok($res);
|
||
}
|
||
|
||
/**
|
||
* 查询指定商家的发帖列表统一组装
|
||
*
|
||
* @param int $userId 发布者用户ID
|
||
* @param int $page 页码
|
||
* @param int $size 每页数量
|
||
* @return array
|
||
*/
|
||
protected function fetchMerchantPosts($userId, $page, $size)
|
||
{
|
||
$q = Db::name('fc_post')
|
||
->where('user_id', $userId)
|
||
->where('status', 1)
|
||
->where('audit_status', 1)
|
||
->whereNull('deletetime');
|
||
|
||
$total = (int)$q->count();
|
||
$rows = Db::name('fc_post')
|
||
->where('user_id', $userId)
|
||
->where('status', 1)
|
||
->where('audit_status', 1)
|
||
->whereNull('deletetime')
|
||
->order('is_top desc, id desc')
|
||
->page($page, $size)
|
||
->select();
|
||
|
||
$list = [];
|
||
foreach ($rows as $r) {
|
||
$catName = '';
|
||
if (!empty($r['category_id'])) {
|
||
$cat = Db::name('fc_category')->where('id', $r['category_id'])->value('name');
|
||
$catName = $cat ? (string)$cat : '';
|
||
}
|
||
|
||
$cover = (string)$r['cover'];
|
||
$mediaArr = $this->jsonArr($r['media']);
|
||
if ($cover === '' && !empty($mediaArr)) {
|
||
$cover = $mediaArr[0];
|
||
}
|
||
|
||
$tags = [];
|
||
if (!empty($r['tags'])) {
|
||
foreach (explode(',', (string)$r['tags']) as $t) {
|
||
$t = trim($t);
|
||
if ($t !== '') {
|
||
$tags[] = $t;
|
||
}
|
||
}
|
||
}
|
||
|
||
$list[] = [
|
||
'id' => (int)$r['id'],
|
||
'title' => (string)$r['title'],
|
||
'content' => (string)$r['content'],
|
||
'price' => (float)$r['price'],
|
||
'priceUnit' => (string)$r['price_unit'],
|
||
'cover' => $cover,
|
||
'media' => $mediaArr,
|
||
'area' => (string)$r['area'],
|
||
'address' => (string)$r['address'],
|
||
'categoryId' => (int)$r['category_id'],
|
||
'categoryName' => $catName,
|
||
'isTop' => (int)$r['is_top'] === 1,
|
||
'views' => (int)$r['views'],
|
||
'commentCount' => (int)$r['comment_count'],
|
||
'createdAt' => $this->iso($r['createtime']),
|
||
'tags' => $tags,
|
||
];
|
||
}
|
||
|
||
return [
|
||
'list' => $list,
|
||
'total' => $total,
|
||
'page' => $page,
|
||
'size' => $size,
|
||
'hasMore' => ($page * $size) < $total,
|
||
];
|
||
}
|
||
|
||
/**
|
||
* 计算两点大地距离(单位 km,保留 1 位小数)
|
||
*
|
||
* @param float $lat1 纬度1
|
||
* @param float $lng1 经度1
|
||
* @param float $lat2 纬度2
|
||
* @param float $lng2 经度2
|
||
* @return float
|
||
*/
|
||
protected function calcDistance($lat1, $lng1, $lat2, $lng2)
|
||
{
|
||
$earthRadius = 6378.137;
|
||
$radLat1 = deg2rad($lat1);
|
||
$radLat2 = deg2rad($lat2);
|
||
$a = $radLat1 - $radLat2;
|
||
$b = deg2rad($lng1) - deg2rad($lng2);
|
||
$s = 2 * asin(sqrt(pow(sin($a / 2), 2) + cos($radLat1) * cos($radLat2) * pow(sin($b / 2), 2)));
|
||
$s = $s * $earthRadius;
|
||
return round($s, 1);
|
||
}
|
||
}
|