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,43 @@
leantime.reactionsController = (function () {
//Functions
let addReactions = function (module, moduleId, reaction, clb) {
leantime.rpc('Reactions.Reactions.react', {
module: module,
moduleId: moduleId,
reaction: reaction
}).then(function (success) {
// The service returns false when the reaction was not applied
// (e.g. the user already reacted) — only update the UI on success.
if (success) {
clb();
}
}).catch(function (e) { console.error('Could not add reaction', e); });
};
let removeReaction = function (module, moduleId, reaction, clb) {
leantime.rpc('Reactions.Reactions.unreact', {
module: module,
moduleId: moduleId,
reaction: reaction
}).then(function (success) {
// Only update the UI when the reaction was actually removed.
if (success) {
clb();
}
}).catch(function (e) { console.error('Could not remove reaction', e); });
};
// Make public what you want to have public, everything else is private
return {
addReactions:addReactions,
removeReaction:removeReaction
};
})();

View File

@@ -0,0 +1,77 @@
<?php
namespace Leantime\Domain\Reactions\Models;
use Leantime\Core\Events\DispatchesEvents;
class Reactions
{
use DispatchesEvents;
/**
* @var array list of available reactions by reaction type
*/
private static array $reactionTypes = [
'sentimentReactions' => [
'like' => '👍',
'anger' => '😡',
'love' => '❤',
'support' => '💯',
'celebrate' => '🎉',
'interesting' => '💡',
'sad' => '😥',
'funny' => '😂',
],
'contentReactions' => [
'upvote' => "<i class='fa-solid fa-up'></i>",
'downvote' => "<i class='fa-solid fa-down'></i>",
],
'entityReactions' => [
'favorite' => "<i class='fa fa-star'></i>",
'watch' => "<i class='fa fa-eye'></i>",
],
];
public static string $favorite = 'favorite';
public static string $watch = 'watch';
public static string $downvote = 'downvote';
public static string $upvote = 'upvote';
public static string $funny = 'funny';
public static string $like = 'like';
public static string $anger = 'anger';
public static string $love = 'love';
public static string $support = 'support';
public static string $celebrate = 'celebrate';
public static string $interesting = 'interesting';
public static string $sad = 'sad';
public static function getReactions(): mixed
{
return self::dispatch_filter('available_reactions', self::$reactionTypes);
}
/**
* @return false|mixed
*/
/**
* @return false|mixed
*/
public static function getReactionsByType(string $type): mixed
{
$reactions = self::dispatch_filter('available_reactions', self::$reactionTypes);
return $reactions[$type] ?? false;
}
}

View File

@@ -0,0 +1,155 @@
<?php
namespace Leantime\Domain\Reactions\Repositories;
use Illuminate\Database\ConnectionInterface;
use Leantime\Core\Db\DatabaseHelper;
use Leantime\Core\Db\Db as DbCore;
/**
* Repository for managing user reactions on entities.
*/
class Reactions
{
private ConnectionInterface $db;
private DatabaseHelper $dbHelper;
public function __construct(DbCore $db, DatabaseHelper $dbHelper)
{
$this->db = $db->getConnection();
$this->dbHelper = $dbHelper;
}
/**
* Add a reaction to an entity.
*/
public function addReaction(int $userId, string $module, int $moduleId, string $reaction): bool
{
return $this->db->table('zp_reactions')->insert([
'module' => $module,
'moduleId' => $moduleId,
'userId' => $userId,
'reaction' => $reaction,
'date' => dtHelper()->userNow()->formatDateTimeForDb(),
]);
}
/**
* getGroupedEntityReactions - gets all reactions for a given entity grouped and counted by reactions
*
* @return array|false returns the array on success or false on failure
*/
public function getGroupedEntityReactions(string $module, int $moduleId): array|false
{
$results = $this->db->table('zp_reactions')
->selectRaw('COUNT(reaction) AS '.$this->dbHelper->wrapColumn('reactionCount').', reaction')
->where('module', $module)
->where('moduleId', $moduleId)
->groupBy('reaction')
->get();
return array_map(fn ($item) => (array) $item, $results->toArray());
}
/**
* getMyReactions - gets user reactions. Can be very broad or very targeted
*/
public function getUserReactions(int $userId, string $module = '', ?int $moduleId = null, string $reaction = ''): array|false
{
$query = $this->db->table('zp_reactions')
->select('id', 'reaction', 'date', 'module', 'moduleId', 'userId')
->where('userId', $userId);
if ($module !== '') {
$query->where('module', $module);
}
if ($moduleId !== null) {
$query->where('moduleId', $moduleId);
}
if ($reaction !== '') {
$query->where('reaction', $reaction);
}
$results = $query->get();
return array_map(fn ($item) => (array) $item, $results->toArray());
}
/**
* removeReactionById - removes a reaction by reaction id
*/
public function removeReactionById(int $id): bool
{
return $this->db->table('zp_reactions')
->where('id', $id)
->delete() > 0;
}
/**
* removeUserReaction - removes a users reaction to an entity
*/
public function removeUserReaction(int $userId, string $module, int $moduleId, string $reaction): bool
{
return $this->db->table('zp_reactions')
->where('module', $module)
->where('moduleId', $moduleId)
->where('userId', $userId)
->where('reaction', $reaction)
->delete() > 0;
}
/**
* getReactionsByModule - gets reactions count by module
*/
public function getReactionsByModule(string $module, ?int $moduleId = null): array|false
{
$query = $this->db->table('zp_reactions')
->selectRaw('reaction, COUNT(reaction) as '.$this->dbHelper->wrapColumn('reactionCount'))
->where('module', $module);
if ($moduleId !== null) {
$query->where('moduleId', $moduleId);
}
$results = $query->groupBy('reaction')
->get();
return array_map(fn ($item) => (array) $item, $results->toArray());
}
/**
* getEntityReactionsWithUsers - gets all reactions for an entity with user names
*
* @return array returns array grouped by reaction with user names
*/
public function getEntityReactionsWithUsers(string $module, int $moduleId): array
{
$results = $this->db->table('zp_reactions')
->select('zp_reactions.reaction', 'zp_reactions.userId', 'zp_user.firstname', 'zp_user.lastname')
->leftJoin('zp_user', 'zp_reactions.userId', '=', 'zp_user.id')
->where('zp_reactions.module', $module)
->where('zp_reactions.moduleId', $moduleId)
->get();
// Group by reaction and collect user names
$grouped = [];
foreach ($results as $row) {
$reaction = $row->reaction;
if (! isset($grouped[$reaction])) {
$grouped[$reaction] = [
'reaction' => $reaction,
'reactionCount' => 0,
'users' => [],
];
}
$grouped[$reaction]['reactionCount']++;
$grouped[$reaction]['users'][] = [
'userId' => $row->userId,
'name' => trim(($row->firstname ?? '').' '.($row->lastname ?? '')),
];
}
return array_values($grouped);
}
}

View File

@@ -0,0 +1,153 @@
<?php
namespace Leantime\Domain\Reactions\Services;
/**
* @api
*/
class Reactions
{
/**
* @var \Leantime\Domain\Reactions\Repositories\Reactions reactions repository
*
* @api
*/
private \Leantime\Domain\Reactions\Repositories\Reactions $reactionsRepo;
public function __construct(\Leantime\Domain\Reactions\Repositories\Reactions $reactionsRepo)
{
$this->reactionsRepo = $reactionsRepo;
}
/**
* Adds a reaction on behalf of the CURRENT (session) user.
*
* JSON-RPC entry point: derives the user from the session rather than
* accepting a userId, so a caller cannot react as another user.
*
* @param string $module The entity module (e.g. 'tickets')
* @param int $moduleId The entity id
* @param string $reaction The reaction key
* @return bool True if the reaction was added
*
* @api
*/
public function react(string $module, int $moduleId, string $reaction): bool
{
return $this->addReaction((int) session('userdata.id'), $module, $moduleId, $reaction);
}
/**
* Removes a reaction on behalf of the CURRENT (session) user.
*
* JSON-RPC entry point: derives the user from the session rather than
* accepting a userId, so a caller cannot remove another user's reaction.
*
* @param string $module The entity module (e.g. 'tickets')
* @param int $moduleId The entity id
* @param string $reaction The reaction key
* @return bool True if the reaction was removed
*
* @api
*/
public function unreact(string $module, int $moduleId, string $reaction): bool
{
return $this->removeReaction((int) session('userdata.id'), $module, $moduleId, $reaction);
}
/**
* addReaction - adds a reaction to an entity, checks if a user has already reacted the same way
*
* Not exposed via JSON-RPC: it accepts an arbitrary $userId. Use the
* session-scoped react() wrapper for JSON-RPC callers.
*/
public function addReaction(int $userId, string $module, int $moduleId, string $reaction): bool
{
if ($module == '' || $moduleId == '' || $userId == '' || $reaction == '') {
return false;
}
// Check if user already reacted in that category
$userReactions = $this->getUserReactions($userId, $module, $moduleId);
$currentReactionType = $this->getReactionType($reaction);
foreach ($userReactions as $previousReaction) {
if ($this->getReactionType($previousReaction['reaction']) == $currentReactionType) {
return false;
}
}
return $this->reactionsRepo->addReaction($userId, $module, $moduleId, $reaction);
}
/**
* getReactionType - returns the category/type of a given reaction
*
*
*
* @api
*/
public function getReactionType(string $reaction): string|false
{
$types = \Leantime\Domain\Reactions\Models\Reactions::getReactions();
foreach ($types as $reactionType => $reactionValues) {
if (isset($reactionValues[$reaction])) {
return $reactionType;
}
}
return false;
}
/**
* getGroupedEntityReactions - gets all reactions for a given entity grouped and counted by reactions
*
*
* @return array|false returns the array on success or false on failure
*
* @api
*/
public function getGroupedEntityReactions(string $module, int $moduleId): array|false
{
return $this->reactionsRepo->getGroupedEntityReactions($module, $moduleId);
}
/**
* getMyReactions - gets user reactions. Can be very broad or very targeted
*
*
*
* @api
*/
public function getUserReactions(int $userId, string $module = '', ?int $moduleId = null, string $reaction = ''): array|false
{
return $this->reactionsRepo->getUserReactions($userId, $module, $moduleId, $reaction);
}
/**
* removeReaction - removes a user's reaction from an entity
*
* Not exposed via JSON-RPC (accepts an arbitrary $userId). Use the
* session-scoped unreact() wrapper for JSON-RPC callers.
*/
public function removeReaction(int $userId, string $module, int $moduleId, string $reaction): bool
{
return $this->reactionsRepo->removeUserReaction($userId, $module, $moduleId, $reaction);
}
/**
* getEntityReactionsWithUsers - gets all reactions for an entity with user names
*
* @return array returns array grouped by reaction with user info
*
* @api
*/
public function getEntityReactionsWithUsers(string $module, int $moduleId): array
{
return $this->reactionsRepo->getEntityReactionsWithUsers($module, $moduleId);
}
}