SckwChgrobotCrontab.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. <?php
  2. declare(strict_types=1);
  3. namespace App\Crontab;
  4. use App\Model\RRobotAutoOrderModel;
  5. use App\Model\RRobotAutoUserModel;
  6. use App\Model\RTradeOrderModel;
  7. use App\Model\TEmployeeMemberModel;
  8. use Hyperf\Contract\StdoutLoggerInterface;
  9. use Hyperf\Crontab\Annotation\Crontab;
  10. use Hyperf\DbConnection\Db;
  11. use Hyperf\Di\Annotation\Inject;
  12. /**
  13. * Desc:自动生成订单任务每天一次
  14. * Author: Spt <2864934511@qq.com>
  15. * Date: 2024-03-13 08:36
  16. */
  17. #[Crontab(rule: "30 23 * * *", name: "SckwChgrobot", callback: "execute", memo: "自动生成订单任务每天一次", enable: "isEnable")]
  18. class SckwChgrobotCrontab
  19. {
  20. // /**
  21. // * 定时任务唯一标识
  22. // */
  23. // protected const code = 'auto_order_crontab';
  24. #[Inject]
  25. private StdoutLoggerInterface $logger;
  26. /**
  27. * 充电最低标准电量
  28. * @var int
  29. */
  30. private static int $charge_minimum_power = 3;
  31. /**
  32. * Desc: 是否随开机启动
  33. * Author: Spt <2864934511@qq.com>
  34. * Date: 2024-03-12 16:08
  35. * @return bool
  36. */
  37. public function isEnable(): bool
  38. {
  39. return true;
  40. }
  41. public function execute(): void
  42. {
  43. $this->logger->info(__CLASS__ . ' 自动生成订单任务开始 ' . datetime());
  44. $this->task();
  45. }
  46. private function task(): bool
  47. {
  48. //获取所有任务
  49. $list = RRobotAutoOrderModel::with(['robot', 'site', 'price'])
  50. ->where('num', '<', 2)//执行次数0无限1一次超过1则不执行
  51. ->where('del_flag', 0)
  52. ->get();
  53. if ($list->isNotEmpty()) {
  54. //获取所有可用人员
  55. $user_list = RRobotAutoUserModel::where('status', 1)->get();
  56. if ($user_list->isEmpty()) {
  57. $this->logger->error(' 查无任务可用用户');
  58. return false;
  59. }
  60. $users = array_column($user_list->toArray(), 'open_id');
  61. //获取所有可用人员
  62. $userLists = TEmployeeMemberModel::with(['user'])
  63. ->whereIn('open_id', $users)//openid 范围查询
  64. ->where('del_flag', 0)//是否删除0否 1是
  65. ->get();
  66. if ($userLists->isEmpty()) {
  67. $this->logger->error(' 查无会员用户:' . json_encode($users, JSON_UNESCAPED_UNICODE));
  68. return false;
  69. } else {
  70. $userLists = $userLists->toArray();
  71. }
  72. foreach ($list as $robot) {
  73. //本轮充放电概率确认
  74. //充
  75. $c_efficiency = $this->randApproximateRate($robot);
  76. // //放
  77. // $d_efficiency = $this->randApproximateRate($robot);
  78. $this->logger->info($robot->id . '本轮充放电概率确认, 充:' . $c_efficiency);// . ' 放:' . $d_efficiency
  79. //总电量:
  80. $total_power = $robot->full_charge;
  81. $this->logger->info($robot->id . "总电量 :" . $total_power);
  82. //本轮机器人充电和放电时间时长确认 满电量*波动概率 (单位:秒)
  83. //充
  84. $charging_c = ceil($total_power * $c_efficiency * 60);
  85. // //放
  86. // $efficiency_f = $total_power * $d_efficiency * 60;
  87. // $this->logger->info($robot->id . "本轮机器人充电放电时间时长确认(单位:秒):" . $charging_c . ' ' . $efficiency_f);
  88. //所差时段开始时间 和 所差时段结束时间 ,循环放电
  89. if ($robot->num == 1) {
  90. $bad_start_periods_time = strtotime($robot->bad_start_periods);
  91. $bad_end_periods_time = strtotime($robot->bad_end_periods);
  92. } else {
  93. $bad_start_periods_time = strtotime(date('Y-m-d') . $robot->bad_start_periods);
  94. $bad_end_periods_time = strtotime(date('Y-m-d') . $robot->bad_end_periods);
  95. }
  96. //电价
  97. if (!$robot->price || empty($robot->price)) {
  98. $this->logger->info('注意当前充电站点无单价配置,将用默认配置:' . $robot->id);
  99. $site_price[] = [
  100. 'site_id' => $robot->site_id,
  101. 'start_time' => '00:00:00',
  102. 'ent_time' => '23:59:59',
  103. 'price' => 0,
  104. 'serve_price' => 0,
  105. ];
  106. } else {
  107. $site_price = $robot->price->toArray();
  108. }
  109. $is_charge = false;//机器人是否需要充电
  110. //默认组装
  111. $dat = [
  112. // "robot" => $robot->robot->imei,
  113. // "order" => "",
  114. // "user" => '',
  115. // "start_time" => $bad_start_periods_time,
  116. // "end_time" => "",
  117. // "time" => 0,//时长
  118. // "soc" => "",//电量占比
  119. // "billing" => "",
  120. // "c_type" => 1,//充电模式
  121. // "start_soc" => 0,//充电开始度数
  122. // "end_soc" => 0,//充电结束度数
  123. "user_id" => '',//用户id
  124. "open_id" => '',//微信appid
  125. "customer_phone" => '',//客户电话
  126. "type" => 2,//1预购订单2点卡充值订单
  127. "buy_order_id" => '',//预缴费订单id/点卡充值订单id(memberOrderId)
  128. "site_id" => $robot->site_id,//站点id
  129. "robot_id" => $robot->robot_id,//机器人id
  130. "order_no" => '',//系统订单号(必须)
  131. "charging_pile_id" => $robot->robot->imei,//充电桩id
  132. "pile_imei" => $robot->robot->imei,//终端 IMEI
  133. "charge_model" => 5,//充电模式(5 自然充满、10 按电\r\n量、15 按金额)
  134. // "plan_power"=>null,//计划充电量(kwh) 非自然充满模\r\n式必填
  135. // "plan_amount"=>null,//计划充电金额,自然充满模式则\r\n为用户余额
  136. "status" => 2,//订单状态(1进行中,2已结算,3未支付(预留),4完成)
  137. // "third_order_no"=>'',//三方订单号(记录 ID)
  138. "duration" => 0,//充电时长(分钟)向上取整-
  139. "voltage" => 220,//电压
  140. "electric_current" => 6,//电流
  141. "power" => 0,//充电量(kwh)-
  142. "amount" => 0,//订单金额-
  143. "charging_status" => 15,//充电状态(5: 待充电,10: 充电中,15: 已结束,20: 已取消)
  144. // "stop_reason"=>'',//结束原因
  145. // "remark"=>'',//备注
  146. // "seq"=>'',//三方下发序列号
  147. // "soc"=>'',//被充车辆 SOC 不一定有
  148. "create_time" => datetime(),//创建时间
  149. "update_time" => datetime(),//创建时间
  150. ];
  151. //第二步,根据所差时段开始时间 和 所差时段结束时间 ,循环放电
  152. $start_time = $bad_start_periods_time;
  153. // $end_time = $dat['start_time'];
  154. $charging_interval = rand($robot->charging_interval_min * 60, $robot->charging_interval_max * 60);//随机间隔充电时间 单位(秒)
  155. while (true) {
  156. $this->logger->info("循环充电");
  157. $data = $dat;
  158. $start_time += $charging_interval;//本次机器人放电时间,即车辆充电开始时间
  159. //本次充电结束时间大于预定时间段,则充电订单生成结束
  160. if ($start_time > $bad_end_periods_time) {
  161. break;
  162. }
  163. //获取此次充电量KWH
  164. $rand_soc = rand($robot->charging_time_start * 100, $robot->charging_time_end * 100) / 100;//乘除是为了创造小数
  165. $this->logger->info($robot->id . '本轮获取此次充电量KWH:' . $rand_soc);
  166. //获取此次充电效率
  167. //本轮放电概率确认
  168. $lde = $this->randApproximateRate($robot);;
  169. $this->logger->info($robot->id . '本轮充放电概率确认, 放:' . $lde);
  170. //判断机器人电量是否足够此次放电,要不超出放电
  171. if ($rand_soc > $total_power) {
  172. $rand_soc = $total_power;
  173. //提示本次放完电机器人就需要充电了
  174. $is_charge = true;
  175. }
  176. //如果本次充电量小于3kwh,直接给机器人充电
  177. if ($rand_soc > self::$charge_minimum_power) {
  178. //获取本次充电时间 时长 = 电量 * 放电率
  179. $fc = floatval(bcmul((string)$rand_soc, (string)$lde, 2));
  180. $duration = ceil($fc);//所需分钟
  181. $end_time = $start_time + ceil($fc * 60);//所需秒钟
  182. //电费 = 站点规定当前时段(电费单价 + 服务单价)* 电量
  183. $amount = 0;
  184. //根据当前时段,判断电费单价和服务费单价
  185. //有配置就有配置单价,没有配置用默认
  186. $t_date_ymd = date('Y-m-d', intval($start_time));
  187. foreach ($site_price as $v) {
  188. $t_start_time = strtotime($t_date_ymd . ' ' . $v['start_time']);
  189. $t_ent_time = strtotime($t_date_ymd . ' ' . $v['ent_time']);
  190. if ($start_time >= $t_start_time && $t_start_time <= $t_ent_time) {
  191. $t_price = ($v['price'] == 0) ? $robot->default_electricity_bill : $v['price'];
  192. $t_serve_price = ($v['serve_price'] == 0) ? $robot->default_service_price : $v['serve_price'];
  193. $amount = floatval(bcmul((string)$rand_soc, bcadd((string)$t_price, (string)$t_serve_price, 2), 2));
  194. }
  195. }
  196. //如果没有计算,则用默认规定的单价
  197. if ($amount == 0) {
  198. $amount = floatval(bcmul((string)$rand_soc, bcadd((string)$robot->default_electricity_bill, (string)$robot->default_service_price, 2), 2));
  199. }
  200. //确认用户
  201. $this_user = $userLists[array_rand($userLists, 1)];
  202. $data['user_id'] = $this_user['user_id'];//
  203. $data['open_id'] = $this_user['open_id'];//
  204. $data['customer_phone'] = $this_user['user']['phone'];//
  205. $data['buy_order_id'] = $this_user['member_number'];//
  206. $data['order_no'] = 'TR' . $this->getPrefixData();//
  207. $data['duration'] = $duration;//
  208. $data['power'] = $rand_soc;//
  209. $data['amount'] = $amount;//
  210. $data['create_time'] = datetime($start_time);//
  211. $data['update_time'] = datetime($end_time);//
  212. //机器人容量减少
  213. $total_power = floatval(bcsub((string)$total_power, (string)$rand_soc, 2));
  214. //根据占比判断是否保存生成订单:
  215. if (rand(0, 100) > $robot->occupancy) {
  216. $this->logger->info("本轮跳过...");
  217. continue;
  218. } else {
  219. $res_data[] = $data;
  220. }
  221. }
  222. //判断是否需要充电
  223. if ($is_charge) {
  224. $start_time += $charging_c;
  225. //刷新 下轮充放电概率确认
  226. //充
  227. $c_efficiency = $this->randApproximateRate($robot);
  228. // //放
  229. // $d_efficiency = $this->randApproximateRate($robot);
  230. //总电量:
  231. $total_power = $robot->full_charge;
  232. //下轮机器人充电和放电时间时长确认 满电量*波动概率 (单位:秒)
  233. //充
  234. $charging_c = ceil($total_power * $c_efficiency * 60);
  235. // //放
  236. // $efficiency_f = $total_power * $d_efficiency * 60;
  237. $this->logger->info(PHP_EOL . $robot->id . '下轮充放电概率确认, 充:' . $c_efficiency);
  238. $is_charge = false;
  239. }
  240. }
  241. if (!empty($res_data)) {
  242. Db::beginTransaction();
  243. try {
  244. $t_id = 0;
  245. foreach ($res_data as $da) {
  246. $t_id = RTradeOrderModel::insertGetId($da);
  247. }
  248. $this->logger->info(datetime() . ' ' . $robot->id . ' 生成成功: ' . ($t_id ? 'true' : 'false') . PHP_EOL . PHP_EOL);
  249. if ($robot->num == 1) {
  250. RRobotAutoOrderModel::where('id', $robot->id)->update(['num' => 2]);
  251. }
  252. Db::commit();
  253. } catch (\Exception $e) {
  254. Db::rollBack();
  255. $this->logger->error(datetime() . ' ' . $robot->id . '生成失败: ' . $e->getMessage() . PHP_EOL . PHP_EOL);
  256. }
  257. }
  258. }
  259. }
  260. return true;
  261. }
  262. /**
  263. * Desc: 随机概率
  264. * Author: Spt <2864934511@qq.com>
  265. * Date: 2024-03-13 15:57
  266. * @param $robot
  267. * @return float|int
  268. */
  269. private function randApproximateRate($robot): float|int
  270. {
  271. return (rand(intval($robot->charging_efficiency_min * 100), intval($robot->charging_efficiency_max * 100))) / 100;
  272. }
  273. /**
  274. * Desc: 自动生成前缀
  275. * Author: Spt <2864934511@qq.com>
  276. * Date: 2024-03-12 15:41
  277. * @param string $prefix
  278. * @return string
  279. */
  280. private function getPrefixData(string $prefix = ''): string
  281. {
  282. return $prefix . date("YmdHis") . rand(0, 9999);
  283. }
  284. /**
  285. * Desc: 格式化时间格式为: *分*秒
  286. * Author: Spt <2864934511@qq.com>
  287. * Date: 2024-03-13 16:39
  288. * @param $seconds
  289. * @return string
  290. */
  291. private function convertSecondsToMinutesAndSeconds($seconds): string
  292. {
  293. $minutes = intval($seconds / 60);
  294. $remainingSeconds = $seconds % 60;
  295. $paddedSeconds = str_pad((string)$remainingSeconds, 2, '0', STR_PAD_LEFT); // 在左侧用0填充,直到长度为2
  296. return $minutes . '分' . $paddedSeconds . '秒';
  297. }
  298. }