ItemService.php 99 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427
  1. <?php
  2. namespace App\Service;
  3. use App\Jobs\ProcessOssTask;
  4. use App\Model\CustomFieldValue;
  5. use App\Model\Device;
  6. use App\Model\Draft;
  7. use App\Model\Employee;
  8. use App\Model\Item;
  9. use App\Model\ItemDetails;
  10. use App\Model\ItemEmployee;
  11. use App\Model\ItemFile;
  12. use App\Model\ItemNode;
  13. use App\Model\ItemNodeDetails;
  14. use App\Model\ItemNodeEmployee;
  15. use App\Model\ItemNodeMission;
  16. use App\Model\ItemNodeMissionContent;
  17. use App\Model\ItemNodeMissionDetails;
  18. use App\Model\ItemNodeMissionEmployee;
  19. use App\Model\SysOssTasks;
  20. use App\Model\Tag;
  21. use App\Model\SysMenu;
  22. use App\Model\WorkFlowTemplates;
  23. use Illuminate\Support\Facades\DB;
  24. class ItemService extends Service
  25. {
  26. public function itemFileUpLoad($data,$user){
  27. // 1. 基础校验
  28. $from = $data['from'] ?? null;
  29. $fromMap = ItemFile::from;
  30. if (empty($from)) return [false, '上传来源类型不能为空'];
  31. if (!isset($fromMap[$from])) return [false, '上传来源类型错误'];
  32. $id = $data['id'] ?? null;
  33. if (empty($id)) return [false, '来源ID不能为空'];
  34. $modelMap = [
  35. ItemFile::from_one => Item::class,
  36. ItemFile::from_two => ItemNode::class,
  37. ItemFile::from_three => ItemNodeMission::class,
  38. ];
  39. $modelClass = $modelMap[$from] ?? "";
  40. $item = $modelClass::where('id', $id)->where('del_time', 0)->first();
  41. if (!$item) return [false, $fromMap[$from] . "不存在或已被删除"];
  42. $itemId = $item->item_id ?? $item->id;
  43. $itemNodeId = $item->item_node_id ?? ($from == ItemFile::from_one ? 0 : $item->id);
  44. $missionId = ($from != ItemFile::from_one && $from != ItemFile::type_two) ? $item->id : 0;
  45. $files = $data['file'] ?? [];
  46. if (empty($files)) return [false, '上传文件不能为空'];
  47. $time = time();
  48. $pendingTasks = [];
  49. foreach ($files as &$file) {
  50. if (empty($file['url']) || empty($file['type']) || empty($file['name'])) {
  51. return [false, '文件信息(URL/类型/名称)不完整'];
  52. }
  53. $file['top_depart_id'] = $user['top_depart_id'];
  54. $file['from'] = $from;
  55. $file['item_id'] = $itemId;
  56. $file['item_node_id'] = $itemNodeId;
  57. $file['item_node_mission_id'] = $missionId;
  58. $file['crt_time'] = $time;
  59. $file['crt_id'] = $user['id'];
  60. $pendingTasks[] = ['type' => SysOssTasks::type_two, 'url' => $file['url'], 'top_depart_id' => $user['top_depart_id'], 'crt_time' => $time];
  61. }
  62. unset($file);
  63. $data['file'] = $files;
  64. try {
  65. DB::beginTransaction();
  66. ItemFile::insert($data['file']);
  67. //触发 OSS 任务
  68. if (!empty($pendingTasks)) {
  69. SysOssTasks::insert($pendingTasks);
  70. ProcessOssTask::dispatch()->onQueue(SysOssTasks::job);
  71. }
  72. DB::commit();
  73. }catch (\Exception $exception){
  74. DB::rollBack();
  75. return [false,$exception->getMessage()];
  76. }
  77. return [true, ''];
  78. }
  79. public function itemFileUpDelete($data,$user){
  80. $fromMap = [
  81. 'custom_fields' => CustomFieldValue::class,
  82. 'item_file' => ItemFile::class,
  83. ];
  84. // 1. 基础校验
  85. $type = $data['type'] ?? null;
  86. if (empty($type)) return [false, '删除来源类型不能为空'];
  87. if (!isset($fromMap[$type])) return [false, '删除来源类型错误'];
  88. $id = $data['id'] ?? null;
  89. if (empty($id)) return [false, '来源ID不能为空'];
  90. $modelClass = $fromMap[$type] ?? "";
  91. $item = $modelClass::where('id', $id)->where('del_time', 0)->first();
  92. if (! $item) return [false, "文件不存在或已被删除"];
  93. try {
  94. DB::beginTransaction();
  95. $time = time();
  96. if($type == 'custom_fields'){
  97. if(empty($item->field_value)) return [false, '文件为空'];
  98. $pendingTasks[] = ['type' => SysOssTasks::type_one, 'url' => $item->field_value, 'top_depart_id' => $user['top_depart_id'], 'crt_time' => $time];
  99. $item->update([
  100. 'field_value' => '',
  101. 'field_name' => ''
  102. ]);
  103. }else{
  104. if(empty($item->url)) return [false, '文件为空'];
  105. $pendingTasks[] = ['type' => SysOssTasks::type_one, 'url' => $item->url, 'top_depart_id' => $user['top_depart_id'], 'crt_time' => $time];
  106. $item->update(['del_time' => $time]);
  107. }
  108. //触发 OSS 任务
  109. if (! empty($pendingTasks)) {
  110. SysOssTasks::insert($pendingTasks);
  111. ProcessOssTask::dispatch()->onQueue(SysOssTasks::job);
  112. }
  113. DB::commit();
  114. }catch (\Exception $exception){
  115. DB::rollBack();
  116. return [false,$exception->getMessage()];
  117. }
  118. return [true, ''];
  119. }
  120. public function itemFileIsDelivery($data, $user)
  121. {
  122. $typeMap = [
  123. 1 => '标记为交付物',
  124. 2 => '解除交付物',
  125. ];
  126. $fromMap = [
  127. 'custom_fields' => CustomFieldValue::class,
  128. 'item_file' => ItemFile::class,
  129. ];
  130. if (empty($data['opt_type']) || !isset($typeMap[$data['opt_type']])) return [false, '操作标识错误'];
  131. if (empty($data['data']) || !is_array($data['data'])) return [false, '文件数据不能为空'];
  132. // 状态值映射
  133. $statusValue = ($data['opt_type'] == 1) ? 1 : 0;
  134. $idsByType = [
  135. 'custom_fields' => [],
  136. 'item_file' => [],
  137. ];
  138. foreach ($data['data'] as $value) {
  139. if (empty($value['id'])) return [false, '文件ID不能为空'];
  140. if (empty($value['type']) || !isset($fromMap[$value['type']])) return [false, '文件类型错误'];
  141. $idsByType[$value['type']][] = $value['id'];
  142. }
  143. try {
  144. DB::beginTransaction();
  145. foreach ($idsByType as $type => $ids) {
  146. if (!empty($ids)) {
  147. $modelClass = $fromMap[$type];
  148. $modelClass::whereIn('id', $ids)->update([
  149. 'is_delivery' => $statusValue,
  150. ]);
  151. }
  152. }
  153. DB::commit();
  154. } catch (\Exception $exception) {
  155. DB::rollBack();
  156. return [false, $exception->getMessage()];
  157. }
  158. return [true, ''];
  159. }
  160. public function changedCommon($user, $oldData){
  161. //创建人 直接通过
  162. if($user['id'] !== $oldData['crt_id']) return true;
  163. //是否存在审批流
  164. if(empty($oldData['review_id'])) return false;
  165. return false;
  166. }
  167. public function itemFinish($data, $user){
  168. if(empty($data['id'])) return [false, 'ID不能为空'];
  169. $model = Item::where('id',$data['id'])
  170. ->where('del_time',0)
  171. ->first();
  172. if(empty($model)) return [false, '项目不存在或已被删除'];
  173. $oldData = $model->toArray();
  174. if($oldData['state'] == Item::TYPE_THREE) return [false, '项目已完成,请勿重复操作'];
  175. try {
  176. DB::beginTransaction();
  177. $bool = $this->changedCommon($user, $oldData);
  178. if($bool) {
  179. // 更新项目状态
  180. $model->approval_state = Item::TYPE_MINUS_ONE;
  181. $model->save();
  182. //触发审批流
  183. $this->createWorkFlow(['id' => $data['id'], 'state' => Item::TYPE_THREE], $user, $model->getTable(), $oldData, 2);
  184. DB::commit();
  185. return [true, '更新记录成功,记录草稿,进入审批流程'];
  186. }
  187. $model->state = Item::TYPE_THREE;
  188. $model->save();
  189. DB::commit();
  190. }catch (\Exception $exception){
  191. DB::rollBack();
  192. return [false,$exception->getMessage()];
  193. }
  194. return [true, ''];
  195. }
  196. public function itemEdit($data, $user){
  197. list($status,$msg) = $this->itemRule($data, $user, false);
  198. if(!$status) return [$status,$msg];
  199. list($status, $msg) = $this->itemEditSave($data, $user);
  200. return [$status, $msg];
  201. }
  202. public function itemEditSave($data, $user){
  203. try {
  204. DB::beginTransaction();
  205. $model = Item::where('id', $data['id'])->first();
  206. $tableName = $model->getTable();
  207. if(isset($data['draft'])){
  208. // 更新项目状态
  209. $model->approval_state = Item::TYPE_MINUS_ONE;
  210. $model->save();
  211. //触发审批流
  212. $this->createWorkFlow($data, $user, $tableName, $model->toArray());
  213. DB::commit();
  214. return [true, '更新记录成功,记录草稿,进入审批流程'];
  215. }
  216. $old_employee_id = $model->charge_id;
  217. $model->code = $data['code'] ?? '';
  218. $model->title = $data['title'] ?? '';
  219. $model->mark = $data['mark'] ?? "";
  220. $model->start_time = $data['start_time'] ?? 0;
  221. $model->end_time = $data['end_time'] ?? 0;
  222. $model->state = $data['state'] ?? 0;
  223. $model->budget = $data['budget'] ?? 0;
  224. $model->charge_id = $data['charge_id'] ?? 0;
  225. $model->field = $data['field'] ?? "";
  226. $model->item_attribute = $data['item_attribute'] ?? 0;
  227. //项目管理的字段
  228. if($user['select_tree_type'] == SysMenu::tree_type_one){
  229. $model->is_review_required = $data['is_review_required'] ?? 0;
  230. $model->review_id = $data['review_id'] ?? 0;
  231. $model->priority_id = $data['priority_id'] ?? 0;
  232. }
  233. $model->save();
  234. $time = time();
  235. // 人员项目表
  236. $this->saveEmployee($data['id'], $time, $data, $user, $old_employee_id);
  237. ItemDetails::where('del_time',0)
  238. ->where('item_id', $model->id)
  239. ->update(['del_time' => $time]);
  240. $this->saveDetail($model->id, $time, $data);
  241. //客户自定义字段
  242. list($status, $msg) = CustomFieldSettingService::syncCustomFieldData($model->id, $tableName, $data, $user);
  243. if (! $status) {
  244. DB::rollBack();
  245. return [false, $msg];
  246. }
  247. DB::commit();
  248. }catch (\Exception $exception){
  249. DB::rollBack();
  250. return [false,$exception->getMessage()];
  251. }
  252. return [true, ''];
  253. }
  254. public function itemAdd($data,$user){
  255. list($status,$msg) = $this->itemRule($data, $user);
  256. if(!$status) return [$status,$msg];
  257. try {
  258. DB::beginTransaction();
  259. $model = new Item();
  260. $tableName = $model->getTable();
  261. $model->code = $data['code'] ?? '';
  262. $model->title = $data['title'] ?? '';
  263. $model->mark = $data['mark'] ?? "";
  264. $model->start_time = $data['start_time'] ?? 0;
  265. $model->end_time = $data['end_time'] ?? 0;
  266. $model->state = $data['state'] ?? 0;
  267. $model->budget = $data['budget'] ?? 0;
  268. $model->charge_id = $data['charge_id'] ?? 0;
  269. $model->field = $data['field'] ?? "";
  270. $model->item_attribute = $data['item_attribute'] ?? 0;
  271. $model->is_review_required = $data['is_review_required'] ?? 0;
  272. $model->review_id = $data['review_id'] ?? 0;
  273. $model->priority_id = $data['priority_id'] ?? 0;
  274. $model->crt_id = $user['id'];
  275. $model->top_depart_id = $data['top_depart_id'];
  276. $model->save();
  277. $time = time();
  278. $this->saveDetail($model->id, $time, $data);
  279. // 人员项目表
  280. $this->saveEmployee($model->id, $time, $data, $user);
  281. list($status, $msg) = CustomFieldSettingService::syncCustomFieldData($model->id, $tableName, $data, $user);
  282. if (!$status) {
  283. DB::rollBack();
  284. return [false, $msg];
  285. }
  286. DB::commit();
  287. }catch (\Exception $exception){
  288. DB::rollBack();
  289. return [false,$exception->getMessage()];
  290. }
  291. return [true, ''];
  292. }
  293. public function checkIsChanged($data, $user) {
  294. // 获取旧数据
  295. list($status, $oldData) = $this->itemDetail($data, $user);
  296. if (!$status) return [false, $oldData];
  297. //工时修改 直接通过
  298. $select_tree_type = $user['select_tree_type'];
  299. if(empty($select_tree_type)) return false;
  300. //创建人 直接通过
  301. $bool = $this->changedCommon($user, $oldData);
  302. if(! $bool) return false;
  303. // --- 表头基础字段比对 ---
  304. $fields = [
  305. 'code', 'title', 'mark', 'state', 'budget',
  306. 'charge_id', 'item_attribute', 'start_time', 'end_time'
  307. ];
  308. // 权限字段校验
  309. if (($user['select_tree_type']) == SysMenu::tree_type_one) {
  310. $fields = array_merge($fields, ['is_review_required', 'review_id', 'priority_id']);
  311. }
  312. foreach ($fields as $field) {
  313. $oldValue = $oldData[$field] ?? '';
  314. $newValue = $data[$field] ?? '';
  315. // 日期格式对齐(确保 Y-m-d 格式一致)
  316. if (in_array($field, ['start_time', 'end_time'])) {
  317. $newValue = !empty($newValue) ? date("Y-m-d", $newValue) : "";
  318. }
  319. if ($oldValue != $newValue) {
  320. return true;
  321. }
  322. }
  323. // --- 自定义字段比对 ---
  324. if (isset($data['custom_fields']) && is_array($data['custom_fields'])) {
  325. $oldCustomMap = array_column($oldData['custom_fields'] ?? [], 'field_value', 'id');
  326. foreach ($data['custom_fields'] as $customField) {
  327. $fid = $customField['id'] ?? 0;
  328. $newVal = $customField['field_value'] ?? '';
  329. $oldVal = $oldCustomMap[$fid] ?? '';
  330. if ($oldVal != $newVal) {
  331. return true;
  332. }
  333. }
  334. }
  335. // --- 表体:人员名单比对 (man_list) ---
  336. $oldMans = collect($oldData['man_list'] ?? [])->pluck('data_id')->sort()->values()->toArray();
  337. $newMans = collect($data['man_list'] ?? [])->pluck('data_id')->sort()->values()->toArray();
  338. if ($oldMans !== $newMans) {
  339. return true;
  340. }
  341. // --- 表体:设备名单比对 (device_list) ---
  342. $oldDevices = collect($oldData['device_list'] ?? [])->pluck('data_id')->sort()->values()->toArray();
  343. $newDevices = collect($data['device_list'] ?? [])->pluck('data_id')->sort()->values()->toArray();
  344. if ($oldDevices !== $newDevices) {
  345. return true;
  346. }
  347. return false; // 没有任何变动
  348. }
  349. public function createDraft($data, $user, $type, $opt_type = 1) {
  350. if(isset($data['draft'])) unset($data['draft']);
  351. Draft::insert([
  352. 'document_type' => $type,
  353. 'document_id' => $data['id'],
  354. 'content' => json_encode($data),
  355. 'opt_user' => json_encode($user),
  356. 'crt_id' => $user['id'],
  357. 'top_depart_id' => $user['top_depart_id'],
  358. 'opt_type' => $opt_type,
  359. 'crt_time' => time()
  360. ]);
  361. }
  362. private function saveDetail($id, $time, $data){
  363. if(! empty($data['man_list'])){
  364. $unit = [];
  365. foreach ($data['man_list'] as $value){
  366. $unit[] = [
  367. 'item_id' => $id,
  368. 'type' => $value['type'],
  369. 'data_id' => $value['data_id'],
  370. 'crt_time' => $time,
  371. 'top_depart_id' => $value['top_depart_id'],
  372. ];
  373. }
  374. if(! empty($unit)) ItemDetails::insert($unit);
  375. }
  376. if(! empty($data['device_list'])){
  377. $receipt = [];
  378. foreach ($data['device_list'] as $value){
  379. $receipt[] = [
  380. 'item_id' => $id,
  381. 'type' => $value['type'],
  382. 'data_id' => $value['data_id'],
  383. 'crt_time' => $time,
  384. 'top_depart_id' => $value['top_depart_id'],
  385. ];
  386. }
  387. if(! empty($receipt)) ItemDetails::insert($receipt);
  388. }
  389. }
  390. private function saveEmployee($id, $time, $data, $user, $old_employee_id = 0){
  391. if($old_employee_id != $data['charge_id']){
  392. ItemEmployee::where('del_time',0)
  393. ->where('item_id', $id)
  394. ->where('data_id', $old_employee_id)
  395. ->delete();
  396. ItemEmployee::insert([
  397. 'item_id' => $id,
  398. 'data_id' => $data['charge_id'],
  399. 'top_depart_id' => $user['top_depart_id'],
  400. 'crt_time' => $time
  401. ]);
  402. }
  403. }
  404. private function getDetail($id){
  405. $data = ItemDetails::where('del_time',0)
  406. ->where('item_id', $id)
  407. ->get()->toArray();
  408. $id = $id2 = [];
  409. foreach ($data as $value){
  410. if($value['type'] == ItemDetails::type_one) {
  411. $id[] = $value['data_id'];
  412. }else{
  413. $id2[] = $value['data_id'];
  414. }
  415. }
  416. $map = Employee::whereIn('id', $id)->select('title','id','number')->get()->toArray();
  417. $map = array_column($map,null,'id');
  418. $map2 = Device::whereIn('id', $id2)->select('code','id','title','type')->get()->toArray();
  419. $map2 = array_column($map2,null,'id');
  420. $unit = $receipt = [];
  421. foreach ($data as $value){
  422. if($value['type'] == ItemDetails::type_one) {
  423. $tmp = $map[$value['data_id']] ?? [];
  424. $unit[] = [
  425. 'type' => $value['type'],
  426. 'data_id' => $value['data_id'],
  427. 'data_title' => $tmp['title'],
  428. 'data_code' => $tmp['number'],
  429. ];
  430. }else{
  431. $tmp = $map2[$value['data_id']] ?? [];
  432. $receipt[] = [
  433. 'type' => $value['type'],
  434. 'data_id' => $value['data_id'],
  435. 'data_title' => $tmp['title'] ?? "",
  436. 'data_code' => $tmp['code'] ?? "",
  437. 'type_title' => Device::$type[$tmp['type']],
  438. ];
  439. }
  440. }
  441. return [
  442. 'man_list' => $unit,
  443. 'device_list' => $receipt,
  444. ];
  445. }
  446. public function getItemMap($ids){
  447. $ids = array_filter((array)$ids);
  448. if (empty($ids)) return [];
  449. return Item::whereIn('id', $ids)
  450. ->select('id', 'title', 'code')
  451. ->get()
  452. ->keyBy('id')
  453. ->toArray();
  454. }
  455. private function checkDelete($data, $type = 1)
  456. {
  457. $id = $data['id'] ?? null;
  458. if (!$id) return [false, '参数错误'];
  459. // 1. 定义配置数组,将不同类型的校验逻辑抽离,消除 if-else 臃肿
  460. $config = [
  461. 1 => [
  462. 'model' => Item::class,
  463. 'name' => '项目',
  464. 'relations' => [
  465. ['model' => ItemNode::class, 'key' => 'item_id', 'msg' => '项目下已存在节点'],
  466. ['model' => ItemNodeMission::class, 'key' => 'item_id', 'msg' => '项目下已存在任务'],
  467. ]
  468. ],
  469. 2 => [
  470. 'model' => ItemNode::class,
  471. 'name' => '节点',
  472. 'relations' => [
  473. ['model' => ItemNodeMission::class, 'key' => 'item_node_id', 'msg' => '节点下已存在任务'],
  474. ]
  475. ],
  476. 3 => [
  477. 'model' => ItemNodeMission::class,
  478. 'name' => '任务',
  479. 'relations' => []
  480. ],
  481. ];
  482. $cfg = $config[$type] ?? null;
  483. if (!$cfg) return [false, '无效的类型'];
  484. // 2. 统一查询主体对象
  485. $target = $cfg['model']::where('del_time', 0)->find($id);
  486. // 3. 健壮性检查:防止操作不存在的记录
  487. if (!$target) return [false, "未找到该{$cfg['name']}"];
  488. // 4. 状态校验:统一判断是否已完成
  489. // 建议在 Model 中统一定义 TYPE_THREE 的常量名或方法
  490. if ($target->approval_state == $cfg['model']::TYPE_MINUS_ONE) {
  491. return [false, "{$cfg['name']}处于审核中"];
  492. }
  493. // 5. 关联依赖校验
  494. foreach ($cfg['relations'] as $rel) {
  495. $exists = $rel['model']::where('del_time', 0)
  496. ->where($rel['key'], $id)
  497. ->exists();
  498. if ($exists) return [false, $rel['msg']];
  499. }
  500. return [true, ''];
  501. }
  502. public function itemDel($data){
  503. if($this->isEmpty($data,'id')) return [false,'请选择数据!'];
  504. list($status, $msg) = $this->checkDelete($data);
  505. if(! $status) return [false, $msg];
  506. try {
  507. DB::beginTransaction();
  508. $time = time();
  509. Item::where('del_time',0)
  510. ->whereIn('id',$data['id'])
  511. ->update(['del_time' => $time]);
  512. ItemDetails::where('del_time',0)
  513. ->whereIn('item_id', $data['id'])
  514. ->update(['del_time' => $time]);
  515. DB::commit();
  516. }catch (\Exception $exception){
  517. DB::rollBack();
  518. return [false,$exception->getMessage()];
  519. }
  520. return [true, ''];
  521. }
  522. public function itemDetail($data, $user){
  523. if($this->isEmpty($data,'id')) return [false,'请选择数据!'];
  524. $customer = Item::where('del_time',0)
  525. ->withCustomData($user)
  526. ->where('id',$data['id'])
  527. ->first();
  528. if(empty($customer)) return [false,'项目不存在或已被删除'];
  529. $customer->getFormattedCustomFields();// 自定义数据附件处理
  530. $customer = $customer->toArray();
  531. $customer['start_time'] = ! empty($customer['start_time']) ? date("Y-m-d", $customer['start_time']) : "";
  532. $customer['end_time'] = ! empty($customer['end_time']) ? date("Y-m-d", $customer['end_time']) : "";
  533. $customer['crt_name'] = Employee::where('id',$customer['crt_id'])->value('title');
  534. $customer['charge_name'] = Employee::where('id',$customer['charge_id'])->value('title');
  535. $customer['crt_time'] = $customer['crt_time'] ? date("Y-m-d H:i:s",$customer['crt_time']): '';
  536. $customer['state_title'] = Item::State_Type[$customer['state']] ?? '';
  537. $customer['approval_state_title'] = Item::State_Type[$customer['approval_state']] ?? '';
  538. $customer['priority_title'] = Tag::where('id', $customer['priority_id'])->value('title') ?? "";
  539. $customer['review_title'] = WorkFlowTemplates::where('id', $customer['review_id'])->value('title') ?? "";
  540. $details = $this->getDetail($data['id']);
  541. $customer = array_merge($customer, $details);
  542. return [true, $customer];
  543. }
  544. public function itemDetailBoard($data, $user){
  545. list($status, $msg) = $this->itemDetail($data, $user);
  546. if(! $status) return [false, $msg];
  547. $customer = $msg;
  548. //节点信息
  549. $customer['node_list'] = $this->itemNodeBoard($data, $user);
  550. //文件归档
  551. $customer['file_list'] = $this->getNodeFile($data, $user, $customer);
  552. return [true, $customer];
  553. }
  554. public function itemNodeBoard($data,$user){
  555. $model = ItemNode::TopAndEmployeeClear($user,$data);
  556. $list = $model->where('del_time',0)
  557. ->where('item_id', $data['id'])
  558. ->select('*')
  559. ->orderby('upd_time', 'desc')
  560. ->orderby('id', 'desc')
  561. ->get()->toArray();
  562. $emp = (new EmployeeService())->getEmployeeMap(array_unique(array_merge_recursive(array_column($list,'charge_id'), array_column($list,'crt_id'))));
  563. $tag = (new TagService())->getTagMap(array_unique(array_merge_recursive(array_column($list,'priority_id'),array_column($list,'node_id'))));
  564. foreach ($list as $key => $value){
  565. $list[$key]['crt_time'] = $value['crt_time'] ? date('Y-m-d H:i:s',$value['crt_time']) : '';
  566. $list[$key]['start_time'] = $value['start_time'] ? date('Y-m-d',$value['start_time']) : '';
  567. $list[$key]['end_time'] = $value['end_time'] ? date('Y-m-d',$value['end_time']) : '';
  568. $list[$key]['crt_name'] = $emp[$value['crt_id']] ?? '';
  569. $list[$key]['charge_name'] = $emp[$value['charge_id']] ?? '';
  570. $list[$key]['state_title'] = ItemNode::State_Type[$value['state']] ?? "";
  571. $list[$key]['approval_state_title'] = Item::State_Type[$value['approval_state']] ?? '';
  572. $priority_tmp = $tag[$value['priority_id']] ?? [];
  573. $list[$key]['priority_title'] = $priority_tmp['title'] ?? '';
  574. $list[$key]['priority_code'] = $priority_tmp['code'] ?? '';
  575. $node_tmp = $tag[$value['node_id']] ?? [];
  576. $list[$key]['node_title'] = $node_tmp['title'] ?? '';
  577. $list[$key]['node_code'] = $node_tmp['code'] ?? '';
  578. $list[$key]['progress'] = rand(1, 100); //todo
  579. }
  580. return $list;
  581. }
  582. public function getNodeFile($data,$user, $item){
  583. $file = ItemFile::where('del_time',0)
  584. ->where('item_id', $data['id'])
  585. ->get()->toArray();
  586. $employee_id = array_unique(array_merge_recursive(array_column($file,'crt_id'), array_column($item['custom_fields'],'crt_id')));
  587. $map = (new EmployeeService())->getEmployeeMap($employee_id);
  588. $return = [];
  589. if(! empty($item['custom_fields'])){
  590. foreach ($item['custom_fields'] as $value){
  591. if($value['field_type'] == CustomFieldValue::type_two){
  592. $return[] = [
  593. 'id' => $value['id'],
  594. 'url' => $value['field_value'],
  595. 'name' => $value['field_name'],
  596. 'show_url' => $value['field_value_show'],
  597. 'type_name' => '项目',
  598. 'type_name_2' => CustomFieldValue::type[CustomFieldValue::type_two],
  599. 'type' => 'custom_fields',
  600. 'from' => ItemFile::from_one,
  601. 'crt_name' => $map[$value['crt_id']],
  602. 'crt_time' => date("Y-m-d", $value['crt_time']),
  603. 'crt_id' => $value['crt_id'],
  604. 'is_delivery' => $value['is_delivery'],
  605. ];
  606. }
  607. }
  608. }
  609. if(! empty($file)){
  610. $fileUploadService = new FileUploadService();
  611. foreach ($file as $value){
  612. $return[] = [
  613. 'id' => $value['id'],
  614. 'url' => $value['url'],
  615. 'name' => $value['name'],
  616. 'show_url' => $fileUploadService->getFileShow($value['url']),
  617. 'type_name' => ItemFile::from[$value['from']],
  618. 'type_name_2' => ItemFile::type[$value['type']],
  619. 'type' => 'item_file',
  620. 'from' => $value['from'],
  621. 'crt_name' => $map[$value['crt_id']],
  622. 'crt_time' => date("Y-m-d", $value['crt_time']),
  623. 'crt_id' => $value['crt_id'],
  624. 'is_delivery' => $value['is_delivery'],
  625. ];
  626. }
  627. }
  628. return $return;
  629. }
  630. public function itemCommon($data,$user, $field = []){
  631. if(empty($field)) $field = Item::$field;
  632. $model = Item::TopClear($user,$data);
  633. $model = $model->where('del_time',0)
  634. ->select($field)
  635. ->orderby('id', 'desc');
  636. if(! empty($data['title'])) $model->where('title', 'LIKE', '%'.$data['title'].'%');
  637. if(! empty($data['code'])) $model->where('code', 'LIKE', '%'.$data['code'].'%');
  638. if(! empty($data['id'])) $model->whereIn('id', $data['id']);
  639. if(! empty($data['state'])) $model->where('state', $data['state']);
  640. if(! empty($data['approval_state'])) $model->where('approval_state', $data['approval_state']);
  641. if(! empty($data['crt_time'][0]) && ! empty($data['crt_time'][1])) {
  642. $return = $this->changeDateToTimeStampAboutRange($data['crt_time']);
  643. $model->where('crt_time','>=',$return[0]);
  644. $model->where('crt_time','<=',$return[1]);
  645. }
  646. return $model;
  647. }
  648. public function itemCommon1($data,$user, $field = []){
  649. if(empty($field)) $field = Item::$field;
  650. $model = Item::TopAndEmployeeClear($user,$data);
  651. $model = $model->where('del_time',0)
  652. ->select($field)
  653. ->orderby('id', 'desc');
  654. if(! empty($data['title'])) $model->where('title', 'LIKE', '%'.$data['title'].'%');
  655. if(! empty($data['code'])) $model->where('code', 'LIKE', '%'.$data['code'].'%');
  656. if(! empty($data['id'])) $model->whereIn('id', $data['id']);
  657. if(! empty($data['state'])) $model->where('state', $data['state']);
  658. if(! empty($data['approval_state'])) $model->where('approval_state', $data['approval_state']);
  659. if(! empty($data['crt_time'][0]) && ! empty($data['crt_time'][1])) {
  660. $return = $this->changeDateToTimeStampAboutRange($data['crt_time']);
  661. $model->where('crt_time','>=',$return[0]);
  662. $model->where('crt_time','<=',$return[1]);
  663. }
  664. return $model;
  665. }
  666. public function itemList($data,$user){
  667. $select_tree_type = $user['select_tree_type'];
  668. if(empty($select_tree_type)){
  669. //工时
  670. $model = $this->itemCommon($data, $user);
  671. }else{
  672. //项目管理
  673. $model = $this->itemCommon1($data, $user);
  674. }
  675. $list = $this->limit($model,'',$data);
  676. $list = $this->fillData($list);
  677. return [true, $list];
  678. }
  679. public function itemRule(&$data, $user, $is_add = true){
  680. $data['top_depart_id'] = $user['top_depart_id'];
  681. if(empty($data['code'])) return [false, '项目编码不能为空'];
  682. if(empty($data['title'])) return [false, '项目名称不能为空'];
  683. if(! empty($data['start_time'])) $data['start_time'] = $this->changeDateToDate($data['start_time']);
  684. if(! empty($data['end_time'])) $data['end_time'] = $this->changeDateToDate($data['end_time'],true);
  685. $isMonthEnd = date('j', $data['end_time']) == date('t', $data['end_time']);
  686. if(! $isMonthEnd) return [false, '项目结束日期必须是当月最后一天'];
  687. if(empty($data['state'])) return [false, '项目状态不能为空'];
  688. if(! isset(Item::State_Type[$data['state']])) return [false, '项目状态不存在'];
  689. if(empty($data['man_list'])) return [false, '人员不能为空'];
  690. foreach ($data['man_list'] as $key => $value){
  691. if(empty($value['type'])) return [false, '类型不能为空'];
  692. if(empty($value['data_id'])) return [false, '人员不能为空'];
  693. $data['man_list'][$key]['top_depart_id'] = $data['top_depart_id'];
  694. }
  695. list($status, $msg) = $this->checkArrayRepeat($data['man_list'],'data_id','人员');
  696. if(! $status) return [false, $msg];
  697. if(empty($data['device_list'])) return [false, '设备不能为空'];
  698. if(isset($data['device_list'])){
  699. foreach ($data['device_list'] as $key => $value){
  700. if(empty($value['type'])) return [false, '类型不能为空'];
  701. if(empty($value['data_id'])) return [false, '设备ID不能为空'];
  702. $data['device_list'][$key]['top_depart_id'] = $data['top_depart_id'];
  703. }
  704. }
  705. list($status, $msg) = $this->checkArrayRepeat($data['device_list'],'data_id','设备');
  706. if(! $status) return [false, $msg];
  707. if(isset($data['budget'])){
  708. $res = $this->checkNumber($data['budget'],2,'non-negative');
  709. if(! $res['valid']) return [false,'预算:' . $res['error']];
  710. }
  711. if(! empty($data['item_attribute']) && ! isset(Item::Item_Attribute[$data['item_attribute']])) return [false, '项目属性不存在'];
  712. if($is_add){
  713. $bool = Item::where('code',$data['code'])
  714. ->where('top_depart_id', $data['top_depart_id'])
  715. ->where('del_time',0)
  716. ->exists();
  717. }else{
  718. if(empty($data['id'])) return [false,'ID不能为空'];
  719. $item = Item::where('id', $data['id'])
  720. ->where('del_time',0)
  721. ->first();
  722. if(empty($item)) return [false, '项目不存在或已被删除'];
  723. $bool = Item::where('code',$data['code'])
  724. ->where('top_depart_id', $data['top_depart_id'])
  725. ->where('id','<>', $data['id'])
  726. ->where('del_time',0)
  727. ->exists();
  728. if($user['select_tree_type'] && $item->approval_state == Item::TYPE_MINUS_ONE) return [false, '审核中不允许编辑'];
  729. }
  730. if($bool) return [false, '项目编码已存在'];
  731. //判断是否编辑 然后需要审核的 需要后 就更新状态 记录草稿
  732. if(! $is_add){
  733. $return = $this->checkIsChanged($data, $user);
  734. if(is_array($return)) {
  735. list($status, $msg) = $return;
  736. return [$status, $msg];
  737. }else{
  738. if($return) $data['draft'] = true;
  739. }
  740. }
  741. return [true, ''];
  742. }
  743. public function fillData($data){
  744. if(empty($data['data'])) return $data;
  745. $emp = (new EmployeeService())->getEmployeeMap(array_unique(array_merge_recursive(array_column($data['data'],'charge_id'), array_column($data['data'],'crt_id'))));
  746. foreach ($data['data'] as $key => $value){
  747. $data['data'][$key]['crt_time'] = $value['crt_time'] ? date('Y-m-d H:i:s',$value['crt_time']) : '';
  748. $data['data'][$key]['start_time'] = $value['start_time'] ? date('Y-m-d',$value['start_time']) : '';
  749. $data['data'][$key]['end_time'] = $value['end_time'] ? date('Y-m-d',$value['end_time']) : '';
  750. $data['data'][$key]['crt_name'] = $emp[$value['crt_id']] ?? '';
  751. $data['data'][$key]['charge_name'] = $emp[$value['charge_id']] ?? '';
  752. $data['data'][$key]['state_title'] = Item::State_Type[$value['state']] ?? "";
  753. $data['data'][$key]['approval_state_title'] = Item::State_Type[$value['approval_state']] ?? "";
  754. }
  755. return $data;
  756. }
  757. /**
  758. * 填充项目导出数据(主表与明细平铺)
  759. */
  760. public function fillDataForExport($data, $column, $user, &$return)
  761. {
  762. if (empty($data)) return;
  763. $dataArray = is_array($data) ? $data : $data->toArray();
  764. $mainIds = array_column($dataArray, 'id');
  765. // 1. 获取详情映射 [item_id => [ [type_title=>..., code_2=>..., title=>...], ... ]]
  766. $detailsMap = $this->getDetailsMap($mainIds, $user);
  767. // 2. 获取主表状态映射
  768. $stateMap = \App\Model\Item::State_Type;
  769. $attrMap = \App\Model\Item::Item_Attribute;
  770. $empMap = Employee::whereIn('id', array_unique(array_column($dataArray,'charge_id')))->get()->keyBy('id');
  771. foreach ($dataArray as $main) {
  772. $itemId = $main['id'];
  773. $details = $detailsMap[$itemId] ?? [];
  774. // 3. 提取并格式化主表信息
  775. $mainInfo = $main;
  776. $mainInfo['state_title'] = $stateMap[$main['state'] ?? ''] ?? '';
  777. $mainInfo['item_attribute_title'] = $attrMap[$main['item_attribute'] ?? ''] ?? '';
  778. $tmpEmp = $empMap[$main['charge_id']] ?? null;
  779. $mainInfo['charge_number'] = $tmpEmp ? $tmpEmp->number : '';
  780. $mainInfo['charge_name'] = $tmpEmp ? $tmpEmp->title : '';
  781. // 日期格式化
  782. $mainInfo['start_time'] = !empty($main['start_time']) ? date('Y-m-d', $main['start_time']) : '';
  783. $mainInfo['end_time'] = !empty($main['end_time']) ? date('Y-m-d', $main['end_time']) : '';
  784. if (empty($details)) {
  785. // 如果单据没有明细,保底出一行
  786. $tempRow = [];
  787. foreach ($column as $col) {
  788. $tempRow[] = $mainInfo[$col] ?? '';
  789. }
  790. $return[] = $tempRow;
  791. } else {
  792. // 4. 核心平铺逻辑:每一行明细都带上主表信息
  793. foreach ($details as $sub) {
  794. // 合并主表数据和详情数据(sub 中包含 type_title, code_2, title 等)
  795. $fullRowData = array_merge($mainInfo, $sub);
  796. $tempRow = [];
  797. foreach ($column as $col) {
  798. $tempRow[] = $fullRowData[$col] ?? '';
  799. }
  800. $return[] = $tempRow;
  801. }
  802. }
  803. }
  804. }
  805. /**
  806. * 获取明细聚合映射表
  807. */
  808. public function getDetailsMap($mainIds, $user)
  809. {
  810. $details = ItemDetails::where('del_time', 0)
  811. ->whereIn('item_id', $mainIds)
  812. ->where('top_depart_id', $user['top_depart_id'])
  813. ->get();
  814. if ($details->isEmpty()) return [];
  815. // 1. 批量提取关联 ID
  816. $empIds = $details->where('type', ItemDetails::type_one)->pluck('data_id')->unique();
  817. $devIds = $details->where('type', ItemDetails::type_two)->pluck('data_id')->unique();
  818. // 2. 批量获取档案 Map
  819. $empMap = Employee::whereIn('id', $empIds)->get()->keyBy('id');
  820. $devMap = Device::whereIn('id', $devIds)->get()->keyBy('id');
  821. $typeNames = ItemDetails::$type_name;
  822. $res = [];
  823. foreach ($details as $item) {
  824. $detailRow = [];
  825. $detailRow['type_title'] = $typeNames[$item->type] ?? '';
  826. // 根据类型处理影子列 code_2 和 名称 title
  827. if ($item->type == ItemDetails::type_one) {
  828. $emp = $empMap[$item->data_id] ?? null;
  829. $detailRow['code_2'] = $emp ? $emp->number : '';
  830. $detailRow['title_2'] = $emp ? $emp->title : '';
  831. } else {
  832. $dev = $devMap[$item->data_id] ?? null;
  833. $detailRow['code_2'] = $dev ? $dev->code : '';
  834. $detailRow['title_2'] = $dev ? $dev->title : '';
  835. }
  836. // 归档到对应的主表 ID 下
  837. $res[$item->item_id][] = $detailRow;
  838. }
  839. return $res;
  840. }
  841. public function itemNodeFinish($data, $user){
  842. if(empty($data['id'])) return [false, 'ID不能为空'];
  843. $model = ItemNode::where('id',$data['id'])
  844. ->where('del_time',0)
  845. ->first();
  846. if(empty($model)) return [false, '项目节点不存在或已被删除'];
  847. $oldData = $model->toArray();
  848. if($oldData['state'] == ItemNode::TYPE_THREE) return [false, '项目节点已完成,请勿重复操作'];
  849. try {
  850. DB::beginTransaction();
  851. $bool = $this->changedCommon($user, $oldData);
  852. if($bool) {
  853. // 更新项目状态
  854. $model->approval_state = ItemNode::TYPE_MINUS_ONE;
  855. $model->save();
  856. //触发审批流
  857. $this->createWorkFlow(['id' => $data['id'], 'state' => ItemNode::TYPE_THREE], $user, $model->getTable(), $oldData, 2);
  858. DB::commit();
  859. return [true, '更新记录成功,记录草稿,进入审批流程'];
  860. }
  861. $model->state = ItemNode::TYPE_THREE;
  862. $model->save();
  863. DB::commit();
  864. }catch (\Exception $exception){
  865. DB::rollBack();
  866. return [false,$exception->getMessage()];
  867. }
  868. return [true, ''];
  869. }
  870. //项目节点
  871. public function itemNodeEdit($data,$user){
  872. list($status,$msg) = $this->itemNodeRule($data, $user, false);
  873. if(!$status) return [$status,$msg];
  874. list($status, $msg) = $this->itemNodeEditSave($data, $user);
  875. return [$status, $msg];
  876. }
  877. public function itemNodeEditSave($data, $user){
  878. try {
  879. DB::beginTransaction();
  880. $model = ItemNode::where('id', $data['id'])->first();
  881. $tableName = $model->getTable();
  882. if(isset($data['draft'])){
  883. // 更新项目节点状态
  884. $model->approval_state = ItemNode::TYPE_MINUS_ONE;
  885. $model->save();
  886. //触发审批流
  887. $this->createWorkFlow($data, $user, $tableName, $model->toArray());
  888. DB::commit();
  889. return [true, '更新记录成功,记录草稿,进入审批流程'];
  890. }
  891. $old_employee_id = $model->charge_id;
  892. $tableName = $model->getTable();
  893. $model->title = $data['title'] ?? '';
  894. $model->mark = $data['mark'] ?? "";
  895. $model->start_time = $data['start_time'] ?? 0;
  896. $model->end_time = $data['end_time'] ?? 0;
  897. $model->is_delivery_required = $data['is_delivery_required'] ?? 0;
  898. $model->entrust_type = $data['entrust_type'] ?? 0;
  899. $model->charge_id = $data['charge_id'] ?? 0;
  900. $model->node_id = $data['node_id'] ?? 0;
  901. $model->node_weight = $data['node_weight'] ?? 0;
  902. $model->is_review_required = $data['is_review_required'] ?? 0;
  903. $model->review_id = $data['review_id'] ?? 0;
  904. $model->priority_id = $data['priority_id'] ?? 0;
  905. $model->state = $data['state'] ?? 0;
  906. $model->save();
  907. $time = time();
  908. ItemNodeDetails::where('del_time',0)
  909. ->where('item_node_id', $model->id)
  910. ->update(['del_time' => $time]);
  911. $this->saveNodeDetail($model->id, $time, $data);
  912. // 人员项目节点表
  913. $this->saveNodeEmployee($model->id, $time, $data, $user, $old_employee_id);
  914. list($status, $msg) = CustomFieldSettingService::syncCustomFieldData($model->id, $tableName, $data, $user);
  915. if (! $status) {
  916. DB::rollBack();
  917. return [false, $msg];
  918. }
  919. DB::commit();
  920. }catch (\Exception $exception){
  921. DB::rollBack();
  922. return [false,$exception->getMessage()];
  923. }
  924. return [true, ''];
  925. }
  926. public function itemNodeAdd($data,$user){
  927. list($status,$msg) = $this->itemNodeRule($data, $user);
  928. if(!$status) return [$status,$msg];
  929. try {
  930. DB::beginTransaction();
  931. $model = new ItemNode();
  932. $model->item_id = $data['item_id'] ?? 0;
  933. $tableName = $model->getTable();
  934. $model->code = $this->generateBillNo([
  935. 'top_depart_id' => $user['top_depart_id'],
  936. 'type' => ItemNode::Order_type,
  937. 'period' => date("Ym", time())
  938. ]);
  939. $model->title = $data['title'] ?? '';
  940. $model->mark = $data['mark'] ?? "";
  941. $model->start_time = $data['start_time'] ?? 0;
  942. $model->end_time = $data['end_time'] ?? 0;
  943. $model->is_delivery_required = $data['is_delivery_required'] ?? 0;
  944. $model->entrust_type = $data['entrust_type'] ?? 0;
  945. $model->charge_id = $data['charge_id'] ?? 0;
  946. $model->node_id = $data['node_id'] ?? 0;
  947. $model->node_weight = $data['node_weight'] ?? 0;
  948. $model->is_review_required = $data['is_review_required'] ?? 0;
  949. $model->review_id = $data['review_id'] ?? 0;
  950. $model->priority_id = $data['priority_id'] ?? 0;
  951. $model->crt_id = $user['id'];
  952. $model->top_depart_id = $data['top_depart_id'];
  953. $model->state = $data['state'] ?? 0;
  954. $model->save();
  955. $time = time();
  956. $this->saveNodeDetail($model->id, $time, $data);
  957. // 人员项目节点表
  958. $this->saveNodeEmployee($model->id, $time, $data, $user);
  959. list($status, $msg) = CustomFieldSettingService::syncCustomFieldData($model->id, $tableName, $data, $user);
  960. if (!$status) {
  961. DB::rollBack();
  962. return [false, $msg];
  963. }
  964. DB::commit();
  965. }catch (\Exception $exception){
  966. DB::rollBack();
  967. return [false,$exception->getMessage()];
  968. }
  969. return [true, ''];
  970. }
  971. public function checkItemNodeIsChanged($data, $user) {
  972. // 1. 获取旧数据
  973. list($status, $oldData) = $this->itemNodeDetail($data, $user);
  974. if (!$status) return [false, $oldData];
  975. //创建人 直接通过
  976. $bool = $this->changedCommon($user, $oldData);
  977. if(! $bool) return false;
  978. // --- 表头基础字段比对 ---
  979. $fields = [
  980. 'code', 'title', 'mark', 'state', 'budget',
  981. 'charge_id', 'is_delivery_required', 'start_time', 'end_time',
  982. 'entrust_type', 'is_review_required', 'review_id', 'priority_id', 'node_id', 'node_weight'
  983. ];
  984. foreach ($fields as $field) {
  985. $oldValue = $oldData[$field] ?? '';
  986. $newValue = $data[$field] ?? '';
  987. // 日期格式对齐(确保 Y-m-d 格式一致)
  988. if (in_array($field, ['start_time', 'end_time'])) {
  989. $newValue = !empty($newValue) ? date("Y-m-d", $newValue) : "";
  990. }
  991. if ($oldValue != $newValue) {
  992. return true;
  993. }
  994. }
  995. // --- 自定义字段比对 ---
  996. if (isset($data['custom_fields']) && is_array($data['custom_fields'])) {
  997. $oldCustomMap = array_column($oldData['custom_fields'] ?? [], 'field_value', 'id');
  998. foreach ($data['custom_fields'] as $customField) {
  999. $fid = $customField['id'] ?? 0;
  1000. $newVal = $customField['field_value'] ?? '';
  1001. $oldVal = $oldCustomMap[$fid] ?? '';
  1002. if ($oldVal != $newVal) {
  1003. return true;
  1004. }
  1005. }
  1006. }
  1007. // --- 表体:人员名单比对 (man_list) ---
  1008. $oldMans = collect($oldData['man_list'] ?? [])->pluck('data_id')->sort()->values()->toArray();
  1009. $newMans = collect($data['man_list'] ?? [])->pluck('data_id')->sort()->values()->toArray();
  1010. if ($oldMans !== $newMans) {
  1011. return true;
  1012. }
  1013. // --- 表体:设备名单比对 (device_list) ---
  1014. $oldDevices = collect($oldData['device_list'] ?? [])->pluck('data_id')->sort()->values()->toArray();
  1015. $newDevices = collect($data['device_list'] ?? [])->pluck('data_id')->sort()->values()->toArray();
  1016. if ($oldDevices !== $newDevices) {
  1017. return true;
  1018. }
  1019. return false; // 没有任何变动
  1020. }
  1021. private function saveNodeEmployee($id, $time, $data, $user, $old_employee_id = 0){
  1022. if($old_employee_id != $data['charge_id']){
  1023. ItemNodeEmployee::where('del_time',0)
  1024. ->where('item_node_id', $id)
  1025. ->where('data_id', $old_employee_id)
  1026. ->delete();
  1027. ItemNodeEmployee::insert([
  1028. 'item_node_id' => $id,
  1029. 'item_id' => $data['item_id'],
  1030. 'data_id' => $data['charge_id'],
  1031. 'top_depart_id' => $user['top_depart_id'],
  1032. 'crt_time' => $time
  1033. ]);
  1034. }
  1035. }
  1036. private function saveNodeDetail($id, $time, $data){
  1037. if(! empty($data['man_list'])){
  1038. $unit = [];
  1039. foreach ($data['man_list'] as $value){
  1040. $unit[] = [
  1041. 'item_id' => $data['item_id'],
  1042. 'item_node_id' => $id,
  1043. 'type' => $value['type'],
  1044. 'data_id' => $value['data_id'],
  1045. 'crt_time' => $time,
  1046. 'top_depart_id' => $value['top_depart_id'],
  1047. ];
  1048. }
  1049. if(! empty($unit)) ItemNodeDetails::insert($unit);
  1050. }
  1051. if(! empty($data['device_list'])){
  1052. $receipt = [];
  1053. foreach ($data['device_list'] as $value){
  1054. $receipt[] = [
  1055. 'item_id' => $data['item_id'],
  1056. 'item_node_id' => $id,
  1057. 'type' => $value['type'],
  1058. 'data_id' => $value['data_id'],
  1059. 'crt_time' => $time,
  1060. 'top_depart_id' => $value['top_depart_id'],
  1061. ];
  1062. }
  1063. if(! empty($receipt)) ItemNodeDetails::insert($receipt);
  1064. }
  1065. }
  1066. private function getNodeDetail($id){
  1067. $data = ItemNodeDetails::where('del_time',0)
  1068. ->where('item_node_id', $id)
  1069. ->get()->toArray();
  1070. $id = $id2 = [];
  1071. foreach ($data as $value){
  1072. if($value['type'] == ItemNodeDetails::type_one) {
  1073. $id[] = $value['data_id'];
  1074. }else{
  1075. $id2[] = $value['data_id'];
  1076. }
  1077. }
  1078. $map = Employee::whereIn('id', $id)->select('title','id','number')->get()->toArray();
  1079. $map = array_column($map,null,'id');
  1080. $map2 = Device::whereIn('id', $id2)->select('code','id','title','type')->get()->toArray();
  1081. $map2 = array_column($map2,null,'id');
  1082. $unit = $receipt = [];
  1083. foreach ($data as $value){
  1084. if($value['type'] == ItemNodeDetails::type_one) {
  1085. $tmp = $map[$value['data_id']] ?? [];
  1086. $unit[] = [
  1087. 'type' => $value['type'],
  1088. 'data_id' => $value['data_id'],
  1089. 'data_title' => $tmp['title'],
  1090. 'data_code' => $tmp['number'],
  1091. ];
  1092. }else{
  1093. $tmp = $map2[$value['data_id']] ?? [];
  1094. $receipt[] = [
  1095. 'type' => $value['type'],
  1096. 'data_id' => $value['data_id'],
  1097. 'data_title' => $tmp['title'] ?? "",
  1098. 'data_code' => $tmp['code'] ?? "",
  1099. 'type_title' => Device::$type[$tmp['type']],
  1100. ];
  1101. }
  1102. }
  1103. return [
  1104. 'man_list' => $unit,
  1105. 'device_list' => $receipt,
  1106. ];
  1107. }
  1108. public function itemNodeDel($data){
  1109. if($this->isEmpty($data,'id')) return [false,'请选择数据!'];
  1110. list($status, $msg) = $this->checkDelete($data,2);
  1111. if(! $status) return [false, $msg];
  1112. try {
  1113. DB::beginTransaction();
  1114. $time = time();
  1115. ItemNode::where('del_time',0)
  1116. ->whereIn('id',$data['id'])
  1117. ->update(['del_time' => $time]);
  1118. ItemNodeDetails::where('del_time',0)
  1119. ->whereIn('item_node_id', $data['id'])
  1120. ->update(['del_time' => $time]);
  1121. DB::commit();
  1122. }catch (\Exception $exception){
  1123. DB::rollBack();
  1124. return [false,$exception->getMessage()];
  1125. }
  1126. return [true, ''];
  1127. }
  1128. public function itemNodeDetail($data, $user){
  1129. if($this->isEmpty($data,'id')) return [false,'请选择数据!'];
  1130. $customer = ItemNode::where('del_time',0)
  1131. ->withCustomData($user)
  1132. ->where('id',$data['id'])
  1133. ->first();
  1134. if(empty($customer)) return [false,'项目节点不存在或已被删除'];
  1135. $customer->getFormattedCustomFields();// 自定义数据附件处理
  1136. $customer = $customer->toArray();
  1137. $customer['start_time'] = ! empty($customer['start_time']) ? date("Y-m-d", $customer['start_time']) : "";
  1138. $customer['end_time'] = ! empty($customer['end_time']) ? date("Y-m-d", $customer['end_time']) : "";
  1139. $customer['crt_name'] = Employee::where('id',$customer['crt_id'])->value('title');
  1140. $customer['charge_name'] = Employee::where('id',$customer['charge_id'])->value('title');
  1141. $customer['crt_time'] = $customer['crt_time'] ? date("Y-m-d H:i:s",$customer['crt_time']): '';
  1142. $customer['state_title'] = ItemNode::State_Type[$customer['state']] ?? '';
  1143. $customer['approval_state_title'] = ItemNode::State_Type[$customer['approval_state']] ?? '';
  1144. $tag = (new TagService())->getTagMap(array_unique([$customer['priority_id'], $customer['node_id'] ]));
  1145. $priority_tmp = $tag[$customer['priority_id']] ?? [];
  1146. $customer['priority_title'] = $priority_tmp['title'] ?? '';
  1147. $customer['priority_code'] = $priority_tmp['code'] ?? '';
  1148. $node_tmp = $tag[$customer['node_id']] ?? [];
  1149. $customer['node_title'] = $node_tmp['title'] ?? '';
  1150. $customer['node_code'] = $node_tmp['code'] ?? '';
  1151. $item = Item::where('id', $customer['item_id'])->first();
  1152. $customer['item_code'] = $item->code;
  1153. $customer['item_title'] = $item->title;
  1154. $customer['review_title'] = WorkFlowTemplates::where('id', $customer['review_id'])->value('title') ?? "";
  1155. $details = $this->getNodeDetail($data['id']);
  1156. $customer = array_merge($customer, $details);
  1157. return [true, $customer];
  1158. }
  1159. public function itemNodeDetailBoard($data, $user){
  1160. list($status, $msg) = $this->itemNodeDetail($data, $user);
  1161. if(! $status) return [false, $msg];
  1162. $customer = $msg;
  1163. //节点信息
  1164. $customer['mission_list'] = $this->itemMissionBoard($data, $user);
  1165. //文件归档
  1166. $customer['file_list'] = $this->getMissionFile($data, $user, $customer);
  1167. return [true, $customer];
  1168. }
  1169. public function itemMissionBoard($data,$user){
  1170. $model = ItemNodeMission::TopAndEmployeeClear($user, $data);
  1171. $list = $model->where('del_time',0)
  1172. ->where('item_node_id', $data['id'])
  1173. ->select('*')
  1174. ->orderby('upd_time', 'desc')
  1175. ->orderby('id', 'desc')
  1176. ->get()->toArray();
  1177. $emp = (new EmployeeService())->getEmployeeMap(array_unique(array_merge_recursive(array_column($list,'charge_id'), array_column($list,'crt_id'))));
  1178. $tag = (new TagService())->getTagMap(array_unique(array_merge_recursive(array_column($list,'priority_id'),array_column($list,'mission_id'))));
  1179. foreach ($list as $key => $value){
  1180. $list[$key]['crt_time'] = $value['crt_time'] ? date('Y-m-d H:i:s',$value['crt_time']) : '';
  1181. $list[$key]['start_time'] = $value['start_time'] ? date('Y-m-d',$value['start_time']) : '';
  1182. $list[$key]['end_time'] = $value['end_time'] ? date('Y-m-d',$value['end_time']) : '';
  1183. $list[$key]['crt_name'] = $emp[$value['crt_id']] ?? '';
  1184. $list[$key]['charge_name'] = $emp[$value['charge_id']] ?? '';
  1185. $list[$key]['state_title'] = ItemNodeMission::State_Type[$value['state']] ?? "";
  1186. $list[$key]['approval_state_title'] = ItemNodeMission::State_Type[$value['approval_state']] ?? "";
  1187. $priority_tmp = $tag[$value['priority_id']] ?? [];
  1188. $list[$key]['priority_title'] = $priority_tmp['title'] ?? '';
  1189. $list[$key]['priority_code'] = $priority_tmp['code'] ?? '';
  1190. $node_tmp = $tag[$value['mission_id']] ?? [];
  1191. $list[$key]['mission_title'] = $node_tmp['title'] ?? '';
  1192. $list[$key]['mission_code'] = $node_tmp['code'] ?? '';
  1193. $list[$key]['progress'] = rand(1, 100); //todo
  1194. }
  1195. return $list;
  1196. }
  1197. public function getMissionFile($data, $user, $item){
  1198. $file = ItemFile::where('del_time',0)
  1199. ->where('item_node_id', $data['id'])
  1200. ->get()->toArray();
  1201. $employee_id = array_unique(array_merge_recursive(array_column($file,'crt_id'), array_column($item['custom_fields'],'crt_id')));
  1202. $map = (new EmployeeService())->getEmployeeMap($employee_id);
  1203. $return = [];
  1204. if(! empty($item['custom_fields'])){
  1205. foreach ($item['custom_fields'] as $value){
  1206. if($value['field_type'] == CustomFieldValue::type_two){
  1207. $return[] = [
  1208. 'id' => $value['id'],
  1209. 'url' => $value['field_value'],
  1210. 'name' => $value['field_name'],
  1211. 'show_url' => $value['field_value_show'],
  1212. 'type_name' => '节点',
  1213. 'type_name_2' => CustomFieldValue::type[CustomFieldValue::type_two],
  1214. 'type' => 'custom_fields',
  1215. 'from' => ItemFile::type_two,
  1216. 'crt_name' => $map[$value['crt_id']],
  1217. 'crt_time' => date("Y-m-d", $value['crt_time']),
  1218. 'crt_id' => $value['crt_id'],
  1219. 'is_delivery' => $value['is_delivery'],
  1220. ];
  1221. }
  1222. }
  1223. }
  1224. if(! empty($file)){
  1225. $fileUploadService = new FileUploadService();
  1226. foreach ($file as $value){
  1227. $return[] = [
  1228. 'id' => $value['id'],
  1229. 'url' => $value['url'],
  1230. 'name' => $value['name'],
  1231. 'show_url' => $fileUploadService->getFileShow($value['url']),
  1232. 'type_name' => ItemFile::from[$value['from']],
  1233. 'type_name_2' => ItemFile::type[$value['type']],
  1234. 'type' => 'item_file',
  1235. 'from' => $value['from'],
  1236. 'crt_name' => $map[$value['crt_id']],
  1237. 'crt_time' => date("Y-m-d", $value['crt_time']),
  1238. 'crt_id' => $value['crt_id'],
  1239. 'is_delivery' => $value['is_delivery'],
  1240. ];
  1241. }
  1242. }
  1243. return $return;
  1244. }
  1245. public function itemNodeCommon($data,$user, $field = []){
  1246. if(empty($field)) $field = ItemNode::$field;
  1247. $model = ItemNode::TopAndEmployeeClear($user,$data);
  1248. $model = $model->where('del_time',0)
  1249. ->select($field)
  1250. ->orderby('id', 'desc');
  1251. if(! empty($data['title'])) $model->where('title', 'LIKE', '%'.$data['title'].'%');
  1252. if(! empty($data['code'])) $model->where('code', 'LIKE', '%'.$data['code'].'%');
  1253. if(! empty($data['id'])) $model->whereIn('id', $data['id']);
  1254. if(isset($data['state'])) $model->where('state', $data['state']);
  1255. if(isset($data['approval_stateapproval_state'])) $model->where('approval_state', $data['approval_state']);
  1256. if(! empty($data['crt_time'][0]) && ! empty($data['crt_time'][1])) {
  1257. $return = $this->changeDateToTimeStampAboutRange($data['crt_time']);
  1258. $model->where('crt_time','>=',$return[0]);
  1259. $model->where('crt_time','<=',$return[1]);
  1260. }
  1261. return $model;
  1262. }
  1263. public function itemNodeList($data,$user){
  1264. $model = $this->itemNodeCommon($data, $user);
  1265. $list = $this->limit($model,'',$data);
  1266. $list = $this->fillNodeData($list);
  1267. return [true, $list];
  1268. }
  1269. public function itemNodeRule(&$data, $user, $is_add = true){
  1270. $data['top_depart_id'] = $user['top_depart_id'];
  1271. if(empty($data['item_id'])) return [false, '项目ID不能为空'];
  1272. $item = Item::where('id', $data['item_id'])->where('del_time',0)->first();
  1273. if(empty($item)) return [false, '项目不存在或已被删除'];
  1274. $item = $item->toArray();
  1275. if(empty($data['title'])) return [false, '节点名称不能为空'];
  1276. if(! empty($data['start_time'])) $data['start_time'] = $this->changeDateToDate($data['start_time']);
  1277. if(! empty($data['end_time'])) $data['end_time'] = $this->changeDateToDate($data['end_time'],true);
  1278. $itemStartTime = $item['start_time'];
  1279. $itemEndTime = $item['end_time'];
  1280. if (!empty($data['start_time'])) {
  1281. $inputStartTime = $data['start_time'];
  1282. if ($itemStartTime > 0 && $inputStartTime < $itemStartTime) {
  1283. return [false, '节点开始时间不能早于项目开始时间:' . date('Y-m-d', $itemStartTime)];
  1284. }
  1285. }
  1286. if (!empty($data['end_time'])) {
  1287. $inputEndTime = $data['end_time'];
  1288. if ($itemEndTime > 0 && $inputEndTime > $itemEndTime) {
  1289. return [false, '节点结束时间不能晚于项目结束时间:' . date('Y-m-d', $itemEndTime)];
  1290. }
  1291. }
  1292. if(! empty($data['man_list'])) {
  1293. foreach ($data['man_list'] as $key => $value){
  1294. if(empty($value['type'])) return [false, '类型不能为空'];
  1295. if(empty($value['data_id'])) return [false, '人员不能为空'];
  1296. $data['man_list'][$key]['top_depart_id'] = $data['top_depart_id'];
  1297. }
  1298. list($status, $msg) = $this->checkArrayRepeat($data['man_list'],'data_id','人员');
  1299. if(! $status) return [false, $msg];
  1300. }
  1301. if(! empty($data['device_list'])) {
  1302. foreach ($data['device_list'] as $key => $value){
  1303. if(empty($value['type'])) return [false, '类型不能为空'];
  1304. if(empty($value['data_id'])) return [false, '设备ID不能为空'];
  1305. $data['device_list'][$key]['top_depart_id'] = $data['top_depart_id'];
  1306. }
  1307. list($status, $msg) = $this->checkArrayRepeat($data['device_list'],'data_id','设备');
  1308. if(! $status) return [false, $msg];
  1309. }
  1310. if($is_add){
  1311. $bool = ItemNode::where('item_id',$data['item_id'])
  1312. ->where('top_depart_id', $data['top_depart_id'])
  1313. ->where('title', $data['title'])
  1314. ->where('del_time',0)
  1315. ->exists();
  1316. }else{
  1317. if(empty($data['id'])) return [false,'ID不能为空'];
  1318. $item = ItemNode::where('id', $data['id'])
  1319. ->where('del_time',0)
  1320. ->first();
  1321. if(empty($item)) return [false, '项目节点不存在或已被删除'];
  1322. $bool = ItemNode::where('title', $data['title'])
  1323. ->where('item_id',$data['item_id'])
  1324. ->where('top_depart_id', $data['top_depart_id'])
  1325. ->where('id','<>',$data['id'])
  1326. ->where('del_time',0)
  1327. ->exists();
  1328. }
  1329. if($bool) return [false, '该项目下节点名称已存在'];
  1330. //判断是否编辑 然后需要审核的 需要后 就更新状态 记录草稿
  1331. if(! $is_add){
  1332. $return = $this->checkItemNodeIsChanged($data, $user);
  1333. if(is_array($return)) {
  1334. list($status, $msg) = $return;
  1335. return [$status, $msg];
  1336. }else{
  1337. if($return) $data['draft'] = true;
  1338. }
  1339. }
  1340. return [true, ''];
  1341. }
  1342. public function fillNodeData($data){
  1343. if(empty($data['data'])) return $data;
  1344. $emp = (new EmployeeService())->getEmployeeMap(array_unique(array_merge_recursive(array_column($data['data'],'charge_id'), array_column($data['data'],'crt_id'))));
  1345. $tag = (new TagService())->getTagMap(array_unique(array_merge_recursive(array_column($data['data'],'priority_id'),array_column($data['data'],'node_id'))));
  1346. $item = Item::whereIn('id', array_unique(array_column($data['data'],'item_id')))
  1347. ->pluck('title','id')
  1348. ->toArray();
  1349. foreach ($data['data'] as $key => $value){
  1350. $data['data'][$key]['crt_time'] = $value['crt_time'] ? date('Y-m-d H:i:s',$value['crt_time']) : '';
  1351. $data['data'][$key]['start_time'] = $value['start_time'] ? date('Y-m-d',$value['start_time']) : '';
  1352. $data['data'][$key]['end_time'] = $value['end_time'] ? date('Y-m-d',$value['end_time']) : '';
  1353. $data['data'][$key]['crt_name'] = $emp[$value['crt_id']] ?? '';
  1354. $data['data'][$key]['charge_name'] = $emp[$value['charge_id']] ?? '';
  1355. $data['data'][$key]['state_title'] = ItemNode::State_Type[$value['state']] ?? "";
  1356. $data['data'][$key]['approval_state_title'] = ItemNode::State_Type[$value['approval_state']] ?? "";
  1357. $priority_tmp = $tag[$value['priority_id']] ?? [];
  1358. $data['data'][$key]['priority_title'] = $priority_tmp['title'] ?? '';
  1359. $data['data'][$key]['priority_code'] = $priority_tmp['code'] ?? '';
  1360. $node_tmp = $tag[$value['node_id']] ?? [];
  1361. $data['data'][$key]['node_title'] = $node_tmp['title'] ?? '';
  1362. $data['data'][$key]['node_code'] = $node_tmp['code'] ?? '';
  1363. $data['data'][$key]['item_title'] = $item[$value['item_id']] ?? '';
  1364. $data['data'][$key]['progress'] = rand(1,100); // todo
  1365. }
  1366. return $data;
  1367. }
  1368. public function itemNodeMissionFinish($data, $user){
  1369. if(empty($data['id'])) return [false, 'ID不能为空'];
  1370. $model = ItemNodeMission::where('id',$data['id'])
  1371. ->where('del_time',0)
  1372. ->first();
  1373. if(empty($model)) return [false, '任务不存在或已被删除'];
  1374. $oldData = $model->toArray();
  1375. if($oldData['state'] == ItemNodeMission::TYPE_THREE) return [false, '任务已完成,请勿重复操作'];
  1376. try {
  1377. DB::beginTransaction();
  1378. $bool = $this->changedCommon($user, $oldData);
  1379. if($bool) {
  1380. // 更新项目状态
  1381. $model->approval_state = ItemNodeMission::TYPE_MINUS_ONE;
  1382. $model->save();
  1383. //触发审批流
  1384. $this->createWorkFlow(['id' => $data['id'], 'state' => ItemNodeMission::TYPE_THREE], $user, $model->getTable(), $oldData, 2);
  1385. DB::commit();
  1386. return [true, '更新记录成功,记录草稿,进入审批流程'];
  1387. }
  1388. $model->state = ItemNodeMission::TYPE_THREE;
  1389. $model->save();
  1390. DB::commit();
  1391. }catch (\Exception $exception){
  1392. DB::rollBack();
  1393. return [false,$exception->getMessage()];
  1394. }
  1395. return [true, ''];
  1396. }
  1397. //项目节点任务
  1398. public function itemNodeMissionEdit($data,$user){
  1399. list($status,$msg) = $this->itemNodeMissionRule($data, $user, false);
  1400. if(!$status) return [$status,$msg];
  1401. list($status, $msg) = $this->itemNodeMissionEditSave($data, $user);
  1402. return [$status, $msg];
  1403. }
  1404. public function itemNodeMissionEditSave($data, $user){
  1405. try {
  1406. DB::beginTransaction();
  1407. $model = ItemNodeMission::where('id', $data['id'])->first();
  1408. $tableName = $model->getTable();
  1409. if(isset($data['draft'])){
  1410. // 更新项目节点状态
  1411. $model->approval_state = ItemNodeMission::TYPE_MINUS_ONE;
  1412. $model->save();
  1413. //触发审批流
  1414. $this->createWorkFlow($data, $user, $tableName, $model->toArray());
  1415. DB::commit();
  1416. return [true, '更新记录成功,记录草稿,进入审批流程'];
  1417. }
  1418. $old_employee_id = $model->charge_id;
  1419. $tableName = $model->getTable();
  1420. $model->title = $data['title'] ?? '';
  1421. $model->mark = $data['mark'] ?? "";
  1422. $model->start_time = $data['start_time'] ?? 0;
  1423. $model->end_time = $data['end_time'] ?? 0;
  1424. $model->is_delivery_required = $data['is_delivery_required'] ?? 0;
  1425. $model->entrust_type = $data['entrust_type'] ?? 0;
  1426. $model->charge_id = $data['charge_id'] ?? 0;
  1427. $model->mission_id = $data['mission_id'] ?? 0;
  1428. $model->mission_weight = $data['mission_weight'] ?? 0;
  1429. $model->is_review_required = $data['is_review_required'] ?? 0;
  1430. $model->review_id = $data['review_id'] ?? 0;
  1431. $model->priority_id = $data['priority_id'] ?? 0;
  1432. $model->state = $data['state'] ?? 0;
  1433. $model->save();
  1434. $time = time();
  1435. ItemNodeMissionDetails::where('del_time',0)
  1436. ->where('item_node_mission_id', $model->id)
  1437. ->update(['del_time' => $time]);
  1438. $this->saveNodeMissionDetail($model->id, $time, $data);
  1439. // 人员项目节点任务表
  1440. $this->saveNodeMissionEmployee($model->id, $time, $data, $user, $old_employee_id);
  1441. list($status, $msg) = CustomFieldSettingService::syncCustomFieldData($model->id, $tableName, $data, $user);
  1442. if (! $status) {
  1443. DB::rollBack();
  1444. return [false, $msg];
  1445. }
  1446. DB::commit();
  1447. }catch (\Exception $exception){
  1448. DB::rollBack();
  1449. return [false,$exception->getMessage()];
  1450. }
  1451. return [true, ''];
  1452. }
  1453. public function itemNodeMissionAdd($data,$user){
  1454. list($status,$msg) = $this->itemNodeMissionRule($data, $user);
  1455. if(!$status) return [$status,$msg];
  1456. try {
  1457. DB::beginTransaction();
  1458. $model = new ItemNodeMission();
  1459. $model->item_id = $data['item_id'] ?? 0;
  1460. $model->item_node_id = $data['item_node_id'] ?? 0;
  1461. $model->parent_item_node_mission_id = $data['parent_item_node_mission_id'] ?? 0;
  1462. $tableName = $model->getTable();
  1463. $model->code = $this->generateBillNo([
  1464. 'top_depart_id' => $user['top_depart_id'],
  1465. 'type' => ItemNodeMission::Order_type,
  1466. 'period' => date("Ym", time())
  1467. ]);
  1468. $model->title = $data['title'] ?? '';
  1469. $model->mark = $data['mark'] ?? "";
  1470. $model->start_time = $data['start_time'] ?? 0;
  1471. $model->end_time = $data['end_time'] ?? 0;
  1472. $model->is_delivery_required = $data['is_delivery_required'] ?? 0;
  1473. $model->entrust_type = $data['entrust_type'] ?? 0;
  1474. $model->charge_id = $data['charge_id'] ?? 0;
  1475. $model->mission_id = $data['mission_id'] ?? 0;
  1476. $model->mission_weight = $data['mission_weight'] ?? 0;
  1477. $model->is_review_required = $data['is_review_required'] ?? 0;
  1478. $model->review_id = $data['review_id'] ?? 0;
  1479. $model->priority_id = $data['priority_id'] ?? 0;
  1480. $model->state = $data['state'] ?? 0;
  1481. $model->crt_id = $user['id'];
  1482. $model->top_depart_id = $data['top_depart_id'];
  1483. $model->save();
  1484. $time = time();
  1485. $this->saveNodeMissionDetail($model->id, $time, $data);
  1486. // 人员项目节点任务表
  1487. $this->saveNodeMissionEmployee($model->id, $time, $data, $user);
  1488. list($status, $msg) = CustomFieldSettingService::syncCustomFieldData($model->id, $tableName, $data, $user);
  1489. if (!$status) {
  1490. DB::rollBack();
  1491. return [false, $msg];
  1492. }
  1493. DB::commit();
  1494. }catch (\Exception $exception){
  1495. DB::rollBack();
  1496. return [false,$exception->getMessage()];
  1497. }
  1498. return [true, ''];
  1499. }
  1500. private function saveNodeMissionEmployee($id, $time, $data, $user, $old_employee_id = 0){
  1501. if($old_employee_id != $data['charge_id']){
  1502. ItemNodeMissionEmployee::where('del_time',0)
  1503. ->where('item_node_mission_id', $id)
  1504. ->where('data_id', $old_employee_id)
  1505. ->delete();
  1506. ItemNodeMissionEmployee::insert([
  1507. 'item_node_mission_id' => $id,
  1508. 'item_node_id' => $data['item_node_id'],
  1509. 'item_id' => $data['item_id'],
  1510. 'data_id' => $data['charge_id'],
  1511. 'top_depart_id' => $user['top_depart_id'],
  1512. 'crt_time' => $time
  1513. ]);
  1514. }
  1515. }
  1516. private function saveNodeMissionDetail($id, $time, $data){
  1517. if(! empty($data['man_list'])){
  1518. $unit = [];
  1519. foreach ($data['man_list'] as $value){
  1520. $unit[] = [
  1521. 'item_id' => $data['item_id'],
  1522. 'item_node_id' => $data['item_node_id'],
  1523. 'item_node_mission_id' => $id,
  1524. 'type' => $value['type'],
  1525. 'data_id' => $value['data_id'],
  1526. 'crt_time' => $time,
  1527. 'top_depart_id' => $value['top_depart_id'],
  1528. ];
  1529. }
  1530. if(! empty($unit)) ItemNodeMissionDetails::insert($unit);
  1531. }
  1532. if(! empty($data['device_list'])){
  1533. $receipt = [];
  1534. foreach ($data['device_list'] as $value){
  1535. $receipt[] = [
  1536. 'item_id' => $data['item_id'],
  1537. 'item_node_id' => $data['item_node_id'],
  1538. 'item_node_mission_id' => $id,
  1539. 'type' => $value['type'],
  1540. 'data_id' => $value['data_id'],
  1541. 'crt_time' => $time,
  1542. 'top_depart_id' => $value['top_depart_id'],
  1543. ];
  1544. }
  1545. if(! empty($receipt)) ItemNodeMissionDetails::insert($receipt);
  1546. }
  1547. }
  1548. private function getNodeMissionDetail($id){
  1549. $data = ItemNodeMissionDetails::where('del_time',0)
  1550. ->where('item_node_mission_id', $id)
  1551. ->get()->toArray();
  1552. $id = $id2 = [];
  1553. foreach ($data as $value){
  1554. if($value['type'] == ItemNodeMissionDetails::type_one) {
  1555. $id[] = $value['data_id'];
  1556. }else{
  1557. $id2[] = $value['data_id'];
  1558. }
  1559. }
  1560. $map = Employee::whereIn('id', $id)->select('title','id','number')->get()->toArray();
  1561. $map = array_column($map,null,'id');
  1562. $map2 = Device::whereIn('id', $id2)->select('code','id','title','type')->get()->toArray();
  1563. $map2 = array_column($map2,null,'id');
  1564. $unit = $receipt = [];
  1565. foreach ($data as $value){
  1566. if($value['type'] == ItemNodeMissionDetails::type_one) {
  1567. $tmp = $map[$value['data_id']] ?? [];
  1568. $unit[] = [
  1569. 'type' => $value['type'],
  1570. 'data_id' => $value['data_id'],
  1571. 'data_title' => $tmp['title'],
  1572. 'data_code' => $tmp['number'],
  1573. ];
  1574. }else{
  1575. $tmp = $map2[$value['data_id']] ?? [];
  1576. $receipt[] = [
  1577. 'type' => $value['type'],
  1578. 'data_id' => $value['data_id'],
  1579. 'data_title' => $tmp['title'] ?? "",
  1580. 'data_code' => $tmp['code'] ?? "",
  1581. 'type_title' => Device::$type[$tmp['type']],
  1582. ];
  1583. }
  1584. }
  1585. return [
  1586. 'man_list' => $unit,
  1587. 'device_list' => $receipt,
  1588. ];
  1589. }
  1590. public function itemNodeMissionDel($data){
  1591. if($this->isEmpty($data,'id')) return [false,'请选择数据!'];
  1592. list($status, $msg) = $this->checkDelete($data,3);
  1593. if(! $status) return [false, $msg];
  1594. try {
  1595. DB::beginTransaction();
  1596. $time = time();
  1597. ItemNodeMission::where('del_time',0)
  1598. ->whereIn('id',$data['id'])
  1599. ->update(['del_time' => $time]);
  1600. ItemNodeMissionDetails::where('del_time',0)
  1601. ->whereIn('item_node_mission_id', $data['id'])
  1602. ->update(['del_time' => $time]);
  1603. DB::commit();
  1604. }catch (\Exception $exception){
  1605. DB::rollBack();
  1606. return [false,$exception->getMessage()];
  1607. }
  1608. return [true, ''];
  1609. }
  1610. public function itemNodeMissionDetail($data, $user){
  1611. if($this->isEmpty($data,'id')) return [false,'请选择数据!'];
  1612. $customer = ItemNodeMission::where('del_time',0)
  1613. ->withCustomData($user)
  1614. ->where('id',$data['id'])
  1615. ->first();
  1616. if(empty($customer)) return [false,'项目节点任务不存在或已被删除'];
  1617. $customer->getFormattedCustomFields();// 自定义数据附件处理
  1618. $customer = $customer->toArray();
  1619. $customer['start_time'] = ! empty($customer['start_time']) ? date("Y-m-d", $customer['start_time']) : "";
  1620. $customer['end_time'] = ! empty($customer['end_time']) ? date("Y-m-d", $customer['end_time']) : "";
  1621. $customer['crt_name'] = Employee::where('id',$customer['crt_id'])->value('title');
  1622. $customer['charge_name'] = Employee::where('id',$customer['charge_id'])->value('title');
  1623. $customer['crt_time'] = $customer['crt_time'] ? date("Y-m-d H:i:s",$customer['crt_time']): '';
  1624. $customer['state_title'] = ItemNodeMission::State_Type[$customer['state']] ?? '';
  1625. $customer['approval_state_title'] = ItemNodeMission::State_Type[$customer['approval_state']] ?? '';
  1626. $tag = (new TagService())->getTagMap(array_unique([$customer['priority_id'], $customer['mission_id'] ]));
  1627. $priority_tmp = $tag[$customer['priority_id']] ?? [];
  1628. $customer['priority_title'] = $priority_tmp['title'] ?? '';
  1629. $customer['priority_code'] = $priority_tmp['code'] ?? '';
  1630. $node_tmp = $tag[$customer['mission_id']] ?? [];
  1631. $customer['mission_title'] = $node_tmp['title'] ?? '';
  1632. $customer['mission_code'] = $node_tmp['code'] ?? '';
  1633. $customer['parent_item_node_mission_title'] = ItemNodeMission::where('id', $customer['parent_item_node_mission_id'])->value('title') ?? '';
  1634. $customer['review_title'] = WorkFlowTemplates::where('id', $customer['review_id'])->value('title') ?? "";
  1635. $item_node_map = $this->getItemNodeMap($customer['id']);
  1636. $customer['item_node_title'] = $item_node_map[$customer['id']];
  1637. $details = $this->getNodeMissionDetail($data['id']);
  1638. $customer = array_merge($customer, $details);
  1639. return [true, $customer];
  1640. }
  1641. public function itemNodeMissionUpdateState($data, $user){
  1642. if(empty($data['id'])) return [false, '任务ID不能为空'];
  1643. if(empty($data['state']) || ! isset(ItemNodeMission::State_Type[$data['state']])) return [false, '状态不存在错误'];
  1644. $model = ItemNodeMission::where('del_time',0)
  1645. ->where('id',$data['id'])
  1646. ->first();
  1647. if(empty($model)) return [false,'项目节点任务不存在或已被删除'];
  1648. if($model->approval_state == ItemNodeMission::TYPE_MINUS_ONE) return [false, '任务审核中'];
  1649. try {
  1650. DB::beginTransaction();
  1651. $model->state = $data['state'];
  1652. $model->save();
  1653. DB::commit();
  1654. }catch (\Exception $exception){
  1655. DB::rollBack();
  1656. return [false,$exception->getMessage()];
  1657. }
  1658. return [true, ''];
  1659. }
  1660. public function itemNodeMissionUpdateProgressContent($data, $user){
  1661. if(empty($data['id'])) return [false, '任务ID不能为空'];
  1662. $customer = ItemNodeMission::where('del_time',0)
  1663. ->where('id',$data['id'])
  1664. ->first();
  1665. if(empty($customer)) return [false,'项目节点任务不存在或已被删除'];
  1666. $customer = $customer->toArray();
  1667. if(empty($data['data'])) return [false, '任务进展明细不能为空'];
  1668. $empDisplayMap = Employee::whereIn('id', array_unique(array_column($data['data'],'data_id')))
  1669. ->get(['id', 'number', 'title'])
  1670. ->mapWithKeys(function($item){
  1671. return [$item->id => "[{$item->number}]{$item->title}"];
  1672. })->toArray();
  1673. foreach ($data['data'] as $key => $value){
  1674. $line = $key + 1;
  1675. $t = "第" . $line . "行";
  1676. if(empty($value['data_id'])) return [false, '人员ID不能为空'];
  1677. $empId = $value['data_id'] ?? 0;
  1678. $empName = $empDisplayMap[$empId] ?? "ID:{$empId}";
  1679. if(empty($value['order_time'])) return [false, '提交日期不能为空'];
  1680. $order_time = $this->changeDateToDate($value['order_time']);
  1681. if($order_time > $customer['end_time'] || $order_time < $customer['start_time']) return [false, $t . "提交日期必须在任务周期内"];
  1682. $data['data'][$key]['order_time'] = $order_time;
  1683. // 校验数字有效性
  1684. $res = $this->checkNumber($value['start_time_hour'], 0, 'non-negative');
  1685. if(!$res['valid']) return [false, $t . "人员{$empName}开始点:" . $res['error']];
  1686. if($value['start_time_hour'] > 23) return [false, false, $t . "人员{$empName}开始点不合法"];
  1687. $res = $this->checkNumber($value['start_time_min'], 0, 'non-negative');
  1688. if(!$res['valid']) return [false, $t ."人员{$empName}开始分:" . $res['error']];
  1689. if($value['start_time_min'] > 60) return [false, false, $t . "人员{$empName}开始点不合法"];
  1690. $res = $this->checkNumber($value['end_time_hour'], 0, 'non-negative');
  1691. if(!$res['valid']) return [false, $t ."人员{$empName}结束点:" . $res['error']];
  1692. if($value['end_time_hour'] > 24) return [false, false, $t . "人员{$empName}结束点不合法"];
  1693. $res = $this->checkNumber($value['end_time_min'], 0, 'non-negative');
  1694. if(!$res['valid']) return [false, $t ."人员{$empName}结束分:" . $res['error']];
  1695. if($value['end_time_min'] > 60) return [false, false, $t . "人员{$empName}结束分不合法"];
  1696. $currentStart = $value['start_time_hour'] * 60 + $value['start_time_min'];
  1697. $currentEnd = $value['end_time_hour'] * 60 + $value['end_time_min'];
  1698. if ($currentStart >= $currentEnd) {
  1699. return [false, $t . "人员{$empName}:开始时间必须早于结束时间"];
  1700. }
  1701. // --- 新增:总分钟数校验 ---
  1702. $calculatedTotal = $currentEnd - $currentStart;
  1703. if (!isset($value['total_work_min']) || intval($value['total_work_min']) !== $calculatedTotal) {
  1704. return [false, $t . "人员{$empName}:工时计算有误,应为 {$calculatedTotal} 分钟"];
  1705. }
  1706. // --- 3. 内部重叠校验(防止一次提交多行重复) ---
  1707. if (isset($internalOverlap[$empId])) {
  1708. foreach ($internalOverlap[$empId] as $period) {
  1709. if ($currentStart < $period['e'] && $period['s'] < $currentEnd) {
  1710. return [false, "人员{$empName}在本次提交的多行明细中时间段重叠"];
  1711. }
  1712. }
  1713. }
  1714. $internalOverlap[$empId][] = ['s' => $currentStart, 'e' => $currentEnd];
  1715. $data['data'][$key]['item_id'] = $customer['item_id'];
  1716. $data['data'][$key]['item_node_id'] = $customer['item_node_id'];
  1717. $data['data'][$key]['item_node_mission_id'] = $customer['id'];
  1718. $data['data'][$key]['top_depart_id'] = $user['top_depart_id'];
  1719. $data['data'][$key]['crt_id'] = $user['id'];
  1720. }
  1721. try {
  1722. DB::beginTransaction();
  1723. $time = time();
  1724. ItemNodeMissionContent::where('item_node_mission_id', $data['id'])->where('del_time',0)->update(['del_time' => $time]);
  1725. ItemNodeMissionContent::insert($data['data']);
  1726. DB::commit();
  1727. }catch (\Exception $exception){
  1728. DB::rollBack();
  1729. return [false,$exception->getMessage()];
  1730. }
  1731. return [true, ''];
  1732. }
  1733. public function itemNodeMissionUpdateProgress($data, $user){
  1734. if(empty($data['id'])) return [false, 'ID不能为空'];
  1735. if(empty($data['progress'])) return [false, '任务进展不能为空'];
  1736. $res = $this->checkNumber($data['progress'],0,'positive');
  1737. if (! $res['valid']) return [false, "任务进展:" . $res['error']];
  1738. if($data['progress'] > 100) return [false, '任务进展不能超过100'];
  1739. ItemNodeMission::where('id', $data['id'])->update(['progress' => $data['progress']]);
  1740. return [true, ''];
  1741. }
  1742. public function itemNodeMissionDetailBoard($data, $user){
  1743. list($status, $msg) = $this->itemNodeMissionDetail($data, $user);
  1744. if(! $status) return [false, $msg];
  1745. $customer = $msg;
  1746. //任务进展信息
  1747. $customer['mission_progress'] = $this->itemMissionProgressBoard($data, $user);
  1748. //文件归档
  1749. $customer['file_list'] = $this->getMissionProgressFile($data, $user, $customer);
  1750. return [true, $customer];
  1751. }
  1752. public function itemMissionProgressBoard($data,$user){
  1753. $list = ItemNodeMissionContent::where('del_time',0)
  1754. ->where('item_node_mission_id', $data['id'])
  1755. ->select('*')
  1756. ->get()->toArray();
  1757. $emp = (new EmployeeService())->getEmployeeMap(array_unique(array_merge_recursive(array_column($list,'data_id'), array_column($list,'crt_id'))));
  1758. foreach ($list as $key => $value){
  1759. $list[$key]['order_time'] = $value['order_time'] ? date('Y-m-d',$value['order_time']) : '';
  1760. $list[$key]['crt_time'] = $value['crt_time'] ? date('Y-m-d H:i:s',$value['crt_time']) : '';
  1761. $list[$key]['crt_name'] = $emp[$value['crt_id']] ?? '';
  1762. $list[$key]['data_name'] = $emp[$value['data_id']] ?? '';
  1763. }
  1764. return $list;
  1765. }
  1766. public function getMissionProgressFile($data, $user, $item){
  1767. $file = ItemFile::where('del_time',0)
  1768. ->where('item_node_mission_id', $data['id'])
  1769. ->get()->toArray();
  1770. $employee_id = array_unique(array_merge_recursive(array_column($file,'crt_id'), array_column($item['custom_fields'],'crt_id')));
  1771. $map = (new EmployeeService())->getEmployeeMap($employee_id);
  1772. $return = [];
  1773. if(! empty($item['custom_fields'])){
  1774. foreach ($item['custom_fields'] as $value){
  1775. if($value['field_type'] == CustomFieldValue::type_two){
  1776. $return[] = [
  1777. 'id' => $value['id'],
  1778. 'url' => $value['field_value'],
  1779. 'name' => $value['field_name'],
  1780. 'show_url' => $value['field_value_show'],
  1781. 'type_name' => '任务',
  1782. 'type_name_2' => CustomFieldValue::type[CustomFieldValue::type_two],
  1783. 'type' => 'custom_fields',
  1784. 'from' => ItemFile::type_two,
  1785. 'crt_name' => $map[$value['crt_id']],
  1786. 'crt_time' => date("Y-m-d", $value['crt_time']),
  1787. 'crt_id' => $value['crt_id'],
  1788. 'is_delivery' => $value['is_delivery'],
  1789. ];
  1790. }
  1791. }
  1792. }
  1793. if(! empty($file)){
  1794. $fileUploadService = new FileUploadService();
  1795. foreach ($file as $value){
  1796. $return[] = [
  1797. 'id' => $value['id'],
  1798. 'url' => $value['url'],
  1799. 'name' => $value['name'],
  1800. 'show_url' => $fileUploadService->getFileShow($value['url']),
  1801. 'type_name' => ItemFile::from[$value['from']],
  1802. 'type_name_2' => ItemFile::type[$value['type']],
  1803. 'type' => 'item_file',
  1804. 'from' => $value['from'],
  1805. 'crt_name' => $map[$value['crt_id']],
  1806. 'crt_time' => date("Y-m-d", $value['crt_time']),
  1807. 'crt_id' => $value['crt_id'],
  1808. 'is_delivery' => $value['is_delivery'],
  1809. ];
  1810. }
  1811. }
  1812. return $return;
  1813. }
  1814. public function itemNodeMissionCommon($data,$user, $field = []){
  1815. if(empty($field)) $field = ItemNodeMission::$field;
  1816. $item_id = $data['item_id'] ?? 0;
  1817. $item_node_id = $data['item_node_id'] ?? 0;
  1818. $model = ItemNodeMission::TopAndEmployeeClear($user,$data);
  1819. $model = $model->where('del_time',0)
  1820. ->when(! empty($item_id),function ($query) use($item_id){
  1821. return $query->where('item_id', $item_id);
  1822. })
  1823. ->when(! empty($item_node_id),function ($query) use($item_node_id){
  1824. return $query->where('item_node_id', $item_node_id);
  1825. })
  1826. ->select($field)
  1827. ->orderby('id', 'desc');
  1828. if(! empty($data['title'])) $model->where('title', 'LIKE', '%'.$data['title'].'%');
  1829. if(! empty($data['code'])) $model->where('code', 'LIKE', '%'.$data['code'].'%');
  1830. if(! empty($data['id'])) $model->whereIn('id', $data['id']);
  1831. if(isset($data['state'])) $model->where('state', $data['state']);
  1832. if(isset($data['approval_state'])) $model->where('approval_state', $data['approval_state']);
  1833. if(! empty($data['crt_time'][0]) && ! empty($data['crt_time'][1])) {
  1834. $return = $this->changeDateToTimeStampAboutRange($data['crt_time']);
  1835. $model->where('crt_time','>=',$return[0]);
  1836. $model->where('crt_time','<=',$return[1]);
  1837. }
  1838. return $model;
  1839. }
  1840. public function itemNodeMissionList($data,$user){
  1841. $model = $this->itemNodeMissionCommon($data, $user);
  1842. $list = $this->limit($model,'',$data);
  1843. $list = $this->fillNodeMissionData($list);
  1844. return [true, $list];
  1845. }
  1846. public function itemNodeMissionListBySearch($data,$user){
  1847. list($status, $model) = $this->itemNodeMissionCommonBySearch($data, $user);
  1848. if(! $status) return [false, $model];
  1849. $list['data'] = $model->get()->toArray();
  1850. $list = $this->fillNodeMissionData($list);
  1851. return [true, $list['data']];
  1852. }
  1853. public function itemNodeMissionCommonBySearch($data,$user, $field = []){
  1854. if(empty($field)) $field = ItemNodeMission::$field;
  1855. if(empty($data['item_id'])) return [false, '请选择项目'];
  1856. $item_node_id = $data['item_node_id'] ?? 0;
  1857. $model = ItemNodeMission::TopAndEmployeeClear($user,$data);
  1858. $model = $model->where('del_time',0)
  1859. ->where('item_id', $data['item_id'])
  1860. ->when(! empty($item_node_id),function ($query) use($item_node_id){
  1861. return $query->where('item_node_id', $item_node_id);
  1862. })
  1863. ->select($field)
  1864. ->orderby('id', 'desc');
  1865. if(! empty($data['title'])) $model->where('title', 'LIKE', '%'.$data['title'].'%');
  1866. if(! empty($data['code'])) $model->where('code', 'LIKE', '%'.$data['code'].'%');
  1867. if(! empty($data['id'])) $model->whereIn('id', $data['id']);
  1868. if(isset($data['state'])) $model->where('state', $data['state']);
  1869. if(isset($data['approval_state'])) $model->where('approval_state', $data['approval_state']);
  1870. if(! empty($data['crt_time'][0]) && ! empty($data['crt_time'][1])) {
  1871. $return = $this->changeDateToTimeStampAboutRange($data['crt_time']);
  1872. $model->where('crt_time','>=',$return[0]);
  1873. $model->where('crt_time','<=',$return[1]);
  1874. }
  1875. return [true, $model];
  1876. }
  1877. public function itemNodeMissionRule(&$data, $user, $is_add = true){
  1878. $data['top_depart_id'] = $user['top_depart_id'];
  1879. if(empty($data['item_node_id']) && empty($data['parent_item_node_mission_id'])) return [false, '节点与父级任务不能同时为空'];
  1880. if(empty($data['item_node_id'])) return [false, '项目节点ID不能为空'];
  1881. $item = ItemNode::where('id', $data['item_node_id'])->where('del_time',0)->first();
  1882. if(empty($item)) return [false, '项目节点不存在或已被删除'];
  1883. $item = $item->toArray();
  1884. $data['item_id'] = $item['item_id'];
  1885. if(! empty($data['parent_item_node_mission_id'])) {
  1886. $bool = ItemNodeMission::where('id', $data['parent_item_node_mission_id'])->where('del_time',0)->exists();
  1887. if(! $bool) return [false, '父任务不存在或已被删除'];
  1888. }
  1889. if(empty($data['title'])) return [false, '任务名称不能为空'];
  1890. if(! empty($data['start_time'])) $data['start_time'] = $this->changeDateToDate($data['start_time']);
  1891. if(! empty($data['end_time'])) $data['end_time'] = $this->changeDateToDate($data['end_time'],true);
  1892. $itemStartTime = $item['start_time'];
  1893. $itemEndTime = $item['end_time'];
  1894. if (!empty($data['start_time'])) {
  1895. $inputStartTime = $data['start_time'];
  1896. if ($itemStartTime > 0 && $inputStartTime < $itemStartTime) {
  1897. return [false, '任务开始时间不能早于项目节点开始时间:' . date('Y-m-d', $itemStartTime)];
  1898. }
  1899. }
  1900. if (!empty($data['end_time'])) {
  1901. $inputEndTime = $data['end_time'];
  1902. if ($itemEndTime > 0 && $inputEndTime > $itemEndTime) {
  1903. return [false, '任务结束时间不能晚于项目节点结束时间:' . date('Y-m-d', $itemEndTime)];
  1904. }
  1905. }
  1906. if(! empty($data['man_list'])) {
  1907. foreach ($data['man_list'] as $key => $value){
  1908. if(empty($value['type'])) return [false, '类型不能为空'];
  1909. if(empty($value['data_id'])) return [false, '人员不能为空'];
  1910. $data['man_list'][$key]['top_depart_id'] = $data['top_depart_id'];
  1911. }
  1912. list($status, $msg) = $this->checkArrayRepeat($data['man_list'],'data_id','人员');
  1913. if(! $status) return [false, $msg];
  1914. }
  1915. if(! empty($data['device_list'])) {
  1916. foreach ($data['device_list'] as $key => $value){
  1917. if(empty($value['type'])) return [false, '类型不能为空'];
  1918. if(empty($value['data_id'])) return [false, '设备ID不能为空'];
  1919. $data['device_list'][$key]['top_depart_id'] = $data['top_depart_id'];
  1920. }
  1921. list($status, $msg) = $this->checkArrayRepeat($data['device_list'],'data_id','设备');
  1922. if(! $status) return [false, $msg];
  1923. }
  1924. if($is_add){
  1925. $bool = ItemNodeMission::where('item_node_id',$data['item_node_id'])
  1926. ->where('top_depart_id', $data['top_depart_id'])
  1927. ->where('title', $data['title'])
  1928. ->where('del_time',0)
  1929. ->exists();
  1930. }else{
  1931. if(empty($data['id'])) return [false,'ID不能为空'];
  1932. $item = ItemNodeMission::where('id', $data['id'])
  1933. ->where('del_time',0)
  1934. ->first();
  1935. if(empty($item)) return [false, '项目节点下任务不存在或已被删除'];
  1936. $bool = ItemNodeMission::where('title', $data['title'])
  1937. ->where('item_node_id',$data['item_node_id'])
  1938. ->where('top_depart_id', $data['top_depart_id'])
  1939. ->where('id','<>',$data['id'])
  1940. ->where('del_time',0)
  1941. ->exists();
  1942. }
  1943. if($bool) return [false, '该项目节点下任务名称已存在'];
  1944. //判断是否编辑 然后需要审核的 需要后 就更新状态 记录草稿
  1945. if(! $is_add){
  1946. $return = $this->checkItemNodeMissionIsChanged($data, $user);
  1947. if(is_array($return)) {
  1948. list($status, $msg) = $return;
  1949. return [$status, $msg];
  1950. }else{
  1951. if($return) $data['draft'] = true;
  1952. }
  1953. }
  1954. return [true, ''];
  1955. }
  1956. public function checkItemNodeMissionIsChanged($data, $user) {
  1957. // 1. 获取旧数据
  1958. list($status, $oldData) = $this->itemNodeMissionDetail($data, $user);
  1959. if (!$status) return [false, $oldData];
  1960. //创建人 直接通过
  1961. $bool = $this->changedCommon($user, $oldData);
  1962. if(! $bool) return false;
  1963. // --- 表头基础字段比对 ---
  1964. $fields = [
  1965. 'code', 'title', 'mark', 'state', 'budget',
  1966. 'charge_id', 'is_delivery_required', 'start_time', 'end_time',
  1967. 'entrust_type', 'is_review_required', 'review_id', 'priority_id', 'mission_id', 'mission_weight', 'progress'
  1968. ];
  1969. foreach ($fields as $field) {
  1970. $oldValue = $oldData[$field] ?? '';
  1971. $newValue = $data[$field] ?? '';
  1972. // 日期格式对齐(确保 Y-m-d 格式一致)
  1973. if (in_array($field, ['start_time', 'end_time'])) {
  1974. $newValue = !empty($newValue) ? date("Y-m-d", $newValue) : "";
  1975. }
  1976. if ($oldValue != $newValue) {
  1977. return true;
  1978. }
  1979. }
  1980. // --- 自定义字段比对 ---
  1981. if (isset($data['custom_fields']) && is_array($data['custom_fields'])) {
  1982. $oldCustomMap = array_column($oldData['custom_fields'] ?? [], 'field_value', 'id');
  1983. foreach ($data['custom_fields'] as $customField) {
  1984. $fid = $customField['id'] ?? 0;
  1985. $newVal = $customField['field_value'] ?? '';
  1986. $oldVal = $oldCustomMap[$fid] ?? '';
  1987. if ($oldVal != $newVal) {
  1988. return true;
  1989. }
  1990. }
  1991. }
  1992. // --- 表体:人员名单比对 (man_list) ---
  1993. $oldMans = collect($oldData['man_list'] ?? [])->pluck('data_id')->sort()->values()->toArray();
  1994. $newMans = collect($data['man_list'] ?? [])->pluck('data_id')->sort()->values()->toArray();
  1995. if ($oldMans !== $newMans) {
  1996. return true;
  1997. }
  1998. // --- 表体:设备名单比对 (device_list) ---
  1999. $oldDevices = collect($oldData['device_list'] ?? [])->pluck('data_id')->sort()->values()->toArray();
  2000. $newDevices = collect($data['device_list'] ?? [])->pluck('data_id')->sort()->values()->toArray();
  2001. if ($oldDevices !== $newDevices) {
  2002. return true;
  2003. }
  2004. return false; // 没有任何变动
  2005. }
  2006. public function fillNodeMissionData($data){
  2007. if(empty($data['data'])) return $data;
  2008. $emp = (new EmployeeService())->getEmployeeMap(array_unique(array_merge_recursive(array_column($data['data'],'charge_id'), array_column($data['data'],'crt_id'))));
  2009. $tag = (new TagService())->getTagMap(array_unique(array_merge_recursive(array_column($data['data'],'priority_id'),array_column($data['data'],'mission_id'))));
  2010. $item_node_map = $this->getItemNodeMap(array_column($data['data'],'id'));
  2011. foreach ($data['data'] as $key => $value){
  2012. $data['data'][$key]['crt_time'] = $value['crt_time'] ? date('Y-m-d H:i:s',$value['crt_time']) : '';
  2013. $data['data'][$key]['start_time'] = $value['start_time'] ? date('Y-m-d',$value['start_time']) : '';
  2014. $data['data'][$key]['end_time'] = $value['end_time'] ? date('Y-m-d',$value['end_time']) : '';
  2015. $data['data'][$key]['crt_name'] = $emp[$value['crt_id']] ?? '';
  2016. $data['data'][$key]['charge_name'] = $emp[$value['charge_id']] ?? '';
  2017. $data['data'][$key]['state_title'] = ItemNodeMission::State_Type[$value['state']] ?? "";
  2018. $data['data'][$key]['approval_state_title'] = ItemNodeMission::State_Type[$value['approval_state']] ?? "";
  2019. $priority_tmp = $tag[$value['priority_id']] ?? [];
  2020. $data['data'][$key]['priority_title'] = $priority_tmp['title'] ?? '';
  2021. $data['data'][$key]['priority_code'] = $priority_tmp['code'] ?? '';
  2022. $node_tmp = $tag[$value['mission_id']] ?? [];
  2023. $data['data'][$key]['mission_title'] = $node_tmp['title'] ?? '';
  2024. $data['data'][$key]['mission_code'] = $node_tmp['code'] ?? '';
  2025. $data['data'][$key]['item_node_title'] = $item_node_map[$value['id']] ?? '';
  2026. }
  2027. return $data;
  2028. }
  2029. public function getItemNodeMap($item_node_mission_id){
  2030. if(! is_array($item_node_mission_id)) $item_node_mission_id = [$item_node_mission_id];
  2031. return ItemNodeMission::from('item_node_mission as a')
  2032. ->leftJoin('item_node as b','b.id','a.item_node_id')
  2033. ->whereIn('a.id', $item_node_mission_id)
  2034. ->pluck('b.title','a.id')
  2035. ->toArray();
  2036. }
  2037. /**
  2038. * 触发生成审批流
  2039. * @param $data 草稿数据
  2040. * @param $user 提交人
  2041. * @param $tableName 操作表
  2042. * @param $oldData 原数据
  2043. * @param $opt_type 操作类型 1 全量编辑 2 单字段编辑
  2044. */
  2045. public function createWorkFlow($data, $user, $tableName, $oldData, $opt_type = 1){
  2046. // 创建草稿
  2047. $this->createDraft($data, $user, $tableName, $opt_type);
  2048. //触发审批
  2049. list($status, $msg) = (new WorkFlowService())->triggerWorkflow($oldData['review_id'],$oldData['id'],$tableName,$user);
  2050. if(! $status) throw new \Exception($msg);
  2051. }
  2052. }