中文操作类型(入库展示用;与库中已有中文值对齐) * * @return array */ public static function actionLabelMap(): array { return [ 'issue_submit' => '下发通知', 'audit_select' => '确认供应商', 'purchase_confirm' => '审核确认供应商', 'purchase_reject' => '审核驳回', 'bid_open_verify' => '开标验证', 'audit_append_supplier' => '补加供应商', 'audit_resend_sms' => '重发短信', 'audit_resend_email' => '重发邮件', 'manual_add' => '手工新增', 'pick_soft_delete' => '初选删除', 'save_qty_price' => '保存数量限价', 'mark_complete' => '直接完结', 'audit_abandon' => '审批重新下发', 'archive_abandon' => '历史重新下发', ]; } /** * 同一业务动作的可选别名(查询时一并匹配) * * @return array */ public static function actionAliases(): array { return [ 'issue_submit' => ['下发通知', '下发', 'issue_submit'], 'audit_select' => ['确认供应商', 'audit_select'], 'purchase_confirm' => ['审核确认供应商', 'purchase_confirm'], 'purchase_reject' => ['审核驳回', 'purchase_reject'], 'bid_open_verify' => ['开标验证', 'bid_open_verify'], 'audit_append_supplier' => ['补加供应商', 'audit_append_supplier'], 'audit_resend_sms' => ['重发短信', 'audit_resend_sms'], 'audit_resend_email' => ['重发邮件', 'audit_resend_email'], 'manual_add' => ['手工新增', 'manual_add'], 'pick_soft_delete' => ['初选删除', 'pick_soft_delete'], 'save_qty_price' => ['保存数量限价', 'save_qty_price'], 'mark_complete' => ['直接完结', '完结', 'mark_complete'], 'audit_abandon' => ['审批重新下发', '重新下发', 'audit_abandon'], 'archive_abandon' => ['历史重新下发', '存证重新下发', 'archive_abandon'], ]; } public static function toActionLabel(string $action): string { $action = trim($action); if ($action === '') { return ''; } $map = self::actionLabelMap(); if (isset($map[$action])) { return $map[$action]; } // 已是中文别名时,归一到主标签 foreach (self::actionAliases() as $code => $aliases) { if (in_array($action, $aliases, true)) { return $map[$code] ?? $action; } } return $action; } /** * 查询用:同时匹配中文标签、别名与历史英文码 * * @param string|array $actions * @return string[] */ public static function expandActionQueryValues($actions): array { $aliases = self::actionAliases(); $map = self::actionLabelMap(); $out = []; foreach ((array)$actions as $a) { $a = trim((string)$a); if ($a === '') { continue; } $out[$a] = true; $code = $a; if (!isset($aliases[$code])) { foreach ($aliases as $c => $list) { if (in_array($a, $list, true) || (isset($map[$c]) && $map[$c] === $a)) { $code = $c; break; } } } if (isset($aliases[$code])) { foreach ($aliases[$code] as $v) { $out[$v] = true; } } if (isset($map[$code])) { $out[$map[$code]] = true; } } return array_keys($out); } /** * 组装入库行(英文字段,操作类型存中文) * * @return array */ public static function buildInsertRow( int $scydgyId, string $action, string $content, ?int $purchaseOrderId, int $adminId, string $adminName ): array { return [ self::COL_SCYDGY_ID => $scydgyId, self::COL_PO_ID => $purchaseOrderId, self::COL_ADMIN_ID => $adminId, self::COL_ADMIN_NAME => self::cut($adminName !== '' ? $adminName : '未知用户', 64), self::COL_ACTION => self::cut(self::toActionLabel($action), 64), self::COL_CONTENT => self::cut($content, 1000), self::COL_TIME => ProcuremenTime::nowDateTime(), ]; } /** * @param array{0:int,1:string} $admin [id, name] */ public static function write(int $scydgyId, string $action, string $content, ?int $purchaseOrderId, array $admin): void { if (!ProcuremenGuard::isValidScydgyId($scydgyId) || $content === '') { return; } $adminId = (int)($admin[0] ?? 0); $adminName = (string)($admin[1] ?? '未知用户'); try { Db::table('purchase_order_oper_log')->insert( self::buildInsertRow($scydgyId, $action, $content, $purchaseOrderId, $adminId, $adminName) ); } catch (\Throwable $e) { Log::write('procuremen oper log: ' . $e->getMessage(), 'error'); } } /** * 查询结果规范化(兼容偶发中文列名 → 英文键) * * @param array $log * @return array */ public static function normalizeRow(array $log): array { $map = [ '工序ID' => 'scydgy_id', '采购订单ID' => 'purchase_order_id', '操作人ID' => 'admin_id', '操作人' => 'admin_name', '操作类型' => 'action', '操作内容' => 'content', '创建时间' => 'createtime', ]; foreach ($map as $cn => $en) { if (array_key_exists($cn, $log) && !array_key_exists($en, $log)) { $log[$en] = $log[$cn]; } } if (isset($log['action'])) { $log['action'] = self::toActionLabel((string)$log['action']); } return $log; } /** * @param string|array $actions */ public static function resolveLatestTime(int $scydgyId, $actions): string { if (!ProcuremenGuard::isValidScydgyId($scydgyId)) { return ''; } $acts = self::expandActionQueryValues($actions); if ($acts === []) { return ''; } try { $log = Db::table('purchase_order_oper_log') ->where(self::COL_SCYDGY_ID, $scydgyId) ->where(self::COL_ACTION, 'in', $acts) ->order('id', 'desc') ->find(); if (is_array($log)) { $log = self::normalizeRow($log); $text = ProcuremenTime::formatDisplayDateTime($log['createtime'] ?? null); if ($text !== '') { return $text; } } } catch (\Throwable $e) { } return ''; } public static function resolveMarkCompleteTime(int $scydgyId): string { return self::resolveLatestTime($scydgyId, 'mark_complete'); } /** * @param array> $logs * @return array> */ public static function formatLogRowsForDisplay(array $logs): array { foreach ($logs as &$lg) { if (!is_array($lg)) { continue; } $lg = self::normalizeRow($lg); $lg['createtime_text'] = ProcuremenTime::formatDisplayDateTime($lg['createtime'] ?? null); $lg['content'] = ProcuremenTime::formatOperLogContent( (string)($lg['content'] ?? ''), (string)($lg['action'] ?? '') ); } unset($lg); return $logs; } private static function cut(string $s, int $max): string { if (function_exists('mb_substr')) { return mb_substr($s, 0, $max, 'UTF-8'); } return strlen($s) <= $max ? $s : substr($s, 0, $max); } }