LaborApplyService.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. <?php
  2. namespace App\Services;
  3. use App\LaborCompany;
  4. use App\LaborCompanyDispatch;
  5. use App\Traits\ServiceAppAop;
  6. use App\LaborApply;
  7. use App\Warehouse;
  8. use Illuminate\Database\Eloquent\Builder;
  9. use Illuminate\Database\Eloquent\Collection;
  10. use Illuminate\Support\Carbon;
  11. use Illuminate\Support\Facades\Cache;
  12. use Illuminate\Support\Facades\DB;
  13. use function DeepCopy\deep_copy;
  14. class LaborApplyService
  15. {
  16. //超时时间 默认为19:00
  17. const TIME_OUT_HOUR = 18;
  18. const LABOR_APPLY_STATUS = 'LABOR_APPLY_STATUS';
  19. //上次次临时工申请分配的时间
  20. const LATELY_LABOR_COMPANY_DISPATCH_CREATED_AT = 'LATELY_LABOR_COMPANY_DISPATCH_CREATED_AT';
  21. const LABOR_APPLY_STATUS_TTL = 60 * 30;
  22. //到岗日期是申请日期的前一天 为保证数据正确 计算实际人数的时间为到岗日期的后一天 故时间为2
  23. const CALCULATION_ARRIVED_MAN_NUM_DEFAULT_SUB_DAYS = 2;
  24. //临时工工作时长最小值 低于这个时间的不计入
  25. const CALCULATION_ARRIVED_MAN_NUM_MIN_ONLINE_DURATION = 1;
  26. use ServiceAppAop;
  27. protected $modelClass = LaborApply::class;
  28. /**
  29. * 获取预约开放状态
  30. *
  31. * @return int 1 开放 2 禁止 3 临时开放
  32. */
  33. public function getCanCreateStatus(): int
  34. {
  35. $can_create_status = (now()->lte(now()->startOfDay()->addHours(self::TIME_OUT_HOUR))) ? LaborApply::CAN_CREATE_STATUS_OPEN : LaborApply::CAN_CREATE_STATUS_FORBID;
  36. if (Cache::has(self::LABOR_APPLY_STATUS)) {
  37. $can_create_status = Cache::get(self::LABOR_APPLY_STATUS);
  38. }
  39. return $can_create_status;
  40. }
  41. /**
  42. * 设置预约开放状态 1 开放 2 禁止 3 临时开放
  43. * @param int $status
  44. */
  45. public function setCanCreateStatus(int $status)
  46. {
  47. Cache::put(self::LABOR_APPLY_STATUS, $status, self::LABOR_APPLY_STATUS_TTL);
  48. }
  49. public function allocationAppendLaborToLaborCompany()
  50. {
  51. $lately_allocation_at = Cache::get(self::LATELY_LABOR_COMPANY_DISPATCH_CREATED_AT);
  52. $labor_applies = LaborApply::query()
  53. ->where('created_at', '>=', $lately_allocation_at)
  54. ->get();
  55. }
  56. /**
  57. * 生产劳务派遣报表
  58. *
  59. * 查询全部仓库
  60. * 遍历仓库
  61. * 按照仓库,查询当日的的申请,将申请的男女工分别加和
  62. * 根据仓库 找到对应的劳务公司集合
  63. * 遍历男工人数
  64. * 确定每个劳务公司提供的男工人数
  65. * 女工也一样
  66. * 如果分配完成后 发现还有剩下的 则将状态设置为2
  67. * @param bool $isAppend
  68. * @return array
  69. */
  70. public function allocationLaborToLaborCompany(bool $isAppend = false): array
  71. {
  72. //当前时间小于 1.00 禁止生成数据
  73. if (now()->lte(now()->startOfDay()->addHours(1))) return ['success' => false, 'error_message' => '当前时间禁止生成临时工派遣数据'];
  74. //上次分配时间 默认为前一天的18:00
  75. $lately_allocation_at = now()->subDay()->startOfDay()->addHours(self::TIME_OUT_HOUR);
  76. //缓存中有上次的同步时间
  77. if (Cache::has(self::LATELY_LABOR_COMPANY_DISPATCH_CREATED_AT)) {
  78. $lately_allocation_at = Cache::get(self::LATELY_LABOR_COMPANY_DISPATCH_CREATED_AT);
  79. }
  80. //派遣日期 为报表生成日期的后一天
  81. $dispatch_date = now()->addDay()->startOfDay()->toDateTimeString();
  82. //查询全部仓库
  83. $warehouses = Warehouse::all();
  84. //给每个仓库计算分配数据 更改申请状态为指派成功
  85. foreach ($warehouses as $warehouse) {
  86. //查询当日的的申请,将申请的男女工分别加和
  87. $builder = DB::table('labor_applies')
  88. ->selectRaw("sum(man_num) as man_num ,sum(woman_num) as woman_num")
  89. ->whereBetween('created_at', [now()->startOfDay(), now()->endOfDay()])
  90. ->where('warehouse_id', $warehouse->id);
  91. //如果是追加模式
  92. if ($isAppend) {
  93. //只查询最后一次分配后的申请
  94. $builder->where('created_at', '>=', $lately_allocation_at);
  95. }
  96. $apply_man_nums = $builder
  97. ->groupBy('warehouse_id')
  98. ->first();
  99. if (empty($apply_man_nums)) continue;
  100. //按照仓库查询对应的劳务所
  101. $companies = $this->getCompanies($warehouse);
  102. //需要的男工人数
  103. $man_num = $apply_man_nums->man_num;
  104. //需要的女工人数
  105. $woman_num = $apply_man_nums->woman_num;
  106. //插入的分配数据
  107. $laborCompanyDispatchInsertArray = $this->buildLaborCompanyDispatchInsertArray($companies, $man_num, $woman_num, $dispatch_date);
  108. //插入分配数据
  109. LaborCompanyDispatch::query()->insert($laborCompanyDispatchInsertArray);
  110. LaborApply::query()
  111. ->whereBetween('created_at', [now()->startOfDay(), now()->endOfDay()])
  112. ->where('warehouse_id', $warehouse->id)
  113. ->update([
  114. 'status' => 2,//指派成功
  115. ]);
  116. }
  117. //保存分配时间为当前时间
  118. Cache::put(self::LATELY_LABOR_COMPANY_DISPATCH_CREATED_AT, now());
  119. return ['success' => true, 'message' => '生成临时工派遣数据成功'];
  120. }
  121. /**
  122. * 构建插入的分配数据
  123. * @param Collection $companies
  124. * @param $man_num
  125. * @param $woman_num
  126. * @param string $dispatch_date
  127. * @return array
  128. */
  129. private function buildLaborCompanyDispatchInsertArray(Collection $companies, $man_num, $woman_num, string $dispatch_date): array
  130. {
  131. //分配数组
  132. $laborCompanyDispatchInsertArray = [];
  133. //使用劳务公司填充默认值
  134. foreach ($companies as $company) {
  135. $laborCompanyDispatchInsertArray[$company->id] = [
  136. 'labor_company_id' => $company->id,
  137. 'dispatch_date' => $dispatch_date,
  138. 'man_num' => 0,
  139. 'woman_num' => 0,
  140. 'exceed_max_labor_num_status' => LaborCompanyDispatch::NOT_EXCEED_MAX_LABOR_NUM,//没有超限额
  141. 'status' => 1,//创建
  142. ];
  143. }
  144. //分配男工
  145. //将需要的工人 依次分配给劳务公司 不管劳务公司能否承担这个人 假设都分配给他(-1) 不能的标记一下不分配 能的就真分配
  146. while ($man_num > 0) {
  147. foreach ($laborCompanyDispatchInsertArray as &$laborCompanyDispatchInsertItem) {
  148. //当前 要分配到该劳务的男工数量
  149. $current_man_num = ($laborCompanyDispatchInsertItem['man_num'] ?? 0) + 1;
  150. //该劳务公司最大的分配人数
  151. $company_can_apply_man_num = $companies->find($laborCompanyDispatchInsertItem['labor_company_id'])->man_num;
  152. //超了
  153. if ($current_man_num > $company_can_apply_man_num) {
  154. //设置状态为超量
  155. $laborCompanyDispatchInsertItem['exceed_max_labor_num_status'] = LaborCompanyDispatch::EXCEED_MAX_LABOR_NUM;
  156. } else {
  157. //赋值给插入的数组
  158. $laborCompanyDispatchInsertItem['man_num'] = $current_man_num;
  159. }
  160. //需要分配的人数 -1
  161. //不管人数超了还是没超 都要-1
  162. $man_num--;
  163. }
  164. unset($laborCompanyDispatchInsertItem);
  165. }
  166. while ($woman_num > 0) {
  167. foreach ($laborCompanyDispatchInsertArray as &$laborCompanyDispatchInsertItem) {
  168. $current_woman_num = ($laborCompanyDispatchInsertItem['woman_num'] ?? 0) + 1;
  169. $company_can_apply_woman_num = $companies->find($laborCompanyDispatchInsertItem['labor_company_id'])->woman_num;
  170. if ($current_woman_num > $company_can_apply_woman_num) {
  171. $laborCompanyDispatchInsertItem['exceed_max_labor_num_status'] = LaborCompanyDispatch::EXCEED_MAX_LABOR_NUM;
  172. } else {
  173. $laborCompanyDispatchInsertItem['woman_num'] = $current_woman_num;
  174. }
  175. $woman_num--;
  176. }
  177. unset($laborCompanyDispatchInsertItem);
  178. }
  179. return $laborCompanyDispatchInsertArray;
  180. }
  181. /**
  182. * 按照仓库查询对应的劳务所 按照优先级排序 单号正序 双号逆序
  183. * @param $warehouse
  184. * @return Builder[]|Collection
  185. */
  186. private function getCompanies($warehouse): Collection
  187. {
  188. return LaborCompany::query()
  189. ->where('warehouse_id', $warehouse->id)->get();
  190. }
  191. /**
  192. * 按照给定的申请日期计算到岗人数
  193. *
  194. * 查询指定日期的 每个小组的申请人数
  195. * 遍历申请数据
  196. * 根据申请数据的仓库小组id,日期(申请日期后一天)查询打卡数据,计算人数之和 更新申请表actual_num
  197. * @param Carbon|null $apply_date
  198. */
  199. public function calculationArrivedManNum(Carbon $apply_date = null)
  200. {
  201. //查询的申请日期
  202. if (empty($apply_date)) {
  203. $apply_date = now()->subDays(self::CALCULATION_ARRIVED_MAN_NUM_DEFAULT_SUB_DAYS);
  204. }
  205. //查询指定日期的 每个小组的申请人数
  206. $laborApplies = DB::table('labor_applies')
  207. ->selectRaw('user_workgroup_id as user_workgroup_id , sum(man_num + woman_num) as apply_num , warehouse_id')
  208. ->whereDate('created_at', $apply_date->toDateString())
  209. ->groupBy('user_workgroup_id', 'warehouse_id')
  210. ->get();
  211. //根据申请数据的仓库小组id,日期(申请日期后一天)
  212. $check_in_at = deep_copy($apply_date)->addDay()->toDateString();
  213. //遍历申请数据
  214. foreach ($laborApplies as $laborApply) {
  215. //查询打卡数据,计算人数之和
  216. $arrived_num =
  217. DB::table('labor_reports')
  218. ->selectRaw("count( DISTINCT 'identity_number',identity_number) as num")//根据身份证号去重防止重复入组
  219. ->where('user_workgroup_id', $laborApply->user_workgroup_id)
  220. ->whereDate('check_in_at', $check_in_at)
  221. ->where('online_duration', '>=', self::CALCULATION_ARRIVED_MAN_NUM_MIN_ONLINE_DURATION)
  222. ->first()->num;
  223. //更新申请表actual_num
  224. LaborApply::query()
  225. ->whereDate('created_at', deep_copy($apply_date)->toDateString())
  226. ->where('user_workgroup_id', $laborApply->user_workgroup_id)
  227. ->update([
  228. 'actual_num' => $arrived_num,
  229. 'status' => 4,//任务完结
  230. ]);
  231. }
  232. //根据分配时间完结任务 $check_in_at:实际入场日期 就是 dispatch_date
  233. LaborCompanyDispatch::query()
  234. ->whereDate('dispatch_date', $check_in_at)
  235. ->update([
  236. 'status' => 4//任务完结
  237. ]);
  238. }
  239. }