sync: 以本地为准同步全量变更至 GitHub

含四端需求文档、前端/后端/超管/触客宝迭代及部署脚本更新;未拉取远程。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Manus AI
2026-05-30 04:24:56 +08:00
parent 711783baf5
commit 5517457929
579 changed files with 45472 additions and 8111 deletions

View File

@@ -18,24 +18,53 @@ class CustomerServiceController extends BaseController
$accountIds1= Db::table('s2_wechat_friend')->where(['accountId' => $accountId,'isDeleted' => 0])->group('wechatAccountId')->column('wechatAccountId');
$accountIds2 = Db::table('s2_wechat_chatroom')->where(['accountId' => $accountId,'isDeleted' => 0])->group('wechatAccountId')->column('wechatAccountId');
// 确保即使有空数组也不会报错,并且去除重复值
$accountIds = array_unique(array_merge($accountIds1 ?: [], $accountIds2 ?: []));
// 同项目设备上的微信号(老坑爹/油条姐等)一律并入侧栏,不论是否掉线
$companyAccountIds = $this->getCompanyWechatAccountIds((int)$companyId);
$accountIds = array_values(array_unique(array_merge($accountIds, $companyAccountIds)));
if (empty($accountIds)) {
return ResponseHelper::success([]);
}
$wechatAliveTime = time() - 86400 * 30;
$list = Db::table('s2_wechat_account')->alias('wa')
->join(['s2_device' => 'd'],'wa.currentDeviceId = d.id','LEFT')
->whereIn('wa.id',$accountIds)
->where('wa.wechatAliveTime','>',$wechatAliveTime)
->order('wa.id desc')
->group('wa.id')
->field([
'wa.*',
'd.imei',
'd.memo as deviceMemoFromDevice',
'd.extra',
'd.alive as s2DeviceAlive',
])
->select();
$ckDevicesByImei = [];
try {
$imeis = [];
foreach ($list as $row) {
if (!empty($row['imei'])) {
$imeis[] = $row['imei'];
}
}
if (!empty($imeis)) {
$ckRows = Db::name('device')
->where('companyId', $companyId)
->where('deleteTime', 0)
->whereIn('imei', array_unique($imeis))
->field('imei,alive,memo,model,brand,extra')
->select();
foreach ($ckRows as $ck) {
$ckDevicesByImei[$ck['imei']] = $ck;
}
}
} catch (\Exception $e) {
// ck device 表不可用时仍返回 S2 列表
}
foreach ($list as $k=>&$v){
$v['createTime'] = !empty($v['createTime']) ? date('Y-m-d H:i:s',$v['createTime']) : '';
$v['updateTime'] = !empty($v['updateTime']) ? date('Y-m-d H:i:s',$v['updateTime']) : '';
@@ -43,9 +72,36 @@ class CustomerServiceController extends BaseController
$momentsSetting = Db::name('kf_moments_settings')->where(['userId' => $userId,'companyId' => $companyId,'wechatId' =>$v['id']])->find();
$v['momentsMax'] = !empty($momentsSetting['max']) ? $momentsSetting['max'] : 5;
$v['momentsNum'] = !empty($momentsSetting['sendNum']) ? $momentsSetting['sendNum'] : 0;
$v['deviceExtra'] = json_decode($v['extra'],true);
$v['deviceExtra'] = json_decode($v['extra'],true) ?: [];
$v['deviceExtra']['imei'] = $v['imei'];
$v['deviceExtra']['memo'] = $v['deviceMemo'];
$memoFromDevice = $v['deviceMemoFromDevice'] ?? ($v['deviceMemo'] ?? '');
$v['deviceExtra']['memo'] = $memoFromDevice ?: ($v['deviceExtra']['memo'] ?? '');
$imei = $v['imei'] ?? '';
if ($imei && isset($ckDevicesByImei[$imei])) {
$ck = $ckDevicesByImei[$imei];
$ckExtra = !empty($ck['extra']) ? (json_decode($ck['extra'], true) ?: []) : [];
if (!empty($ck['memo'])) {
$v['deviceExtra']['memo'] = $ck['memo'];
}
if (!empty($ck['model']) || !empty($ck['brand'])) {
$v['deviceExtra']['market_name'] = trim(($ck['brand'] ?? '') . ' ' . ($ck['model'] ?? ''));
}
if (isset($ck['alive'])) {
$v['deviceAlive'] = (int)$ck['alive'];
}
if (isset($ckExtra['battery'])) {
$v['deviceExtra']['battery'] = $ckExtra['battery'];
}
}
if (!empty($v['s2DeviceAlive']) && (int)$v['s2DeviceAlive'] === 1) {
$v['deviceAlive'] = 1;
}
$v['isOnline'] = ((int)($v['deviceAlive'] ?? 0) === 1)
|| ((int)($v['wechatAlive'] ?? 0) === 1)
|| ((int)($v['keFuAlive'] ?? 0) === 1);
unset(
$v['accountUserName'],
$v['accountRealName'],
@@ -53,10 +109,67 @@ class CustomerServiceController extends BaseController
$v['extra'],
$v['imei'],
$v['deviceMemo'],
$v['deviceMemoFromDevice'],
$v['s2DeviceAlive'],
);
}
unset($v);
usort($list, function ($a, $b) {
$oa = !empty($a['isOnline']) ? 1 : 0;
$ob = !empty($b['isOnline']) ? 1 : 0;
if ($oa !== $ob) {
return $ob - $oa;
}
return strcmp($a['nickname'] ?? '', $b['nickname'] ?? '');
});
return ResponseHelper::success($list);
}
}
/**
* 本项目设备登录过的微信号s2_wechat_account.id
*/
private function getCompanyWechatAccountIds(int $companyId): array
{
if ($companyId <= 0) {
return [];
}
$ids = [];
try {
// 1) device_wechat_login 表
$wechatIds = Db::name('device_wechat_login')
->alias('l')
->join('device d', 'd.id = l.deviceId')
->where('d.companyId', $companyId)
->where('d.deleteTime', 0)
->group('l.wechatId')
->column('l.wechatId');
if (!empty($wechatIds)) {
$ids = array_merge($ids, Db::table('s2_wechat_account')
->whereIn('wechatId', $wechatIds)
->column('id') ?: []);
}
// 2) ck_device.imei → s2_device → s2_wechat_account补全未写入 login 表的微信号)
$imeis = Db::name('device')
->where('companyId', $companyId)
->where('deleteTime', 0)
->where('imei', '<>', '')
->column('imei');
if (!empty($imeis)) {
$s2DeviceIds = Db::table('s2_device')
->whereIn('imei', array_unique($imeis))
->column('id');
if (!empty($s2DeviceIds)) {
$ids = array_merge($ids, Db::table('s2_wechat_account')
->whereIn('currentDeviceId', $s2DeviceIds)
->column('id') ?: []);
}
}
} catch (\Exception $e) {
// 表差异不阻断
}
return array_values(array_unique(array_filter(array_map('intval', $ids))));
}
}