* Date: 2024-03-13 08:36 */ #[Crontab(rule: "0 *\/1 * * *", name: "SckwChgrobot", callback: "execute", memo: "自动生成订单任务每天一次", enable: "isEnable")] class SckwChgrobotCrontab { // /** // * 定时任务唯一标识 // */ // protected const code = 'auto_order_crontab'; #[Inject] private StdoutLoggerInterface $logger; private static array $users = [ 'oJdn-7JDOOCzlOgS6rE5CsXw_PbM',//pdy // '',// ]; /** * 充电最低标准电量 * @var int */ private static int $charge_minimum_power = 3; /** * Desc: 是否随开机启动 * Author: Spt <2864934511@qq.com> * Date: 2024-03-12 16:08 * @return bool */ public function isEnable(): bool { return true; } public function execute(): void { $this->logger->info(__CLASS__ . ' 自动生成订单任务开始 ' . datetime()); $this->task(); } private function task(): bool { //插入测试员工 - ok //INSERT INTO `t_employee`(`employee_id`, `system_type`, `login_name`, `login_pwd`, `open_id`, `actual_name`, `birthday`, `phone`, `remark`) VALUES (87, 5, '18589088034', '18589088034', 'oJdn-7JDOOCzlOgS6rE5CsXw_PbM', 'pdy', '2001-02-01 14:37:37', '18589088034', '测试号,不删除'); //插入测试员工会员卡 - ok //INSERT INTO `r_member_order`(`id`, `open_id`, `type`, `user_id`, `order_no`, `member_no`, `end_time`, `create_time`, `update_time`, `status`, `del_flag`, `member_facility_id`) VALUES (NULL, 'oJdn-7JDOOCzlOgS6rE5CsXw_PbM', 1, 87, 'M2024031316352413', '202403131437082', '2024-03-15 16:35:24', '2024-03-08 16:35:24', '2024-03-08 16:35:24', 0, 0, NULL); //INSERT INTO `r_employee_member`(`open_id`, `user_id`, `member_number`, `create_time`, `update_time`, `end_time`) VALUES ('oJdn-7JDOOCzlOgS6rE5CsXw_PbM', 87,'202403131437082','2024-03-13 14:34:01','2024-03-13 14:34:03','2029-04-01 14:34:05') ; // //获取所有任务 $list = RRobotAutoOrderModel::with(['robot', 'site', 'price']) ->where('num', '<', 2)//执行次数0无限1一次超过1则不执行 ->where('del_flag', 0) ->get(); if ($list->isNotEmpty()) { //获取所有可用人员 $userLists = TEmployeeMemberModel::with(['user']) ->whereIn('open_id', self::$users)//openid 范围查询 ->where('del_flag', 0)//是否删除0否 1是 ->get(); if ($userLists->isEmpty()) { $this->logger->error(' 查无会员用户:' . json_encode(self::$users, JSON_UNESCAPED_UNICODE)); return false; } else { $userLists = $userLists->toArray(); } foreach ($list as $robot) { //本轮充放电概率确认 //充 $c_efficiency = $this->randApproximateRate($robot); // //放 // $d_efficiency = $this->randApproximateRate($robot); $this->logger->info($robot->id . '本轮充放电概率确认, 充:' . $c_efficiency);// . ' 放:' . $d_efficiency //总电量: $total_power = $robot->full_charge; $this->logger->info($robot->id . "总电量 :" . $total_power); //本轮机器人充电和放电时间时长确认 满电量*波动概率 (单位:秒) //充 $charging_c = ceil($total_power * $c_efficiency * 60); // //放 // $efficiency_f = $total_power * $d_efficiency * 60; // $this->logger->info($robot->id . "本轮机器人充电放电时间时长确认(单位:秒):" . $charging_c . ' ' . $efficiency_f); //所差时段开始时间 和 所差时段结束时间 ,循环放电 if ($robot->num == 1) { $bad_start_periods_time = strtotime($robot->bad_start_periods); $bad_end_periods_time = strtotime($robot->bad_end_periods); } else { $bad_start_periods_time = strtotime(date('Y-m-d') . $robot->bad_start_periods); $bad_end_periods_time = strtotime(date('Y-m-d') . $robot->bad_end_periods); } //电价 if (!$robot->price || empty($robot->price)) { $this->logger->info('注意当前充电站点无单价配置,将用默认配置:' . $robot->id); $site_price[] = [ 'site_id' => $robot->site_id, 'start_time' => '00:00:00', 'ent_time' => '23:59:59', 'price' => 0, 'serve_price' => 0, ]; } else { $site_price = $robot->price->toArray(); } $is_charge = false;//机器人是否需要充电 //默认组装 $dat = [ // "robot" => $robot->robot->imei, // "order" => "", // "user" => '', // "start_time" => $bad_start_periods_time, // "end_time" => "", // "time" => 0,//时长 // "soc" => "",//电量占比 // "billing" => "", // "c_type" => 1,//充电模式 // "start_soc" => 0,//充电开始度数 // "end_soc" => 0,//充电结束度数 "user_id" => '',//用户id "open_id" => '',//微信appid "customer_phone" => '',//客户电话 "type" => 2,//1预购订单2点卡充值订单 "buy_order_id" => '',//预缴费订单id/点卡充值订单id(memberOrderId) "site_id" => $robot->site_id,//站点id "robot_id" => $robot->robot_id,//机器人id "order_no" => '',//系统订单号(必须) "charging_pile_id" => $robot->robot->imei,//充电桩id "pile_imei" => $robot->robot->imei,//终端 IMEI "charge_model" => 5,//充电模式(5 自然充满、10 按电\r\n量、15 按金额) // "plan_power"=>null,//计划充电量(kwh) 非自然充满模\r\n式必填 // "plan_amount"=>null,//计划充电金额,自然充满模式则\r\n为用户余额 "status" => 4,//订单状态(1进行中,2已结算,3未支付(预留),4完成) // "third_order_no"=>'',//三方订单号(记录 ID) "duration" => 0,//充电时长(分钟)向上取整- "voltage" => 220,//电压 "electric_current" => 6,//电流 "power" => 0,//充电量(kwh)- "amount" => 0,//订单金额- "charging_status" => 15,//充电状态(5: 待充电,10: 充电中,15: 已结束,20: 已取消) // "stop_reason"=>'',//结束原因 // "remark"=>'',//备注 // "seq"=>'',//三方下发序列号 // "soc"=>'',//被充车辆 SOC 不一定有 "create_time" => datetime(),//创建时间 "update_time" => datetime(),//创建时间 ]; //第二步,根据所差时段开始时间 和 所差时段结束时间 ,循环放电 $start_time = $bad_start_periods_time; // $end_time = $dat['start_time']; $charging_interval = rand($robot->charging_interval_min * 60, $robot->charging_interval_max * 60);//随机间隔充电时间 单位(秒) while (true) { $this->logger->info("循环充电"); $data = $dat; $start_time += $charging_interval;//本次机器人放电时间,即车辆充电开始时间 //本次充电结束时间大于预定时间段,则充电订单生成结束 if ($start_time > $bad_end_periods_time) { break; } //获取此次充电量KWH $rand_soc = rand($robot->charging_time_start * 100, $robot->charging_time_end * 100) / 100;//乘除是为了创造小数 $this->logger->info($robot->id . '本轮获取此次充电量KWH:' . $rand_soc); //获取此次充电效率 //本轮放电概率确认 $lde = $this->randApproximateRate($robot);; $this->logger->info($robot->id . '本轮充放电概率确认, 放:' . $lde); //判断机器人电量是否足够此次放电,要不超出放电 if ($rand_soc > $total_power) { $rand_soc = $total_power; //提示本次放完电机器人就需要充电了 $is_charge = true; } //如果本次充电量小于3kwh,直接给机器人充电 if ($rand_soc > self::$charge_minimum_power) { //获取本次充电时间 时长 = 电量 * 放电率 $fc = floatval(bcmul((string)$rand_soc, (string)$lde, 2)); $duration = ceil($fc);//所需分钟 $end_time = $start_time + ceil($fc * 60);//所需秒钟 //电费 = 站点规定当前时段(电费单价 + 服务单价)* 电量 $amount = 0; //根据当前时段,判断电费单价和服务费单价 //有配置就有配置单价,没有配置用默认 $t_date_ymd = date('Y-m-d', intval($start_time)); foreach ($site_price as $v) { $t_start_time = strtotime($t_date_ymd . ' ' . $v['start_time']); $t_ent_time = strtotime($t_date_ymd . ' ' . $v['ent_time']); if ($start_time >= $t_start_time && $t_start_time <= $t_ent_time) { $t_price = ($v['price'] == 0) ? $robot->default_electricity_bill : $v['price']; $t_serve_price = ($v['serve_price'] == 0) ? $robot->default_service_price : $v['serve_price']; $amount = floatval(bcmul((string)$rand_soc, bcadd((string)$t_price, (string)$t_serve_price, 2), 2)); } } //如果没有计算,则用默认规定的单价 if ($amount == 0) { $amount = floatval(bcmul((string)$rand_soc, bcadd((string)$robot->default_electricity_bill, (string)$robot->default_service_price, 2), 2)); } //确认用户 $this_user = $userLists[array_rand($userLists, 1)]; $data['user_id'] = $this_user['user_id'];// $data['open_id'] = $this_user['open_id'];// $data['customer_phone'] = $this_user['user']['phone'];// $data['buy_order_id'] = $this_user['member_number'];// $data['order_no'] = 'TR' . $this->getPrefixData();// $data['duration'] = $duration;// $data['power'] = $rand_soc;// $data['amount'] = $amount;// $data['create_time'] = datetime($start_time);// $data['update_time'] = datetime($end_time);// //机器人容量减少 $total_power = floatval(bcsub((string)$total_power, (string)$rand_soc, 2)); //根据占比判断是否保存生成订单: if (rand(0, 100) > $robot->occupancy) { $this->logger->info("本轮跳过..."); continue; } else { $res_data[] = $data; } } //判断是否需要充电 if ($is_charge) { $start_time += $charging_c; //刷新 下轮充放电概率确认 //充 $c_efficiency = $this->randApproximateRate($robot); // //放 // $d_efficiency = $this->randApproximateRate($robot); //总电量: $total_power = $robot->full_charge; //下轮机器人充电和放电时间时长确认 满电量*波动概率 (单位:秒) //充 $charging_c = ceil($total_power * $c_efficiency * 60); // //放 // $efficiency_f = $total_power * $d_efficiency * 60; $this->logger->info(PHP_EOL . PHP_EOL . $robot->id . '下轮充放电概率确认, 充:' . $c_efficiency); $is_charge = false; } } if (!empty($res_data)) { Db::beginTransaction(); try { var_dump(json_encode($res_data, JSON_UNESCAPED_UNICODE)); // $res = (new \App\Model\RTradeOrderModel)->save($res_data); $t_id = 0; foreach ($res_data as $da) { $t_id = RTradeOrderModel::insertGetId($da); } $this->logger->info(datetime() . ' ' . $robot->id . ' 生成成功: ' . ($t_id ? 'true' : 'false')); if ($robot->num == 1) { RRobotAutoOrderModel::where('id', $robot->id)->update(['num' => 2]); } Db::commit(); } catch (\Exception $e) { Db::rollBack(); $this->logger->error(datetime() . ' ' . $robot->id . '生成失败: ' . $e->getMessage()); } } } } return true; } /** * Desc: 随机概率 * Author: Spt <2864934511@qq.com> * Date: 2024-03-13 15:57 * @param $robot * @return float|int */ private function randApproximateRate($robot): float|int { return (rand(intval($robot->charging_efficiency_min * 100), intval($robot->charging_efficiency_max * 100))) / 100; } /** * Desc: 自动生成前缀 * Author: Spt <2864934511@qq.com> * Date: 2024-03-12 15:41 * @param string $prefix * @return string */ private function getPrefixData(string $prefix = ''): string { return $prefix . date("YmdHis") . rand(0, 9999); } /** * Desc: 格式化时间格式为: *分*秒 * Author: Spt <2864934511@qq.com> * Date: 2024-03-13 16:39 * @param $seconds * @return string */ private function convertSecondsToMinutesAndSeconds($seconds): string { $minutes = intval($seconds / 60); $remainingSeconds = $seconds % 60; $paddedSeconds = str_pad((string)$remainingSeconds, 2, '0', STR_PAD_LEFT); // 在左侧用0填充,直到长度为2 return $minutes . '分' . $paddedSeconds . '秒'; } }