## GitHub 同步说明 | 项 | 内容 | |:---|:---| | 仓库 | https://github.com/fnvtk/cunkebao_v3 | | 分支 | develop | | 方向 | 本地 → GitHub(单向 push) | | 是否拉取远程 | 否(未 fetch / pull / merge) | | 是否改本地源文件 | 否(仅 git add/commit,未编辑业务/文档正文) | | 远程基准 | origin/develop @b5b40e8b| | 本批工作区变更 | 999 files, +47622 / -4007 lines | ## 一并推送的本地已有 commit(此前未 push) 1.3aa7ecf8deploy: 官网 ckb.quwanzhi.com 宝塔上线 DNS+SSL+验收文档 2.16a70045fix(官网): 桌面导航仅保留一个留资 CTA 并重新上线 3.dbe7b7aafix(官网): Hero 改为主 CTA 按钮并去掉重复 1200+ 数据条 4.ac82726echore(部署): 五端上线验收46项并统一留资与缓存版本 ## 本 commit 目录统计 | 目录 | 文件数 | 说明 | |:---|---:|:---| | 开发文档/ | 639 | 10、项目管理、未完成项跨仓汇总、五端需求/部署/接口 | | Server/ | 129 | 后端 API、迁移、部署相关 | | Touchkebao/ | 80 | PC 工作台、获客、算力 | | 官网/ | 61 | ckb.quwanzhi.com 静态站与 CTA 迭代 | | Cunkebao/ | 49 | 移动端场景/设置/流量池 | | SuperAdmin/ | 26 | 超管后台 | | .cursor/ | 11 | 规则与 Skill | | 其他 | 14 | docker-compose、.obsidian、.codegraph 等 | ## 重点文件(本地为准) - 开发文档/1、需求/修改/未完成项与跨仓验收汇总.md - 开发文档/10、项目管理/账号密码与登录环境一览.md - 开发文档/10、项目管理/(新建项目管理目录与截图) - 官网/ 全站 + 宝塔上线文档 - 五端上线验收 46 项(见 ac82726e) ## 刻意未上传 - node_modules/、.DS_Store、.smart-env/ - .开发文档_nested_git_backup/ - **/__pycache__/ ## 验收 - 提交页(含本说明): https://github.com/fnvtk/cunkebao_v3/commit/HEAD - 分支树: https://github.com/fnvtk/cunkebao_v3/tree/develop Co-authored-by: Cursor <cursoragent@cursor.com>
173 lines
7.1 KiB
PHP
173 lines
7.1 KiB
PHP
<?php
|
||
|
||
namespace app\superadmin\controller\traffic;
|
||
|
||
use app\common\service\TrafficPoolSystemIdentifierService;
|
||
use app\common\service\WechatIdentityService;
|
||
use think\Controller;
|
||
use think\Db;
|
||
use think\facade\Request;
|
||
|
||
/**
|
||
* 客户详情(V2 traffic_pool_company)
|
||
*/
|
||
class GetPoolDetailController extends Controller
|
||
{
|
||
protected function columnExistsRaw(string $table, string $column): bool
|
||
{
|
||
try {
|
||
$rows = Db::query("SHOW COLUMNS FROM `{$table}` LIKE '{$column}'");
|
||
return !empty($rows);
|
||
} catch (\Throwable $e) {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
protected function formatGender($gender): string
|
||
{
|
||
switch ((int) $gender) {
|
||
case 1:
|
||
return '男';
|
||
case 2:
|
||
return '女';
|
||
default:
|
||
return '保密';
|
||
}
|
||
}
|
||
|
||
protected function formatRfmType($type): string
|
||
{
|
||
$map = [
|
||
1 => '重要价值',
|
||
2 => '重要发展',
|
||
3 => '重要保持',
|
||
4 => '重要挽留',
|
||
5 => '一般价值',
|
||
6 => '一般发展',
|
||
7 => '一般保持',
|
||
8 => '一般挽留',
|
||
];
|
||
return $map[(int) $type] ?? '未评估';
|
||
}
|
||
|
||
public function index()
|
||
{
|
||
$id = Request::param('id/d');
|
||
if (!$id) {
|
||
return json(['code' => 400, 'msg' => '参数错误']);
|
||
}
|
||
|
||
try {
|
||
$row = Db::name('traffic_pool_company')->alias('tpc')
|
||
->join('traffic_pool tp', 'tp.id = tpc.poolId')
|
||
->leftJoin('company c', 'c.companyId = tpc.companyId')
|
||
->where('tpc.id', $id)
|
||
->where('tpc.isDel', 0)
|
||
->field([
|
||
'tpc.id', 'tpc.createTime', 'tpc.identifier', 'tpc.ownerWechatId',
|
||
'tpc.rfmScore', 'tpc.rfmType', 'tpc.totalOrderAmount', 'tpc.totalOrderCount',
|
||
'tpc.phone', 'tpc.realName',
|
||
'tp.nickname', 'tp.avatar', 'tp.region', 'tp.gender', 'tp.wechatId',
|
||
'c.name as projectName',
|
||
])
|
||
->find();
|
||
|
||
if (!$row) {
|
||
return json(['code' => 404, 'msg' => '记录不存在']);
|
||
}
|
||
|
||
$identifier = (string) ($row['identifier'] ?? '');
|
||
$wechatId = (string) ($row['wechatId'] ?? '');
|
||
if (TrafficPoolSystemIdentifierService::isExcluded($identifier)
|
||
|| TrafficPoolSystemIdentifierService::isExcluded($wechatId)) {
|
||
return json(['code' => 404, 'msg' => '记录不存在']);
|
||
}
|
||
|
||
$source = Db::name('traffic_pool_source')
|
||
->where('poolCompanyId', $id)
|
||
->order('id', 'desc')
|
||
->value('sourceName');
|
||
$sourceQuery = Db::name('traffic_pool_source')
|
||
->where('poolCompanyId', $id)
|
||
->field('id,sourceName,createTime');
|
||
$journeySlicedByOwner = false;
|
||
if (!empty($row['ownerWechatId']) && $this->columnExistsRaw('ck_traffic_pool_source', 'ownerWechatId')) {
|
||
$sourceQuery->where('ownerWechatId', $row['ownerWechatId']);
|
||
$journeySlicedByOwner = true;
|
||
}
|
||
$sourceRows = $sourceQuery
|
||
->order('id', 'desc')
|
||
->limit(20)
|
||
->select();
|
||
$sourceRows = $sourceRows ? (is_array($sourceRows) ? $sourceRows : $sourceRows->toArray()) : [];
|
||
|
||
$tags = Db::name('traffic_pool_tag')
|
||
->where('poolCompanyId', $id)
|
||
->where('isDel', 0)
|
||
->column('tagName');
|
||
|
||
$ownerDisplay = $row['ownerWechatId'] ?: '';
|
||
if ($ownerDisplay !== '') {
|
||
$wa = (new WechatIdentityService())->identityMap([$row['ownerWechatId']]);
|
||
$wa = $wa[$row['ownerWechatId']] ?? [];
|
||
if ($wa) {
|
||
$ownerDisplay = $wa['nickname'] ?: ($wa['alias'] ?: $row['ownerWechatId']);
|
||
}
|
||
}
|
||
$ownerWechat = [];
|
||
if (!empty($row['ownerWechatId'])) {
|
||
$identity = (new WechatIdentityService())->identityMap([$row['ownerWechatId']]);
|
||
$ownerWechat = $identity[$row['ownerWechatId']] ?? ['wechatId' => $row['ownerWechatId'], 'nickname' => '', 'alias' => '', 'avatar' => ''];
|
||
}
|
||
$journey = [];
|
||
foreach ($sourceRows as $sr) {
|
||
$journey[] = [
|
||
'type' => 'source',
|
||
'label' => (string) ($sr['sourceName'] ?? ''),
|
||
'time' => !empty($sr['createTime']) ? date('Y-m-d H:i:s', (int) $sr['createTime']) : '',
|
||
];
|
||
}
|
||
|
||
$rfmScore = is_array($row['rfmScore'])
|
||
? (float) ($row['rfmScore']['total'] ?? 0)
|
||
: (float) ($row['rfmScore'] ?? 0);
|
||
|
||
return json([
|
||
'code' => 200,
|
||
'msg' => '获取成功',
|
||
'data' => [
|
||
'source' => $source ?: '',
|
||
'sources' => array_map(function ($sr) {
|
||
return [
|
||
'sourceName' => (string) ($sr['sourceName'] ?? ''),
|
||
'createTime' => !empty($sr['createTime']) ? date('Y-m-d H:i:s', (int) $sr['createTime']) : '',
|
||
];
|
||
}, $sourceRows),
|
||
'journey' => $journey,
|
||
'journeySlicedByOwner' => $journeySlicedByOwner,
|
||
'addTime' => $row['createTime'] ? date('Y-m-d H:i:s', (int) $row['createTime']) : null,
|
||
'projectName' => $row['projectName'] ?: '',
|
||
'avatar' => $row['avatar'] ?: '',
|
||
'nickname' => $row['nickname'] ?: ($row['identifier'] ?: '未知'),
|
||
'region' => $row['region'] ?: '',
|
||
'gender' => $this->formatGender($row['gender'] ?? 0),
|
||
'tags' => $tags ?: [],
|
||
'wechatId' => $row['wechatId'] ?: $row['identifier'],
|
||
'ownerWechatId' => $row['ownerWechatId'] ?: '',
|
||
'ownerWechatDisplay' => $ownerDisplay ?: '未绑定微信号',
|
||
'ownerWechat' => $ownerWechat,
|
||
'rfmScore' => round($rfmScore, 2),
|
||
'rfmType' => (int) ($row['rfmType'] ?? 0),
|
||
'rfmTypeLabel' => $this->formatRfmType($row['rfmType'] ?? 0),
|
||
'totalOrderAmount' => round((float) ($row['totalOrderAmount'] ?? 0), 2),
|
||
'totalOrderCount' => (int) ($row['totalOrderCount'] ?? 0),
|
||
'phone' => $row['phone'] ?: '',
|
||
'realName' => $row['realName'] ?: '',
|
||
],
|
||
]);
|
||
} catch (\Exception $e) {
|
||
return json(['code' => 500, 'msg' => '系统错误:' . $e->getMessage()]);
|
||
}
|
||
}
|
||
}
|