From 803a9433ddad049547557b6b4c80dc2237b6597b Mon Sep 17 00:00:00 2001 From: weizong song Date: Sat, 28 Feb 2026 17:23:12 +0800 Subject: [PATCH] up --- app/Console/Commands/CheckOrderFee.php | 118 +++++++++++++++++++++++++ 项目理解.md | 89 +++++++++++++++++++ 2 files changed, 207 insertions(+) create mode 100644 app/Console/Commands/CheckOrderFee.php create mode 100644 项目理解.md diff --git a/app/Console/Commands/CheckOrderFee.php b/app/Console/Commands/CheckOrderFee.php new file mode 100644 index 0000000..ccf6999 --- /dev/null +++ b/app/Console/Commands/CheckOrderFee.php @@ -0,0 +1,118 @@ +argument('start_date'); + $end = $this->argument('end_date'); + + try { + $startDate = Carbon::createFromFormat('Y-m-d', $start)->startOfDay(); + $endDate = Carbon::createFromFormat('Y-m-d', $end)->endOfDay(); + } catch (\Exception $exception) { + $this->error('日期格式错误,请使用 YYYY-MM-DD,例如 2025-02-01'); + return 1; + } + + if ($startDate->gt($endDate)) { + $this->error('开始日期不能大于结束日期'); + return 1; + } + + $projectId = $this->option('project_id'); + $limit = (int) $this->option('limit'); + $limit = $limit > 0 ? $limit : 50; + + $this->info(sprintf( + '检测区间:%s ~ %s%s', + $startDate->toDateString(), + $endDate->toDateString(), + $projectId ? ",项目 ID:{$projectId}" : '' + )); + + $query = OrderItems::query() + ->whereBetween('service_date', [$startDate->toDateString(), $endDate->toDateString()]) + ->with(['order', 'product']); + + if ($projectId) { + $query->whereHas('order', function ($q) use ($projectId) { + $q->where('project_id', $projectId); + }); + } + + $total = 0; + $rowsForTable = []; + + $query->chunkById(200, function ($items) use (&$total, &$rowsForTable, $limit) { + foreach ($items as $item) { + /** @var \App\Models\OrderItems $item */ + $total++; + $storedFee = (float) $item->fee; + + $calcItem = clone $item; + if ($item->relationLoaded('order')) { + $calcItem->setRelation('order', $item->getRelation('order')); + } + if ($item->relationLoaded('product')) { + $calcItem->setRelation('product', $item->getRelation('product')); + } + + $calcItem->calculateFee(); + $calculatedFee = (float) $calcItem->fee; + + if (count($rowsForTable) < $limit) { + $rowsForTable[] = [ + 'ID' => $item->id, + '订单ID' => $item->order_id, + '日期' => $item->service_date, + '总价' => $item->total, + '原fee' => $storedFee, + '算法fee' => $calculatedFee, + '是否扣款' => $item->paid_at ? '是' : '否', + ]; + } + } + }); + + if (empty($rowsForTable)) { + $this->info('该时间段无子订单记录'); + } else { + $this->table( + ['ID', '订单ID', '日期', '总价', '原fee', '算法fee', '是否扣款'], + $rowsForTable + ); + } + + $this->info(sprintf('共检测 %d 条子订单。', $total)); + + return 0; + } +} diff --git a/项目理解.md b/项目理解.md new file mode 100644 index 0000000..0a33c12 --- /dev/null +++ b/项目理解.md @@ -0,0 +1,89 @@ +# 天天护工平台:项目理解(基于仓库源码) + +## 1. 项目定位 +- 这是一个医院陪护业务管理系统,核心是围绕“订单-护工-床位-结算”闭环运行。 +- 系统支持多角色协同:后台管理员(Admin)、客户端用户(Customer)、项目管理端(Manager)、护工端(Worker)。 +- 业务目标不是单纯下单,而是包含排班指派、每日子订单、自动扣款、退款、统计分析等运营链路。 + +## 2. 技术架构理解 +- 后端:Laravel 7 + PHP 7.2.5+(`composer.json`)。 +- 认证: + - `admin` 使用 session guard。 + - `customer/manager/worker` 使用 JWT guard(`config/auth.php`)。 +- 权限: + - 管理后台使用 `authorize:admin` + `rbac:admin`。 + - RBAC 规则按 URL 前缀匹配权限(`app/Http/Middleware/Rbac.php`)。 +- 前端形态(仓库内可见): + - 管理后台模板资源:`public/hyper`。 + - 客户端 H5 静态资源:`public/h5`、`public/h5_test`。 + - 数据看板页面:`public/dashboard`。 +- 前端构建:Laravel Mix + Vue2 + Bootstrap4(`package.json`)。 + +## 3. 路由与端划分 +路由主要集中在 `routes/web.php`,按前缀拆分: +- `/admin/*`:后台管理,包含权限/角色/项目/产品/护工/经理/订单/统计/支付账户管理。 +- `/customer/*`:用户端,下单、患者管理、可用护工查询、充值、评价等。 +- `/manager/*`:项目管理端,订单处理、派单、改价、结算、充值退款、统计等。 +- `/worker/*`:护工端,登录、个人信息、订单列表/详情。 +- `/swagger/*`:接口文档相关入口。 + +备注:`routes/api.php` 仅保留 Laravel 默认示例接口,业务 API 基本走 `web.php`。 + +## 4. 核心业务对象 +从模型层看,业务主干是: +- 订单主表:`Orders`(状态机、总价、关联患者/客户/护工/床位/产品)。 +- 订单日明细:`OrderItems`(按服务日生成、用于日扣款与工资结算)。 +- 资源对象:`Project/Building/Area/Room/Bed`(医院空间层级)。 +- 人员对象:`Paramedic`(护工)、`Manager`(项目管理人员)、`Customer`(下单用户)。 +- 交易对象:`Balance/Recharge/Refund`。 +- 配置对象:`Product/ProductItems/ProductParamedicLevel/Factor/FactorItems`(服务项和计价因子)。 + +## 5. 我理解的关键业务流程 + +### 5.1 下单与执行 +1. 客户端按项目获取产品、病区床位、可用护工。 +2. 创建订单后,订单在状态流转中推进: + - `待确认(0)` -> `待指派(10)` -> `进行中(20)` -> `已完成(100)`(`app/Models/Orders.php`)。 +3. 管理端在进行中订单上执行派单、改价、变更等操作。 + +### 5.2 每日子订单与扣款 +1. 定时任务每分钟执行 `order-items:create-daily`,为服务日生成 `order_items`。 +2. 下午 14:00-23:59 每分钟执行自动扣款(`OrderItems::autoCheckout()`),并记录日志。 +3. 子订单 `paid_at` 决定是否已扣款及工资归属月份(`OrderItems::getPaidStatusAttribute()`)。 + +### 5.3 退款链路 +- 每分钟执行自动退款逻辑(`Refund::autoRefund()`),同时支持管理端按订单手工充值/退款。 + +### 5.4 审计与追踪 +- `Orders` 在更新时会对变更字段写入 `order_audit`(批次+操作人),用于追踪关键字段修改历史。 + +## 6. 数据隔离与权限边界 +- `Orders` 等模型挂了 `AdminProjectScope` 全局作用域:当 admin 账号配置了 `project_ids`,查询会自动按项目过滤。 +- 管理后台权限为“角色 -> 权限 URL 前缀”模型,核心在 `spatie/laravel-permission` 之上实现。 +- 另有导出权限的角色检测逻辑(`CommonModel::checkExport()`)。 + +## 7. 事件与可扩展点 +当前事件总线已有: +- `FactorSaved`、`ManagerSaved`、`AdminSaved`、`ProjectSaved`、`ProductSaved`、`RechargeSucceed`、`OrderAssigned`。 + +这说明项目采用了“核心流程 + 事件监听补充动作”的演进模式,便于后续插入通知、同步、统计等旁路能力。 + +## 8. 对当前代码状态的判断 +- 这是一个长期维护中的业务系统,不是新脚手架;有较完整的运营功能(统计、导出、日志、自动任务)。 +- 代码里存在历史兼容痕迹(例如大量 `whereRaw`、`web.php` 承担 API、注释中有 TODO),但主线流程清晰可维护。 +- 仓库内已有多个“说明文档”文件,建议后续统一维护一份“权威架构文档”,减少信息分散。 + +## 9. 待确认事项(建议下一步澄清) +- 多端前端代码是否全部在本仓库(当前可见主要是编译产物,源工程边界需要确认)。 +- 支付参数与回调验签的生产部署策略(当前看账户数据在库内配置+代码调用)。 +- 子订单自动扣款规模上限与失败重试策略是否满足当前业务量。 +- `routes/web.php` 中 API 与后台页面耦合是否要逐步拆分。 + +## 10. 建议的文档化落地 +建议后续把文档分为三层: +- `01-业务流程.md`:从下单到结算的时序图和状态机。 +- `02-技术架构.md`:鉴权、权限、任务调度、事件机制。 +- `03-运维手册.md`:定时任务、支付回调排障、日志定位路径。 + +--- +本文档依据当前仓库源码整理,重点描述“已在代码中可验证”的理解,供后续开发与沟通对齐。