Merge branch 'develop' of https://gitee.com/Tyssen/yi-shi into develop

This commit is contained in:
Ghost
2025-03-24 14:59:52 +08:00
50 changed files with 6797 additions and 14 deletions

View File

@@ -13,4 +13,10 @@ Route::group('v1/auth', function () {
// 需要JWT认证的接口
Route::get('info', 'app\\common\\controller\\Auth@info')->middleware(['jwt']); // 获取用户信息
Route::post('refresh', 'app\\common\\controller\\Auth@refresh')->middleware(['jwt']); // 刷新令牌
});
});
// 附件上传相关路由
Route::group('v1/', function () {
Route::post('attachment/upload', 'app\\common\\controller\\Attachment@upload'); // 上传附件
Route::get('attachment/:id', 'app\\common\\controller\\Attachment@info'); // 获取附件信息
})->middleware(['jwt']);

View File

@@ -0,0 +1,140 @@
<?php
namespace app\common\controller;
use think\Controller;
use think\facade\Request;
use app\common\model\Attachment as AttachmentModel;
use app\common\util\AliyunOSS;
class Attachment extends Controller
{
/**
* 上传文件
* @return \think\response\Json
*/
public function upload()
{
try {
// 获取上传文件
$file = Request::file('file');
if (!$file) {
return json([
'code' => 400,
'msg' => '请选择要上传的文件'
]);
}
// 验证文件
$validate = \think\facade\Validate::rule([
'file' => [
'fileSize' => 10485760, // 10MB
'fileExt' => 'jpg,jpeg,png,gif,doc,docx,pdf,zip,rar',
'fileMime' => 'image/jpeg,image/png,image/gif,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/pdf,application/zip,application/x-rar-compressed'
]
]);
if (!$validate->check(['file' => $file])) {
return json([
'code' => 400,
'msg' => $validate->getError()
]);
}
// 生成文件hash
$hashKey = md5_file($file->getRealPath());
// 检查文件是否已存在
$existFile = AttachmentModel::getByHashKey($hashKey);
if ($existFile) {
return json([
'code' => 200,
'msg' => '文件已存在',
'data' => [
'id' => $existFile['id'],
'name' => $existFile['name'],
'url' => $existFile['source']
]
]);
}
// 生成OSS对象名称
$objectName = AliyunOSS::generateObjectName($file->getOriginalName());
// 上传到OSS
$result = AliyunOSS::uploadFile($file->getRealPath(), $objectName);
if (!$result['success']) {
return json([
'code' => 500,
'msg' => '文件上传失败:' . $result['error']
]);
}
// 保存到数据库
$attachmentData = [
'name' => Request::param('name') ?: $file->getOriginalName(),
'hash_key' => $hashKey,
'server' => 'aliyun_oss',
'source' => $result['url'],
'size' => $result['size'],
'suffix' => pathinfo($file->getOriginalName(), PATHINFO_EXTENSION)
];
$attachmentId = AttachmentModel::addAttachment($attachmentData);
if (!$attachmentId) {
return json([
'code' => 500,
'msg' => '保存附件信息失败'
]);
}
return json([
'code' => 200,
'msg' => '上传成功',
'data' => [
'id' => $attachmentId,
'name' => $attachmentData['name'],
'url' => $attachmentData['source']
]
]);
} catch (\Exception $e) {
return json([
'code' => 500,
'msg' => '上传失败:' . $e->getMessage()
]);
}
}
/**
* 获取附件信息
* @param int $id 附件ID
* @return \think\response\Json
*/
public function info($id)
{
try {
$attachment = AttachmentModel::find($id);
if (!$attachment) {
return json([
'code' => 404,
'msg' => '附件不存在'
]);
}
return json([
'code' => 200,
'msg' => '获取成功',
'data' => $attachment
]);
} catch (\Exception $e) {
return json([
'code' => 500,
'msg' => '获取失败:' . $e->getMessage()
]);
}
}
}

View File

@@ -0,0 +1,55 @@
<?php
namespace app\common\model;
use think\Model;
class Attachment extends Model
{
// 设置表名
protected $name = 'attachments';
// 设置主键
protected $pk = 'id';
// 自动写入时间戳
protected $autoWriteTimestamp = 'datetime';
// 定义时间戳字段名
protected $createTime = 'create_at';
protected $updateTime = 'update_at';
protected $deleteTime = 'delete_at';
// 定义字段类型
protected $type = [
'id' => 'integer',
'dl_count' => 'integer',
'size' => 'integer',
'scene' => 'integer',
'create_at' => 'datetime',
'update_at' => 'datetime',
'delete_at' => 'datetime'
];
/**
* 添加附件记录
* @param array $data 附件数据
* @return int|bool
*/
public static function addAttachment($data)
{
$attachment = new self();
return $attachment->allowField(true)->save($data);
}
/**
* 根据hash_key获取附件
* @param string $hashKey
* @return array|null
*/
public static function getByHashKey($hashKey)
{
return self::where('hash_key', $hashKey)
->where('delete_at', null)
->find();
}
}

View File

@@ -0,0 +1,77 @@
<?php
namespace app\common\util;
use OSS\OssClient;
use OSS\Core\OssException;
class AliyunOSS
{
// OSS配置信息
const ACCESS_KEY_ID = 'your_access_key_id';
const ACCESS_KEY_SECRET = 'your_access_key_secret';
const ENDPOINT = 'oss-cn-hangzhou.aliyuncs.com';
const BUCKET = 'your_bucket_name';
/**
* 获取OSS客户端实例
* @return OssClient
* @throws OssException
*/
public static function getClient()
{
try {
return new OssClient(
self::ACCESS_KEY_ID,
self::ACCESS_KEY_SECRET,
self::ENDPOINT
);
} catch (OssException $e) {
throw new OssException('创建OSS客户端失败' . $e->getMessage());
}
}
/**
* 上传文件到OSS
* @param string $filePath 本地文件路径
* @param string $objectName OSS对象名称
* @return array
* @throws OssException
*/
public static function uploadFile($filePath, $objectName)
{
try {
$client = self::getClient();
// 上传文件
$result = $client->uploadFile(self::BUCKET, $objectName, $filePath);
// 获取文件访问URL
$url = $client->signUrl(self::BUCKET, $objectName, 3600);
return [
'success' => true,
'url' => $url,
'object_name' => $objectName,
'size' => filesize($filePath),
'mime_type' => mime_content_type($filePath)
];
} catch (OssException $e) {
return [
'success' => false,
'error' => $e->getMessage()
];
}
}
/**
* 生成OSS对象名称
* @param string $originalName 原始文件名
* @return string
*/
public static function generateObjectName($originalName)
{
$ext = pathinfo($originalName, PATHINFO_EXTENSION);
$name = md5(uniqid(mt_rand(), true));
return date('Y/m/d/') . $name . '.' . $ext;
}
}

View File

@@ -1,6 +1,6 @@
<?php
namespace app\common;
namespace app\common\util;
use Darabonba\OpenApi\Models\Config;
use AlibabaCloud\SDK\Dysmsapi\V20170525\Dysmsapi;
@@ -62,4 +62,4 @@ class AliyunSMS {
return FALSE;
}
}
}

View File

@@ -5,15 +5,26 @@
use think\facade\Route;
// 定义RESTful风格的API路由 - 设备管理相关
Route::group('v1/devices', function () {
// 设备列表和查询
Route::get('', 'app\\devices\\controller\\Device@index'); // 获取设备列表
Route::get('count', 'app\\devices\\controller\\Device@count'); // 获取设备总数
Route::get(':id', 'app\\devices\\controller\\Device@read'); // 获取设备详情
// 定义RESTful风格的API路由
Route::group('v1/', function () {
// 设备管理
Route::post('', 'app\\devices\\controller\\Device@save'); // 添加设备
Route::put('refresh', 'app\\devices\\controller\\Device@refresh'); // 刷新设备状态
Route::delete(':id', 'app\\devices\\controller\\Device@delete'); // 删除设备
// 设备管理相关
Route::group('devices', function () {
Route::get('', 'app\\devices\\controller\\Device@index'); // 获取设备列表
Route::get('count', 'app\\devices\\controller\\Device@count'); // 获取设备总数
Route::get(':id', 'app\\devices\\controller\\Device@read'); // 获取设备详情
Route::post('', 'app\\devices\\controller\\Device@save'); // 添加设备
Route::put('refresh', 'app\\devices\\controller\\Device@refresh'); // 刷新设备状态
Route::delete(':id', 'app\\devices\\controller\\Device@delete'); // 删除设备
});
// 设备微信相关
Route::group('device/wechats', function () {
Route::get('count', 'app\\devices\\controller\\DeviceWechat@count'); // 获取在线微信账号数量
Route::get('device-count', 'app\\devices\\controller\\DeviceWechat@deviceCount'); // 获取有登录微信的设备数量
Route::get('', 'app\\devices\\controller\\DeviceWechat@index'); // 获取在线微信账号列表
Route::get(':id', 'app\\devices\\controller\\DeviceWechat@detail'); // 获取微信号详情
Route::put('refresh', 'app\\devices\\controller\\DeviceWechat@refresh'); // 刷新设备微信状态
Route::post('transfer-friends', 'app\\devices\\controller\\DeviceWechat@transferFriends'); // 微信好友转移
});
})->middleware(['jwt']);

View File

@@ -0,0 +1,476 @@
<?php
namespace app\devices\controller;
use think\Controller;
use app\devices\model\WechatAccount;
use think\facade\Request;
use think\Db;
/**
* 设备微信控制器
*/
class DeviceWechat extends Controller
{
/**
* 获取在线微信账号数量
* @return \think\response\Json
*/
public function count()
{
try {
// 获取在线微信账号数量
$count = WechatAccount::getOnlineWechatCount();
return json([
'code' => 200,
'msg' => '获取成功',
'data' => [
'count' => $count
]
]);
} catch (\Exception $e) {
return json([
'code' => 500,
'msg' => '获取失败:' . $e->getMessage()
]);
}
}
/**
* 获取有登录微信的设备数量
* @return \think\response\Json
*/
public function deviceCount()
{
try {
// 获取有登录微信的设备数量
$count = WechatAccount::getDeviceWithWechatCount();
return json([
'code' => 200,
'msg' => '获取成功',
'data' => [
'count' => $count
]
]);
} catch (\Exception $e) {
return json([
'code' => 500,
'msg' => '获取失败:' . $e->getMessage()
]);
}
}
/**
* 刷新设备微信状态
* @return \think\response\Json
*/
public function refresh()
{
try {
return json([
'code' => 200,
'msg' => '刷新成功',
'data' => []
]);
} catch (\Exception $e) {
return json([
'code' => 500,
'msg' => '获取失败:' . $e->getMessage()
]);
}
}
/**
* 获取在线微信账号列表
* @return \think\response\Json
*/
public function index()
{
try {
// 获取查询条件
$where = [];
// 微信ID
$wechatId = Request::param('wechat_id');
if (!empty($wechatId)) {
$where['wechatId'] = ['like', "%{$wechatId}%"];
}
// 昵称
$nickname = Request::param('nickname');
if (!empty($nickname)) {
$where['nickname|accountNickname'] = ['like', "%{$nickname}%"];
}
// 获取分页参数
$page = (int)Request::param('page', 1);
$limit = (int)Request::param('limit', 10);
// 获取排序参数
$sort = Request::param('sort', 'id');
$order = Request::param('order', 'desc');
// 获取在线微信账号列表
$list = WechatAccount::getOnlineWechatList($where, "{$sort} {$order}", $page, $limit);
// 处理返回数据
$data = [];
foreach ($list->items() as $item) {
// 计算今日可添加好友数量(这里使用一个示例算法,你可以根据实际需求修改)
$canAddFriendCount = 30 - (isset($item['yesterdayMsgCount']) ? intval($item['yesterdayMsgCount']) : 0);
if ($canAddFriendCount < 0) {
$canAddFriendCount = 0;
}
// 计算今日新增好友数量(示例数据,实际需要从数据库获取或通过其他方式计算)
// 这里只是一个示例,你需要根据实际情况替换
$todayNewFriendCount = mt_rand(0, 10); // 随机生成0-10的数字作为示例
$data[] = [
'id' => $item['id'],
'wechatId' => $item['wechatId'],
'nickname' => $item['nickname'] ?: $item['accountNickname'],
'avatar' => $item['avatar'],
'accountUserName' => $item['accountUserName'],
'status' => $item['wechatAlive'] ? '在线' : '离线',
'deviceStatus' => $item['deviceAlive'] ? '在线' : '离线',
'totalFriend' => $item['totalFriend'],
'canAddFriendCount' => $canAddFriendCount,
'deviceInfo' => $item['imei'] . ($item['deviceMemo'] ? " ({$item['deviceMemo']})" : ''),
'todayNewFriendCount' => $todayNewFriendCount
];
}
return json([
'code' => 200,
'msg' => '获取成功',
'data' => [
'total' => $list->total(),
'list' => $data
]
]);
} catch (\Exception $e) {
return json([
'code' => 500,
'msg' => '获取失败:' . $e->getMessage()
]);
}
}
/**
* 获取微信号详情
* @param int $id 微信号ID
* @return \think\response\Json
*/
public function detail($id)
{
try {
// 获取微信号基本信息
$wechat = WechatAccount::where('id', $id)
->where('isDeleted', 0)
->find();
if (!$wechat) {
return json([
'code' => 404,
'msg' => '微信号不存在'
]);
}
// 计算账号年龄(从创建时间到现在)
$accountAge = 0;
if ($wechat['createTime']) {
$createTime = strtotime($wechat['createTime']);
$now = time();
$accountAge = floor(($now - $createTime) / (24 * 3600));
}
// 计算活跃程度(根据消息数)
$activityLevel = '低';
if ($wechat['thirtyDayMsgCount'] > 1000) {
$activityLevel = '高';
} elseif ($wechat['thirtyDayMsgCount'] > 500) {
$activityLevel = '中';
}
// 评估账号权重(示例算法)
$weight = 0;
// 基础权重
$weight += 10;
// 好友数量权重
$weight += min($wechat['totalFriend'] / 100, 20);
// 活跃度权重
$weight += min($wechat['thirtyDayMsgCount'] / 100, 20);
// 账号年龄权重
$weight += min($accountAge / 30, 10);
// 在线状态权重
if ($wechat['wechatAlive']) {
$weight += 5;
}
// 获取限制记录(示例数据,实际需要从数据库获取)
$restrictions = [
[
'type' => '添加好友限制',
'reason' => '频繁添加好友',
'startTime' => date('Y-m-d H:i:s', strtotime('-1 day')),
'endTime' => date('Y-m-d H:i:s', strtotime('+1 day'))
]
];
// 获取微信好友列表
$friends = Db::table('tk_wechat_friend')
->where('wechatAccountId', $id)
->where('isDeleted', 0)
->field([
'id',
'wechatId',
'nickname',
'avatar',
'gender',
'region',
'signature',
'labels',
'createTime'
])
->select();
// 处理返回数据
$data = [
'basicInfo' => [
'id' => $wechat['id'],
'wechatId' => $wechat['wechatId'],
'nickname' => $wechat['nickname'] ?: $wechat['accountNickname'],
'avatar' => $wechat['avatar'],
'status' => $wechat['wechatAlive'] ? '在线' : '离线',
'deviceStatus' => $wechat['deviceAlive'] ? '在线' : '离线',
'deviceInfo' => $wechat['imei'] . ($wechat['deviceMemo'] ? " ({$wechat['deviceMemo']})" : ''),
'gender' => $wechat['gender'],
'region' => $wechat['region'],
'signature' => $wechat['signature']
],
'statistics' => [
'totalFriend' => $wechat['totalFriend'],
'maleFriend' => $wechat['maleFriend'],
'femaleFriend' => $wechat['femaleFriend'],
'canAddFriendCount' => 30 - (isset($wechat['yesterdayMsgCount']) ? intval($wechat['yesterdayMsgCount']) : 0),
'yesterdayMsgCount' => $wechat['yesterdayMsgCount'],
'sevenDayMsgCount' => $wechat['sevenDayMsgCount'],
'thirtyDayMsgCount' => $wechat['thirtyDayMsgCount']
],
'accountInfo' => [
'age' => $accountAge,
'activityLevel' => $activityLevel,
'weight' => round($weight, 2),
'createTime' => $wechat['createTime'],
'lastUpdateTime' => $wechat['updateTime']
],
'restrictions' => $restrictions,
'friends' => $friends
];
return json([
'code' => 200,
'msg' => '获取成功',
'data' => $data
]);
} catch (\Exception $e) {
return json([
'code' => 500,
'msg' => '获取失败:' . $e->getMessage()
]);
}
}
/**
* 微信好友转移
* 将一个微信号的好友转移至另一个在线微信号
*
* @return \think\response\Json
*/
public function transferFriends()
{
try {
// 获取请求参数
$sourceWechatId = Request::param('source_id'); // 源微信账号ID
$targetWechatId = Request::param('target_id'); // 目标微信账号ID
// 参数验证
if (empty($sourceWechatId) || empty($targetWechatId)) {
return json([
'code' => 400,
'msg' => '参数错误源微信账号ID和目标微信账号ID不能为空'
]);
}
// 检查源微信账号是否存在
$sourceWechat = WechatAccount::where('id', $sourceWechatId)
->where('isDeleted', 0)
->find();
if (!$sourceWechat) {
return json([
'code' => 404,
'msg' => '源微信账号不存在'
]);
}
// 检查目标微信账号是否存在且在线
$targetWechat = WechatAccount::where('id', $targetWechatId)
->where('isDeleted', 0)
->where('wechatAlive', 1)
->where('deviceAlive', 1)
->find();
if (!$targetWechat) {
return json([
'code' => 404,
'msg' => '目标微信账号不存在或不在线'
]);
}
// 获取源微信账号的好友列表
$friends = Db::table('tk_wechat_friend')
->where('wechatAccountId', $sourceWechatId)
->where('isDeleted', 0)
->select();
// 统计好友数量
$totalFriends = count($friends);
if ($totalFriends == 0) {
return json([
'code' => 400,
'msg' => '源微信账号没有可转移的好友'
]);
}
// 开始事务
Db::startTrans();
try {
$successCount = 0;
$failCount = 0;
$duplicateCount = 0;
$failList = [];
foreach ($friends as $friend) {
// 检查目标微信账号是否已经有此好友
$existFriend = Db::table('tk_wechat_friend')
->where('wechatAccountId', $targetWechatId)
->where('wechatId', $friend['wechatId'])
->where('isDeleted', 0)
->find();
if ($existFriend) {
// 已经存在此好友,跳过
$duplicateCount++;
continue;
}
// 准备插入数据
$newFriend = [
'wechatAccountId' => $targetWechatId,
'alias' => $friend['alias'],
'wechatId' => $friend['wechatId'],
'conRemark' => $friend['conRemark'],
'nickname' => $friend['nickname'],
'pyInitial' => $friend['pyInitial'],
'quanPin' => $friend['quanPin'],
'avatar' => $friend['avatar'],
'gender' => $friend['gender'],
'region' => $friend['region'],
'addFrom' => $friend['addFrom'],
'labels' => $friend['labels'],
'signature' => $friend['signature'],
'isDeleted' => 0,
'isPassed' => $friend['isPassed'],
'accountId' => $friend['accountId'],
'extendFields' => $friend['extendFields'],
'accountUserName' => $friend['accountUserName'],
'accountRealName' => $friend['accountRealName'],
'accountNickname' => $friend['accountNickname'],
'ownerAlias' => $targetWechat['alias'],
'ownerWechatId' => $targetWechat['wechatId'],
'ownerNickname' => $targetWechat['nickname'] ?: $targetWechat['accountNickname'],
'ownerAvatar' => $targetWechat['avatar'],
'phone' => $friend['phone'],
'thirdParty' => $friend['thirdParty'],
'groupId' => $friend['groupId'],
'passTime' => $friend['passTime'],
'additionalPicture' => $friend['additionalPicture'],
'desc' => $friend['desc'],
'country' => $friend['country'],
'province' => $friend['province'],
'city' => $friend['city'],
'createTime' => date('Y-m-d H:i:s'),
'updateTime' => date('Y-m-d H:i:s')
];
// 插入新好友记录
$result = Db::table('tk_wechat_friend')->insert($newFriend);
if ($result) {
$successCount++;
} else {
$failCount++;
$failList[] = [
'id' => $friend['id'],
'wechatId' => $friend['wechatId'],
'nickname' => $friend['nickname']
];
}
}
// 更新两个微信账号的好友数量
$maleFriendsCount = Db::table('tk_wechat_friend')
->where('wechatAccountId', $targetWechatId)
->where('isDeleted', 0)
->where('gender', 1)
->count();
$femaleFriendsCount = Db::table('tk_wechat_friend')
->where('wechatAccountId', $targetWechatId)
->where('isDeleted', 0)
->where('gender', 2)
->count();
$totalFriendsCount = $maleFriendsCount + $femaleFriendsCount;
// 更新目标微信账号的好友数量
WechatAccount::where('id', $targetWechatId)
->update([
'totalFriend' => $totalFriendsCount,
'maleFriend' => $maleFriendsCount,
'femaleFriend' => $femaleFriendsCount,
'updateTime' => date('Y-m-d H:i:s')
]);
// 提交事务
Db::commit();
return json([
'code' => 200,
'msg' => '好友转移成功',
'data' => [
'total' => $totalFriends,
'success' => $successCount,
'fail' => $failCount,
'duplicate' => $duplicateCount,
'failList' => $failList
]
]);
} catch (\Exception $e) {
// 回滚事务
Db::rollback();
throw $e;
}
} catch (\Exception $e) {
return json([
'code' => 500,
'msg' => '好友转移失败:' . $e->getMessage()
]);
}
}
}

View File

@@ -0,0 +1,129 @@
<?php
namespace app\devices\model;
use think\Model;
use think\Db;
/**
* 微信账号模型类
*/
class WechatAccount extends Model
{
// 设置表名
protected $name = 'wechat_account';
// 设置主键
protected $pk = 'id';
// 自动写入时间戳
protected $autoWriteTimestamp = 'datetime';
// 定义时间戳字段名
protected $createTime = 'createTime';
protected $updateTime = 'updateTime';
// 定义字段类型
protected $type = [
'id' => 'integer',
'deviceAccountId' => 'integer',
'keFuAlive' => 'integer',
'deviceAlive' => 'integer',
'wechatAlive' => 'integer',
'yesterdayMsgCount' => 'integer',
'sevenDayMsgCount' => 'integer',
'thirtyDayMsgCount' => 'integer',
'totalFriend' => 'integer',
'maleFriend' => 'integer',
'femaleFriend' => 'integer',
'gender' => 'integer',
'currentDeviceId' => 'integer',
'isDeleted' => 'integer',
'groupId' => 'integer'
];
/**
* 获取在线微信账号数量
*
* @param array $where 额外的查询条件
* @return int 微信账号数量
*/
public static function getOnlineWechatCount($where = [])
{
$condition = [
'deviceAlive' => 1,
'wechatAlive' => 1,
'isDeleted' => 0
];
// 合并额外条件
if (!empty($where)) {
$condition = array_merge($condition, $where);
}
return self::where($condition)->count();
}
/**
* 获取有登录微信的设备数量
*
* @param array $where 额外的查询条件
* @return int 设备数量
*/
public static function getDeviceWithWechatCount($where = [])
{
$condition = [
'deviceAlive' => 1,
'isDeleted' => 0
];
// 合并额外条件
if (!empty($where)) {
$condition = array_merge($condition, $where);
}
return self::where($condition)->count();
}
/**
* 获取在线微信账号列表
*
* @param array $where 额外的查询条件
* @param string $order 排序方式
* @param int $page 页码
* @param int $limit 每页数量
* @return \think\Paginator 分页对象
*/
public static function getOnlineWechatList($where = [], $order = 'id desc', $page = 1, $limit = 10)
{
$condition = [
'wechatAlive' => 1,
'deviceAlive' => 1,
'isDeleted' => 0
];
// 合并额外条件
if (!empty($where)) {
$condition = array_merge($condition, $where);
}
return self::where($condition)
->field([
'id',
'wechatId',
'accountNickname',
'nickname',
'accountUserName',
'avatar',
'wechatAlive',
'deviceAlive',
'totalFriend',
'maleFriend',
'femaleFriend',
'imei',
'deviceMemo',
'yesterdayMsgCount'
])
->order($order)
->paginate($limit, false, ['page' => $page]);
}
}