protected function updatePeriodSnapshot($type, $startDate, $periodName, $shopId)
{
$periodEnd = $this->getPeriodEndDate($startDate, $type);
$startTime = strtotime($startDate);
$endTime = $periodEnd == $startDate
? strtotime($periodEnd . ' 23:59:59')
: strtotime($periodEnd);
$uniqueSign = CheckEmployee::generateUniqueSign($startTime, $type)[2];
$exists = CheckEmployee::where([
'shop_id' => $shopId,
'type' => $type,
'unique_sign' => $uniqueSign,
])->find();
if ($exists) {
Log::info("{$periodName} 快照已存在,跳过:{$startDate}");
return;
}
Log::info("开始更新 {$periodName} 快照: {$startDate} 至 {$periodEnd}");
# 公共 where
$baseWhere = [
['status', '=', 2],
['shop_id', '=', $shopId],
['create_time', 'between', [$startTime, $endTime]],
];
# 一次查所有订单数据
$orders = Order::where($baseWhere)->select()->toArray();
if (!$orders) {
Log::info("无订单数据,无需生成快照");
return;
}
# 按员工 + 设备号 分组
$grouped = [];
foreach ($orders as $o) {
$key = $o['op_employee_id'] . '_' . $o['device_num'];
$grouped[$key][] = $o;
}
# 统计新会员数
$newMemberNum = Member::where(['shop_id' => $shopId])
->whereBetweenTime('create_time', $startTime, $endTime)
->count();
$addData = [];
foreach ($grouped as $key => $list) {
[$employeeId, $deviceNum] = explode('_', $key);
$orderIds = array_column($list, 'id');
$stat = CheckEmployee::getCheckEmployeeData(
$list,
$orderIds,
$shopId,
$startTime,
$endTime
);
$addData[] = [
'shop_id' => $shopId,
'device_num' => $deviceNum,
'employee_id' => $employeeId,
'start_time' => $startTime,
'end_time' => $endTime,
'type' => $type,
'unique_sign' => $uniqueSign,
'cash_money' => $stat['cashMoney'],
'remain_money' => $stat['remainMoney'],
'saving_money' => $stat['savingMoney'],
'credit_money' => $stat['creditMoney'],
'scan_money' => $stat['scanMoney'],
'cash_order_num' => $stat['cashOrderNum'],
'scan_order_num' => $stat['scanOrderNum'],
'remain_order_num' => $stat['remainOrderNum'],
'saving_order_num' => $stat['savingOrderNum'],
'credit_order_num' => $stat['creditOrderNum'],
'total_order_num' => $stat['totalOrderNum'],
'new_member_num' => $newMemberNum,
'quick_money' => $stat['quickMoney'],
'sale_income' => $stat['saleIncome'],
'com_income' => $stat['comIncome'],
'service_money' => $stat['serviceMoney'],
'normal_money' => $stat['normalMoney'],
'total_charge_money' => $stat['totalChargeMoney'],
'total_charge_times_money' => $stat['totalChargeTimesMoney'],
'total_continue_money' => $stat['totalContinueMoney'],
'total_combo_money' => $stat['totalComboMoney'],
'total_refund_money' => $stat['totalRefundMoney'],
'vip_money' => $stat['vipMoney'],
];
}
$this->saveEmployeeSnapshots($addData, $shopId, $type, $startTime,$endTime);
Log::info("{$periodName} 快照更新完成");
}
protected function getPeriodEndDate($startDate, $periodType)
{
$start = strtotime($startDate);
switch ($periodType) {
case 2: # 周
return date('Y-m-d', $start + 6 * 86400);
case 3: # 月
return date('Y-m-t', $start);
case 4: # 季
$month = date('m', $start);
$year = date('Y', $start);
$endMonth = $month + 2;
if ($endMonth > 12) {
$endMonth -= 12;
$year++;
}
$endMonth = str_pad((string)$endMonth, 2, '0', STR_PAD_LEFT);
return date('Y-m-t', strtotime("{$year}-{$endMonth}-01"));
case 5: # 年
return date('Y-12-31', $start);
default:
return $startDate;
}
}
public static function generateUniqueSign($timestamp, $type)
{
$startTime = 0;
$endTime = 0;
$uniqueSign = '';
$year = date('Y', $timestamp);
if ($type == 1) {
$uniqueSign = date('Y-m-d', $timestamp);
$startTime = strtotime($uniqueSign . ' 00:00:00');
$endTime = strtotime($uniqueSign . ' 23:59:59');
}
if ($type == 2) {
$week = date('W', $timestamp);
$uniqueSign = "{$year}-W{$week}";
$startTime = self::getStartTimeByWeekYear($week, $year);
$endTime = self::getEndTimeByWeekYear($week, $year);
}
if ($type == 3) {
$uniqueSign = date('Y-m', $timestamp);
$startTime = self::getStartTimeByYearMonth($year, date('n', $timestamp));
$endTime = self::getEndTimeByYearMonth($year, date('n', $timestamp));
}
if ($type == 4) {
$quarter = self::getQuarterNum($timestamp);
$uniqueSign = "{$year}-Q{$quarter}";
$startTime = self::getStartTimeByQuarter($year, $quarter);
$endTime = self::getEndTimeByQuarter($year, $quarter);
}
if ($type == 5) {
$year = date('Y', $timestamp);
$uniqueSign = $year;
$startTime = self::getStartTimeByYear($year);
$endTime = self::getEndTimeByYear($year);
}
return [$startTime, $endTime, $uniqueSign];
}
public static function getCheckEmployeeData(array $orders, array $orderIds, int $shopId, int $startTime, int $endTime)
{
# -------- 金额累加 --------
$cashMoney = array_sum(array_column($orders, 'cash'));
$remainMoney = array_sum(array_column($orders, 'remain_money'));
$scanMoney = array_sum(array_column($orders, 'scan'));
$savingMoney = array_sum(array_column($orders, 'saving'));
$creditMoney = array_sum(array_column($orders, 'credit'));
# 找零金额
$roundMoney = array_sum(array_column($orders, 'round_down_money'));
$cashMoney -= $roundMoney;
# -------- 订单数量 --------
$totalOrderNum = count($orders);
# -------- 支付方式订单数 --------
$cashOrderNum = $scanOrderNum = $remainOrderNum = $savingOrderNum = $creditOrderNum = 0;
# -------- 按订单类型金额 --------
$totalChargeMoney = 0;
$totalChargeTimesMoney = 0;
$totalContinueMoney = 0;
$totalComboMoney = 0;
$vipMoney = 0;
$totalRefundMoney = 0;
# -------- 总金额 --------
$totalMoney = 0;
foreach ($orders as $o) {
$totalMoney += $o['real_money'];
# 支付方式
if ($o['pay_type'] & 1) $cashOrderNum++;
if ($o['pay_type'] & 2) $scanOrderNum++;
if ($o['pay_type'] & 4) $remainOrderNum++;
if ($o['pay_type'] & 8) $savingOrderNum++;
if ($o['pay_type'] & 16) $creditOrderNum++;
# 订单类型
switch ($o['order_type']) {
case 3:
$totalChargeMoney += $o['real_money'];
break;
case 4:
$totalChargeTimesMoney += $o['real_money'];
break;
case 6:
$totalRefundMoney += $o['real_money'];
break;
case 7:
case 8:
$totalContinueMoney += $o['real_money'];
break;
case 9:
$totalComboMoney += $o['real_money'];
break;
case 10:
$vipMoney += $o['real_money'];
break;
}
}
# 综合 / 销售收入
$comIncome = $totalMoney + $totalRefundMoney - $remainMoney;
$saleIncome = $comIncome - $totalChargeMoney;
# -------- 商品类金额 --------
$serviceMoney = OrderDetail::alias('od')
->join('goods g', 'g.id = od.goods_id')
->whereBetween('od.create_time', [$startTime, $endTime])
->whereIn('od.order_id', $orderIds)
->whereIn('g.goods_type', [2, 3, 4, 6])
->sum(Db::raw('goods_num * goods_sale_price'));
$normalMoney = OrderDetail::alias('od')
->join('goods g', 'g.id = od.goods_id')
->whereBetween('od.create_time', [$startTime, $endTime])
->whereIn('od.order_id', $orderIds)
->whereIn('g.goods_type', [1, 5])
->sum(Db::raw('goods_num * goods_sale_price'));
$quickMoney = OrderDetail::where([
['order_id', 'in', $orderIds],
['goods_id', '=', -2],
['order_band_id', '=', 0],
])->sum(Db::raw('goods_num * goods_sale_price'));
return compact(
'serviceMoney', 'normalMoney', 'quickMoney',
'saleIncome', 'comIncome',
'totalChargeMoney', 'totalChargeTimesMoney', 'totalRefundMoney',
'totalContinueMoney', 'totalComboMoney', 'vipMoney',
'totalOrderNum',
'cashMoney', 'remainMoney', 'savingMoney', 'creditMoney', 'scanMoney',
'cashOrderNum', 'scanOrderNum', 'remainOrderNum', 'savingOrderNum', 'creditOrderNum'
);
}
protected function saveEmployeeSnapshots(array $employeeRows, int $shopId, int $type, int $startTime, int $endTime)
{
# 保存员工 + 设备 -- 层级3
CheckEmployee::saveAll($employeeRows);
# 单店汇总 -- 层级2
$shopRow = $this->buildShopSummary($employeeRows, $shopId, $type, $startTime,$endTime);
if ($shopRow) {
CheckEmployee::create($shopRow);
}
# 全店汇总 -- 层级1
$totalRow = $this->buildTotalSummary($employeeRows, $type, $startTime,$endTime);
if ($totalRow) {
CheckEmployee::create($totalRow);
}
}
protected function buildShopSummary(array $rows, int $shopId, int $type, int $startTime, int $endTime)
{
if (empty($rows)) return null;
$sum = $this->sumSnapshotRows($rows);
return array_merge($sum, [
'shop_id' => $shopId,
'device_num' => -1,
'employee_id'=> -1,
'type' => $type,
'unique_sign'=> CheckEmployee::generateUniqueSign($startTime, $type)[2],
'start_time' => $startTime,
'end_time' => $endTime,
]);
}
protected function buildTotalSummary(array $rows, int $type, int $startTime, int $endTime)
{
if (empty($rows)) {
return null;
}
$sum = $this->sumSnapshotRows($rows);
return array_merge($sum, [
'shop_id' => 0,
'device_num' => 0,
'employee_id' => 0,
'type' => $type,
'unique_sign' => CheckEmployee::generateUniqueSign($startTime, $type)[2],
'start_time' => $startTime,
'end_time' => $endTime,
]);
}
protected function sumSnapshotRows(array $rows)
{
$fields = [
'cash_money','remain_money','saving_money','credit_money','scan_money',
'cash_order_num','scan_order_num','remain_order_num','saving_order_num','credit_order_num',
'total_order_num','new_member_num','quick_money','sale_income','com_income',
'service_money','normal_money','total_charge_money','total_charge_times_money',
'total_continue_money','total_combo_money','total_refund_money','vip_money'
];
$sum = array_fill_keys($fields, 0);
foreach ($rows as $row) {
foreach ($fields as $f) {
$sum[$f] += $row[$f] ?? 0;
}
}
return $sum;
}