From 14735af0b387e5c6cdb21b7903f7036d4611f1f2 Mon Sep 17 00:00:00 2001 From: ShengLong <452591453@qq.com> Date: Fri, 8 May 2026 16:07:39 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AE=A2=E5=8D=95=E4=BF=AE=E6=94=B9=E9=87=91?= =?UTF-8?q?=E9=A2=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AGENTS.md | 32 ++++- admin/src/api/tcm.ts | 5 + .../consumer/prescription/order_list.vue | 136 +++++++++++++++++- .../tcm/PrescriptionOrderController.php | 15 ++ .../logic/tcm/PrescriptionOrderLogic.php | 46 ++++++ .../tcm/PrescriptionOrderValidate.php | 4 + .../1.9.20260508/add_update_amount_menu.sql | 17 +++ 7 files changed, 252 insertions(+), 3 deletions(-) create mode 100644 server/sql/1.9.20260508/add_update_amount_menu.sql diff --git a/AGENTS.md b/AGENTS.md index 08bdd318..5d421941 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -18,12 +18,40 @@ If you're using Codex or another agent-capable tool, additional project-scoped h ## Subagents -- ALWAYS wait for all subagents to complete before yielding. +- ALWAYS wait for every spawned subagent to reach a terminal status before yielding, acting on partial results, or spawning followups. + - On Codex, this means calling the `wait` tool with the subagent's thread id (requires `multi_agent_v2`). Do NOT infer completion from elapsed time. + - On Claude Code / OpenCode, this means awaiting the Task/agent tool result before continuing. +- NEVER cancel or re-spawn a subagent that hasn't finished. If a subagent appears stuck, raise the wait timeout (Codex default 30s, max 1h) before judging it broken. - Spawn subagents automatically when: - Parallelizable work (e.g., install + verify, npm test + typecheck, multiple tasks from plan) - - Long-running or blocking tasks where a worker can run independently. + - Long-running or blocking tasks where a worker can run independently - Isolation for risky changes or checks +### Codex-only — `spawn_agent` parameters + +When calling `spawn_agent`, ALWAYS pass `fork_turns="none"`. Without it the child inherits the parent transcript and sees your prior `spawn_agent(...)` records, then applies the "wait for spawned subagents" rule to itself — causing `wait_agent` self-deadlock. + +```text +spawn_agent(agent_type="trellis-implement", message="...", fork_turns="none") +``` + +### Codex-only — multi-subagent close-loop + +When `wait` returns a `completed` notification, treat it as an event signal — not as "all done". Run this loop: + +1. Maintain an `expected_agents` set of dispatched sub-agent thread IDs. +2. After each `wait` update: + 1. Call `list_agents` to inspect ALL live agents' status. + 2. For each agent now in a terminal state: + - Verify its promised deliverable exists (e.g. `{task_dir}/research/*.md`). + - Read or summarize as needed. + - `close_agent` to release the slot. + - Remove from `expected_agents`. + 3. If `expected_agents` still contains running agents → keep waiting. + 4. If `expected_agents` is empty → continue main flow. +3. Never `wait` on an agent that has already reported `completed`. +4. If a `completed` agent is missing its deliverable, treat it as failed — surface that in your report instead of re-waiting. + Managed by Trellis. Edits outside this block are preserved; edits inside may be overwritten by a future `trellis update`. diff --git a/admin/src/api/tcm.ts b/admin/src/api/tcm.ts index 8a31caa9..9250a78a 100644 --- a/admin/src/api/tcm.ts +++ b/admin/src/api/tcm.ts @@ -480,6 +480,11 @@ export function prescriptionOrderLogs(params: { id: number }) { return request.get({ url: '/tcm.prescriptionOrder/logs', params }) } +/** 修改订单金额 */ +export function prescriptionOrderUpdateAmount(params: { id: number; amount_update: number }) { + return request.post({ url: '/tcm.prescriptionOrder/updateAmount', params }) +} + // ========== 处方库 ========== // 处方库列表 diff --git a/admin/src/views/consumer/prescription/order_list.vue b/admin/src/views/consumer/prescription/order_list.vue index 158f177c..f9ef43ee 100644 --- a/admin/src/views/consumer/prescription/order_list.vue +++ b/admin/src/views/consumer/prescription/order_list.vue @@ -663,7 +663,19 @@ -
总金额
+
+
总金额
+ + 修改 + +
¥{{ detailData.amount }}
@@ -1185,6 +1197,48 @@ + + + +
¥{{ formatMoney(detailData?.amount) }}
+
+ + + +
+ 已完成或已取消订单不可修改。提交成功后会刷新详情与操作日志。 +
+
+ +
+ | null>(null) const logisticsTracePhoneTail = ref('') const detailLogs = ref([]) +const updateAmountVisible = ref(false) +const updateAmountSaving = ref(false) +const updateAmountFormRef = ref() +const updateAmountForm = reactive({ + id: 0, + amount_update: undefined as number | undefined +}) +const updateAmountRules: FormRules = { + amount_update: [ + { required: true, message: '请输入订单金额', trigger: 'blur' }, + { + validator: (_rule, value, callback) => { + const num = Number(value) + if (!Number.isFinite(num) || num <= 0) { + callback(new Error('订单金额必须大于0')) + return + } + callback() + }, + trigger: 'blur' + } + ] +} function canViewPrescriptionOrderLogs() { const p = userStore.perms || [] @@ -3325,6 +3409,7 @@ function logActionText(act: string) { revoke_pay_audit: '撤回支付审核', gancao_submit: '甘草下单', patch_rx_patient: '处方患者信息', + update_amount: '修改订单金额', complete: '完成订单' } return m[act] || act @@ -3409,6 +3494,24 @@ function resetPatchRxPatientForm() { patchRxPatientForm.phone = '' } +function resetUpdateAmountDialog() { + updateAmountForm.id = 0 + updateAmountForm.amount_update = undefined + updateAmountFormRef.value?.clearValidate() +} + +function openUpdateAmount() { + const row = detailData.value + if (!canUpdateAmount(row)) { + feedback.msgWarning('当前订单状态不允许修改金额') + return + } + updateAmountForm.id = Number(row?.id) || 0 + updateAmountForm.amount_update = Number(row?.amount) || undefined + updateAmountVisible.value = true + nextTick(() => updateAmountFormRef.value?.clearValidate()) +} + async function refreshCurrentPrescriptionOrderDetail() { const id = detailData.value?.id if (!id) return @@ -3448,6 +3551,37 @@ async function submitPatchRxPatient() { } } +async function submitUpdateAmount() { + const form = updateAmountFormRef.value + if (!form) return + try { + await form.validate() + } catch { + return + } + if (!canUpdateAmount(detailData.value)) { + feedback.msgWarning('当前订单状态不允许修改金额') + return + } + const orderId = updateAmountForm.id + updateAmountSaving.value = true + try { + await prescriptionOrderUpdateAmount({ + id: orderId, + amount_update: Number(updateAmountForm.amount_update) + }) + feedback.msgSuccess('修改成功') + updateAmountVisible.value = false + await refreshCurrentPrescriptionOrderDetail() + await fetchLogs(orderId) + getLists() + } catch { + /* 拦截器已提示 */ + } finally { + updateAmountSaving.value = false + } +} + async function openDetail(id: number) { // 修复 Bug:显式彻底清空缓存,防止前一次弹窗的数据残留 detailData.value = null diff --git a/server/app/adminapi/controller/tcm/PrescriptionOrderController.php b/server/app/adminapi/controller/tcm/PrescriptionOrderController.php index 5450e0b6..68bcdef1 100644 --- a/server/app/adminapi/controller/tcm/PrescriptionOrderController.php +++ b/server/app/adminapi/controller/tcm/PrescriptionOrderController.php @@ -246,6 +246,21 @@ class PrescriptionOrderController extends BaseAdminController return $this->success('', $result); } + /** + * 修改订单金额 + */ + public function updateAmount() + { + $params = (new PrescriptionOrderValidate())->post()->goCheck('updateAmount'); + $result = PrescriptionOrderLogic::updateAmount($params, $this->adminId, $this->adminInfo); + + if ($result === false) { + return $this->fail(PrescriptionOrderLogic::getError()); + } + + return $this->success('修改成功'); + } + /** * 为「已发货」订单新增一条关联支付单,并重置支付单审核状态为待审核 */ diff --git a/server/app/adminapi/logic/tcm/PrescriptionOrderLogic.php b/server/app/adminapi/logic/tcm/PrescriptionOrderLogic.php index 3a302f1b..693920fa 100644 --- a/server/app/adminapi/logic/tcm/PrescriptionOrderLogic.php +++ b/server/app/adminapi/logic/tcm/PrescriptionOrderLogic.php @@ -2432,6 +2432,52 @@ class PrescriptionOrderLogic ]; } + /** + * 修改订单金额 + */ + public static function updateAmount(array $params, int $adminId, array $adminInfo): bool + { + $id = (int) $params['id']; + $newAmount = round((float) $params['amount_update'], 2); + + // 查询订单 + $order = PrescriptionOrder::find($id); + if (!$order) { + self::setError('订单不存在'); + return false; + } + + // 数据权限检查 + if (!self::checkDataScope($order->toArray(), $adminId, $adminInfo)) { + self::setError('无权限操作此订单'); + return false; + } + + // 状态检查:已完成(3)和已取消(4)不允许修改 + if (in_array((int) $order->fulfillment_status, [3, 4], true)) { + self::setError('已完成或已取消的订单不允许修改金额'); + return false; + } + + $oldAmount = round((float) $order->amount, 2); + + // 金额未变化 + if (abs($newAmount - $oldAmount) < 0.01) { + self::setError('金额未发生变化'); + return false; + } + + // 更新金额 + $order->amount = $newAmount; + $order->save(); + + // 记录日志 + $summary = sprintf('将订单金额从 ¥%.2f 修改为 ¥%.2f', $oldAmount, $newAmount); + self::writeLog($id, $adminId, $adminInfo, 'update_amount', $summary); + + return true; + } + private static function writeLog(int $orderId, int $adminId, array $adminInfo, string $action, string $summary): void { $adminName = $adminInfo['name'] ?? ''; diff --git a/server/app/adminapi/validate/tcm/PrescriptionOrderValidate.php b/server/app/adminapi/validate/tcm/PrescriptionOrderValidate.php index 56459913..472c9437 100644 --- a/server/app/adminapi/validate/tcm/PrescriptionOrderValidate.php +++ b/server/app/adminapi/validate/tcm/PrescriptionOrderValidate.php @@ -36,6 +36,7 @@ class PrescriptionOrderValidate extends BaseValidate 'patient_name' => 'require|max:50', 'phone' => 'require|max:20', 'phone_tail' => 'max:20|regex:/^\\d*$/', + 'amount_update' => 'require|float|gt:0', ]; protected $message = [ @@ -50,6 +51,8 @@ class PrescriptionOrderValidate extends BaseValidate 'patient_name.require' => '请输入患者姓名', 'phone.require' => '请输入手机号', 'phone_tail.regex' => '手机后四位仅支持数字', + 'amount_update.require' => '请输入订单金额', + 'amount_update.gt' => '订单金额必须大于0', ]; protected $scene = [ @@ -77,5 +80,6 @@ class PrescriptionOrderValidate extends BaseValidate 'submitGancaoRecipel' => ['id'], 'previewGancaoRecipel' => ['id'], 'patchPrescriptionPatient' => ['id', 'patient_name', 'phone'], + 'updateAmount' => ['id', 'amount_update'], ]; } diff --git a/server/sql/1.9.20260508/add_update_amount_menu.sql b/server/sql/1.9.20260508/add_update_amount_menu.sql new file mode 100644 index 00000000..620957bd --- /dev/null +++ b/server/sql/1.9.20260508/add_update_amount_menu.sql @@ -0,0 +1,17 @@ +-- 处方业务订单:修改订单金额 +-- 执行前确认表前缀为 zyt_;若已有相同 perms 则跳过。 +-- 需在「角色权限」中为有权限的角色勾选本按钮权限。 + +SET @po_menu_id := (SELECT id FROM zyt_system_menu WHERE perms = 'tcm.prescriptionOrder/lists' LIMIT 1); + +INSERT INTO zyt_system_menu ( + pid, type, name, icon, sort, perms, paths, component, + selected, params, is_cache, is_show, is_disable, create_time, update_time +) +SELECT + @po_menu_id, 'A', '修改订单金额', '', 60, + 'tcm.prescriptionOrder/updateAmount', '', '', + '', '', 0, 1, 0, UNIX_TIMESTAMP(), UNIX_TIMESTAMP() +FROM DUAL +WHERE @po_menu_id IS NOT NULL + AND NOT EXISTS (SELECT 1 FROM zyt_system_menu WHERE perms = 'tcm.prescriptionOrder/updateAmount');