1133 lines
39 KiB
PHP
1133 lines
39 KiB
PHP
<?php
|
||
if (!defined('__TYPECHO_ROOT_DIR__')) exit;
|
||
|
||
/**
|
||
* MyTrack 动作处理器
|
||
*
|
||
* @package MyTrack
|
||
*/
|
||
class MyTrack_Action extends Typecho_Widget implements Widget_Interface_Do
|
||
{
|
||
/**
|
||
* 数据库连接
|
||
*
|
||
* @access private
|
||
* @var PDO
|
||
*/
|
||
private $db;
|
||
|
||
/**
|
||
* 构造函数
|
||
*
|
||
* @access public
|
||
* @param mixed $request request对象
|
||
* @param mixed $response response对象
|
||
* @param mixed $params 参数列表
|
||
* @return void
|
||
*/
|
||
public function __construct($request, $response, $params = NULL)
|
||
{
|
||
parent::__construct($request, $response, $params);
|
||
$this->db = MyTrack_Plugin::getDbConnection();
|
||
}
|
||
|
||
/**
|
||
* 动作入口
|
||
*
|
||
* @access public
|
||
* @return void
|
||
*/
|
||
public function action()
|
||
{
|
||
$this->widget('Widget_User')->pass('administrator');
|
||
$this->on($this->request->is('do=add'))->add();
|
||
$this->on($this->request->is('do=update'))->update();
|
||
$this->on($this->request->is('do=delete'))->delete();
|
||
$this->on($this->request->is('do=get'))->get();
|
||
$this->on($this->request->is('do=getAll'))->getAll();
|
||
$this->on($this->request->is('do=getOne'))->get();
|
||
$this->on($this->request->is('do=list'))->list();
|
||
$this->on($this->request->is('do=batchDelete'))->batchDelete();
|
||
$this->on($this->request->is('do=import'))->import();
|
||
$this->on($this->request->is('do=export'))->export();
|
||
$this->on($this->request->is('do=exportSimple'))->exportSimple();
|
||
$this->on($this->request->is('do=getArticleImages'))->getArticleImages();
|
||
$this->on($this->request->is('do=getById'))->getById();
|
||
$this->on($this->request->is('do=getRelatedArticlesInfo'))->getRelatedArticlesInfo();
|
||
|
||
// 默认返回列表
|
||
$this->response->throwJson(array(
|
||
'success' => false,
|
||
'message' => '无效的操作'
|
||
));
|
||
}
|
||
|
||
/**
|
||
* 检测重复数据(基于经纬度)
|
||
*
|
||
* @access private
|
||
* @param float $latitude 纬度
|
||
* @param float $longitude 经度
|
||
* @param int $excludeId 要排除的ID(更新时使用)
|
||
* @return array|false 返回重复的数据信息,false表示无重复
|
||
*/
|
||
private function checkDuplicate($latitude, $longitude, $excludeId = null)
|
||
{
|
||
try {
|
||
// 根据经纬度进行检测,使用较高精度(0.000001度约等于0.1米)
|
||
$sql = "SELECT id, name FROM plugin_track_footprint
|
||
WHERE ABS(latitude - ?) < 0.000001
|
||
AND ABS(longitude - ?) < 0.000001";
|
||
|
||
$params = array($latitude, $longitude);
|
||
|
||
if ($excludeId !== null) {
|
||
$sql .= " AND id != ?";
|
||
$params[] = $excludeId;
|
||
}
|
||
|
||
$sql .= " LIMIT 1";
|
||
|
||
$stmt = $this->db->prepare($sql);
|
||
$stmt->execute($params);
|
||
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
||
|
||
return $result ? $result : false;
|
||
} catch (PDOException $e) {
|
||
error_log('检测重复数据失败: ' . $e->getMessage());
|
||
return false;
|
||
}
|
||
}
|
||
|
||
public function add()
|
||
{
|
||
// 原有的参数获取
|
||
$latitude = $this->request->get('latitude');
|
||
$longitude = $this->request->get('longitude');
|
||
$name = $this->request->get('name');
|
||
$description = $this->request->get('description');
|
||
$tags = $this->request->get('tags');
|
||
$article_cid = $this->request->get('article_cid');
|
||
$urlLabel = $this->request->get('urlLabel');
|
||
$url = $this->request->get('url');
|
||
$photos = $this->request->get('photos');
|
||
$date = $this->request->get('date');
|
||
|
||
// 新增字段
|
||
$address = $this->request->get('address');
|
||
$location_type = $this->request->get('location_type');
|
||
$rating_level = $this->request->get('rating_level', 0);
|
||
$categories = $this->request->get('categories');
|
||
$review = $this->request->get('review');
|
||
$markerColor = $this->request->get('markerColor');
|
||
$related_articles = $this->request->get('related_articles');
|
||
$highlights = $this->request->get('highlights');
|
||
|
||
// 如果提供了日期,确保格式为 YYYY-MM-DD HH:MM:SS
|
||
if (!empty($date)) {
|
||
$date = date('Y-m-d H:i:s', strtotime($date));
|
||
}
|
||
|
||
// 验证必填字段
|
||
if (empty($latitude) || empty($longitude) || empty($name)) {
|
||
$this->response->throwJson(array(
|
||
'success' => false,
|
||
'message' => '经纬度和地点名称不能为空'
|
||
));
|
||
}
|
||
|
||
// 检查重复数据(基于经纬度)
|
||
$duplicate = $this->checkDuplicate($latitude, $longitude);
|
||
if ($duplicate !== false && isset($duplicate['name']) && isset($duplicate['id'])) {
|
||
$this->response->throwJson(array(
|
||
'success' => false,
|
||
'message' => "该位置已存在地点:{$duplicate['name']}(ID: {$duplicate['id']}),请勿重复添加"
|
||
));
|
||
}
|
||
|
||
try {
|
||
// 如果提供了文章CID,获取文章信息和图片
|
||
if (!empty($article_cid)) {
|
||
$articleInfo = MyTrack_Plugin::getArticleInfo($article_cid);
|
||
|
||
// 如果没有提供图片链接,尝试从文章中提取(限制4张)
|
||
if (empty($photos) && !empty($articleInfo['images'])) {
|
||
$limitedImages = array_slice($articleInfo['images'], 0, 4);
|
||
$photos = implode(',', $limitedImages);
|
||
}
|
||
|
||
// 如果没有提供文章标题,尝试从文章中获取
|
||
if (empty($urlLabel) && !empty($articleInfo['title'])) {
|
||
$urlLabel = $articleInfo['title'];
|
||
}
|
||
|
||
// 如果没有提供文章链接,尝试从文章中获取
|
||
if (empty($url) && !empty($articleInfo['link'])) {
|
||
$url = $articleInfo['link'];
|
||
}
|
||
|
||
// 如果没有提供标签,尝试从文章中获取
|
||
if (empty($tags) && !empty($articleInfo['tags'])) {
|
||
$tags = implode(',', $articleInfo['tags']);
|
||
}
|
||
}
|
||
|
||
$stmt = $this->db->prepare("INSERT INTO plugin_track_footprint
|
||
(latitude, longitude, name, address, location_type, rating_level, categories, review, description, tags, article_cid, urlLabel, url, photos, date, markerColor, related_articles, highlights)
|
||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
||
|
||
$stmt->execute(array(
|
||
$latitude,
|
||
$longitude,
|
||
$name,
|
||
$address,
|
||
$location_type,
|
||
$rating_level,
|
||
$categories,
|
||
$review,
|
||
$description,
|
||
$tags,
|
||
$article_cid,
|
||
$urlLabel,
|
||
$url,
|
||
$photos,
|
||
$date,
|
||
$markerColor,
|
||
$related_articles,
|
||
$highlights
|
||
));
|
||
|
||
$id = $this->db->lastInsertId();
|
||
|
||
// 清除足迹缓存
|
||
$this->clearFootprintCache();
|
||
|
||
$this->response->throwJson(array(
|
||
'success' => true,
|
||
'message' => '足迹添加成功',
|
||
'data' => array(
|
||
'id' => $id
|
||
)
|
||
));
|
||
} catch (PDOException $e) {
|
||
$this->response->throwJson(array(
|
||
'success' => false,
|
||
'message' => '添加失败: ' . $e->getMessage()
|
||
));
|
||
}
|
||
}
|
||
|
||
public function update()
|
||
{
|
||
$id = $this->request->get('id');
|
||
$latitude = $this->request->get('latitude');
|
||
$longitude = $this->request->get('longitude');
|
||
$name = $this->request->get('name');
|
||
$description = $this->request->get('description');
|
||
$tags = $this->request->get('tags');
|
||
$article_cid = $this->request->get('article_cid');
|
||
$urlLabel = $this->request->get('urlLabel');
|
||
$url = $this->request->get('url');
|
||
$photos = $this->request->get('photos');
|
||
$date = $this->request->get('date');
|
||
|
||
// 新增字段
|
||
$address = $this->request->get('address');
|
||
$location_type = $this->request->get('location_type');
|
||
$rating_level = $this->request->get('rating_level', 0);
|
||
$categories = $this->request->get('categories');
|
||
$review = $this->request->get('review');
|
||
$markerColor = $this->request->get('markerColor');
|
||
$related_articles = $this->request->get('related_articles');
|
||
$highlights = $this->request->get('highlights');
|
||
|
||
// 验证必填字段
|
||
if (empty($id) || empty($latitude) || empty($longitude) || empty($name)) {
|
||
$this->response->throwJson(array(
|
||
'success' => false,
|
||
'message' => 'ID、经纬度和地点名称不能为空'
|
||
));
|
||
}
|
||
|
||
// 检查重复数据(基于经纬度,排除自身)
|
||
$duplicate = $this->checkDuplicate($latitude, $longitude, $id);
|
||
if ($duplicate !== false && isset($duplicate['name']) && isset($duplicate['id'])) {
|
||
$this->response->throwJson(array(
|
||
'success' => false,
|
||
'message' => "该位置已存在其他地点:{$duplicate['name']}(ID: {$duplicate['id']}),请修改经纬度"
|
||
));
|
||
}
|
||
|
||
try {
|
||
// 如果提供了文章CID,获取文章信息和图片
|
||
if (!empty($article_cid)) {
|
||
$articleInfo = MyTrack_Plugin::getArticleInfo($article_cid);
|
||
|
||
if (empty($photos) && !empty($articleInfo['images'])) {
|
||
$limitedImages = array_slice($articleInfo['images'], 0, 4);
|
||
$photos = implode(',', $limitedImages);
|
||
}
|
||
|
||
if (empty($urlLabel) && !empty($articleInfo['title'])) {
|
||
$urlLabel = $articleInfo['title'];
|
||
}
|
||
|
||
if (empty($url) && !empty($articleInfo['link'])) {
|
||
$url = $articleInfo['link'];
|
||
}
|
||
|
||
if (empty($tags) && !empty($articleInfo['tags'])) {
|
||
$tags = implode(',', $articleInfo['tags']);
|
||
}
|
||
}
|
||
|
||
$stmt = $this->db->prepare("UPDATE plugin_track_footprint
|
||
SET latitude = ?, longitude = ?, name = ?, address = ?, location_type = ?, rating_level = ?, categories = ?, review = ?, description = ?, tags = ?, article_cid = ?,
|
||
urlLabel = ?, url = ?, photos = ?, date = ?, markerColor = ?, related_articles = ?, highlights = ?
|
||
WHERE id = ?");
|
||
|
||
$result = $stmt->execute(array(
|
||
$latitude,
|
||
$longitude,
|
||
$name,
|
||
$address,
|
||
$location_type,
|
||
$rating_level,
|
||
$categories,
|
||
$review,
|
||
$description,
|
||
$tags,
|
||
$article_cid,
|
||
$urlLabel,
|
||
$url,
|
||
$photos,
|
||
$date,
|
||
$markerColor,
|
||
$related_articles,
|
||
$highlights,
|
||
$id
|
||
));
|
||
|
||
if ($result) {
|
||
// 清除足迹缓存
|
||
$this->clearFootprintCache();
|
||
|
||
$this->response->throwJson(array(
|
||
'success' => true,
|
||
'message' => '足迹更新成功'
|
||
));
|
||
} else {
|
||
$this->response->throwJson(array(
|
||
'success' => false,
|
||
'message' => '足迹不存在或更新失败'
|
||
));
|
||
}
|
||
} catch (PDOException $e) {
|
||
$this->response->throwJson(array(
|
||
'success' => false,
|
||
'message' => '更新失败: ' . $e->getMessage()
|
||
));
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取关联文章信息
|
||
*
|
||
* @access public
|
||
* @return void
|
||
*/
|
||
public function getRelatedArticlesInfo()
|
||
{
|
||
$related_articles = $this->request->get('related_articles');
|
||
|
||
if (empty($related_articles)) {
|
||
$this->response->throwJson(array(
|
||
'success' => false,
|
||
'message' => '关联文章不能为空'
|
||
));
|
||
}
|
||
|
||
try {
|
||
$result = MyTrack_Plugin::getRelatedArticlesInfo($related_articles);
|
||
|
||
$this->response->throwJson(array(
|
||
'success' => true,
|
||
'data' => $result
|
||
));
|
||
} catch (Exception $e) {
|
||
$this->response->throwJson(array(
|
||
'success' => false,
|
||
'message' => '获取关联文章信息失败: ' . $e->getMessage()
|
||
));
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 批量删除足迹
|
||
*
|
||
* @access public
|
||
* @return void
|
||
*/
|
||
public function batchDelete()
|
||
{
|
||
$ids = $this->request->filter('int')->getArray('footprint');
|
||
|
||
if (empty($ids)) {
|
||
$rawIds = $this->request->get('footprint');
|
||
if (!empty($rawIds)) {
|
||
$ids = is_array($rawIds) ? $rawIds : array($rawIds);
|
||
$ids = array_map('intval', $ids);
|
||
}
|
||
}
|
||
|
||
if (empty($ids) || !is_array($ids)) {
|
||
$this->response->throwJson(array(
|
||
'success' => false,
|
||
'message' => '请选择要删除的足迹'
|
||
));
|
||
}
|
||
|
||
$ids = array_filter($ids, function($id) {
|
||
return is_numeric($id) && $id > 0;
|
||
});
|
||
|
||
if (empty($ids)) {
|
||
$this->response->throwJson(array(
|
||
'success' => false,
|
||
'message' => '无效的足迹ID'
|
||
));
|
||
}
|
||
|
||
try {
|
||
$placeholders = implode(',', array_fill(0, count($ids), '?'));
|
||
$stmt = $this->db->prepare("DELETE FROM plugin_track_footprint WHERE id IN ($placeholders)");
|
||
$result = $stmt->execute($ids);
|
||
|
||
if ($result) {
|
||
$this->clearFootprintCache();
|
||
|
||
$this->response->throwJson(array(
|
||
'success' => true,
|
||
'message' => '足迹删除成功'
|
||
));
|
||
} else {
|
||
$this->response->throwJson(array(
|
||
'success' => false,
|
||
'message' => '删除失败,请检查足迹是否存在'
|
||
));
|
||
}
|
||
} catch (PDOException $e) {
|
||
$this->response->throwJson(array(
|
||
'success' => false,
|
||
'message' => '删除失败: ' . $e->getMessage()
|
||
));
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 删除足迹
|
||
*
|
||
* @access public
|
||
* @return void
|
||
*/
|
||
public function delete()
|
||
{
|
||
$id = $this->request->get('id');
|
||
|
||
if (empty($id)) {
|
||
$this->response->throwJson(array(
|
||
'success' => false,
|
||
'message' => 'ID不能为空'
|
||
));
|
||
}
|
||
|
||
try {
|
||
$stmt = $this->db->prepare("DELETE FROM plugin_track_footprint WHERE id = ?");
|
||
$result = $stmt->execute(array($id));
|
||
|
||
if ($result) {
|
||
$this->clearFootprintCache();
|
||
|
||
$this->response->throwJson(array(
|
||
'success' => true,
|
||
'message' => '足迹删除成功'
|
||
));
|
||
} else {
|
||
$this->response->throwJson(array(
|
||
'success' => false,
|
||
'message' => '足迹不存在或删除失败'
|
||
));
|
||
}
|
||
} catch (PDOException $e) {
|
||
$this->response->throwJson(array(
|
||
'success' => false,
|
||
'message' => '删除失败: ' . $e->getMessage()
|
||
));
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取单个足迹
|
||
*
|
||
* @access public
|
||
* @return void
|
||
*/
|
||
public function get()
|
||
{
|
||
$id = $this->request->get('id');
|
||
|
||
if (empty($id)) {
|
||
$this->response->throwJson(array(
|
||
'success' => false,
|
||
'message' => 'ID不能为空'
|
||
));
|
||
}
|
||
|
||
try {
|
||
$stmt = $this->db->prepare("SELECT * FROM plugin_track_footprint WHERE id = ?");
|
||
$stmt->execute(array($id));
|
||
$footprint = $stmt->fetch(PDO::FETCH_ASSOC);
|
||
|
||
if ($footprint) {
|
||
if (!empty($footprint['related_articles'])) {
|
||
$footprint['related_articles_info'] = MyTrack_Plugin::getRelatedArticlesInfo($footprint['related_articles']);
|
||
}
|
||
|
||
$this->response->throwJson(array(
|
||
'success' => true,
|
||
'data' => $footprint
|
||
));
|
||
} else {
|
||
$this->response->throwJson(array(
|
||
'success' => false,
|
||
'message' => '足迹不存在'
|
||
));
|
||
}
|
||
} catch (PDOException $e) {
|
||
$this->response->throwJson(array(
|
||
'success' => false,
|
||
'message' => '查询失败: ' . $e->getMessage()
|
||
));
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 通过ID获取单个足迹(公开接口)
|
||
*
|
||
* @access public
|
||
* @return void
|
||
*/
|
||
public function getById()
|
||
{
|
||
$id = $this->request->get('id');
|
||
|
||
if (empty($id)) {
|
||
$this->response->throwJson(array(
|
||
'success' => false,
|
||
'message' => 'ID不能为空'
|
||
));
|
||
}
|
||
|
||
try {
|
||
$stmt = $this->db->prepare("SELECT * FROM plugin_track_footprint WHERE id = ?");
|
||
$stmt->execute(array($id));
|
||
$footprint = $stmt->fetch(PDO::FETCH_ASSOC);
|
||
|
||
if ($footprint) {
|
||
if (!empty($footprint['categories'])) {
|
||
$footprint['categories'] = explode(',', $footprint['categories']);
|
||
} else {
|
||
$footprint['categories'] = array();
|
||
}
|
||
|
||
if (!empty($footprint['highlights'])) {
|
||
$footprint['highlights'] = explode(',', $footprint['highlights']);
|
||
} else {
|
||
$footprint['highlights'] = array();
|
||
}
|
||
|
||
if (!empty($footprint['article_cid'])) {
|
||
$articleInfo = MyTrack_Plugin::getArticleInfo($footprint['article_cid']);
|
||
if (!empty($articleInfo['title']) && empty($footprint['urlLabel'])) {
|
||
$footprint['urlLabel'] = $articleInfo['title'];
|
||
}
|
||
if (!empty($articleInfo['link']) && empty($footprint['url'])) {
|
||
$footprint['url'] = $articleInfo['link'];
|
||
}
|
||
}
|
||
|
||
if (!empty($footprint['related_articles'])) {
|
||
$footprint['related_articles_info'] = MyTrack_Plugin::getRelatedArticlesInfo($footprint['related_articles']);
|
||
}
|
||
|
||
$this->response->throwJson(array(
|
||
'success' => true,
|
||
'data' => $footprint
|
||
));
|
||
} else {
|
||
$this->response->throwJson(array(
|
||
'success' => false,
|
||
'message' => '足迹不存在'
|
||
));
|
||
}
|
||
} catch (PDOException $e) {
|
||
$this->response->throwJson(array(
|
||
'success' => false,
|
||
'message' => '查询失败: ' . $e->getMessage()
|
||
));
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取足迹列表
|
||
*
|
||
* @access public
|
||
* @return void
|
||
*/
|
||
public function list()
|
||
{
|
||
$page = $this->request->get('page', 1);
|
||
$size = $this->request->get('size', 10);
|
||
$offset = ($page - 1) * $size;
|
||
|
||
try {
|
||
$countStmt = $this->db->query("SELECT COUNT(*) FROM plugin_track_footprint");
|
||
$total = $countStmt->fetchColumn();
|
||
|
||
$stmt = $this->db->prepare("SELECT * FROM plugin_track_footprint
|
||
ORDER BY date DESC, created_at DESC
|
||
LIMIT ? OFFSET ?");
|
||
$stmt->execute(array($size, $offset));
|
||
$footprints = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||
|
||
foreach ($footprints as &$footprint) {
|
||
if (!empty($footprint['related_articles'])) {
|
||
$footprint['related_articles_info'] = MyTrack_Plugin::getRelatedArticlesInfo($footprint['related_articles']);
|
||
}
|
||
}
|
||
|
||
$this->response->throwJson(array(
|
||
'success' => true,
|
||
'data' => array(
|
||
'total' => $total,
|
||
'page' => $page,
|
||
'size' => $size,
|
||
'pages' => ceil($total / $size),
|
||
'items' => $footprints
|
||
)
|
||
));
|
||
} catch (PDOException $e) {
|
||
$this->response->throwJson(array(
|
||
'success' => false,
|
||
'message' => '查询失败: ' . $e->getMessage()
|
||
));
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 导入足迹
|
||
*
|
||
* @access public
|
||
* @return void
|
||
*/
|
||
public function import()
|
||
{
|
||
if (!isset($_FILES['file']) || $_FILES['file']['error'] !== UPLOAD_ERR_OK) {
|
||
$this->response->throwJson(array(
|
||
'success' => false,
|
||
'message' => '请上传有效的文件'
|
||
));
|
||
}
|
||
|
||
$file = $_FILES['file'];
|
||
$fileType = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
|
||
|
||
if (!in_array($fileType, array('json'))) {
|
||
$this->response->throwJson(array(
|
||
'success' => false,
|
||
'message' => '只支持JSON格式的文件'
|
||
));
|
||
}
|
||
|
||
try {
|
||
$content = file_get_contents($file['tmp_name']);
|
||
|
||
$encoding = mb_detect_encoding($content, array('UTF-8', 'GBK', 'GB2312', 'ASCII'), true);
|
||
if ($encoding !== 'UTF-8' && $encoding !== false) {
|
||
$content = mb_convert_encoding($content, 'UTF-8', $encoding);
|
||
}
|
||
|
||
$data = json_decode($content, true);
|
||
if (json_last_error() !== JSON_ERROR_NONE) {
|
||
throw new Exception('JSON格式错误: ' . json_last_error_msg());
|
||
}
|
||
|
||
if (!is_array($data)) {
|
||
throw new Exception('JSON格式不正确,应该是一个数组');
|
||
}
|
||
|
||
$successCount = 0;
|
||
$errorCount = 0;
|
||
$errors = array();
|
||
|
||
$this->db->beginTransaction();
|
||
|
||
foreach ($data as $index => $item) {
|
||
try {
|
||
if (empty($item['latitude']) || empty($item['longitude']) || empty($item['name'])) {
|
||
$errors[] = "第" . ($index + 1) . "行: 缺少经纬度或地点名称";
|
||
$errorCount++;
|
||
continue;
|
||
}
|
||
|
||
// 导入时检查重复数据(基于经纬度)
|
||
$duplicate = $this->checkDuplicate($item['latitude'], $item['longitude']);
|
||
if ($duplicate !== false && isset($duplicate['name']) && isset($duplicate['id'])) {
|
||
$errors[] = "第" . ($index + 1) . "行: 该位置已存在地点:{$duplicate['name']}(ID: {$duplicate['id']}),跳过导入";
|
||
$errorCount++;
|
||
continue;
|
||
}
|
||
|
||
$categories = '';
|
||
if (isset($item['categories'])) {
|
||
if (is_array($item['categories'])) {
|
||
$categories = implode(',', $item['categories']);
|
||
} else {
|
||
$categories = $item['categories'];
|
||
}
|
||
}
|
||
|
||
$highlights = '';
|
||
if (isset($item['highlights'])) {
|
||
if (is_array($item['highlights'])) {
|
||
$highlights = implode(',', $item['highlights']);
|
||
} else {
|
||
$highlights = $item['highlights'];
|
||
}
|
||
}
|
||
|
||
$photos = '';
|
||
if (isset($item['photos'])) {
|
||
if (is_array($item['photos'])) {
|
||
$photos = implode(',', $item['photos']);
|
||
} else {
|
||
$photos = $item['photos'];
|
||
}
|
||
}
|
||
|
||
$related_articles = '';
|
||
if (isset($item['related_articles'])) {
|
||
if (is_array($item['related_articles'])) {
|
||
$related_articles = implode(',', $item['related_articles']);
|
||
} else {
|
||
$related_articles = $item['related_articles'];
|
||
}
|
||
}
|
||
|
||
$stmt = $this->db->prepare("INSERT INTO plugin_track_footprint
|
||
(latitude, longitude, name, address, location_type, rating_level, categories, review,
|
||
description, tags, article_cid, urlLabel, url, photos, date, markerColor,
|
||
related_articles, highlights, created_at, updated_at)
|
||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
||
|
||
$result = $stmt->execute(array(
|
||
$item['latitude'] ?? null,
|
||
$item['longitude'] ?? null,
|
||
$item['name'] ?? '',
|
||
$item['address'] ?? null,
|
||
$item['location_type'] ?? null,
|
||
$item['rating_level'] ?? 0,
|
||
$categories,
|
||
$item['review'] ?? null,
|
||
$item['description'] ?? null,
|
||
$item['tags'] ?? null,
|
||
$item['article_cid'] ?? null,
|
||
$item['urlLabel'] ?? null,
|
||
$item['url'] ?? null,
|
||
$photos,
|
||
$item['date'] ?? null,
|
||
$item['markerColor'] ?? null,
|
||
$related_articles,
|
||
$highlights,
|
||
$item['created_at'] ?? date('Y-m-d H:i:s'),
|
||
$item['updated_at'] ?? date('Y-m-d H:i:s')
|
||
));
|
||
|
||
if ($result) {
|
||
$successCount++;
|
||
} else {
|
||
$errors[] = "第" . ($index + 1) . "行: 插入失败";
|
||
$errorCount++;
|
||
}
|
||
} catch (PDOException $e) {
|
||
$errorCount++;
|
||
$errors[] = "第" . ($index + 1) . "行: " . $e->getMessage();
|
||
}
|
||
}
|
||
|
||
$this->db->commit();
|
||
|
||
$this->clearFootprintCache();
|
||
|
||
$this->response->throwJson(array(
|
||
'success' => true,
|
||
'message' => "导入完成,成功: {$successCount},失败: {$errorCount}",
|
||
'data' => array(
|
||
'success' => $successCount,
|
||
'error' => $errorCount,
|
||
'errors' => $errors
|
||
)
|
||
));
|
||
} catch (Exception $e) {
|
||
$this->db->rollBack();
|
||
|
||
$this->response->throwJson(array(
|
||
'success' => false,
|
||
'message' => '导入失败: ' . $e->getMessage()
|
||
));
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 导出足迹
|
||
*
|
||
* @access public
|
||
* @return void
|
||
*/
|
||
public function export()
|
||
{
|
||
$format = $this->request->get('format', 'json');
|
||
|
||
if (!in_array($format, array('json'))) {
|
||
$this->response->throwJson(array(
|
||
'success' => false,
|
||
'message' => '不支持的导出格式'
|
||
));
|
||
}
|
||
|
||
try {
|
||
$stmt = $this->db->query("SELECT
|
||
id,
|
||
latitude,
|
||
longitude,
|
||
name,
|
||
address,
|
||
location_type,
|
||
rating_level,
|
||
categories,
|
||
review,
|
||
description,
|
||
tags,
|
||
article_cid,
|
||
urlLabel,
|
||
url,
|
||
photos,
|
||
date,
|
||
markerColor,
|
||
related_articles,
|
||
highlights,
|
||
created_at,
|
||
updated_at
|
||
FROM plugin_track_footprint
|
||
ORDER BY date DESC, created_at DESC");
|
||
|
||
$footprints = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||
|
||
if (empty($footprints)) {
|
||
$this->response->throwJson(array(
|
||
'success' => false,
|
||
'message' => '没有数据可导出'
|
||
));
|
||
}
|
||
|
||
$filename = 'mytrack_footprints_' . date('Ymd_His') . '.json';
|
||
$content = json_encode($footprints, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||
|
||
header('Content-Type: application/json');
|
||
header('Content-Disposition: attachment; filename="' . $filename . '"');
|
||
header('Content-Length: ' . strlen($content));
|
||
header('Cache-Control: no-cache, no-store, must-revalidate');
|
||
header('Pragma: no-cache');
|
||
header('Expires: 0');
|
||
|
||
echo $content;
|
||
exit;
|
||
|
||
} catch (PDOException $e) {
|
||
$this->response->throwJson(array(
|
||
'success' => false,
|
||
'message' => '导出失败: ' . $e->getMessage()
|
||
));
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 精简导出足迹
|
||
*
|
||
* @access public
|
||
* @return void
|
||
*/
|
||
public function exportSimple()
|
||
{
|
||
$format = $this->request->get('format', 'json');
|
||
|
||
if (!in_array($format, array('json'))) {
|
||
$this->response->throwJson(array(
|
||
'success' => false,
|
||
'message' => '不支持的导出格式'
|
||
));
|
||
}
|
||
|
||
try {
|
||
$stmt = $this->db->query("SELECT
|
||
name,
|
||
latitude,
|
||
longitude,
|
||
date,
|
||
url,
|
||
urlLabel,
|
||
categories,
|
||
markerColor,
|
||
photos,
|
||
related_articles,
|
||
highlights
|
||
FROM plugin_track_footprint
|
||
ORDER BY date DESC");
|
||
|
||
$footprints = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||
|
||
if (empty($footprints)) {
|
||
$this->response->throwJson(array(
|
||
'success' => false,
|
||
'message' => '没有数据可导出'
|
||
));
|
||
}
|
||
|
||
$locations = array();
|
||
foreach ($footprints as $footprint) {
|
||
$coordinates = $footprint['longitude'] . ',' . $footprint['latitude'];
|
||
|
||
$categories = array();
|
||
if (!empty($footprint['categories'])) {
|
||
$categoryArray = explode(',', $footprint['categories']);
|
||
foreach ($categoryArray as $category) {
|
||
$category = trim($category);
|
||
switch($category) {
|
||
case 'visited':
|
||
$categories[] = '去过';
|
||
break;
|
||
case 'want':
|
||
$categories[] = '想去';
|
||
break;
|
||
case 'plan':
|
||
$categories[] = '计划';
|
||
break;
|
||
default:
|
||
$categories[] = $category;
|
||
}
|
||
}
|
||
}
|
||
|
||
$highlights = !empty($footprint['highlights']) ? explode(',', $footprint['highlights']) : array();
|
||
$photos = !empty($footprint['photos']) ? explode(',', $footprint['photos']) : array();
|
||
$relatedArticles = !empty($footprint['related_articles']) ? explode(',', $footprint['related_articles']) : array();
|
||
|
||
$locations[] = array(
|
||
'name' => $footprint['name'] ?? '',
|
||
'coordinates' => $coordinates,
|
||
'date' => $footprint['date'] ?? '',
|
||
'url' => $footprint['url'] ?? '',
|
||
'urlLabel' => $footprint['urlLabel'] ?? '',
|
||
'categories' => $categories,
|
||
'highlights' => $highlights,
|
||
'markerColor' => $footprint['markerColor'] ?? '',
|
||
'photos' => $photos,
|
||
'relatedArticles' => $relatedArticles
|
||
);
|
||
}
|
||
|
||
$exportData = array('locations' => $locations);
|
||
$filename = 'mytrack_simple_' . date('Ymd_His') . '.json';
|
||
$content = json_encode($exportData, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||
|
||
header('Content-Type: application/json');
|
||
header('Content-Disposition: attachment; filename="' . $filename . '"');
|
||
header('Content-Length: ' . strlen($content));
|
||
header('Cache-Control: no-cache, no-store, must-revalidate');
|
||
header('Pragma: no-cache');
|
||
header('Expires: 0');
|
||
|
||
echo $content;
|
||
exit;
|
||
|
||
} catch (PDOException $e) {
|
||
$this->response->throwJson(array(
|
||
'success' => false,
|
||
'message' => '导出失败: ' . $e->getMessage()
|
||
));
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取所有足迹(用于前台地图显示)
|
||
*
|
||
* @access public
|
||
* @return void
|
||
*/
|
||
public function getAll()
|
||
{
|
||
try {
|
||
$options = Typecho_Widget::widget('Widget_Options')->plugin('MyTrack');
|
||
$cacheExpire = isset($options->cacheExpire) && is_numeric($options->cacheExpire) && $options->cacheExpire >= 0
|
||
? intval($options->cacheExpire) * 24 * 60 * 60
|
||
: 7 * 24 * 60 * 60;
|
||
|
||
$cacheKey = 'mytrack_footprints_data';
|
||
|
||
if ($cacheExpire > 0) {
|
||
$cacheFile = __DIR__ . '/cache/' . md5($cacheKey) . '.cache';
|
||
|
||
if (file_exists($cacheFile) && filemtime($cacheFile) + $cacheExpire > time()) {
|
||
$cachedData = unserialize(file_get_contents($cacheFile));
|
||
if ($cachedData !== false) {
|
||
$this->response->throwJson(array(
|
||
'success' => true,
|
||
'data' => $cachedData,
|
||
'from_cache' => true
|
||
));
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
|
||
$stmt = $this->db->query("SELECT * FROM plugin_track_footprint ORDER BY date ASC, created_at ASC");
|
||
$footprints = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||
|
||
$validFootprints = array_filter($footprints, function($footprint) {
|
||
$longitude = is_numeric($footprint['longitude']) ? floatval($footprint['longitude']) : null;
|
||
$latitude = is_numeric($footprint['latitude']) ? floatval($footprint['latitude']) : null;
|
||
|
||
return $longitude !== null && $latitude !== null &&
|
||
!is_nan($longitude) && !is_nan($latitude) &&
|
||
$longitude >= -180 && $longitude <= 180 &&
|
||
$latitude >= -90 && $latitude <= 90;
|
||
});
|
||
|
||
$validFootprints = array_values($validFootprints);
|
||
|
||
foreach ($validFootprints as &$footprint) {
|
||
if (!empty($footprint['related_articles'])) {
|
||
$footprint['related_articles_info'] = MyTrack_Plugin::getRelatedArticlesInfo($footprint['related_articles']);
|
||
}
|
||
}
|
||
|
||
if ($cacheExpire > 0) {
|
||
$cacheDir = __DIR__ . '/cache';
|
||
if (!is_dir($cacheDir)) {
|
||
mkdir($cacheDir, 0755, true);
|
||
}
|
||
|
||
$cacheFile = $cacheDir . '/' . md5($cacheKey) . '.cache';
|
||
file_put_contents($cacheFile, serialize($validFootprints));
|
||
}
|
||
|
||
$this->response->throwJson(array(
|
||
'success' => true,
|
||
'data' => $validFootprints,
|
||
'from_cache' => false
|
||
));
|
||
} catch (PDOException $e) {
|
||
$this->response->throwJson(array(
|
||
'success' => false,
|
||
'message' => '查询失败: ' . $e->getMessage()
|
||
));
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取文章图片
|
||
*
|
||
* @access public
|
||
* @return void
|
||
*/
|
||
public function getArticleImages()
|
||
{
|
||
$article_cid = $this->request->get('article_cid');
|
||
|
||
if (empty($article_cid)) {
|
||
$this->response->throwJson(array(
|
||
'success' => false,
|
||
'message' => '文章CID不能为空'
|
||
));
|
||
}
|
||
|
||
try {
|
||
$articleInfo = MyTrack_Plugin::getArticleInfo($article_cid);
|
||
|
||
if (!empty($articleInfo)) {
|
||
$response = array(
|
||
'success' => true,
|
||
'message' => '获取文章信息成功',
|
||
'images' => $articleInfo['images'] ?? array(),
|
||
'text' => $articleInfo['text'] ?? ''
|
||
);
|
||
|
||
if (!empty($articleInfo['title'])) {
|
||
$response['title'] = $articleInfo['title'];
|
||
}
|
||
|
||
if (!empty($articleInfo['link'])) {
|
||
$response['link'] = $articleInfo['link'];
|
||
}
|
||
|
||
if (!empty($articleInfo['tags'])) {
|
||
$response['tags'] = $articleInfo['tags'];
|
||
}
|
||
|
||
if (!empty($articleInfo['created'])) {
|
||
$response['created'] = $articleInfo['created'];
|
||
}
|
||
|
||
$this->response->throwJson($response);
|
||
} else {
|
||
$this->response->throwJson(array(
|
||
'success' => true,
|
||
'message' => '该文章中没有找到内容',
|
||
'images' => array(),
|
||
'text' => ''
|
||
));
|
||
}
|
||
} catch (Exception $e) {
|
||
$this->response->throwJson(array(
|
||
'success' => false,
|
||
'message' => '获取文章信息失败: ' . $e->getMessage()
|
||
));
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 清除足迹缓存
|
||
*
|
||
* @access private
|
||
* @return void
|
||
*/
|
||
private function clearFootprintCache()
|
||
{
|
||
try {
|
||
$cacheDir = __DIR__ . '/cache';
|
||
|
||
if (!is_dir($cacheDir)) {
|
||
return;
|
||
}
|
||
|
||
$files = glob($cacheDir . '/*.cache');
|
||
foreach ($files as $file) {
|
||
if (is_file($file)) {
|
||
unlink($file);
|
||
}
|
||
}
|
||
} catch (Exception $e) {
|
||
error_log('清除MyTrack缓存失败: ' . $e->getMessage());
|
||
}
|
||
}
|
||
} |