代码提交
This commit is contained in:
@@ -62,9 +62,7 @@ class AccountController extends BaseController
|
||||
|
||||
// 保存数据到数据库
|
||||
if (!empty($response['results'])) {
|
||||
foreach ($response['results'] as $item) {
|
||||
$this->saveAccount($item);
|
||||
}
|
||||
$this->saveAccount($response['results']);
|
||||
}
|
||||
|
||||
if ($isInner) {
|
||||
@@ -703,42 +701,41 @@ class AccountController extends BaseController
|
||||
* 保存账号数据到数据库
|
||||
* @param array $item 账号数据
|
||||
*/
|
||||
private function saveAccount($item)
|
||||
private function saveAccount($data)
|
||||
{
|
||||
// 将日期时间字符串转换为时间戳
|
||||
$createTime = isset($item['createTime']) ? strtotime($item['createTime']) : null;
|
||||
$deleteTime = isset($item['deleteTime']) ? strtotime($item['deleteTime']) : null;
|
||||
$sqlData = [];
|
||||
foreach ($data as $item) {
|
||||
$sqlData[] = [
|
||||
'id' => $item['id'],
|
||||
'accountType' => isset($item['accountType']) ? $item['accountType'] : 0,
|
||||
'status' => isset($item['status']) ? $item['status'] : 0,
|
||||
'tenantId' => isset($item['tenantId']) ? $item['tenantId'] : 0,
|
||||
'userName' => isset($item['userName']) ? $item['userName'] : '',
|
||||
'realName' => isset($item['realName']) ? $item['realName'] : '',
|
||||
'nickname' => isset($item['nickname']) ? $item['nickname'] : '',
|
||||
'avatar' => isset($item['avatar']) ? $item['avatar'] : '',
|
||||
'phone' => isset($item['phone']) ? $item['phone'] : '',
|
||||
'memo' => isset($item['memo']) ? $item['memo'] : '',
|
||||
'createTime' => $createTime,
|
||||
'creator' => isset($item['creator']) ? $item['creator'] : 0,
|
||||
'creatorUserName' => isset($item['creatorUserName']) ? $item['creatorUserName'] : '',
|
||||
'creatorRealName' => isset($item['creatorRealName']) ? $item['creatorRealName'] : '',
|
||||
'departmentId' => isset($item['departmentId']) ? $item['departmentId'] : 0,
|
||||
'departmentName' => isset($item['departmentName']) ? $item['departmentName'] : '',
|
||||
'privilegeIds' => isset($item['privilegeIds']) ? json_encode($item['privilegeIds']) : json_encode([]),
|
||||
'alive' => isset($item['alive']) ? $item['alive'] : false,
|
||||
'hasXiakeAccount' => isset($item['hasXiakeAccount']) ? $item['hasXiakeAccount'] : false,
|
||||
'isDeleted' => isset($item['isDeleted']) ? $item['isDeleted'] : false,
|
||||
'deleteTime' => $deleteTime
|
||||
];
|
||||
}
|
||||
|
||||
$data = [
|
||||
'id' => $item['id'],
|
||||
'accountType' => isset($item['accountType']) ? $item['accountType'] : 0,
|
||||
'status' => isset($item['status']) ? $item['status'] : 0,
|
||||
'tenantId' => isset($item['tenantId']) ? $item['tenantId'] : 0,
|
||||
'userName' => isset($item['userName']) ? $item['userName'] : '',
|
||||
'realName' => isset($item['realName']) ? $item['realName'] : '',
|
||||
'nickname' => isset($item['nickname']) ? $item['nickname'] : '',
|
||||
'avatar' => isset($item['avatar']) ? $item['avatar'] : '',
|
||||
'phone' => isset($item['phone']) ? $item['phone'] : '',
|
||||
'memo' => isset($item['memo']) ? $item['memo'] : '',
|
||||
'createTime' => $createTime,
|
||||
'creator' => isset($item['creator']) ? $item['creator'] : 0,
|
||||
'creatorUserName' => isset($item['creatorUserName']) ? $item['creatorUserName'] : '',
|
||||
'creatorRealName' => isset($item['creatorRealName']) ? $item['creatorRealName'] : '',
|
||||
'departmentId' => isset($item['departmentId']) ? $item['departmentId'] : 0,
|
||||
'departmentName' => isset($item['departmentName']) ? $item['departmentName'] : '',
|
||||
'privilegeIds' => isset($item['privilegeIds']) ? json_encode($item['privilegeIds']) : json_encode([]),
|
||||
'alive' => isset($item['alive']) ? $item['alive'] : false,
|
||||
'hasXiakeAccount' => isset($item['hasXiakeAccount']) ? $item['hasXiakeAccount'] : false,
|
||||
'isDeleted' => isset($item['isDeleted']) ? $item['isDeleted'] : false,
|
||||
'deleteTime' => $deleteTime
|
||||
];
|
||||
|
||||
// 使用tenantId作为唯一性判断
|
||||
$account = CompanyAccountModel::where('id', $item['id'])->find();
|
||||
if ($account) {
|
||||
$account->save($data);
|
||||
} else {
|
||||
CompanyAccountModel::create($data);
|
||||
}
|
||||
$account = new CompanyAccountModel();
|
||||
$account->saveAll($account);
|
||||
}
|
||||
}
|
||||
@@ -54,11 +54,9 @@ class WechatChatroomController extends BaseController
|
||||
// 保存数据到数据库
|
||||
if (!empty($response['results'])) {
|
||||
$isUpdate = false;
|
||||
foreach ($response['results'] as $item) {
|
||||
$updated = $this->saveChatroom($item);
|
||||
if($updated && $isDel == 0){
|
||||
$isUpdate = true;
|
||||
}
|
||||
$updated = $this->saveChatroom($response['results']);
|
||||
if($updated && $isDel == 0){
|
||||
$isUpdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,44 +78,51 @@ class WechatChatroomController extends BaseController
|
||||
* 保存群聊数据到数据库
|
||||
* @param array $item 群聊数据
|
||||
*/
|
||||
private function saveChatroom($item)
|
||||
private function saveChatroom($data)
|
||||
{
|
||||
$data = [
|
||||
'id' => $item['id'],
|
||||
'wechatAccountId' => $item['wechatAccountId'],
|
||||
'wechatAccountAlias' => $item['wechatAccountAlias'],
|
||||
'wechatAccountWechatId' => $item['wechatAccountWechatId'],
|
||||
'wechatAccountAvatar' => $item['wechatAccountAvatar'],
|
||||
'wechatAccountNickname' => $item['wechatAccountNickname'],
|
||||
'chatroomId' => $item['chatroomId'],
|
||||
'hasMe' => $item['hasMe'],
|
||||
'chatroomOwnerNickname' => isset($item['chatroomOwnerNickname']) ? $item['chatroomOwnerNickname'] : '',
|
||||
'chatroomOwnerAvatar' => isset($item['chatroomOwnerAvatar']) ? $item['chatroomOwnerAvatar'] : '',
|
||||
'conRemark' => isset($item['conRemark']) ? $item['conRemark'] : '',
|
||||
'nickname' => isset($item['nickname']) ? $item['nickname'] : '',
|
||||
'pyInitial' => isset($item['pyInitial']) ? $item['pyInitial'] : '',
|
||||
'quanPin' => isset($item['quanPin']) ? $item['quanPin'] : '',
|
||||
'chatroomAvatar' => isset($item['chatroomAvatar']) ? $item['chatroomAvatar'] : '',
|
||||
'members' => is_array($item['members']) ? json_encode($item['members']) : json_encode([]),
|
||||
'isDeleted' => isset($item['isDeleted']) ? $item['isDeleted'] : 0,
|
||||
'deleteTime' => !empty($item['isDeleted']) ? strtotime($item['deleteTime']) : 0,
|
||||
'createTime' => isset($item['createTime']) ? strtotime($item['createTime']) : 0,
|
||||
'accountId' => isset($item['accountId']) ? $item['accountId'] : 0,
|
||||
'accountUserName' => isset($item['accountUserName']) ? $item['accountUserName'] : '',
|
||||
'accountRealName' => isset($item['accountRealName']) ? $item['accountRealName'] : '',
|
||||
'accountNickname' => isset($item['accountNickname']) ? $item['accountNickname'] : '',
|
||||
'groupId' => isset($item['groupId']) ? $item['groupId'] : 0,
|
||||
'updateTime' => time()
|
||||
];
|
||||
|
||||
$sqlData = [];
|
||||
foreach ($data as $item) {
|
||||
$sqlData[] = [
|
||||
'id' => $item['id'],
|
||||
'wechatAccountId' => $item['wechatAccountId'],
|
||||
'wechatAccountAlias' => $item['wechatAccountAlias'],
|
||||
'wechatAccountWechatId' => $item['wechatAccountWechatId'],
|
||||
'wechatAccountAvatar' => $item['wechatAccountAvatar'],
|
||||
'wechatAccountNickname' => $item['wechatAccountNickname'],
|
||||
'chatroomId' => $item['chatroomId'],
|
||||
'hasMe' => $item['hasMe'],
|
||||
'chatroomOwnerNickname' => isset($item['chatroomOwnerNickname']) ? $item['chatroomOwnerNickname'] : '',
|
||||
'chatroomOwnerAvatar' => isset($item['chatroomOwnerAvatar']) ? $item['chatroomOwnerAvatar'] : '',
|
||||
'conRemark' => isset($item['conRemark']) ? $item['conRemark'] : '',
|
||||
'nickname' => isset($item['nickname']) ? $item['nickname'] : '',
|
||||
'pyInitial' => isset($item['pyInitial']) ? $item['pyInitial'] : '',
|
||||
'quanPin' => isset($item['quanPin']) ? $item['quanPin'] : '',
|
||||
'chatroomAvatar' => isset($item['chatroomAvatar']) ? $item['chatroomAvatar'] : '',
|
||||
'members' => is_array($item['members']) ? json_encode($item['members']) : json_encode([]),
|
||||
'isDeleted' => isset($item['isDeleted']) ? $item['isDeleted'] : 0,
|
||||
'deleteTime' => !empty($item['isDeleted']) ? strtotime($item['deleteTime']) : 0,
|
||||
'createTime' => isset($item['createTime']) ? strtotime($item['createTime']) : 0,
|
||||
'accountId' => isset($item['accountId']) ? $item['accountId'] : 0,
|
||||
'accountUserName' => isset($item['accountUserName']) ? $item['accountUserName'] : '',
|
||||
'accountRealName' => isset($item['accountRealName']) ? $item['accountRealName'] : '',
|
||||
'accountNickname' => isset($item['accountNickname']) ? $item['accountNickname'] : '',
|
||||
'groupId' => isset($item['groupId']) ? $item['groupId'] : 0,
|
||||
'updateTime' => time()
|
||||
];
|
||||
}
|
||||
|
||||
// 使用chatroomId和wechatAccountId的组合作为唯一性判断
|
||||
$chatroom = WechatChatroomModel::where('id',$item['id'])->find();
|
||||
|
||||
$chatroom = new WechatChatroomModel();
|
||||
$chatroom->saveAll($sqlData);
|
||||
if ($chatroom) {
|
||||
$chatroom->save($data);
|
||||
return true;
|
||||
} else {
|
||||
WechatChatroomModel::create($data);
|
||||
if ($chatroom->isUpdate()){
|
||||
return true;
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -488,6 +488,38 @@ if (!function_exists('getUserAction')) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('formatRelativeTime')) {
|
||||
/**
|
||||
* 将时间戳格式化为相对时间(中文)
|
||||
* 例:半年前 / 1个月前 / 3周前 / 1天前 / 5小时前 / 5分钟前 / 刚刚
|
||||
* @param int $timestamp Unix 时间戳(秒)
|
||||
* @return string
|
||||
*/
|
||||
function formatRelativeTime($timestamp)
|
||||
{
|
||||
if (empty($timestamp) || !is_numeric($timestamp)) {
|
||||
return '';
|
||||
}
|
||||
$now = time();
|
||||
$diff = max(0, $now - (int)$timestamp);
|
||||
|
||||
$minute = 60;
|
||||
$hour = 60 * $minute;
|
||||
$day = 24 * $hour;
|
||||
$week = 7 * $day;
|
||||
$month = 30 * $day; // 近似
|
||||
$halfYear = 6 * $month; // 近似
|
||||
|
||||
if ($diff >= $halfYear) return '半年前';
|
||||
if ($diff >= $month) return floor($diff / $month) . '个月前';
|
||||
if ($diff >= $week) return floor($diff / $week) . '周前';
|
||||
if ($diff >= $day) return floor($diff / $day) . '天前';
|
||||
if ($diff >= $hour) return floor($diff / $hour) . '小时前';
|
||||
if ($diff >= $minute) return floor($diff / $minute) . '分钟前';
|
||||
return '刚刚';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!function_exists('exit_data')) {
|
||||
|
||||
|
||||
@@ -62,14 +62,18 @@ Route::group('v1/', function () {
|
||||
|
||||
// 流量池相关
|
||||
Route::group('traffic/pool', function () {
|
||||
Route::get('getPackage', 'app\cunkebao\controller\TrafficController@getPackage');
|
||||
|
||||
|
||||
Route::get('', 'app\cunkebao\controller\traffic\GetPotentialListWithInCompanyV1Controller@index');
|
||||
Route::get('getUserJourney', 'app\cunkebao\controller\traffic\GetPotentialListWithInCompanyV1Controller@getUserJourney');
|
||||
Route::get('getUserTags', 'app\cunkebao\controller\traffic\GetPotentialListWithInCompanyV1Controller@getUserTags');
|
||||
Route::get('getUserInfo', 'app\cunkebao\controller\traffic\GetPotentialListWithInCompanyV1Controller@getUser');
|
||||
Route::get('getPackage', 'app\cunkebao\controller\traffic\GetPotentialListWithInCompanyV1Controller@getPackage');
|
||||
Route::post('addPackage', 'app\cunkebao\controller\traffic\GetPotentialListWithInCompanyV1Controller@addPackage');
|
||||
|
||||
|
||||
|
||||
|
||||
Route::get('converted', 'app\cunkebao\controller\traffic\GetConvertedListWithInCompanyV1Controller@index');
|
||||
Route::get('types', 'app\cunkebao\controller\traffic\GetPotentialTypeSectionV1Controller@index');
|
||||
Route::get('sources', 'app\cunkebao\controller\traffic\GetTrafficSourceSectionV1Controller@index');
|
||||
|
||||
55
Server/application/cunkebao/controller/RFMController.php
Normal file
55
Server/application/cunkebao/controller/RFMController.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace app\cunkebao\controller;
|
||||
|
||||
class RFMController extends BaseController
|
||||
{
|
||||
/**
|
||||
* 计算 RFM 评分(默认规则)
|
||||
* @param int|null $recencyDays 最近购买天数
|
||||
* @param int $frequency 购买次数
|
||||
* @param float $monetary 购买金额
|
||||
* @return array{R:int,F:int,M:int}
|
||||
*/
|
||||
public static function calcRfmScores($recencyDays, $frequency, $monetary)
|
||||
{
|
||||
$recencyDays = is_numeric($recencyDays) ? (int)$recencyDays : 9999;
|
||||
$frequency = max(0, (int)$frequency);
|
||||
$monetary = max(0, (float)$monetary);
|
||||
return [
|
||||
'R' => self::scoreR_Default($recencyDays),
|
||||
'F' => self::scoreF_Default($frequency),
|
||||
'M' => self::scoreM_Default($monetary),
|
||||
];
|
||||
}
|
||||
|
||||
// 默认规则
|
||||
protected static function scoreR_Default(int $days): int
|
||||
{
|
||||
if ($days <= 30) return 5;
|
||||
if ($days <= 60) return 4;
|
||||
if ($days <= 90) return 3;
|
||||
if ($days <= 120) return 2;
|
||||
return 1;
|
||||
}
|
||||
protected static function scoreF_Default(int $times): int
|
||||
{
|
||||
if ($times >= 10) return 5;
|
||||
if ($times >= 6) return 4;
|
||||
if ($times >= 3) return 3;
|
||||
if ($times >= 2) return 2;
|
||||
if ($times >= 1) return 1;
|
||||
return 0;
|
||||
}
|
||||
protected static function scoreM_Default(float $amount): int
|
||||
{
|
||||
if ($amount >= 2000) return 5;
|
||||
if ($amount >= 1000) return 4;
|
||||
if ($amount >= 500) return 3;
|
||||
if ($amount >= 200) return 2;
|
||||
if ($amount > 0) return 1;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
68
Server/application/cunkebao/controller/TrafficController.php
Normal file
68
Server/application/cunkebao/controller/TrafficController.php
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace app\cunkebao\controller;
|
||||
|
||||
use library\ResponseHelper;
|
||||
use think\Db;
|
||||
use app\cunkebao\controller\RFMController;
|
||||
|
||||
class TrafficController extends BaseController
|
||||
{
|
||||
|
||||
public function getPackage()
|
||||
{
|
||||
|
||||
$page = $this->request->param('page', 1);
|
||||
$limit = $this->request->param('limit', 10);
|
||||
$keyword = $this->request->param('keyword', '');
|
||||
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
$package = Db::name('traffic_source_package')->alias('tsp')
|
||||
->join('traffic_source_package_item tspi', 'tspi.packageId=tsp.id', 'left')
|
||||
->whereIn('tsp.companyId', [$companyId, 0])
|
||||
->field('tsp.id,tsp.name,tsp.description,tsp.pic,tsp.isSys as type,tsp.createTime,count(tspi.id) as num')
|
||||
->group('tsp.id');
|
||||
|
||||
if (!empty($keyword)) {
|
||||
$package->where('tsp.name|tsp.description', 'like', '%' . $keyword . '%');
|
||||
}
|
||||
|
||||
$list = $package->page($page, $limit)->order('isSys ASC,id DESC')->select();
|
||||
$total = $package->count();
|
||||
|
||||
$rfmRule = 'default';
|
||||
foreach ($list as $k => &$v) {
|
||||
if ($v['type'] != 1) {
|
||||
$v['createTime'] = !empty($v['createTime']) ? formatRelativeTime($v['createTime']) : '';
|
||||
}else{
|
||||
$v['createTime'] = '';
|
||||
}
|
||||
|
||||
// RFM 评分(示例:以创建时间近似最近活跃,num 近似频次;金额若无则为 0)
|
||||
$recencyDays = isset($v['createTime']) && is_numeric($v['createTime']) ? floor((time() - (int)$v['createTime']) / 86400) : null;
|
||||
// 如果上方被格式化为文本,则尝试从原始结果集取原值
|
||||
if (!is_numeric($recencyDays) || $recencyDays === null) {
|
||||
$rawCreate = isset($list[$k]['createTime']) ? $list[$k]['createTime'] : null;
|
||||
$recencyDays = is_numeric($rawCreate) ? floor((time() - (int)$rawCreate) / 86400) : 9999;
|
||||
}
|
||||
$frequency = (int)($v['num'] ?? 0);
|
||||
$monetary = (float)($v['monetary'] ?? 0);
|
||||
|
||||
$scores = RFMController::calcRfmScores($recencyDays, $frequency, $monetary);
|
||||
$v['R'] = $scores['R'];
|
||||
$v['F'] = $scores['F'];
|
||||
$v['M'] = $scores['M'];
|
||||
$v['RFM'] = $scores['R'] + $scores['F'] + $scores['M'];
|
||||
|
||||
}
|
||||
unset($v);
|
||||
|
||||
$data = [
|
||||
'total' => $total,
|
||||
'list' => $list,
|
||||
];
|
||||
|
||||
return ResponseHelper::success($data);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -359,39 +359,7 @@ class GetPotentialListWithInCompanyV1Controller extends BaseController
|
||||
}
|
||||
|
||||
|
||||
public function getPackage()
|
||||
{
|
||||
|
||||
$page = $this->request->param('page', 1);
|
||||
$limit = $this->request->param('limit', 10);
|
||||
$keyword = $this->request->param('keyword', '');
|
||||
|
||||
$companyId = $this->getUserInfo('companyId');
|
||||
$package = Db::name('traffic_source_package')->alias('tsp')
|
||||
->join('traffic_source_package_item tspi', 'tspi.packageId=tsp.id', 'left')
|
||||
->whereIn('tsp.companyId', [$companyId, 0])
|
||||
->field('tsp.id,tsp.name,tsp.description,tsp.createTime,count(tspi.id) as num')
|
||||
->group('tsp.id');
|
||||
|
||||
if (!empty($keyword)) {
|
||||
$package->where('tsp.name|tsp.description', 'like', '%' . $keyword . '%');
|
||||
}
|
||||
|
||||
$list = $package->page($page, $limit)->select();
|
||||
$total = $package->count();
|
||||
|
||||
foreach ($list as $k => &$v) {
|
||||
$v['createTime'] = !empty($v['createTime']) ? date('Y-m-d H:i:s', $v['createTime']) : '';
|
||||
}
|
||||
unset($v);
|
||||
|
||||
$data = [
|
||||
'total' => $total,
|
||||
'list' => $list,
|
||||
];
|
||||
|
||||
return ResponseHelper::success($data);
|
||||
}
|
||||
|
||||
|
||||
public function addPackage()
|
||||
|
||||
@@ -108,93 +108,33 @@ class WorkbenchMomentsJob
|
||||
public function execute2()
|
||||
{
|
||||
try {
|
||||
// 每日重置发送次数(允许10分钟误差)
|
||||
$now = time();
|
||||
$todayStart = strtotime(date('Y-m-d 00:00:00'));
|
||||
if ($now - $todayStart >= 0 && $now - $todayStart <= 600) {
|
||||
$cacheKey = 'moments_settings_reset_' . date('Ymd');
|
||||
if (!Cache::has($cacheKey)) {
|
||||
Db::table('ck_kf_moments_settings')->where('sendNum', '<>', 0)
|
||||
->update(['sendNum' => 0, 'updateTime' => $now]);
|
||||
Cache::set($cacheKey, 1, 7200); // 2小时缓存,防止重复重置
|
||||
}
|
||||
}
|
||||
// 获取所有工作台
|
||||
$kfMoments = KfMoments::where(['isSend' => 0, 'isDel' => 0])->where('sendTime', '<=', time() + 120)->order('id desc')->select();
|
||||
// 1) 每日重置
|
||||
$this->resetDailyCountersIfNeeded();
|
||||
|
||||
// 2) 获取发送窗口内的任务
|
||||
[$nowTs, $kfMoments] = $this->getWindowTasks();
|
||||
foreach ($kfMoments as $val) {
|
||||
$sendData = json_decode($val->sendData,true);
|
||||
$endTime = strtotime($sendData['endTime']);
|
||||
if ($endTime <= time() + 1800){
|
||||
$endTime = time() + 3600;
|
||||
$sendData['endTime'] = date('Y-m-d H:i:s', $endTime);
|
||||
}
|
||||
switch ($sendData['momentContentType']) {
|
||||
case 1:
|
||||
$sendData['link'] = ['image' => ''];
|
||||
$sendData['picUrlList'] = [];
|
||||
$sendData['videoUrl'] = '';
|
||||
break;
|
||||
case 2:
|
||||
$sendData['link'] = ['image' => ''];
|
||||
$sendData['videoUrl'] = '';
|
||||
break;
|
||||
case 3:
|
||||
$sendData['link'] = ['image' => ''];
|
||||
$sendData['picUrlList'] = [];
|
||||
break;
|
||||
case 4:
|
||||
$sendData['picUrlList'] = [];
|
||||
$sendData['videoUrl'] = '';
|
||||
break;
|
||||
default:
|
||||
$sendData['link'] = ['image' => ''];
|
||||
$sendData['picUrlList'] = [];
|
||||
$sendData['videoUrl'] = '';
|
||||
break;
|
||||
}
|
||||
$companyId = (int)($val['companyId'] ?? 0);
|
||||
$userId = (int)($val['userId'] ?? 0);
|
||||
|
||||
// 2.1) 数据规范化
|
||||
$sendData = json_decode($val->sendData, true);
|
||||
$sendData = $this->normalizeSendData($sendData);
|
||||
|
||||
// 2.2) 账号额度过滤
|
||||
$items = $sendData['jobPublishWechatMomentsItems'] ?? [];
|
||||
if (empty($items)) { continue; }
|
||||
$allowed = $this->filterAccountsByQuota($companyId, $userId, $items);
|
||||
if (empty($allowed)) { continue; }
|
||||
$sendData['jobPublishWechatMomentsItems'] = $allowed;
|
||||
|
||||
// 3) 下发
|
||||
$moments = new Moments();
|
||||
$moments->addJob($sendData);
|
||||
KfMoments::where(['id' => $val['id']])->update(['isSend' => 1]);
|
||||
|
||||
// 统计发送次数(ck_kf_moments_settings)
|
||||
try {
|
||||
$nowTs = time();
|
||||
$companyId = (int)($val['companyId'] ?? 0);
|
||||
$userId = (int)($val['userId'] ?? 0);
|
||||
$items = $sendData['jobPublishWechatMomentsItems'] ?? [];
|
||||
foreach ($items as $it) {
|
||||
$wechatId = (int)($it['wechatAccountId'] ?? 0);
|
||||
if ($wechatId <= 0) { continue; }
|
||||
|
||||
$cond = [
|
||||
'companyId' => $companyId,
|
||||
'userId' => $userId,
|
||||
'wechatId' => $wechatId,
|
||||
];
|
||||
|
||||
$setting = Db::table('ck_kf_moments_settings')->where($cond)->find();
|
||||
if ($setting) {
|
||||
Db::table('ck_kf_moments_settings')
|
||||
->where('id', $setting['id'])
|
||||
->update([
|
||||
'sendNum' => Db::raw('sendNum + 1'),
|
||||
'updateTime' => $nowTs,
|
||||
]);
|
||||
} else {
|
||||
Db::table('ck_kf_moments_settings')->insert([
|
||||
'companyId' => $companyId,
|
||||
'userId' => $userId,
|
||||
'wechatId' => $wechatId,
|
||||
'max' => 5,
|
||||
'sendNum' => 1,
|
||||
'createTime' => $nowTs,
|
||||
'updateTime' => $nowTs,
|
||||
]);
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $statE) {
|
||||
Log::error('朋友圈发送统计失败: ' . $statE->getMessage());
|
||||
}
|
||||
// 4) 统计
|
||||
$this->incrementSendStats($companyId, $userId, $allowed);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
Log::error("朋友圈同步任务异常: " . $e->getMessage());
|
||||
@@ -202,6 +142,118 @@ class WorkbenchMomentsJob
|
||||
}
|
||||
}
|
||||
|
||||
protected function resetDailyCountersIfNeeded()
|
||||
{
|
||||
$now = time();
|
||||
$todayStart = strtotime(date('Y-m-d 00:00:00'));
|
||||
if ($now - $todayStart >= 0 && $now - $todayStart <= 600) {
|
||||
$cacheKey = 'moments_settings_reset_' . date('Ymd');
|
||||
if (!Cache::has($cacheKey)) {
|
||||
Db::table('ck_kf_moments_settings')->where('sendNum', '<>', 0)
|
||||
->update(['sendNum' => 0, 'updateTime' => $now]);
|
||||
Cache::set($cacheKey, 1, 7200);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function getWindowTasks()
|
||||
{
|
||||
$nowTs = time();
|
||||
$windowStart = $nowTs - 300;
|
||||
$windowEnd = $nowTs + 300;
|
||||
$kfMoments = KfMoments::where(['isSend' => 0, 'isDel' => 0])
|
||||
->whereBetween('sendTime', [$windowStart, $windowEnd])
|
||||
->order('id desc')->select();
|
||||
return [$nowTs, $kfMoments];
|
||||
}
|
||||
|
||||
protected function normalizeSendData(array $sendData)
|
||||
{
|
||||
$endTime = strtotime($sendData['endTime'] ?? '');
|
||||
if ($endTime <= time() + 1800) {
|
||||
$endTime = time() + 3600;
|
||||
$sendData['endTime'] = date('Y-m-d H:i:s', $endTime);
|
||||
}
|
||||
switch ($sendData['momentContentType'] ?? 1) {
|
||||
case 1:
|
||||
$sendData['link'] = ['image' => ''];
|
||||
$sendData['picUrlList'] = [];
|
||||
$sendData['videoUrl'] = '';
|
||||
break;
|
||||
case 2:
|
||||
$sendData['link'] = ['image' => ''];
|
||||
$sendData['videoUrl'] = '';
|
||||
break;
|
||||
case 3:
|
||||
$sendData['link'] = ['image' => ''];
|
||||
$sendData['picUrlList'] = [];
|
||||
break;
|
||||
case 4:
|
||||
$sendData['picUrlList'] = [];
|
||||
$sendData['videoUrl'] = '';
|
||||
break;
|
||||
default:
|
||||
$sendData['link'] = ['image' => ''];
|
||||
$sendData['picUrlList'] = [];
|
||||
$sendData['videoUrl'] = '';
|
||||
break;
|
||||
}
|
||||
return $sendData;
|
||||
}
|
||||
|
||||
protected function filterAccountsByQuota(int $companyId, int $userId, array $items)
|
||||
{
|
||||
$wechatIds = array_values(array_filter(array_map(function($it){ return (int)($it['wechatAccountId'] ?? 0); }, $items)));
|
||||
if (empty($wechatIds)) { return []; }
|
||||
$settings = Db::table('ck_kf_moments_settings')
|
||||
->where('companyId', $companyId)
|
||||
->where('userId', $userId)
|
||||
->whereIn('wechatId', $wechatIds)
|
||||
->column('id,max,sendNum', 'wechatId');
|
||||
$allowed = [];
|
||||
foreach ($items as $it) {
|
||||
$wid = (int)($it['wechatAccountId'] ?? 0);
|
||||
if ($wid <= 0) { continue; }
|
||||
if (isset($settings[$wid])) {
|
||||
$max = (int)$settings[$wid]['max'];
|
||||
$sent = (int)$settings[$wid]['sendNum'];
|
||||
if ($sent < ($max > 0 ? $max : 5)) { $allowed[] = $it; }
|
||||
} else {
|
||||
$allowed[] = $it;
|
||||
}
|
||||
}
|
||||
return $allowed;
|
||||
}
|
||||
|
||||
protected function incrementSendStats(int $companyId, int $userId, array $items)
|
||||
{
|
||||
try {
|
||||
$nowTs = time();
|
||||
foreach ($items as $it) {
|
||||
$wechatId = (int)($it['wechatAccountId'] ?? 0);
|
||||
if ($wechatId <= 0) { continue; }
|
||||
$cond = ['companyId' => $companyId, 'userId' => $userId, 'wechatId' => $wechatId];
|
||||
$setting = Db::table('ck_kf_moments_settings')->where($cond)->find();
|
||||
if ($setting) {
|
||||
Db::table('ck_kf_moments_settings')->where('id', $setting['id'])
|
||||
->update(['sendNum' => Db::raw('sendNum + 1'), 'updateTime' => $nowTs]);
|
||||
} else {
|
||||
Db::table('ck_kf_moments_settings')->insert([
|
||||
'companyId' => $companyId,
|
||||
'userId' => $userId,
|
||||
'wechatId' => $wechatId,
|
||||
'max' => 5,
|
||||
'sendNum' => 1,
|
||||
'createTime' => $nowTs,
|
||||
'updateTime' => $nowTs,
|
||||
]);
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
Log::error('朋友圈发送统计失败: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 处理内容发送
|
||||
|
||||
@@ -157,7 +157,7 @@ class Adapter implements WeChatServiceInterface
|
||||
{
|
||||
$task = Db::name('customer_acquisition_task')
|
||||
->where(['status' => 1, 'deleteTime' => 0])
|
||||
->whereRaw("id % $process_count_for_status_0 = {$current_worker_id}")
|
||||
/* ->whereRaw("id % $process_count_for_status_0 = {$current_worker_id}")*/
|
||||
->order('id desc')
|
||||
->select();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user