386 lines
12 KiB
PHP
386 lines
12 KiB
PHP
<?php
|
||
class DevelopmentHistory_Action
|
||
{
|
||
/**
|
||
* 数据库对象
|
||
*/
|
||
private $db;
|
||
private $prefix;
|
||
|
||
/**
|
||
* 请求对象
|
||
*/
|
||
private $request;
|
||
|
||
/**
|
||
* 响应对象
|
||
*/
|
||
private $response;
|
||
|
||
/**
|
||
* 构造函数
|
||
*/
|
||
public function __construct($request = null, $response = null)
|
||
{
|
||
$this->db = Typecho_Db::get();
|
||
$this->prefix = $this->db->getPrefix();
|
||
$this->request = $request ?: Typecho_Request::getInstance();
|
||
$this->response = $response ?: Typecho_Response::getInstance();
|
||
}
|
||
|
||
/**
|
||
* 添加历史记录 - 修复:使用Typecho正确的SQL构建方法
|
||
*/
|
||
public function addHistory($data)
|
||
{
|
||
try {
|
||
$now = new Typecho_Date(Typecho_Date::gmtTime());
|
||
|
||
$insert = array(
|
||
'content' => $data['content'],
|
||
'event_date' => $data['event_date'],
|
||
'post_cids' => isset($data['post_cids']) ? $this->cleanCids($data['post_cids']) : '',
|
||
'created' => $now->format('Y-m-d H:i:s'),
|
||
'modified' => $now->format('Y-m-d H:i:s'),
|
||
'status' => 1
|
||
);
|
||
|
||
// 使用Typecho的查询构建器,但先验证数据
|
||
$this->validateInsertData($insert);
|
||
|
||
// 修复:正确构建并执行插入查询
|
||
// 在Typecho中,insert()->rows()需要调用query()方法来执行
|
||
$query = $this->db->insert($this->prefix . 'development_history')->rows($insert);
|
||
|
||
// 执行查询
|
||
$this->db->query($query);
|
||
|
||
// 获取最后插入的ID - Typecho的方式
|
||
$row = $this->db->fetchRow($this->db->select('LAST_INSERT_ID() as id'));
|
||
return $row ? $row['id'] : 0;
|
||
|
||
} catch (Exception $e) {
|
||
error_log("addHistory Error: " . $e->getMessage());
|
||
error_log("Data: " . print_r($data, true));
|
||
throw new Exception("发布失败: " . $e->getMessage());
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 验证插入数据
|
||
*/
|
||
private function validateInsertData(&$data)
|
||
{
|
||
// 确保所有必需字段都有值
|
||
if (empty($data['content'])) {
|
||
throw new Exception("内容不能为空");
|
||
}
|
||
|
||
if (empty($data['event_date'])) {
|
||
throw new Exception("日期不能为空");
|
||
}
|
||
|
||
// 验证日期格式
|
||
if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $data['event_date'])) {
|
||
throw new Exception("日期格式错误,应为YYYY-MM-DD");
|
||
}
|
||
|
||
// 清理和转义内容
|
||
$data['content'] = trim($data['content']);
|
||
if (empty($data['content'])) {
|
||
throw new Exception("内容不能为空");
|
||
}
|
||
|
||
// 确保所有字段都是字符串
|
||
foreach ($data as $key => $value) {
|
||
if (!is_string($value) && !is_numeric($value)) {
|
||
$data[$key] = (string)$value;
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 批量添加历史记录(按事件时间排序插入,越近的ID越大)
|
||
*/
|
||
public function batchAddHistories($histories)
|
||
{
|
||
// 按事件时间排序,越近的时间越晚插入,这样ID越大
|
||
usort($histories, function($a, $b) {
|
||
return strtotime($a['event_date']) - strtotime($b['event_date']);
|
||
});
|
||
|
||
$now = new Typecho_Date(Typecho_Date::gmtTime());
|
||
$nowStr = $now->format('Y-m-d H:i:s');
|
||
|
||
foreach ($histories as $history) {
|
||
$insert = array(
|
||
'content' => $history['content'],
|
||
'event_date' => $history['event_date'],
|
||
'post_cids' => '',
|
||
'created' => $nowStr,
|
||
'modified' => $nowStr,
|
||
'status' => 1
|
||
);
|
||
|
||
$query = $this->db->insert($this->prefix . 'development_history')->rows($insert);
|
||
$this->db->query($query);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 更新历史记录
|
||
*/
|
||
public function updateHistory($data)
|
||
{
|
||
try {
|
||
$now = new Typecho_Date(Typecho_Date::gmtTime());
|
||
|
||
$update = array(
|
||
'content' => $data['edit_content'],
|
||
'event_date' => $data['edit_event_date'],
|
||
'post_cids' => isset($data['edit_post_cids']) ? $this->cleanCids($data['edit_post_cids']) : '',
|
||
'modified' => $now->format('Y-m-d H:i:s')
|
||
);
|
||
|
||
$query = $this->db->update($this->prefix . 'development_history')
|
||
->rows($update)
|
||
->where('id = ?', intval($data['edit_id']));
|
||
|
||
$this->db->query($query);
|
||
|
||
} catch (Exception $e) {
|
||
error_log("updateHistory Error: " . $e->getMessage());
|
||
throw new Exception("更新失败: " . $e->getMessage());
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 删除历史记录
|
||
*/
|
||
public function deleteHistory($id)
|
||
{
|
||
$query = $this->db->delete($this->prefix . 'development_history')->where('id = ?', intval($id));
|
||
$this->db->query($query);
|
||
}
|
||
|
||
/**
|
||
* 批量删除
|
||
*/
|
||
public function deleteHistories($ids)
|
||
{
|
||
foreach ($ids as $id) {
|
||
$this->deleteHistory(intval($id));
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取历史记录
|
||
*/
|
||
public function getHistories($page = 1, $perPage = 10)
|
||
{
|
||
$offset = ($page - 1) * $perPage;
|
||
|
||
$query = $this->db->select()
|
||
->from($this->prefix . 'development_history')
|
||
->where('status = ?', 1)
|
||
->order('id', Typecho_Db::SORT_DESC)
|
||
->limit($perPage)
|
||
->offset($offset);
|
||
|
||
return $this->db->fetchAll($query);
|
||
}
|
||
|
||
/**
|
||
* 获取所有历史记录(用于导出)
|
||
*/
|
||
public function getAllHistories()
|
||
{
|
||
$query = $this->db->select()
|
||
->from($this->prefix . 'development_history')
|
||
->where('status = ?', 1)
|
||
->order('id', Typecho_Db::SORT_DESC);
|
||
|
||
return $this->db->fetchAll($query);
|
||
}
|
||
|
||
/**
|
||
* 获取总记录数
|
||
*/
|
||
public function getTotalCount()
|
||
{
|
||
$query = $this->db->select('COUNT(*) as count')
|
||
->from($this->prefix . 'development_history')
|
||
->where('status = ?', 1);
|
||
|
||
$result = $this->db->fetchRow($query);
|
||
return $result['count'];
|
||
}
|
||
|
||
/**
|
||
* 获取单条记录
|
||
*/
|
||
public function getHistory($id)
|
||
{
|
||
$query = $this->db->select()
|
||
->from($this->prefix . 'development_history')
|
||
->where('id = ?', $id)
|
||
->limit(1);
|
||
|
||
return $this->db->fetchRow($query);
|
||
}
|
||
|
||
/**
|
||
* 根据文章CID获取文章信息
|
||
*/
|
||
public function getPostByCid($cid)
|
||
{
|
||
try {
|
||
$query = $this->db->select('cid, title, slug, created')
|
||
->from($this->prefix . 'contents')
|
||
->where('cid = ?', intval($cid))
|
||
->where('type = ?', 'post')
|
||
->where('status = ?', 'publish')
|
||
->limit(1);
|
||
|
||
$post = $this->db->fetchRow($query);
|
||
|
||
if ($post) {
|
||
$options = Typecho_Widget::widget('Widget_Options');
|
||
if (!empty($post['slug'])) {
|
||
$post['url'] = Typecho_Common::url($post['slug'] . '.html', $options->index);
|
||
} else {
|
||
$post['url'] = Typecho_Common::url('archives/' . $post['cid'], $options->index);
|
||
}
|
||
}
|
||
|
||
return $post;
|
||
} catch (Exception $e) {
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 清理CID字符串
|
||
*/
|
||
private function cleanCids($cids)
|
||
{
|
||
if (empty($cids)) {
|
||
return '';
|
||
}
|
||
|
||
$cidsArray = array_filter(array_map('trim', explode(',', $cids)));
|
||
$validCids = array();
|
||
|
||
foreach ($cidsArray as $cid) {
|
||
if (is_numeric($cid) && $cid > 0) {
|
||
$validCids[] = intval($cid);
|
||
}
|
||
}
|
||
|
||
// 去重并返回逗号分隔的字符串
|
||
return implode(',', array_unique($validCids));
|
||
}
|
||
|
||
/**
|
||
* 导出数据为文本格式
|
||
*/
|
||
public function exportData()
|
||
{
|
||
$histories = $this->getAllHistories();
|
||
$content = '';
|
||
|
||
foreach ($histories as $history) {
|
||
// 使用 Y.m.d 格式导出
|
||
$date = date('Y.m.d', strtotime($history['event_date']));
|
||
$text = str_replace(array("\r\n", "\r", "\n"), " ", $history['content']);
|
||
$content .= $date . ' ' . $text . "\n";
|
||
}
|
||
|
||
return $content;
|
||
}
|
||
|
||
/**
|
||
* 导入数据(增强版,支持调试)
|
||
*/
|
||
public function importData($text)
|
||
{
|
||
// 标准化换行符
|
||
$text = str_replace("\r\n", "\n", $text);
|
||
$text = str_replace("\r", "\n", $text);
|
||
|
||
$lines = explode("\n", $text);
|
||
$imported = 0;
|
||
$failed = 0;
|
||
$failReasons = array(); // 记录失败原因
|
||
$histories = array();
|
||
|
||
foreach ($lines as $lineNum => $line) {
|
||
$line = trim($line);
|
||
if (empty($line)) continue;
|
||
|
||
// 移除行尾的分号(如果有)
|
||
if (substr($line, -1) === ';') {
|
||
$line = substr($line, 0, -1);
|
||
$line = trim($line);
|
||
}
|
||
|
||
// 找到第一个空格的位置来分割日期和内容
|
||
$firstSpacePos = strpos($line, ' ');
|
||
if ($firstSpacePos === false) {
|
||
$failed++;
|
||
$failReasons[] = "第 " . ($lineNum + 1) . " 行:找不到空格分隔日期和内容 - '{$line}'";
|
||
continue;
|
||
}
|
||
|
||
$date = trim(substr($line, 0, $firstSpacePos));
|
||
$content = trim(substr($line, $firstSpacePos + 1));
|
||
|
||
// 验证日期格式 - 支持单个数字的月份和日期
|
||
// 允许的格式:YYYY.M.D, YYYY.MM.D, YYYY.M.DD, YYYY.MM.DD
|
||
$datePattern = '/^(\d{4})\.(\d{1,2})\.(\d{1,2})$/';
|
||
if (!preg_match($datePattern, $date, $matches)) {
|
||
$failed++;
|
||
$failReasons[] = "第 " . ($lineNum + 1) . " 行:日期格式错误,应为 YYYY.M[M].D[D] - '{$date}'";
|
||
continue;
|
||
}
|
||
|
||
// 检查日期是否有效
|
||
$year = intval($matches[1]);
|
||
$month = intval($matches[2]);
|
||
$day = intval($matches[3]);
|
||
|
||
if (!checkdate($month, $day, $year)) {
|
||
$failed++;
|
||
$failReasons[] = "第 " . ($lineNum + 1) . " 行:无效的日期 - {$date}";
|
||
continue;
|
||
}
|
||
|
||
// 转换日期格式为 Y-m-d 以便数据库存储,补零成两位数字
|
||
$dbDate = sprintf('%04d-%02d-%02d', $year, $month, $day);
|
||
|
||
// 验证内容
|
||
if (empty($content)) {
|
||
$failed++;
|
||
$failReasons[] = "第 " . ($lineNum + 1) . " 行:内容为空";
|
||
continue;
|
||
}
|
||
|
||
$histories[] = array(
|
||
'event_date' => $dbDate,
|
||
'content' => $content
|
||
);
|
||
$imported++;
|
||
}
|
||
|
||
if (!empty($histories)) {
|
||
$this->batchAddHistories($histories);
|
||
}
|
||
|
||
// 返回包含失败原因的结果
|
||
return array(
|
||
'imported' => $imported,
|
||
'failed' => $failed,
|
||
'fail_reasons' => $failReasons
|
||
);
|
||
}
|
||
} |