OneBot: Leantime 改造版源码(BOM/Univer 表格/AI 接管/品牌替换等)
This commit is contained in:
25
app/Domain/Tickets/Models/BoardSummary.php
Normal file
25
app/Domain/Tickets/Models/BoardSummary.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace Leantime\Domain\Tickets\Models;
|
||||
|
||||
use Carbon\CarbonImmutable;
|
||||
|
||||
/**
|
||||
* At-a-glance metrics for a task board, computed from the tickets currently on
|
||||
* screen (so the numbers always reflect the active filters). Rendered in the
|
||||
* page-header sub-line.
|
||||
*/
|
||||
class BoardSummary
|
||||
{
|
||||
/** Total tasks on the board. */
|
||||
public int $total = 0;
|
||||
|
||||
/** Tasks with no assignee (editorId). */
|
||||
public int $unassigned = 0;
|
||||
|
||||
/** Tasks whose due date falls between today and the end of the current week. */
|
||||
public int $dueThisWeek = 0;
|
||||
|
||||
/** Most recent change on the board (max ticket modified date), null if none. */
|
||||
public ?CarbonImmutable $lastUpdated = null;
|
||||
}
|
||||
112
app/Domain/Tickets/Models/TicketDesignTokens.php
Normal file
112
app/Domain/Tickets/Models/TicketDesignTokens.php
Normal file
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
namespace Leantime\Domain\Tickets\Models;
|
||||
|
||||
/**
|
||||
* Design tokens for ticket visualization
|
||||
* Centralizes priority, effort, type, and status mappings
|
||||
*/
|
||||
class TicketDesignTokens
|
||||
{
|
||||
/**
|
||||
* Priority levels with labels and color mappings
|
||||
*/
|
||||
public const PRIORITIES = [
|
||||
1 => [
|
||||
'label' => 'Critical',
|
||||
'cssVar' => '--priority-critical',
|
||||
'color' => '#C73E5C', // Design spec
|
||||
'icon' => 'thermometer-full',
|
||||
'fill' => 1.0, // Thermometer fill level (0.0-1.0)
|
||||
],
|
||||
2 => [
|
||||
'label' => 'High',
|
||||
'cssVar' => '--priority-high',
|
||||
'color' => '#E85A5A', // Design spec
|
||||
'icon' => 'thermometer-three-quarters',
|
||||
'fill' => 0.8,
|
||||
],
|
||||
3 => [
|
||||
'label' => 'Medium',
|
||||
'cssVar' => '--priority-medium',
|
||||
'color' => '#F5A623', // Design spec
|
||||
'icon' => 'thermometer-half',
|
||||
'fill' => 0.6,
|
||||
],
|
||||
4 => [
|
||||
'label' => 'Low',
|
||||
'cssVar' => '--priority-low',
|
||||
'color' => '#2ECC71', // Design spec
|
||||
'icon' => 'thermometer-quarter',
|
||||
'fill' => 0.4,
|
||||
],
|
||||
5 => [
|
||||
'label' => 'Lowest',
|
||||
'cssVar' => '--priority-lowest',
|
||||
'color' => '#6B7280', // Design spec
|
||||
'icon' => 'thermometer-empty',
|
||||
'fill' => 0.2,
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Effort/Story points with labels and size mappings
|
||||
*/
|
||||
public const EFFORTS = [
|
||||
0.5 => ['label' => '< 2min', 'size' => 'xxs', 'tshirtLabel' => 'XXS'],
|
||||
1 => ['label' => 'XS', 'size' => 'xs', 'tshirtLabel' => 'XS'],
|
||||
2 => ['label' => 'S', 'size' => 'sm', 'tshirtLabel' => 'S'],
|
||||
3 => ['label' => 'M', 'size' => 'md', 'tshirtLabel' => 'M'],
|
||||
5 => ['label' => 'L', 'size' => 'lg', 'tshirtLabel' => 'L'],
|
||||
8 => ['label' => 'XL', 'size' => 'xl', 'tshirtLabel' => 'XL'],
|
||||
13 => ['label' => 'XXL', 'size' => 'xxl', 'tshirtLabel' => 'XXL'],
|
||||
];
|
||||
|
||||
/**
|
||||
* Ticket types with emoji icons
|
||||
*/
|
||||
public const TYPES = [
|
||||
'story' => ['label' => 'Story', 'icon' => '👤', 'faIcon' => 'fa-book'],
|
||||
'task' => ['label' => 'Task', 'icon' => '📋', 'faIcon' => 'fa-check-square'],
|
||||
'subtask' => ['label' => 'Subtask', 'icon' => '📋', 'faIcon' => 'fa-diagram-successor'],
|
||||
'bug' => ['label' => 'Bug', 'icon' => '🐛', 'faIcon' => 'fa-bug'],
|
||||
'feature' => ['label' => 'Feature', 'icon' => '✨', 'faIcon' => 'fa-star'],
|
||||
'epic' => ['label' => 'Epic', 'icon' => '🏔️', 'faIcon' => 'fa-mountain'],
|
||||
'documentation' => ['label' => 'Documentation', 'icon' => '📄', 'faIcon' => 'fa-file'],
|
||||
'improvement' => ['label' => 'Improvement', 'icon' => '🔧', 'faIcon' => 'fa-wrench'],
|
||||
'research' => ['label' => 'Research', 'icon' => '🔬', 'faIcon' => 'fa-flask'],
|
||||
];
|
||||
|
||||
/**
|
||||
* Get priority token by ID
|
||||
*
|
||||
* @param int $id Priority ID (1-5)
|
||||
* @return array|null Priority configuration array or null if not found
|
||||
*/
|
||||
public static function getPriority(int $id): ?array
|
||||
{
|
||||
return self::PRIORITIES[$id] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get effort token by points
|
||||
*
|
||||
* @param float $points Story points value
|
||||
* @return array|null Effort configuration array or null if not found
|
||||
*/
|
||||
public static function getEffort(float $points): ?array
|
||||
{
|
||||
return self::EFFORTS[$points] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get type token by name
|
||||
*
|
||||
* @param string $type Ticket type name
|
||||
* @return array|null Type configuration array or null if not found
|
||||
*/
|
||||
public static function getType(string $type): ?array
|
||||
{
|
||||
return self::TYPES[$type] ?? null;
|
||||
}
|
||||
}
|
||||
171
app/Domain/Tickets/Models/Tickets.php
Normal file
171
app/Domain/Tickets/Models/Tickets.php
Normal file
@@ -0,0 +1,171 @@
|
||||
<?php
|
||||
|
||||
namespace Leantime\Domain\Tickets\Models;
|
||||
|
||||
class Tickets
|
||||
{
|
||||
public mixed $id = null;
|
||||
|
||||
public ?string $headline = '';
|
||||
|
||||
public mixed $type = null;
|
||||
|
||||
public ?string $description = '';
|
||||
|
||||
public mixed $projectId = null;
|
||||
|
||||
public mixed $projectDescription = null;
|
||||
|
||||
public mixed $editorId = null;
|
||||
|
||||
public mixed $userId = null;
|
||||
|
||||
public mixed $priority = null;
|
||||
|
||||
public mixed $sortIndex = null;
|
||||
|
||||
public mixed $date = null;
|
||||
|
||||
public mixed $timelineDate = null;
|
||||
|
||||
public mixed $timelineDateToFinish = null;
|
||||
|
||||
public mixed $dateToFinish = null;
|
||||
|
||||
public mixed $timeToFinish = null;
|
||||
|
||||
public mixed $status = 3;
|
||||
|
||||
public mixed $storypoints = null;
|
||||
|
||||
public mixed $hourRemaining = null;
|
||||
|
||||
public mixed $planHours = null;
|
||||
|
||||
public mixed $sprint = null;
|
||||
|
||||
public ?string $acceptanceCriteria = '';
|
||||
|
||||
public ?string $outcomeImpact = '';
|
||||
|
||||
public mixed $tags = null;
|
||||
|
||||
public mixed $url = null;
|
||||
|
||||
public mixed $editFrom = null;
|
||||
|
||||
public mixed $timeFrom = null;
|
||||
|
||||
public mixed $editTo = null;
|
||||
|
||||
public mixed $timeTo = null;
|
||||
|
||||
public mixed $dependingTicketId = null;
|
||||
|
||||
public ?string $parentHeadline = '';
|
||||
|
||||
public mixed $milestoneid = null;
|
||||
|
||||
public ?string $projectName = '';
|
||||
|
||||
public ?string $clientName = '';
|
||||
|
||||
public ?string $userFirstname = '';
|
||||
|
||||
public ?string $userLastname = '';
|
||||
|
||||
public ?string $editorFirstname = '';
|
||||
|
||||
public ?string $editorLastname = '';
|
||||
|
||||
public mixed $doneTickets = null;
|
||||
|
||||
public mixed $allTickets = null;
|
||||
|
||||
public mixed $percentDone = null;
|
||||
|
||||
public mixed $milestoneHeadline = null;
|
||||
|
||||
public mixed $milestoneColor = null;
|
||||
|
||||
public mixed $editorProfileId = null;
|
||||
|
||||
public mixed $bookedHours = null;
|
||||
|
||||
public ?array $children = null;
|
||||
|
||||
public ?array $collaborators = null;
|
||||
|
||||
public mixed $modified = null;
|
||||
|
||||
/**
|
||||
* @param false $values
|
||||
*/
|
||||
public function __construct(array|bool $values = false)
|
||||
{
|
||||
|
||||
if ($values !== false) {
|
||||
$this->id = $values['id'] ?? '';
|
||||
$this->headline = $values['headline'] ?? '';
|
||||
$this->type = $values['type'] ?? '';
|
||||
$this->description = $values['description'] ?? '';
|
||||
$this->projectId = $values['projectId'] ?? '';
|
||||
$this->editorId = $values['editorId'] ?? '';
|
||||
$this->userId = $values['userId'] ?? '';
|
||||
$this->priority = $values['priority'] ?? '';
|
||||
|
||||
$this->date = $values['date'] ?? date('Y-m-d H:i:s');
|
||||
$this->dateToFinish = $values['dateToFinish'] ?? '';
|
||||
$this->status = $values['status'] ?? '3';
|
||||
$this->storypoints = $values['storypoints'] ?? '';
|
||||
$this->hourRemaining = $values['hourRemaining'] ?? '';
|
||||
$this->planHours = $values['planHours'] ?? '';
|
||||
$this->sprint = $values['sprint'] ?? '';
|
||||
$this->acceptanceCriteria = $values['acceptanceCriteria'] ?? '';
|
||||
$this->outcomeImpact = $values['outcomeImpact'] ?? '';
|
||||
$this->tags = $values['tags'] ?? '';
|
||||
$this->editFrom = $values['editFrom'] ?? '';
|
||||
$this->editTo = $values['editTo'] ?? '';
|
||||
$this->dependingTicketId = $values['dependingTicketId'] ?? '';
|
||||
$this->milestoneid = $values['milestoneid'] ?? '';
|
||||
$this->projectName = $values['projectName'] ?? '';
|
||||
$this->clientName = $values['clientName'] ?? '';
|
||||
$this->userFirstname = $values['userFirstname'] ?? '';
|
||||
$this->userLastname = $values['userLastname'] ?? '';
|
||||
$this->editorFirstname = $values['editorFirstname'] ?? '';
|
||||
$this->editorLastname = $values['editorLastname'] ?? '';
|
||||
$this->modified = $values['modified'] ?? '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the ticket to a string representation that's useful for AI consumption
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
$output = "Ticket #{$this->id}: {$this->headline}\n";
|
||||
$output .= "Type: {$this->type}\n";
|
||||
$output .= "Status: {$this->status}\n";
|
||||
$output .= "Priority: {$this->priority}\n";
|
||||
|
||||
if (! empty($this->description)) {
|
||||
$output .= 'Description: '.strip_tags($this->description)."\n";
|
||||
}
|
||||
|
||||
if (! empty($this->dateToFinish)) {
|
||||
$output .= "Due Date: {$this->dateToFinish}\n";
|
||||
}
|
||||
|
||||
if (! empty($this->editorFirstname) || ! empty($this->editorLastname)) {
|
||||
$output .= "Assigned To: {$this->editorFirstname} {$this->editorLastname}\n";
|
||||
}
|
||||
|
||||
if (! empty($this->storypoints)) {
|
||||
$output .= "Story Points: {$this->storypoints}\n";
|
||||
}
|
||||
|
||||
$output .= "Project ID: {$this->projectId}\n";
|
||||
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user