OneBot: Leantime 改造版源码(BOM/Univer 表格/AI 接管/品牌替换等)

This commit is contained in:
wangruiguo
2026-09-03 18:49:20 +08:00
commit d647428529
3501 changed files with 1988906 additions and 0 deletions

View File

@@ -0,0 +1,267 @@
<?php
namespace Leantime\Domain\Tickets\Controllers;
use Carbon\Carbon;
use Illuminate\Contracts\Container\BindingResolutionException;
use Leantime\Core\Controller\Controller;
use Leantime\Core\Controller\Frontcontroller;
use Leantime\Core\Support\FromFormat;
use Leantime\Domain\Comments\Services\Comments as CommentService;
use Leantime\Domain\Files\Services\Files as FileService;
use Leantime\Domain\Projects\Services\Projects as ProjectService;
use Leantime\Domain\Sprints\Services\Sprints as SprintService;
use Leantime\Domain\Tickets\Services\Tickets as TicketService;
use Leantime\Domain\Tickets\Services\TicketResource as TicketResourceService;
use Leantime\Domain\Timesheets\Services\Timesheets as TimesheetService;
use Leantime\Domain\Users\Services\Users as UserService;
use Symfony\Component\HttpFoundation\Response;
class ShowTicket extends Controller
{
private ProjectService $projectService;
private TicketService $ticketService;
private SprintService $sprintService;
private FileService $fileService;
private CommentService $commentService;
private TimesheetService $timesheetService;
private UserService $userService;
private TicketResourceService $ticketResourceService;
public function init(
ProjectService $projectService,
TicketService $ticketService,
SprintService $sprintService,
FileService $fileService,
CommentService $commentService,
TimesheetService $timesheetService,
UserService $userService,
TicketResourceService $ticketResourceService
): void {
$this->projectService = $projectService;
$this->ticketService = $ticketService;
$this->sprintService = $sprintService;
$this->fileService = $fileService;
$this->commentService = $commentService;
$this->timesheetService = $timesheetService;
$this->userService = $userService;
$this->ticketResourceService = $ticketResourceService;
if (session()->exists('lastPage') === false) {
session(['lastPage' => BASE_URL.'/tickets/showKanban']);
}
}
/**
* @throws BindingResolutionException
*/
public function get($params): Response
{
if (! isset($params['id'])) {
return $this->tpl->displayPartial('errors.error400', responseCode: 400);
}
$id = (int) ($params['id']);
$ticket = $this->ticketService->getTicket($id);
if ($ticket === false) {
return $this->tpl->display('errors.error500', responseCode: 500);
}
// Ensure this ticket belongs to the current project
if (session('currentProject') != $ticket->projectId) {
$this->projectService->changeCurrentSessionProject($ticket->projectId);
return Frontcontroller::redirect(BASE_URL.'/tickets/showTicket/'.$id);
}
// Delete file
if (isset($params['delFile']) === true) {
if ($result = $this->fileService->deleteFile($params['delFile'])) {
$this->tpl->setNotification($this->language->__('notifications.file_deleted'), 'success');
return Frontcontroller::redirect(BASE_URL.'/tickets/showTicket/'.$id.'#files');
}
$this->tpl->setNotification($this->language->__('notifications.file_deleted_error'), 'error');
}
// Delete comment
if (isset($params['delComment']) === true) {
$commentId = (int) ($params['delComment']);
if ($this->commentService->deleteComment($commentId)) {
$this->tpl->setNotification($this->language->__('notifications.comment_deleted'), 'success');
$response = Frontcontroller::redirect(BASE_URL.'/tickets/showTicket/'.$id);
$response->headers->set('HX-Trigger', 'ticketUpdate');
return $response;
}
$this->tpl->setNotification($this->language->__('notifications.comment_deleted_error'), 'error');
}
// Delete Subtask
if (isset($params['delSubtask']) === true) {
}
$this->tpl->assign('ticket', $ticket);
$this->tpl->assign('ticketParents', $this->ticketService->getAllPossibleParents($ticket));
$this->tpl->assign('statusLabels', $this->ticketService->getStatusLabels());
$this->tpl->assign('ticketTypes', $this->ticketService->getTicketTypes());
$this->tpl->assign('ticketTypeIcons', $this->ticketService->getTypeIcons());
$this->tpl->assign('efforts', $this->ticketService->getEffortLabels());
$this->tpl->assign('priorities', $this->ticketService->getPriorityLabels());
$allProjectMilestones = $this->ticketService->getAllMilestones([
'sprint' => '',
'type' => 'milestone',
'currentProject' => session('currentProject'),
]);
$this->tpl->assign('milestones', $allProjectMilestones);
$this->tpl->assign('sprints', $this->sprintService->getAllSprints(session('currentProject')));
$this->tpl->assign('kind', $this->timesheetService->getLoggableHourTypes());
$this->tpl->assign('ticketHours', $this->timesheetService->getLoggedHoursForTicketByDate($id));
$this->tpl->assign('userHours', $this->timesheetService->getUsersTicketHours($id, session('userdata.id')));
$this->tpl->assign('timesheetsAllHours', $this->timesheetService->getSumLoggedHoursForTicket($id));
$this->tpl->assign('remainingHours', $this->timesheetService->getRemainingHours($ticket));
$this->tpl->assign('userInfo', $this->userService->getUser(session('userdata.id')));
$this->tpl->assign('users', $this->projectService->getUsersWithAccessToProject((int) $ticket->projectId));
$projectData = $this->projectService->getProject($ticket->projectId);
$this->tpl->assign('projectData', $projectData);
$comments = $this->commentService->getComments('ticket', $id);
$this->tpl->assign('numComments', count($comments));
$this->tpl->assign('comments', $comments);
$files = $this->fileService->getFilesByModule('ticket', $id);
$this->tpl->assign('numFiles', count($files));
$this->tpl->assign('files', $files);
$this->tpl->assign('onTheClock', $this->timesheetService->isClocked(session('userdata.id')));
$this->tpl->assign('timesheetValues', [
'kind' => '',
'date' => Carbon::now(session('usersettings.timezone'))->setTimezone('UTC'),
'hours' => '',
'description' => '',
]);
// TODO: Refactor thumbnail generation in file manager
$this->tpl->assign('imgExtensions', ['jpg', 'jpeg', 'png', 'gif', 'psd', 'bmp', 'tif', 'thm', 'yuv']);
$allAssignedprojects = $this->projectService->getProjectsUserHasAccessTo(session('userdata.id'));
$this->tpl->assign('allAssignedprojects', $allAssignedprojects);
// 关联资源BOM/工艺文件/工具清单/文件/wiki
$this->tpl->assign('linkedResources', $this->ticketResourceService->getLinkedResources($id));
$this->tpl->assign('resourceCandidates', $this->ticketResourceService->listCandidates((int) $ticket->projectId));
$response = $this->tpl->displayPartial('tickets.showTicketModal');
$response->headers->set('HX-Trigger', 'ticketUpdate');
return $response;
}
/**
* @throws BindingResolutionException
*/
public function post($params): Response
{
if (! isset($_GET['id'])) {
return $this->tpl->display('errors.error400', responseCode: 400);
}
$tab = '';
$id = (int) ($_GET['id']);
$ticket = $this->ticketService->getTicket($id);
if ($ticket === false) {
return $this->tpl->display('errors.error500', responseCode: 500);
}
// Upload File
if (isset($params['upload'])) {
if ($this->fileService->upload($_FILES, 'ticket', $id, $ticket)) {
$this->tpl->setNotification($this->language->__('notifications.file_upload_success'), 'success');
} else {
$this->tpl->setNotification($this->language->__('notifications.file_upload_error'), 'error');
}
$tab = '#files';
}
// Add or edit a comment
if (isset($params['comment']) === true && isset($params['text']) && $params['text'] != '' && isset($params['edit-comment-helper']) && $params['edit-comment-helper'] !== '') {
if ($this->commentService->editComment($_POST, (int) $params['edit-comment-helper'])) {
$this->tpl->setNotification($this->language->__('notifications.comment_edited_success'), 'success');
} else {
$this->tpl->setNotification($this->language->__('notifications.comment_edit_error'), 'error');
}
$tab = '#comment';
} elseif (isset($params['comment']) === true && isset($params['text']) && $params['text'] != '') {
if ($this->commentService->addComment($_POST, 'ticket', $id, $ticket)) {
$this->tpl->setNotification($this->language->__('notifications.comment_create_success'), 'success');
} else {
$this->tpl->setNotification($this->language->__('notifications.comment_create_error'), 'error');
}
$tab = '#comment';
}
// Log time
if (isset($params['saveTimes']) === true) {
try {
$result = $this->timesheetService->logTime($id, $params);
$this->tpl->setNotification($this->language->__('notifications.time_logged_success'), 'success');
} catch (\Exception $e) {
$this->tpl->setNotification($e->getMessage(), 'error');
}
}
// Save Ticket
if (isset($params['saveTicket']) === true || isset($params['saveAndCloseTicket']) === true) {
// $params['projectId'] = $ticket->projectId;
$params['id'] = $id;
// Prepare values, time comes in as 24hours from time input. Service expects time to be in local user format
$params['timeToFinish'] = format(value: $params['timeToFinish'] ?? '', fromFormat: FromFormat::User24hTime)->userTime24toUserTime();
$params['timeFrom'] = format(value: $params['timeFrom'] ?? '', fromFormat: FromFormat::User24hTime)->userTime24toUserTime();
$params['timeTo'] = format(value: $params['timeTo'] ?? '', fromFormat: FromFormat::User24hTime)->userTime24toUserTime();
$result = $this->ticketService->updateTicket($params);
if ($result === true) {
$this->tpl->setNotification($this->language->__('notifications.ticket_saved'), 'success');
} else {
$this->tpl->setNotification($this->language->__($result['msg']), 'error');
}
if (isset($params['saveAndCloseTicket']) === true && $params['saveAndCloseTicket'] == 1) {
$response = Frontcontroller::redirect(BASE_URL.'/tickets/showTicket/'.$id.'?closeModal=1');
$response->headers->set('HX-Trigger', 'ticketUpdate');
return $response;
}
}
$response = Frontcontroller::redirect(BASE_URL.'/tickets/showTicket/'.$id.''.$tab);
$response->headers->set('HX-Trigger', 'ticketUpdate');
return $response;
}
}