2.1 版本文件

This commit is contained in:
2026-08-14 10:55:06 +08:00
commit a3434ec620
67 changed files with 12559 additions and 0 deletions
+147
View File
@@ -0,0 +1,147 @@
<?php
// 前台提交工单(须登录)。支持普通工单与「续期申请」(关联周期订单)。
require __DIR__ . '/includes/init.php';
requireLogin('ticket.php');
$user = currentUser();
$pageTitle = __('ticket_page.title');
$depts = db()->query('SELECT * FROM ' . tn('ticket_departments') . ' ORDER BY sort, id')->fetchAll();
if (empty($depts)) {
$depts = [['id' => 0, 'name' => __('ticket.meta_unspecified'), 'description' => '']];
}
// 当前用户可续期的订单(周期商品 + 已发货/已完成)
$renewOrders = db()->prepare(
'SELECT o.id, o.order_no, o.period_days, o.expires_at FROM ' . tn('orders') . ' o
WHERE o.user_id = ? AND o.period_days > 0 AND o.status IN ("shipped","completed") ORDER BY o.created_at DESC'
);
$renewOrders->execute([$user['id']]);
$renewOrders = $renewOrders->fetchAll();
$renewOrderId = (int) ($_GET['order_id'] ?? 0);
$msg = '';
$err = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
verifyCsrf();
$dept = (int) ($_POST['dept_id'] ?? 0);
$subject = trim($_POST['subject'] ?? '');
$message = trim($_POST['message'] ?? '');
$priority = (int) ($_POST['priority'] ?? 2);
$type = in_array($_POST['type'] ?? 'general', ['general', 'renewal'], true) ? $_POST['type'] : 'general';
$orderId = (int) ($_POST['order_id'] ?? 0);
if ($type === 'renewal') {
// 校验关联订单确实属于本人且可续期
$okOrder = false;
foreach ($renewOrders as $ro) {
if ((int) $ro['id'] === $orderId) { $okOrder = true; break; }
}
if (!$okOrder) {
$err = __('ticket_page.err_order_invalid');
} elseif ($subject === '') {
$subject = __('ticket_page.subject_renewal_prefix') . $orderId;
}
}
if ($subject === '') $err = __('ticket_page.err_subject_empty');
elseif (strlen($subject) > 160) $err = __('ticket_page.err_subject_long');
elseif ($message === '') $err = __('ticket_page.err_message_empty');
elseif (!in_array($priority, [1, 2, 3], true)) $err = __('ticket_page.err_priority_bad');
if ($err === '') {
db()->prepare(
'INSERT INTO ' . tn('tickets') .
' (user_id, dept_id, order_id, type, subject, message, priority, status, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,NOW(),NOW())'
)->execute([$user['id'], $dept, $type === 'renewal' ? $orderId : null, $type, $subject, $message, $priority, 'open']);
$tid = db()->lastInsertId();
// 通过 SMTP 通知处理人员(失败不影响工单入库)
$deptNameMap = [];
foreach ($depts as $d) { $deptNameMap[$d['id']] = $d['name']; }
$deptName = $deptNameMap[$dept] ?? __('ticket.meta_unspecified');
$cfg = smtpConfig();
if (!empty($cfg['notify_emails'])) {
$base = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https' : 'http')
. '://' . ($_SERVER['HTTP_HOST'] ?? 'localhost') . dirname($_SERVER['SCRIPT_NAME']);
$url = rtrim($base, '/') . '/admin/tickets.php?view=' . $tid;
$typeLabel = ticketTypeLabel($type);
$orderLine = $type === 'renewal'
? '<p><strong>类型:</strong>续期申请(关联订单 #' . $orderId . '</p>'
: '<p><strong>类型:strong>普通工单</p>';
$body = '<div style="font-family:sans-serif;max-width:560px;margin:auto">'
. '<h2 style="color:#1a73e8">[' . h(SITE_NAME) . '] 收到新工单 #' . $tid . '</h2>'
. '<p><strong>提交人:</strong>' . h($user['username']) . '' . h($user['email']) . '</p>'
. '<p><strong>部门:</strong>' . h($deptName) . '</p>'
. '<p><strong>优先级:</strong>' . ticketPriorityLabel($priority) . '</p>'
. $orderLine
. '<p><strong>标题:</strong>' . h($subject) . '</p>'
. '<div style="background:#f5f7fb;border-left:4px solid #1a73e8;padding:12px 16px;margin:10px 0;white-space:pre-wrap">' . h($message) . '</div>'
. '<p><a href="' . $url . '" style="background:#1a73e8;color:#fff;padding:10px 18px;border-radius:6px;text-decoration:none;display:inline-block">前往后台处理</a></p>'
. '</div>';
fnwSendMail($cfg['notify_emails'], '[' . SITE_NAME . '] 新工单 #' . $tid . ' ' . $subject, $body);
}
redirect('mytickets.php?ok=1');
}
}
require 'includes/header.php';
?>
<section class="section">
<h2 class="sec-title"><i class="fas fa-ticket-alt"></i> <?= __('ticket_page.title') ?></h2>
<p class="muted" style="margin-bottom:18px;"><?= __('ticket_page.desc_hint') ?></p>
<?php if ($err): ?><p class="form-err"><?= h($err) ?></p><?php endif; ?>
<div class="panel" style="max-width:680px;">
<form method="post" action="" class="grid-form">
<?= csrfField() ?>
<label><?= __('ticket.type_label', ['type' => ''])
?><select name="type" id="ticketType">
<option value="general" <?= $renewOrderId ? '' : 'selected' ?>><?= __('ticket_page.type_general') ?></option>
<?php if (!empty($renewOrders)): ?>
<option value="renewal" <?= $renewOrderId ? 'selected' : '' ?>><?= __('ticket_page.type_renewal') ?></option>
<?php endif; ?>
</select>
</label>
<label><?= __('ticket_page.select_dept') ?>
<select name="dept_id">
<?php foreach ($depts as $d): ?>
<option value="<?= (int) $d['id'] ?>"><?= h($d['name']) ?><?= $d['description'] ? '' . h($d['description']) . '' : '' ?></option>
<?php endforeach; ?>
</select>
</label>
<?php if (!empty($renewOrders)): ?>
<label class="span2"><?= __('ticket_page.related_order_label') ?>
<select name="order_id" id="ticketOrder">
<option value="0"><?= __('ticket_page.order_no_link') ?></option>
<?php foreach ($renewOrders as $ro): ?>
<option value="<?= (int) $ro['id'] ?>" <?= $renewOrderId === (int) $ro['id'] ? 'selected' : '' ?>>
#<?= (int) $ro['id'] ?> · <?= h($ro['order_no']) ?> · <?= __('ticket_page.renewal_period') ?> <?= periodLabel($ro['period_days']) ?> · <?= __('ticket_page.renewal_expire') ?> <?= expiryText($ro['expires_at']) ?>
</option>
<?php endforeach; ?>
</select>
</label>
<?php endif; ?>
<label class="span2"><?= __('ticket.subject') ?> *<input type="text" name="subject" id="ticketSubject" maxlength="160" value="<?= $renewOrderId ? __('ticket_page.subject_renewal_prefix') . $renewOrderId : '' ?>" placeholder="<?= __('ticket_page.subject_placeholder') ?>" required></label>
<label class="span2"><?= __('ticket.content') ?> *<textarea name="message" rows="6" placeholder="<?= __('ticket_page.message_placeholder') ?>" required></textarea></label>
<label><?= __('ticket.priority') ?>
<select name="priority">
<option value="1"><?= __('ticket.low') ?></option>
<option value="2" selected><?= __('ticket.normal') ?></option>
<option value="3"><?= __('ticket.high') ?></option>
</select>
</label>
<label><?= __('ticket_page.contact_email') ?>
<input type="text" value="<?= h($user['email']) ?>" readonly>
</label>
<div class="form-actions span2">
<button type="submit" class="btn btn-primary"><i class="fas fa-paper-plane"></i> <?= __('ticket_page.submit_btn') ?></button>
<a href="mytickets.php" class="btn btn-ghost"><?= __('ticket_page.view_my_tickets') ?></a>
</div>
</form>
</div>
</section>
<?php require 'includes/footer.php'; ?>