cqp 2 天之前
父節點
當前提交
fe50031a04
共有 3 個文件被更改,包括 335 次插入6 次删除
  1. 26 0
      app/Http/Controllers/Api/ItemController.php
  2. 307 6
      app/Service/ItemService.php
  3. 2 0
      routes/api.php

+ 26 - 0
app/Http/Controllers/Api/ItemController.php

@@ -364,6 +364,32 @@ class ItemController extends BaseController
         }
     }
 
+    public function itemNodeMissionBatchUpdateProgressContent(Request $request)
+    {
+        $service = new ItemService();
+        $user = $request->userData;
+        list($status,$data) = $service->itemNodeMissionBatchUpdateProgressContent($request->all(),$user);
+
+        if($status){
+            return $this->json_return(200,'',$data);
+        }else{
+            return $this->json_return(201,$data);
+        }
+    }
+
+    public function itemNodeMissionContentDelete(Request $request)
+    {
+        $service = new ItemService();
+        $user = $request->userData;
+        list($status,$data) = $service->itemNodeMissionContentDelete($request->all(),$user);
+
+        if($status){
+            return $this->json_return(200,'',$data);
+        }else{
+            return $this->json_return(201,$data);
+        }
+    }
+
     public function itemNodeMissionUpdateState(Request $request)
     {
         $service = new ItemService();

+ 307 - 6
app/Service/ItemService.php

@@ -2664,6 +2664,8 @@ class ItemService extends Service
             ->first();
         if(empty($customer)) return [false,'项目节点任务不存在或已被删除'];
         $customer = $customer->toArray();
+        if($customer['state'] == ItemNodeMission::TYPE_THREE) return [false, '任务已完成'];
+
         if(empty($data['data'])) return [false, '任务进展明细不能为空'];
 
         $empDisplayMap = Employee::whereIn('id', array_unique(array_column($data['data'],'data_id')))
@@ -2731,7 +2733,9 @@ class ItemService extends Service
             DB::beginTransaction();
             $time = time();
 
-            ItemNodeMissionContent::where('item_node_mission_id', $data['id'])->where('del_time',0)->update(['del_time' => $time]);
+            ItemNodeMissionContent::where('item_node_mission_id', $data['id'])
+                ->where('del_time',0)
+                ->update(['del_time' => $time]);
 
             ItemNodeMissionContent::insert($data['data']);
 
@@ -2746,12 +2750,236 @@ class ItemService extends Service
 
     public function itemNodeMissionUpdateProgress($data, $user){
         if(empty($data['id'])) return [false, 'ID不能为空'];
+        $customer = ItemNodeMission::where('del_time',0)
+            ->where('id',$data['id'])
+            ->first();
+        if(empty($customer)) return [false,'项目节点任务不存在或已被删除'];
+        $order = $customer->toArray();
+        if($order['state'] == ItemNodeMission::TYPE_THREE) return [false, '任务已完成'];
         if(empty($data['progress'])) return [false, '任务进展不能为空'];
         $res = $this->checkNumber($data['progress'],0,'positive');
         if (! $res['valid']) return [false, "任务进展:" . $res['error']];
         if($data['progress'] > 100) return [false, '任务进展不能超过100'];
 
-        ItemNodeMission::where('id', $data['id'])->update(['progress' => $data['progress']]);
+        $customer->progress = $data['progress'];
+        $customer->save();
+
+        return [true, ''];
+    }
+
+    public function itemNodeMissionBatchUpdateProgressContent($data, $user)
+    {
+        if (empty($data['data']) || !is_array($data['data'])) {
+            return [false, '任务进展明细不能为空'];
+        }
+
+        // 1. 批量收集涉及的所有任务 ID 和人员 ID,用于前置内存缓存
+        $missionIds = array_filter(array_unique(array_column($data['data'], 'item_node_mission_id')));
+        $empIds     = array_filter(array_unique(array_column($data['data'], 'data_id')));
+        $detailIds  = array_filter(array_unique(array_column($data['data'], 'id'))); // 收集所有传了的明细ID
+
+        if (empty($missionIds)) return [false, '明细中缺少有效的任务ID'];
+
+        // 2. 批量查出相关任务和人员花名册,杜绝循环查 SQL
+        $missionsMap = ItemNodeMission::where('del_time', 0)->whereIn('id', $missionIds)->get()->keyBy('id')->toArray();
+        $empDisplayMap = Employee::whereIn('id', $empIds)->get(['id', 'number', 'title'])->mapWithKeys(function($item){
+            return [$item->id => "[{$item->number}]{$item->title}"];
+        })->toArray();
+
+        // 3. 一次性把这些任务/人在这些日期的已有数据库报告捞出来(用于做高效率的重叠比对)
+        // 这样就不用在 for 循环里去一条条 count 查重了
+        $dbPeriods = ItemNodeMissionContent::where('del_time', 0)
+            ->whereIn('item_node_mission_id', $missionIds)
+            ->whereIn('data_id', $empIds)
+            ->get(['id', 'item_node_mission_id', 'data_id', 'order_time', 'start_time_hour', 'start_time_min', 'end_time_hour', 'end_time_min'])
+            ->toArray();
+
+        // 用于校验“本次提交内部”时间段是否重叠
+        $internalOverlap = [];
+        // 最终准备执行的数据集
+        $finalRows = [];
+
+        // 4. 循环校验
+        foreach ($data['data'] as $key => $value) {
+            $line = $key + 1;
+            $t = "第 " . $line . " 行:";
+
+            $detailId  = $value['id'] ?? 0; // 明细自增ID(有代表修改,无代表新增)
+            $missionId = $value['item_node_mission_id'] ?? 0;
+            $empId     = $value['data_id'] ?? 0;
+
+            if (empty($missionId) || empty($empId)) return [false, $t . '任务ID和人员ID不能为空'];
+            if (!isset($missionsMap[$missionId])) return [false, $t . "任务[ID:{$missionId}]不存在"];
+
+            $customer = $missionsMap[$missionId];
+            $empName  = $empDisplayMap[$empId] ?? "ID:{$empId}";
+
+            if ($customer['state'] == ItemNodeMission::TYPE_THREE) {
+                return [false, $t . "任务【{$customer['title']}】已完成,不可操作"];
+            }
+
+            // 日期校验
+            if (empty($value['order_time'])) return [false, $t . '提交日期不能为空'];
+            $order_time = $this->changeDateToDate($value['order_time']);
+            if ($order_time > $customer['end_time'] || $order_time < $customer['start_time']) {
+                return [false, $t . "人员{$empName}的提交日期必须在任务周期内"];
+            }
+
+            // 基础数字校验 (调用你的校验函数)
+            if ($value['start_time_hour'] > 23 || $value['start_time_min'] > 59 || $value['end_time_hour'] > 24 || $value['end_time_min'] > 59) {
+                return [false, $t . "人员{$empName}提交的时间点不合法"];
+            }
+
+            // 转化为分钟数
+            $currentStart = $value['start_time_hour'] * 60 + $value['start_time_min'];
+            $currentEnd   = $value['end_time_hour'] * 60 + $value['end_time_min'];
+
+            if ($currentStart >= $currentEnd) return [false, $t . "人员{$empName}:开始时间必须早于结束时间"];
+
+            // 总分钟数校验
+            $calculatedTotal = $currentEnd - $currentStart;
+            if (!isset($value['total_work_min']) || intval($value['total_work_min']) !== $calculatedTotal) {
+                return [false, $t . "人员{$empName}:工时计算有误,应为 {$calculatedTotal} 分钟"];
+            }
+
+            // --- 5. 校验【本次提交内部】的重叠 ---
+            if (isset($internalOverlap[$missionId][$empId][$order_time])) {
+                foreach ($internalOverlap[$missionId][$empId][$order_time] as $period) {
+                    if ($currentStart < $period['e'] && $period['s'] < $currentEnd) {
+                        return [false, $t . "人员{$empName}在本次提交的多行明细中时间段重叠"];
+                    }
+                }
+            }
+            $internalOverlap[$missionId][$empId][$order_time][] = ['s' => $currentStart, 'e' => $currentEnd];
+
+            // --- 6. 核心:校验【与数据库已有数据】的重叠 ---
+            foreach ($dbPeriods as $dbRow) {
+                // 只有当 任务、人、日期 完全一致时,才需要比对时间段
+                if ($dbRow['item_node_mission_id'] == $missionId && $dbRow['data_id'] == $empId && $dbRow['order_time'] == $order_time) {
+
+                    // 【核心优化点】:如果是编辑状态,排查重叠时必须“排除掉自己这条记录”
+                    if ($detailId > 0 && $dbRow['id'] == $detailId) {
+                        continue;
+                    }
+
+                    $dbStart = $dbRow['start_time_hour'] * 60 + $dbRow['start_time_min'];
+                    $dbEnd   = $dbRow['end_time_hour'] * 60 + $dbRow['end_time_min'];
+
+                    // 判断时间段是否有交集:开始A < 结束B 并且 开始B < 结束A
+                    if ($currentStart < $dbEnd && $dbStart < $currentEnd) {
+                        return [false, $t . "人员{$empName}在系统已有记录中存在重叠的时间段({$dbRow['start_time_hour']}:{$dbRow['start_time_min']} ~ {$dbRow['end_time_hour']}:{$dbRow['end_time_min']})"];
+                    }
+                }
+            }
+
+            // 收集待处理数据
+            $finalRows[] = [
+                'id'                         => $detailId, // 捎带上ID,后面分流用
+                'item_id'                    => $customer['item_id'],
+                'item_node_id'               => $customer['item_node_id'],
+                'item_node_mission_id'       => $missionId,
+                'data_id'                    => $empId,
+                'content'                    => $value['content'] ?? '',
+                'order_time'                 => $order_time,
+                'total_work_min'             => $calculatedTotal,
+                'start_time_hour'            => $value['start_time_hour'],
+                'start_time_min'             => $value['start_time_min'],
+                'end_time_hour'              => $value['end_time_hour'],
+                'end_time_min'               => $value['end_time_min'],
+            ];
+        }
+
+        // 7. 事务处理:分流执行 Update 和 Insert
+        try {
+            DB::beginTransaction();
+            $time = time();
+
+            $inserts = [];
+            foreach ($finalRows as $row) {
+                $id = $row['id'];
+                unset($row['id']); // 从数组中剥离自增 ID
+
+                if ($id > 0) {
+                    // 情况 A:有 ID,执行精准修改
+                    ItemNodeMissionContent::where('id', $id)->update($row);
+                } else {
+                    // 情况 B:没有 ID,放入待插入队列
+                    $row['top_depart_id'] = $user['top_depart_id'];
+                    $row['crt_id']        = $user['id'];
+                    $row['crt_time']      = $time;
+                    $inserts[] = $row;
+                }
+            }
+
+            // 批量插入新数据
+            if (!empty($inserts)) {
+                ItemNodeMissionContent::insert($inserts);
+            }
+
+            DB::commit();
+        } catch (\Exception $exception) {
+            DB::rollBack();
+            return [false, '保存失败:' . $exception->getMessage()];
+        }
+
+        return [true, ''];
+    }
+
+    /**
+     * 批量删除任务报告
+     * @param array $data ['id' => [1, 2, 3...]] 报告ID数组
+     * @param array $user 当前登录人信息
+     * @return array [bool, string]
+     */
+    public function itemNodeMissionContentDelete($data, $user)
+    {
+        // 兼容前端可能传单个 ID 或 ID 数组的情况
+        $ids = isset($data['id']) ? (array)$data['id'] : [];
+        $ids = array_filter(array_unique($ids));
+
+        if (empty($ids)) {
+            return [false, '请选择要删除的任务报告'];
+        }
+
+        // 1. 连表查询这些报告所属任务的状态
+        // 目的:找出有没有属于“已完成”状态任务的报告
+        $completedMissions = DB::table('item_node_mission_content as e')
+            ->join('item_node_mission as i', 'e.item_node_mission_id', '=', 'i.id')
+            ->whereIn('e.id', $ids)
+            ->where('e.del_time', 0)
+            ->where('i.state', ItemNodeMission::TYPE_THREE) // 任务已完成状态
+            ->select('e.id as content_id', 'i.title as mission_title')
+            ->get();
+
+        // 2. 核心校验:如果存在已完成任务的报告,直接拦截报错
+        if ($completedMissions->isNotEmpty()) {
+            $firstError = $completedMissions->first();
+            return [false, "任务【{$firstError->mission_title}】已完成,不允许删除该任务下的任何进展报告"];
+        }
+
+        // 3. 事务处理:批量执行软删除
+        try {
+            DB::beginTransaction();
+            $time = time();
+
+            // 批量将这些报告的 del_time 更新为当前时间戳(软删除)
+            $effectRows = ItemNodeMissionContent::whereIn('id', $ids)
+                ->where('del_time', 0)
+                ->update([
+                    'del_time' => $time,
+                    'upd_time' => $time // 顺便把更新时间也同步了
+                ]);
+
+            if ($effectRows === 0) {
+                DB::rollBack();
+                return [false, '未找到有效的任务报告或报告已被删除'];
+            }
+
+            DB::commit();
+        } catch (\Exception $exception) {
+            DB::rollBack();
+            return [false, '删除失败:' . $exception->getMessage()];
+        }
 
         return [true, ''];
     }
@@ -2948,6 +3176,7 @@ class ItemService extends Service
 
         $item_id = $data['item_id'] ?? 0;
         $item_node_id = $data['item_node_id'] ?? 0;
+        $except_state = $data['except_state'] ?? 0;
         $model = ItemNodeMission::TopAndEmployeeClear($user,$data);
         $model = $model->where('del_time',0)
             ->when(! empty($item_id),function ($query) use($item_id){
@@ -2956,6 +3185,9 @@ class ItemService extends Service
             ->when(! empty($item_node_id),function ($query) use($item_node_id){
                 return $query->where('item_node_id', $item_node_id);
             })
+            ->when(! empty($except_state),function ($query) use($except_state){
+                return $query->whereNotIn('state', (array)$except_state);
+            })
             ->select($field)
             ->orderby('id', 'desc');
 
@@ -2977,7 +3209,7 @@ class ItemService extends Service
     public function itemNodeMissionList($data,$user){
         $model = $this->itemNodeMissionCommon($data, $user);
         $list = $this->limit($model,'',$data);
-        $list = $this->fillNodeMissionData($list);
+        $list = $this->fillNodeMissionData($list, $data, $user);
 
         return [true, $list];
     }
@@ -2987,7 +3219,7 @@ class ItemService extends Service
         if(! $status) return [false, $model];
 
         $list['data'] = $model->get()->toArray();
-        $list = $this->fillNodeMissionData($list);
+        $list = $this->fillNodeMissionData($list, $data, $user);
 
         return [true, $list['data']];
     }
@@ -3196,12 +3428,14 @@ class ItemService extends Service
         return false; // 没有任何变动
     }
 
-    public function fillNodeMissionData($data){
+    public function fillNodeMissionData($data, $ergs, $user){
         if(empty($data['data'])) return $data;
 
         $emp = (new EmployeeService())->getEmployeeMap(array_unique(array_merge_recursive(array_column($data['data'],'charge_id'), array_column($data['data'],'crt_id'))));
         $tag = (new TagService())->getTagMap(array_unique(array_merge_recursive(array_column($data['data'],'priority_id'),array_column($data['data'],'mission_id'))));
-        $item_node_map = $this->getItemNodeMap(array_column($data['data'],'id'));
+        $mission_id = array_column($data['data'],'id');
+        $item_node_map = $this->getItemNodeMap($mission_id);
+        if(isset($ergs['get_item_node_mission_man'])) $mission_man = $this->getMissionMembersMap($mission_id, $user['id']);
         foreach ($data['data'] as $key => $value){
             $data['data'][$key]['crt_time'] = $value['crt_time'] ? date('Y-m-d H:i:s',$value['crt_time']) : '';
             $data['data'][$key]['start_time'] = $value['start_time'] ? date('Y-m-d',$value['start_time']) : '';
@@ -3219,11 +3453,77 @@ class ItemService extends Service
             $data['data'][$key]['mission_code'] = $node_tmp['code'] ?? '';
             $data['data'][$key]['mission_color'] = $node_tmp['color'] ?? '';
             $data['data'][$key]['item_node_title'] = $item_node_map[$value['id']] ?? '';
+            $data['data'][$key]['mission_man'] = $mission_man[$value['id']] ?? [];
         }
 
         return $data;
     }
 
+    /**
+     * 根据任务 ID 数组获取当前登录人可见的任务成员 Map 结构(传统数组对象结构)
+     * * @param array $missionIds 任务 ID 数组 [1, 2, 3...]
+     * @param int $userId 当前登录人 ID
+     * @return array [mission_id => [ ["id" => 1, "title" => "张三"], ... ]]
+     */
+    public function getMissionMembersMap(array $missionIds, int $userId)
+    {
+        if (empty($missionIds)) {
+            return [];
+        }
+
+        // 1. 批量查出当前用户在这些任务里哪些是【负责人】身份
+        $chargeMissionIds = \DB::table('item_node_mission_employee')
+            ->whereIn('item_node_mission_id', $missionIds)
+            ->where('data_id', $userId)
+            ->where('del_time', 0)
+            ->pluck('item_node_mission_id')
+            ->toArray();
+
+        // 2. 准备查询成员表的 Query,并连表关联 employee
+        $query = \DB::table('item_node_mission_details as d')
+            ->join('employee as e', 'd.data_id', '=', 'e.id')
+            ->whereIn('d.item_node_mission_id', $missionIds)
+            ->where('d.type', 1)
+            ->where('d.del_time', 0)
+            ->where('e.del_time', 0)
+            ->select('d.item_node_mission_id', 'd.data_id as id', 'e.title'); // 别名直接对齐你需要的 id 和 title
+
+        // 3. 核心分流:找出哪些任务用户【不是负责人】
+        $notChargeMissionIds = array_diff($missionIds, $chargeMissionIds);
+
+        if (!empty($notChargeMissionIds)) {
+            $query->where(function ($q) use ($chargeMissionIds, $userId) {
+                if (!empty($chargeMissionIds)) {
+                    $q->whereIn('d.item_node_mission_id', $chargeMissionIds)
+                        ->orWhere(function ($subQ) use ($userId) {
+                            $subQ->where('d.data_id', $userId);
+                        });
+                } else {
+                    $q->where('d.data_id', $userId);
+                }
+            });
+        }
+
+        // 4. 查出过滤后的所有成员数据
+        $details = $query->get();
+
+        // 5. 初始化 Map 壳子
+        $resultMap = [];
+        foreach ($missionIds as $id) {
+            $resultMap[$id] = [];
+        }
+
+        // 6. 组装成你需要的规范二维结构
+        foreach ($details as $detail) {
+            $resultMap[$detail->item_node_mission_id][] = [
+                'id'    => $detail->id,
+                'title' => $detail->title
+            ];
+        }
+
+        return $resultMap;
+    }
+
     public function getItemNodeMap($item_node_mission_id){
         if(! is_array($item_node_mission_id)) $item_node_mission_id = [$item_node_mission_id];
         return ItemNodeMission::from('item_node_mission as a')
@@ -3652,6 +3952,7 @@ class ItemService extends Service
             ->first();
         if (empty($customer)) return [false, '项目节点任务不存在或已被删除'];
         $customer = $customer->toArray();
+        if($customer['state'] == ItemNodeMission::TYPE_THREE) return [false, '任务已完成'];
 
         if (empty($data['data']) || !is_array($data['data'])) return [false, '任务进展明细不能为空'];
 

+ 2 - 0
routes/api.php

@@ -129,6 +129,8 @@ Route::group(['middleware'=> ['checkLogin']],function ($route){
     $route->any('itemNodeMissionFinish', 'Api\ItemController@itemNodeMissionFinish'); // 完结
     $route->any('itemNodeMissionUpdateProgress', 'Api\ItemController@itemNodeMissionUpdateProgress'); // 更新任务进展
     $route->any('itemNodeMissionUpdateProgressContent', 'Api\ItemController@itemNodeMissionUpdateProgressContent'); // 更新任务进展人员日报
+    $route->any('itemNodeMissionBatchUpdateProgressContent', 'Api\ItemController@itemNodeMissionBatchUpdateProgressContent'); // 批量操作任务进展人员日报
+    $route->any('itemNodeMissionContentDelete', 'Api\ItemController@itemNodeMissionContentDelete'); // 批量删除任务进展人员日报
     $route->any('itemNodeMissionUpdateState', 'Api\ItemController@itemNodeMissionUpdateState'); // 拖拽更新任务状态
     $route->any('itemNodeMissionList', 'Api\ItemController@itemNodeMissionList');
     $route->any('itemGannetList', 'Api\ItemController@itemGannetList');