| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127 |
- <?php
- namespace App\Service;
- use App\Model\Item;
- use App\Model\ItemNode;
- use App\Model\ItemNodeMission;
- class ProgressCalculatorService extends Service
- {
- // 定义常量,方便调用
- const TYPE_MISSION = 'mission';
- const TYPE_NODE = 'node';
- const TYPE_ITEM = 'item';
- /**
- * 统一进度计算入口
- * @param string $type 发生变更的层级类型: mission, node, item
- * @param int $id 对应层级的主键 ID
- */
- public static function calculate($type, $id) {
- if (empty($id)) return;
- switch ($type) {
- case self::TYPE_MISSION:
- // 1. 如果是任务变了,找到未删除的任务,进而拿到它所属的节点 ID
- $mission = ItemNodeMission::where('id', $id)
- ->where('del_time', 0)
- ->first();
- if ($mission) {
- $mission->progress = 100.0;
- $mission->save();
- self::handleNodeProgress($mission->item_node_id);
- }
- break;
- case self::TYPE_NODE:
- // 2. 如果是节点变了,直接计算该节点
- self::handleNodeProgress($id);
- break;
- case self::TYPE_ITEM:
- // 3. 如果是项目层级触发,直接计算项目
- self::handleItemProgress($id);
- break;
- }
- }
- /**
- * 核心逻辑:计算节点进度并自动向上触发项目计算(私有,不对外)
- */
- private static function handleNodeProgress($nodeId) {
- // 严格限制:只有未删除的节点才参与计算
- $node = ItemNode::where('id', $nodeId)
- ->where('del_time', 0)
- ->first();
- if (!$node) return;
- // 查找该节点下所有未删除的任务
- $missions = ItemNodeMission::where('item_node_id', $nodeId)
- ->where('del_time', 0)
- ->get();
- if ($missions->isEmpty()) {
- // 无任务,进度由自身状态决定
- $node->progress = ($node->state == ItemNodeMission::TYPE_THREE) ? 100.00 : 0.00;
- } else {
- $totalWeight = $missions->sum('mission_weight');
- if ($totalWeight <= 0) {
- // 如果建了任务但权重全是 0,按任务个数平均分
- $completedCount = $missions->where('state', 3)->count();
- $node->progress = round(($completedCount / $missions->count()) * 100, 2);
- } else {
- // 已完成(state = 3)的任务权重和
- $completedWeight = $missions->where('state', 3)->sum('mission_weight');
- $node->progress = round(($completedWeight / $totalWeight) * 100, 2);
- }
- }
- $node->save();
- // 【自动冒泡】节点算完,自动去算它所属的项目
- self::handleItemProgress($node->item_id);
- }
- /**
- * 核心逻辑:计算项目进度(私有,不对外)
- */
- private static function handleItemProgress($itemId) {
- // 严格限制:只有未删除的项目才参与计算
- $item = Item::where('id', $itemId)
- ->where('del_time', 0)
- ->first();
- if (!$item) return;
- // 查找该项目下所有未删除的节点
- $nodes = ItemNode::where('item_id', $itemId)
- ->where('del_time', 0)
- ->get();
- if ($nodes->isEmpty()) {
- // 无节点,进度由自身状态决定
- $item->progress = ($item->state == ItemNode::TYPE_THREE) ? 100.00 : 0.00;;
- $item->save();
- return;
- }
- $totalWeight = $nodes->sum('node_weight');
- if ($totalWeight <= 0) {
- // 如果有节点但权重全写了 0,按节点个数平均分配
- $currentSum = 0;
- foreach ($nodes as $node) {
- $currentSum += ($node->progress / 100);
- }
- $item->progress = round(($currentSum / $nodes->count()) * 100, 2);
- } else {
- // 采用复合算法:SUM(节点权重 * 节点自身的 progress) / 总权重
- $currentSum = 0;
- foreach ($nodes as $node) {
- $currentSum += $node->node_weight * ($node->progress / 100);
- }
- $item->progress = round(($currentSum / $totalWeight) * 100, 2);
- }
- $item->save();
- }
- }
|