Merge branch 'master' of ssh://118.31.104.155:4422/mnt/git/v2.tiantianxinye.365care

# Conflicts:
#	public/h5/.DS_Store
master
cody 4 months ago
commit d3d4ce718e

BIN
.DS_Store vendored

Binary file not shown.

@ -4,6 +4,7 @@ namespace App\Console\Commands;
use App\Models\OrderItems;
use App\Models\Orders;
use App\Models\Project;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
@ -44,8 +45,12 @@ class CreateTodayOrderItems extends Command
$threshold = 50;
DB::enableQueryLog();
// 获取所有状态为1的项目
$projects = Project::where("status", 1)->pluck("id")->toArray();
//获取正在进行中的订单,即使已经到了截止日,只要状态是在进行中的都继续生成
$unGeneratedOrders = (new Orders())->whereIn("status", [Orders::STATUS_ONGOING])
->whereIn("project_id", $projects)
->whereRaw("DATEDIFF(`from_date`, now()) <= 0")
->whereDoesntHave("orderItems", function ($query) {
$query->whereRaw("DATEDIFF(`service_date`, now()) = 0");

@ -0,0 +1,333 @@
<?php
namespace App\Console\Commands;
use App\Models\Balance;
use App\Models\OrderItems;
use App\Models\Orders;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
class FixOrderItemsPaidAt extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'order-items:fix-paid-at {date} {--dry-run : 试运行模式,只显示预览信息,不执行实际修复}';
/**
* The console command description.
*
* @var string
*/
protected $description = '修复指定日期 service_date 的 order_items 表中滞后于 service_date 的 paid_at 字段';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$date = $this->argument('date');
// 验证日期格式
try {
$serviceDate = Carbon::createFromFormat('Y-m-d', $date);
} catch (\Exception $e) {
$this->error("日期格式错误,请使用 Y-m-d 格式例如2025-12-01");
return 1;
}
$serviceDateStr = $serviceDate->format('Y-m-d');
$serviceDateEnd = $serviceDateStr . ' 23:59:59';
// 计算 service_date 所在月份的月底最后一秒
$monthEnd = $serviceDate->copy()->endOfMonth()->format('Y-m-d 23:59:59');
$isDryRun = $this->option('dry-run');
if ($isDryRun) {
$this->warn("=== 试运行模式:将只显示预览信息,不会执行实际修复 ===");
$this->line('');
}
$this->info("开始" . ($isDryRun ? "预览" : "修复") . " {$serviceDateStr} 的订单项...");
$this->info("目标时间点:{$serviceDateEnd}");
$this->info("余额计算时间点:{$monthEnd}(当月月底最后一秒)");
// 查询需要修复的记录
// 条件paid_at 的日期晚于 service_date且不在同一月份
$items = OrderItems::where('service_date', $serviceDateStr)
->whereNotNull('paid_at')
->whereRaw("DATE(`paid_at`) > '{$serviceDateStr}'")
->whereRaw("DATE_FORMAT(`paid_at`, '%Y-%m') > DATE_FORMAT('{$serviceDateStr}', '%Y-%m')")
->where('total', '>', 0)
->with(['order' => function ($query) {
$query->select('id', 'customer_id');
}])
->get();
$totalCount = $items->count();
$this->info("找到 {$totalCount} 条需要检查的记录");
if ($totalCount == 0) {
$this->info("没有需要修复的记录");
return 0;
}
$fixedCount = 0;
$skippedCount = 0;
$errorCount = 0;
$previewData = [];
if (!$isDryRun) {
$bar = $this->output->createProgressBar($totalCount);
$bar->start();
} else {
$this->line('');
$this->info("预览将要修复的记录:");
$this->line('');
}
foreach ($items as $item) {
try {
// 获取客户ID
$customerId = $item->order ? $item->order->customer_id : null;
if (!$customerId) {
if (!$isDryRun) {
$this->line('');
$this->warn("订单项 {$item->id} 没有关联的客户,跳过");
$bar->advance();
}
$skippedCount++;
continue;
}
// 计算在 service_date 所在月份月底最后一秒时的客户余额
$balanceAtTime = $this->calculateCustomerBalanceAtTime($customerId, $monthEnd, $item->id);
// 判断是否满足修复条件
if ($balanceAtTime >= $item->total) {
// 满足修复条件
$originalPaidAt = $item->paid_at;
// 检查是否有关联的 balance 记录
$balanceRecord = Balance::where('belongs_type', OrderItems::class)
->where('belongs_id', $item->id)
->where('customer_id', $customerId)
->first();
if ($isDryRun) {
// 试运行模式:只收集预览信息
$previewData[] = [
'order_item_id' => $item->id,
'order_id' => $item->order_id,
'customer_id' => $customerId,
'service_date' => $item->service_date,
'total' => $item->total,
'balance_at_time' => $balanceAtTime,
'original_paid_at' => $originalPaidAt,
'new_paid_at' => $serviceDateEnd,
'has_balance_record' => $balanceRecord ? '是' : '否',
'balance_record_id' => $balanceRecord ? $balanceRecord->id : null,
];
$fixedCount++;
} else {
// 实际执行修复
DB::beginTransaction();
try {
// 更新 order_items
$item->update([
'paid_at' => $serviceDateEnd,
'paid_at_original' => $originalPaidAt
]);
// 更新关联的 balance 记录
if ($balanceRecord) {
$balanceRecord->update([
'created_at' => $serviceDateEnd
]);
} else {
$this->line('');
$this->warn("订单项 {$item->id} 没有找到关联的 balance 记录");
}
DB::commit();
$fixedCount++;
Log::info("修复订单项 {$item->id}:原 paid_at {$originalPaidAt} -> {$serviceDateEnd}");
} catch (\Exception $e) {
DB::rollBack();
$errorCount++;
$this->line('');
$this->error("修复订单项 {$item->id} 失败:" . $e->getMessage());
Log::error("修复订单项 {$item->id} 失败:" . $e->getMessage());
}
}
} else {
// 余额不足,不满足修复条件
if ($isDryRun) {
$previewData[] = [
'order_item_id' => $item->id,
'order_id' => $item->order_id,
'customer_id' => $customerId,
'service_date' => $item->service_date,
'total' => $item->total,
'balance_at_time' => $balanceAtTime,
'original_paid_at' => $item->paid_at,
'new_paid_at' => '不满足条件',
'has_balance_record' => '-',
'balance_record_id' => null,
'skip_reason' => '余额不足(余额:' . $balanceAtTime . ' < 金额:' . $item->total . '',
];
}
$skippedCount++;
}
} catch (\Exception $e) {
$errorCount++;
if (!$isDryRun) {
$this->line('');
$this->error("处理订单项 {$item->id} 时出错:" . $e->getMessage());
}
Log::error("处理订单项 {$item->id} 时出错:" . $e->getMessage());
}
if (!$isDryRun) {
$bar->advance();
}
}
if (!$isDryRun) {
$bar->finish();
}
$this->line('');
$this->line('');
// 试运行模式:显示详细预览信息
if ($isDryRun && !empty($previewData)) {
$this->info("=== 预览详情 ===");
$this->line('');
// 显示满足条件的记录
$willFix = array_filter($previewData, function($item) {
return !isset($item['skip_reason']);
});
if (!empty($willFix)) {
$this->info("满足条件将修复的记录(" . count($willFix) . " 条):");
$this->line('');
$this->table(
['订单项ID', '订单ID', '客户ID', '服务日期', '金额', '当时余额', '原paid_at', '新paid_at', '有balance记录'],
array_map(function($item) {
return [
$item['order_item_id'],
$item['order_id'],
$item['customer_id'],
$item['service_date'],
$item['total'],
$item['balance_at_time'],
$item['original_paid_at'],
$item['new_paid_at'],
$item['has_balance_record'],
];
}, $willFix)
);
$this->line('');
}
// 显示跳过的记录
$willSkip = array_filter($previewData, function($item) {
return isset($item['skip_reason']);
});
if (!empty($willSkip)) {
$this->warn("不满足条件将跳过的记录(" . count($willSkip) . " 条):");
$this->line('');
$this->table(
['订单项ID', '订单ID', '客户ID', '服务日期', '金额', '当时余额', '原paid_at', '跳过原因'],
array_map(function($item) {
return [
$item['order_item_id'],
$item['order_id'],
$item['customer_id'],
$item['service_date'],
$item['total'],
$item['balance_at_time'],
$item['original_paid_at'],
$item['skip_reason'],
];
}, $willSkip)
);
$this->line('');
}
}
// 输出统计信息
$this->info(($isDryRun ? "预览" : "修复") . "完成!");
$this->table(
['统计项', '数量'],
[
['总记录数', $totalCount],
['满足条件' . ($isDryRun ? '(将修复)' : '并修复'), $fixedCount],
['余额不足跳过', $skippedCount],
['处理失败', $errorCount],
]
);
if ($isDryRun) {
$this->line('');
$this->comment("提示:这是试运行模式,没有执行实际修复。");
$this->comment("要执行实际修复,请运行命令时不加 --dry-run 参数。");
}
return 0;
}
/**
* 计算指定时间点的客户余额
*
* @param int $customerId
* @param string $datetime
* @param int $excludeOrderItemId 排除当前订单项(因为它在修复前的时间点还不存在)
* @return float
*/
private function calculateCustomerBalanceAtTime($customerId, $datetime, $excludeOrderItemId = null)
{
// 查询在该时间点之前的所有 balance 记录
$query = Balance::where('customer_id', $customerId)
->where('created_at', '<=', $datetime)
->whereNull('deleted_at');
// 排除当前订单项的 balance 记录(因为它在修复前的时间点还不存在)
// 需要排除 belongs_type 为 OrderItems 且 belongs_id 为当前订单项 ID 的记录
if ($excludeOrderItemId) {
$query->where(function ($q) use ($excludeOrderItemId) {
$q->where('belongs_type', '!=', OrderItems::class)
->orWhere(function ($subQ) use ($excludeOrderItemId) {
$subQ->where('belongs_type', OrderItems::class)
->where('belongs_id', '!=', $excludeOrderItemId);
});
});
}
// 累加所有 money 字段money 为正数表示充值/退款,负数表示扣款)
$balance = $query->sum('money');
return (float) $balance;
}
}

@ -0,0 +1,233 @@
<?php
namespace App\Console\Commands;
use App\Models\Balance;
use App\Models\OrderItems;
use App\Models\Orders;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
class RollbackOrderItemsPaidAt extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'order-items:rollback-paid-at {date} {--dry-run : 试运行模式,只显示预览信息,不执行实际回滚}';
/**
* The console command description.
*
* @var string
*/
protected $description = '回滚指定日期 service_date 的 order_items 表中被修复的 paid_at 字段,恢复到修复前的状态';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$date = $this->argument('date');
// 验证日期格式
try {
$serviceDate = Carbon::createFromFormat('Y-m-d', $date);
} catch (\Exception $e) {
$this->error("日期格式错误,请使用 Y-m-d 格式例如2025-12-01");
return 1;
}
$serviceDateStr = $serviceDate->format('Y-m-d');
$serviceDateEnd = $serviceDateStr . ' 23:59:59';
$isDryRun = $this->option('dry-run');
if ($isDryRun) {
$this->warn("=== 试运行模式:将只显示预览信息,不会执行实际回滚 ===");
$this->line('');
}
$this->info("开始" . ($isDryRun ? "预览" : "回滚") . " {$serviceDateStr} 的订单项修复...");
// 查询需要回滚的记录
// 条件:有 paid_at_original说明被修复过且 paid_at 等于修复后的值
$items = OrderItems::where('service_date', $serviceDateStr)
->whereNotNull('paid_at_original')
->where('paid_at', $serviceDateEnd)
->with(['order' => function ($query) {
$query->select('id', 'customer_id');
}])
->get();
$totalCount = $items->count();
$this->info("找到 {$totalCount} 条需要回滚的记录");
if ($totalCount == 0) {
$this->info("没有需要回滚的记录");
return 0;
}
$rolledBackCount = 0;
$errorCount = 0;
$previewData = [];
if (!$isDryRun) {
$bar = $this->output->createProgressBar($totalCount);
$bar->start();
} else {
$this->line('');
$this->info("预览将要回滚的记录:");
$this->line('');
}
foreach ($items as $item) {
try {
// 获取客户ID
$customerId = $item->order ? $item->order->customer_id : null;
if (!$customerId) {
if (!$isDryRun) {
$this->line('');
$this->warn("订单项 {$item->id} 没有关联的客户,跳过");
$bar->advance();
}
$errorCount++;
continue;
}
$originalPaidAt = $item->paid_at_original;
$currentPaidAt = $item->paid_at;
// 检查是否有关联的 balance 记录
$balanceRecord = Balance::where('belongs_type', OrderItems::class)
->where('belongs_id', $item->id)
->where('customer_id', $customerId)
->first();
if ($isDryRun) {
// 试运行模式:只收集预览信息
$previewData[] = [
'order_item_id' => $item->id,
'order_id' => $item->order_id,
'customer_id' => $customerId,
'service_date' => $item->service_date,
'current_paid_at' => $currentPaidAt,
'original_paid_at' => $originalPaidAt,
'has_balance_record' => $balanceRecord ? '是' : '否',
'balance_record_id' => $balanceRecord ? $balanceRecord->id : null,
'balance_current_created_at' => $balanceRecord ? $balanceRecord->created_at : null,
];
$rolledBackCount++;
} else {
// 实际执行回滚
DB::beginTransaction();
try {
// 恢复 paid_at
$item->update([
'paid_at' => $originalPaidAt,
'paid_at_original' => null // 清空,表示已回滚
]);
// 恢复关联的 balance 记录的 created_at
if ($balanceRecord) {
$balanceRecord->update([
'created_at' => $originalPaidAt
]);
} else {
$this->line('');
$this->warn("订单项 {$item->id} 没有找到关联的 balance 记录");
}
DB::commit();
$rolledBackCount++;
Log::info("回滚订单项 {$item->id}paid_at {$currentPaidAt} -> {$originalPaidAt}");
} catch (\Exception $e) {
DB::rollBack();
$errorCount++;
$this->line('');
$this->error("回滚订单项 {$item->id} 失败:" . $e->getMessage());
Log::error("回滚订单项 {$item->id} 失败:" . $e->getMessage());
}
}
} catch (\Exception $e) {
$errorCount++;
if (!$isDryRun) {
$this->line('');
$this->error("处理订单项 {$item->id} 时出错:" . $e->getMessage());
}
Log::error("处理订单项 {$item->id} 时出错:" . $e->getMessage());
}
if (!$isDryRun) {
$bar->advance();
}
}
if (!$isDryRun) {
$bar->finish();
}
$this->line('');
$this->line('');
// 试运行模式:显示详细预览信息
if ($isDryRun && !empty($previewData)) {
$this->info("=== 预览详情 ===");
$this->line('');
$this->info("将要回滚的记录(" . count($previewData) . " 条):");
$this->line('');
$this->table(
['订单项ID', '订单ID', '客户ID', '服务日期', '当前paid_at', '回滚后paid_at', '有balance记录', 'balance当前created_at'],
array_map(function($item) {
return [
$item['order_item_id'],
$item['order_id'],
$item['customer_id'],
$item['service_date'],
$item['current_paid_at'],
$item['original_paid_at'],
$item['has_balance_record'],
$item['balance_current_created_at'] ?: '-',
];
}, $previewData)
);
$this->line('');
}
// 输出统计信息
$this->info(($isDryRun ? "预览" : "回滚") . "完成!");
$this->table(
['统计项', '数量'],
[
['总记录数', $totalCount],
['成功' . ($isDryRun ? '(将回滚)' : '回滚'), $rolledBackCount],
['处理失败', $errorCount],
]
);
if ($isDryRun) {
$this->line('');
$this->comment("提示:这是试运行模式,没有执行实际回滚。");
$this->comment("要执行实际回滚,请运行命令时不加 --dry-run 参数。");
}
return 0;
}
}

@ -26,6 +26,8 @@ class ProjectForm extends Form
$this->add("complaint_mobile", Field::TEXT, ["label" => "投诉电话"]);
// 是否需要签订协议
$this->add("agreement", Field::SELECT, ["label" => "是否需要签订协议", "choices" => [0 => "否", 1 => "是"]]);
// 状态
$this->add("status", Field::SELECT, ["label" => "状态", "choices" => [1 => "启用", 0 => "禁用"], "default_value" => 1]);
// 协议
$this->add("content", Field::TEXTAREA, ["label" => "协议"]);
$this->add("profile", Field::TEXTAREA, ["label" => "简介"]);

@ -10,6 +10,7 @@ namespace App\Http\Controllers\Admin;
use App\Exports\OrdersExport;
use App\Models\AdminAreaLink;
use App\Models\AdminBuildingLink;
use App\Models\Area;
use App\Models\Bed;
use App\Models\Building;
@ -40,10 +41,43 @@ class OrdersController extends CommonController
$building_id = $request->get('building_id');
$area_id = $request->get('area_id');
// 楼栋
$buildings = Building::where('project_id', $project_id)->get();
// 病区
$areas = Area::where('project_id', $project_id)->get();
// 判断是否护士长
$userId = auth()->id();
$roleId = Role::where('name', 'like', '%护士长%')->where('guard_name', 'admin')->value('id');
$hushizhang = DB::table('model_has_roles')->where('role_id', $roleId)
->where('model_type', 'App\Admin')->where('model_id', $userId)->count();
// 是否院方管理
$roleId = Role::where('name', 'like', '%院方管理%')->where('guard_name', 'admin')->value('id');
$yuanfang = DB::table('model_has_roles')->where('role_id', $roleId)
->where('model_type', 'App\Admin')->where('model_id', $userId)->count();
// 楼栋 - 如果是院方管理,只显示有权限的楼栋
$buildingsQuery = Building::where('project_id', $project_id);
if ($yuanfang) {
$user = auth()->user();
$buildingId = AdminBuildingLink::where('project_id', $project_id)->where('admin_id', $user->id)->pluck('building_id');
if ($buildingId->isNotEmpty()) {
$buildingsQuery = $buildingsQuery->whereIn('id', $buildingId);
}
}
$buildings = $buildingsQuery->get();
// 病区 - 如果是护士长,只显示有权限的病区;如果是院方管理,只显示有权限楼栋下的病区
$areasQuery = Area::where('project_id', $project_id);
if ($hushizhang) {
$user = auth()->user();
$areaId = AdminAreaLink::where('project_id', $project_id)->where('admin_id', $user->id)->pluck('area_id');
if ($areaId->isNotEmpty()) {
$areasQuery = $areasQuery->whereIn('id', $areaId);
}
} elseif ($yuanfang) {
$user = auth()->user();
$buildingId = AdminBuildingLink::where('project_id', $project_id)->where('admin_id', $user->id)->pluck('building_id');
if ($buildingId->isNotEmpty()) {
$areasQuery = $areasQuery->whereIn('building_id', $buildingId);
}
}
$areas = $areasQuery->get();
$month = request()->month ?? '全部';
@ -64,22 +98,45 @@ class OrdersController extends CommonController
});
}
// 判断是否护士长
$userId = auth()->id();
$roleId = Role::where('name', 'like', '%护士长%')->where('guard_name', 'admin')->value('id');
$hushizhang = DB::table('model_has_roles')->where('role_id', $roleId)
->where('model_type', 'App\Admin')->where('model_id', $userId)->count();
// 是否院方管理
$roleId = Role::where('name', 'like', '%院方管理%')->where('guard_name', 'admin')->value('id');
$yuanfang = DB::table('model_has_roles')->where('role_id', $roleId)
->where('model_type', 'App\Admin')->where('model_id', $userId)->count();
if ($hushizhang) {
$user = auth()->user();
$areaId = AdminAreaLink::where('project_id', $project_id)->where('admin_id', $user->id)->pluck('area_id');
$bedList = Bed::whereIn('area_id', $areaId)->pluck('id');
$this->model = $this->model->whereIn('bed_id', $bedList);
if ($areaId->isNotEmpty()) {
$bedList = Bed::whereIn('area_id', $areaId)->pluck('id');
if ($bedList->isNotEmpty()) {
$this->model = $this->model->whereIn('bed_id', $bedList);
} else {
// 如果没有关联的床位,返回空结果
$this->model = $this->model->whereIn('bed_id', []);
}
} else {
// 如果没有关联的病区,返回空结果
$this->model = $this->model->whereIn('bed_id', []);
}
}
// 院方管理角色:根据 building 权限过滤
if ($yuanfang) {
$user = auth()->user();
$buildingId = AdminBuildingLink::where('project_id', $project_id)->where('admin_id', $user->id)->pluck('building_id');
if ($buildingId->isNotEmpty()) {
$areaId = Area::whereIn('building_id', $buildingId)->pluck('id');
if ($areaId->isNotEmpty()) {
$bedList = Bed::whereIn('area_id', $areaId)->pluck('id');
if ($bedList->isNotEmpty()) {
$this->model = $this->model->whereIn('bed_id', $bedList);
} else {
// 如果没有关联的床位,返回空结果
$this->model = $this->model->whereIn('bed_id', []);
}
} else {
// 如果楼栋下没有病区,返回空结果
$this->model = $this->model->whereIn('bed_id', []);
}
} else {
// 如果没有关联的楼栋,返回空结果
$this->model = $this->model->whereIn('bed_id', []);
}
}
if ($request->date) {
@ -274,7 +331,15 @@ class OrdersController extends CommonController
$hushizhang = DB::table('model_has_roles')->where('role_id', $roleId)
->where('model_type', 'App\Admin')
->where('model_id', $userId)->count();
// 是否院方管理
$roleId = Role::where('name', 'like', '%院方管理%')->where('guard_name', 'admin')->value('id');
$yuanfang = DB::table('model_has_roles')->where('role_id', $roleId)
->where('model_type', 'App\Admin')
->where('model_id', $userId)->count();
$areaId = [];
$buildingId = [];
if ($hushizhang) {
$user = auth()->user();
$areaId = AdminAreaLink::where(function ($qeury) use ($project_id) {
@ -282,11 +347,21 @@ class OrdersController extends CommonController
$qeury->where('project_id', $project_id);
}
})->where('admin_id', $user->id)->pluck('area_id');
} elseif ($yuanfang) {
$user = auth()->user();
$buildingId = AdminBuildingLink::where(function ($qeury) use ($project_id) {
if ($project_id) {
$qeury->where('project_id', $project_id);
}
})->where('admin_id', $user->id)->pluck('building_id');
}
$data = Area::where(function ($query) use ($areaId, $building_id) {
$data = Area::where(function ($query) use ($areaId, $buildingId, $building_id) {
if ($areaId) {
$query->whereIn('area.id', $areaId);
}
if ($buildingId) {
$query->whereIn('area.building_id', $buildingId);
}
if ($building_id) {
$query->where('building_id', $building_id);
}
@ -315,8 +390,16 @@ class OrdersController extends CommonController
if ($data->first()) {
$data = $data->toArray();
}
// 获取楼栋
$buildings = Building::where('project_id', $project_id)->where('project_id', $project_id)->get();
// 获取楼栋 - 如果是院方管理,只显示有权限的楼栋
$buildingsQuery = Building::where('project_id', $project_id);
if ($yuanfang) {
$user = auth()->user();
$buildingId = AdminBuildingLink::where('project_id', $project_id)->where('admin_id', $user->id)->pluck('building_id');
if ($buildingId->isNotEmpty()) {
$buildingsQuery = $buildingsQuery->whereIn('id', $buildingId);
}
}
$buildings = $buildingsQuery->get();
return view($this->bladePath . ".artboard", compact("data", "projects", "project_id", "buildings", "building_id"));
}

@ -15,6 +15,7 @@ use App\Forms\AskSubmitForm;
use App\Forms\AskSubmitScForm;
use App\Forms\ProjectForm;
use App\Models\AdminAreaLink;
use App\Models\AdminBuildingLink;
use App\Models\Area;
use App\Models\AskSubmit;
use App\Models\Bed;
@ -48,7 +49,7 @@ class ProjectController extends CommonController
public function index(Request $request)
{
$data = $this->model->with(["paramedicLevels","wechatpayAccount"])->paginate(10);
$data = $this->model->with(["paramedicLevels","wechatpayAccount","alipayAccount"])->paginate(10);
return view($this->bladePath . ".index", compact("data"));
}
@ -125,6 +126,26 @@ class ProjectController extends CommonController
return $this->ajaxResponse($headList);
}
public function getYuanfangList()
{
$buildingId = \request('building_id', 0);
$project_id = \request('project_id');
// 获取院方管理
$roleId = Role::where('name', 'like', '%院方管理%')->where('guard_name', 'admin')->value('id');
$adminIds = DB::table('model_has_roles')->where('role_id', $roleId)->where('model_type', 'App\Admin')->pluck('model_id');
// 全量院方管理数据
$yuanfangList = Admin::whereIn('id', $adminIds)->whereRaw("find_in_set('$project_id',project_ids)")->get();
// 获取楼栋选中的院方管理
foreach ($yuanfangList as $item) {
$item->selected = '';
if ($buildingId) {
$has = AdminBuildingLink::where('building_id', $buildingId)->where('admin_id', $item->id)->count();
if ($has) $item->selected = 'selected';
}
}
return $this->ajaxResponse($yuanfangList);
}
public function createSub(Request $request)
{
switch ($request->type) {
@ -135,6 +156,21 @@ class ProjectController extends CommonController
"myindex" => $request->myindex
];
$res = (new Building())->create($data);
// 添加选中的院方管理
if (isset($request->yuanfang) && is_array($request->yuanfang)) {
$links = [];
foreach ($request->yuanfang as $item) {
$links[] = [
'admin_id' => $item,
'building_id' => $res->id,
'project_id' => $res->project_id,
'created_at' => date('Y-m-d H:i:s')
];
}
if (!empty($links)) {
AdminBuildingLink::insert($links);
}
}
break;
case "building":
$parent = Building::find($request->id);
@ -210,7 +246,24 @@ class ProjectController extends CommonController
];
switch ($request->type) {
case "building":
$res = (new Building())->find($request->id)->update($data);
$res = (new Building())->find($request->id);
$res->update($data);
// 删除原来关联,添加新关联
AdminBuildingLink::where('building_id', $request->id)->delete();
if (isset($request->yuanfang) && is_array($request->yuanfang)) {
$links = [];
foreach ($request->yuanfang as $item) {
$links[] = [
'admin_id' => $item,
'building_id' => $request->id,
'project_id' => $res->project_id,
'created_at' => date('Y-m-d H:i:s')
];
}
if (!empty($links)) {
AdminBuildingLink::insert($links);
}
}
break;
case "area":
$res = (new Area())->find($request->id);
@ -563,4 +616,32 @@ class ProjectController extends CommonController
return $this->success("新增成功", '', $model);
}
/**
* 快速切换项目状态
*/
public function toggleStatus(Request $request)
{
try {
$id = $request->id;
$status = $request->status;
if (!in_array($status, [0, 1])) {
return $this->error("状态值不正确");
}
$project = $this->model->find($id);
if (!$project) {
return $this->error("项目不存在");
}
$project->status = $status;
$project->save();
$statusText = $status == 1 ? "启用" : "禁用";
return $this->success("项目已{$statusText}");
} catch (\Exception $exception) {
return $this->error("操作失败:" . $exception->getMessage());
}
}
}

@ -11,6 +11,7 @@ namespace App\Http\Controllers\Admin;
use App\Customer;
use App\Libs\AlipayF2F;
use App\Models\AdminAreaLink;
use App\Models\AdminBuildingLink;
use App\Models\Area;
use App\Models\Balance;
use App\Models\Bed;
@ -178,6 +179,7 @@ class StatisticsController extends CommonController
$start_timestamp = strtotime($month);
$end_timestamp = strtotime("+1 month", strtotime($month));
//根据项目获取相关数据
$prev_month_balance = Balance::whereRaw("UNIX_TIMESTAMP(`created_at`) < " . $start_timestamp)
->whereHas("order", function ($query) use ($project_id) {
@ -259,17 +261,49 @@ class StatisticsController extends CommonController
->where('model_type', 'App\Admin')
->where('model_id', $userId)
->count();
// 获取这个护士长病区的订单
// 是否院方管理
$roleId = Role::where('name', 'like', '%院方管理%')->where('guard_name', 'admin')->value('id');
$yuanfang = DB::table('model_has_roles')->where('role_id', $roleId)
->where('model_type', 'App\Admin')
->where('model_id', $userId)
->count();
// 获取这个护士长病区的订单或院方管理楼栋的订单
$user = auth()->user();
$areaId = AdminAreaLink::where('project_id', $project_id)->where('admin_id', $user->id)->pluck('area_id');
$bedList = Bed::whereIn('area_id', $areaId)->pluck('id');
$orderIds = Orders::whereIn('bed_id', $bedList)->pluck('id');
$orderIds = [];
if ($hushizhang) {
$areaId = AdminAreaLink::where('project_id', $project_id)->where('admin_id', $user->id)->pluck('area_id');
if ($areaId->isNotEmpty()) {
$bedList = Bed::whereIn('area_id', $areaId)->pluck('id');
if ($bedList->isNotEmpty()) {
$orderIds = Orders::whereIn('bed_id', $bedList)->pluck('id');
}
}
} elseif ($yuanfang) {
$buildingId = AdminBuildingLink::where('project_id', $project_id)->where('admin_id', $user->id)->pluck('building_id');
if ($buildingId->isNotEmpty()) {
$areaId = Area::whereIn('building_id', $buildingId)->pluck('id');
if ($areaId->isNotEmpty()) {
$bedList = Bed::whereIn('area_id', $areaId)->pluck('id');
if ($bedList->isNotEmpty()) {
$orderIds = Orders::whereIn('bed_id', $bedList)->pluck('id');
}
}
}
// 如果没有关联数据,$orderIds 保持为空数组,后续查询会返回空结果
}
$recharges = Recharge::with(["manager", "order", "patient"])
->where(function ($query) use ($hushizhang, $orderIds) {
if ($hushizhang) {
$query->whereIn('order_id', $orderIds);
->where(function ($query) use ($hushizhang, $yuanfang, $orderIds) {
if ($hushizhang || $yuanfang) {
if (!empty($orderIds)) {
$query->whereIn('order_id', $orderIds);
} else {
// 如果没有关联数据,返回空结果
$query->whereIn('order_id', []);
}
}
})
->withCount("refunds")
@ -282,9 +316,14 @@ class StatisticsController extends CommonController
$recharges_sum = $recharges->sum('money');
$refunds = Refund::whereNotNull("paid_at")
->where(function ($query) use ($hushizhang, $orderIds) {
if ($hushizhang) {
$query->whereIn('order_id', $orderIds);
->where(function ($query) use ($hushizhang, $yuanfang, $orderIds) {
if ($hushizhang || $yuanfang) {
if (!empty($orderIds)) {
$query->whereIn('order_id', $orderIds);
} else {
// 如果没有关联数据,返回空结果
$query->whereIn('order_id', []);
}
}
})
->whereRaw("DATE_FORMAT(`paid_at`,'%Y-%m') = '{$month}'")
@ -404,7 +443,15 @@ class StatisticsController extends CommonController
$hushizhang = DB::table('model_has_roles')->where('role_id', $roleId)
->where('model_type', 'App\Admin')
->where('model_id', $userId)->count();
// 是否院方管理
$roleId = Role::where('name', 'like', '%院方管理%')->where('guard_name', 'admin')->value('id');
$yuanfang = DB::table('model_has_roles')->where('role_id', $roleId)
->where('model_type', 'App\Admin')
->where('model_id', $userId)->count();
$areaId = [];
$buildingId = [];
if ($hushizhang) {
$user = auth()->user();
$areaId = AdminAreaLink::where(function ($query) use ($project_id) {
@ -412,11 +459,21 @@ class StatisticsController extends CommonController
$query->where('project_id', $project_id);
}
})->where('admin_id', $user->id)->pluck('area_id');
} elseif ($yuanfang) {
$user = auth()->user();
$buildingId = AdminBuildingLink::where(function ($qeury) use ($project_id) {
if ($project_id) {
$qeury->where('project_id', $project_id);
}
})->where('admin_id', $user->id)->pluck('building_id');
}
$data = Area::where('project_id', $project_id)->with('project', 'building')->where(function ($query) use ($areaId) {
$data = Area::where('project_id', $project_id)->with('project', 'building')->where(function ($query) use ($areaId, $buildingId) {
if ($areaId) {
$query->whereIn('id', $areaId);
}
if ($buildingId) {
$query->whereIn('building_id', $buildingId);
}
})->paginate(40);
$data->appends($request->all())->render();

@ -0,0 +1,9 @@
<?php
namespace App\Models;
class AdminBuildingLink extends CommonModel
{
}

@ -218,10 +218,15 @@ class OrderItems extends SoftDeletesModel
$last_id = cache("last_auto_checkout_order_item_id", 0);
Log::channel("daily_auto_checkout")->info("Last id:" . $last_id);
// 获取所有状态为1的项目
$projects = Project::where("status", 1)->pluck("id")->toArray();
$unpaid_order_items = (new OrderItems())
->whereHas("order", function ($query) {
$query->where("status", Orders::STATUS_ONGOING);
->whereHas("order", function ($query) use ($projects) {
$query->where("status", Orders::STATUS_ONGOING)
->whereIn("project_id", $projects);
})
->where("total", ">", 0)
->whereNull("paid_at")
->where("id", ">", $last_id)
->with("customer");

@ -20,7 +20,7 @@ class CreateProject extends Migration
$table->text("profile")->nullable();
$table->string("logo")->nullable();
$table->text("banners")->nullable();
$table->string("status")->default(0);
$table->string("status")->default(1)->comment("1:启用,0:禁用");
$table->timestamps();
$table->softDeletes();
});

@ -0,0 +1,33 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class UpdateOrderItemsAddPaidAtOriginal extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table("order_items", function (Blueprint $table) {
$table->timestamp("paid_at_original")->nullable()->after("paid_at");
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table("order_items", function (Blueprint $table) {
$table->dropColumn("paid_at_original");
});
}
}

@ -0,0 +1,36 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateAdminBuildingLinks extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('admin_building_links', function (Blueprint $table) {
$table->increments('id');
$table->integer("building_id")->nullable();
$table->integer("project_id")->nullable();
$table->string("admin_id")->nullable();
$table->timestamps();
$table->softDeletes();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('admin_building_links');
}
}

BIN
public/.DS_Store vendored

Binary file not shown.

File diff suppressed because one or more lines are too long

BIN
public/h5/.DS_Store vendored

Binary file not shown.

@ -1,2 +1,2 @@
<!DOCTYPE html><html lang=zh-CN><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><title>护工管理</title><script>var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') || CSS.supports('top: constant(a)'))
document.write('<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' + (coverSupport ? ', viewport-fit=cover' : '') + '" />')</script><link rel=stylesheet href=/h5/static/index.883130ca.css></head><body><noscript><strong>Please enable JavaScript to continue.</strong></noscript><div id=app></div><script src=/h5/static/js/chunk-vendors.2916c8b2.js></script><script src=/h5/static/js/index.31cd4916.js></script></body></html>
document.write('<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' + (coverSupport ? ', viewport-fit=cover' : '') + '" />')</script><link rel=stylesheet href=/h5/static/index.ed4a2d2b.css></head><body><noscript><strong>Please enable JavaScript to continue.</strong></noscript><div id=app></div><script src=/h5/static/js/chunk-vendors.2037823a.js></script><script src=/h5/static/js/index.e62bb5c2.js></script></body></html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 426 KiB

After

Width:  |  Height:  |  Size: 426 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

Before

Width:  |  Height:  |  Size: 7.0 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 8.0 KiB

Before

Width:  |  Height:  |  Size: 121 KiB

After

Width:  |  Height:  |  Size: 121 KiB

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 7.6 KiB

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Before

Width:  |  Height:  |  Size: 778 B

After

Width:  |  Height:  |  Size: 778 B

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Before

Width:  |  Height:  |  Size: 757 B

After

Width:  |  Height:  |  Size: 757 B

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Before

Width:  |  Height:  |  Size: 1021 B

After

Width:  |  Height:  |  Size: 1021 B

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save