| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805 |
- <?php
- namespace app\common\library;
- use think\Db;
- use think\Log;
- /**
- * 协助采购操作日志(purchase_order_oper_log)
- *
- * 表字段为英文;写入时「action」存中文操作类型,查询时兼容历史英文码。
- */
- class ProcuremenOperLog
- {
- public const COL_SCYDGY_ID = 'scydgy_id';
- public const COL_PO_ID = 'purchase_order_id';
- public const COL_ADMIN_ID = 'admin_id';
- public const COL_ADMIN_NAME = 'admin_name';
- public const COL_ACTION = 'action';
- public const COL_CONTENT = 'content';
- public const COL_TIME = 'createtime';
- /**
- * 英文码 => 中文操作类型(入库展示用;与库中已有中文值对齐)
- *
- * @return array<string, string>
- */
- 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<string, string[]>
- */
- 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<int, string> $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<string, mixed>
- */
- 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<string, mixed> $log
- * @return array<string, mixed>
- */
- 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<int, string> $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<int, array<string, mixed>> $logs
- * @param array<int, string> $processNameBySid scydgy_id => 工序名称
- * @return array<int, array<string, mixed>>
- */
- public static function formatLogRowsForDisplay(array $logs, array $processNameBySid = []): 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'] ?? '')
- );
- $lg['action_text'] = self::toActionLabel((string)($lg['action'] ?? ''));
- $lg = self::enrichSaveQtyPriceProcessName($lg, $processNameBySid);
- }
- unset($lg);
- $logs = self::mergeDuplicateProcessLogs($logs, $processNameBySid);
- $logs = self::enrichAdminDisplayFields($logs);
- foreach ($logs as &$lg) {
- if (!is_array($lg)) {
- continue;
- }
- $lg = self::splitActionAndDetailForDisplay($lg);
- }
- unset($lg);
- return $logs;
- }
- /**
- * 展示拆成「操作类型」+「操作说明」,说明里不再重复类型前缀
- *
- * @param array<string, mixed> $lg
- * @return array<string, mixed>
- */
- protected static function splitActionAndDetailForDisplay(array $lg): array
- {
- $action = trim((string)($lg['action_text'] ?? ''));
- if ($action === '') {
- $action = self::toActionLabel((string)($lg[self::COL_ACTION] ?? ''));
- }
- $content = trim((string)($lg[self::COL_CONTENT] ?? ''));
- $content = self::stripLeadingActionPrefixes($content, $action);
- if ($action === '') {
- $action = '操作';
- }
- $lg['action_text'] = $action;
- $lg[self::COL_CONTENT] = $content;
- return $lg;
- }
- /**
- * @return string[] 按长度降序
- */
- protected static function contentActionPrefixes(string $action): array
- {
- $action = self::toActionLabel(trim($action));
- $list = [];
- if ($action !== '') {
- $list[] = $action;
- }
- // 历史/文案别名
- $aliases = [
- '下发通知' => ['下发', '下发通知'],
- '保存数量限价' => ['保存数量限价', '保存本次数量、最高限价'],
- '审批重新下发' => ['审批重新下发', '重新下发'],
- '历史重新下发' => ['历史重新下发', '存证重新下发', '重新下发'],
- '审核确认供应商' => ['审核确认供应商', '采购确认'],
- '确认供应商' => ['确认供应商'],
- '开标验证' => ['开标验证'],
- '补加供应商' => ['补加供应商'],
- '直接完结' => ['直接完结', '完结'],
- '审核驳回' => ['审核驳回', '采购终审驳回'],
- ];
- if (isset($aliases[$action])) {
- foreach ($aliases[$action] as $a) {
- $list[] = $a;
- }
- }
- $list = array_values(array_unique(array_filter(array_map('trim', $list))));
- usort($list, static function ($a, $b) {
- return mb_strlen((string)$b, 'UTF-8') <=> mb_strlen((string)$a, 'UTF-8');
- });
- return $list;
- }
- protected static function stripLeadingActionPrefixes(string $content, string $action): string
- {
- $content = trim($content);
- if ($content === '') {
- return '';
- }
- foreach (self::contentActionPrefixes($action) as $p) {
- foreach ([$p . ':', $p . ':'] as $pref) {
- if (mb_strpos($content, $pref, 0, 'UTF-8') === 0) {
- return trim(mb_substr($content, mb_strlen($pref, 'UTF-8'), null, 'UTF-8'));
- }
- }
- }
- return $content;
- }
- /**
- * 历史「保存数量限价」说明补工序名:…:压折线本次数量「330」,最高限价「10」
- *
- * @param array<string, mixed> $lg
- * @param array<int, string> $processNameBySid
- * @return array<string, mixed>
- */
- protected static function enrichSaveQtyPriceProcessName(array $lg, array $processNameBySid): array
- {
- $action = trim((string)($lg['action_text'] ?? $lg[self::COL_ACTION] ?? ''));
- if ($action !== '保存数量限价' && $action !== 'save_qty_price') {
- return $lg;
- }
- $content = trim((string)($lg[self::COL_CONTENT] ?? ''));
- if ($content === '') {
- return $lg;
- }
- $sid = (int)($lg[self::COL_SCYDGY_ID] ?? 0);
- $proc = trim((string)($processNameBySid[$sid] ?? ''));
- if ($proc === '') {
- return $lg;
- }
- // 已含工序名则不重复
- if (mb_strpos($content, $proc . '本次数量', 0, 'UTF-8') !== false
- || mb_strpos($content, '(' . $proc . ')', 0, 'UTF-8') !== false) {
- return $lg;
- }
- if (mb_strpos($content, ':本次数量', 0, 'UTF-8') !== false) {
- $lg[self::COL_CONTENT] = preg_replace('/:本次数量/u', ':' . $proc . '本次数量', $content, 1);
- } elseif (mb_strpos($content, ':本次数量', 0, 'UTF-8') !== false) {
- $lg[self::COL_CONTENT] = preg_replace('/:本次数量/u', ':' . $proc . '本次数量', $content, 1);
- }
- return $lg;
- }
- /**
- * 操作人前加所属组别;说明文案中的人员名同样补组别
- *
- * @param array<int, array<string, mixed>> $logs
- * @return array<int, array<string, mixed>>
- */
- public static function enrichAdminDisplayFields(array $logs): array
- {
- $ids = [];
- foreach ($logs as $lg) {
- if (!is_array($lg)) {
- continue;
- }
- $id = (int)($lg[self::COL_ADMIN_ID] ?? 0);
- if ($id > 0) {
- $ids[$id] = true;
- }
- }
- $map = self::loadAdminGroupNicknameMap(array_keys($ids));
- // 仅用于纠正历史误伤:说明里曾把「组别 昵称」拼进供应商名,展示时还原为昵称/公司名
- $nameToLabeled = self::buildAdminNameLabelMap(self::loadAllAdminGroupNicknameMap());
- foreach (self::buildAdminNameLabelMap($map) as $name => $labeled) {
- $nameToLabeled[$name] = $labeled;
- }
- uksort($nameToLabeled, static function ($a, $b) {
- return mb_strlen((string)$b, 'UTF-8') <=> mb_strlen((string)$a, 'UTF-8');
- });
- foreach ($logs as &$lg) {
- if (!is_array($lg)) {
- continue;
- }
- $id = (int)($lg[self::COL_ADMIN_ID] ?? 0);
- $info = ($id > 0 && isset($map[$id])) ? $map[$id] : null;
- $nick = is_array($info) ? trim((string)($info['nickname'] ?? '')) : '';
- if ($nick === '') {
- $nick = trim((string)($lg[self::COL_ADMIN_NAME] ?? ''));
- }
- if ($nick === '') {
- $nick = '未知用户';
- }
- $group = is_array($info) ? trim((string)($info['group_name'] ?? '')) : '';
- $lg['admin_group'] = $group !== '' ? $group : '—';
- $lg['admin_nickname'] = $nick;
- // 操作人显示「组别 昵称」
- $lg[self::COL_ADMIN_NAME] = self::formatAdminWithGroup($group, $nick);
- $content = (string)($lg[self::COL_CONTENT] ?? '');
- if ($content !== '' && $nameToLabeled !== []) {
- // 先还原历史误伤的供应商名
- $content = self::unprefixMistakenNamesInText($content, $nameToLabeled);
- // 开标验证等说明里是人员名,再补组别
- $actionLabel = self::toActionLabel((string)($lg['action_text'] ?? $lg[self::COL_ACTION] ?? ''));
- if (self::actionContentNeedsPersonGroup($actionLabel)) {
- $content = self::prefixNamesInText($content, $nameToLabeled);
- }
- $lg[self::COL_CONTENT] = $content;
- }
- }
- unset($lg);
- return $logs;
- }
- /** 操作说明里会出现人员姓名、需补所属组别的类型 */
- protected static function actionContentNeedsPersonGroup(string $action): bool
- {
- $action = self::toActionLabel(trim($action));
- return $action === '开标验证';
- }
- /**
- * 按 admin.id 解析展示名「组别 昵称」
- *
- * @param array<int, int> $adminIds
- * @return array<int, string>
- */
- public static function resolveAdminDisplayLabels(array $adminIds): array
- {
- $map = self::loadAdminGroupNicknameMap($adminIds);
- $out = [];
- foreach ($adminIds as $rawId) {
- $id = (int)$rawId;
- if ($id <= 0) {
- continue;
- }
- $info = isset($map[$id]) && is_array($map[$id]) ? $map[$id] : null;
- $nick = is_array($info) ? trim((string)($info['nickname'] ?? '')) : '';
- $group = is_array($info) ? trim((string)($info['group_name'] ?? '')) : '';
- if ($nick === '') {
- $nick = 'ID' . $id;
- }
- $out[$id] = self::formatAdminWithGroup($group, $nick);
- }
- return $out;
- }
- /**
- * @param array<int, array{nickname:string,group_name:string}> $adminMap
- * @return array<string, string> 原名 => 「组别 名称」
- */
- protected static function buildAdminNameLabelMap(array $adminMap): array
- {
- $out = [];
- foreach ($adminMap as $info) {
- if (!is_array($info)) {
- continue;
- }
- $nick = trim((string)($info['nickname'] ?? ''));
- if ($nick === '') {
- continue;
- }
- $group = trim((string)($info['group_name'] ?? ''));
- $labeled = self::formatAdminWithGroup($group, $nick);
- if ($labeled !== $nick) {
- $out[$nick] = $labeled;
- }
- }
- // 长名优先,避免短名误伤
- uksort($out, static function ($a, $b) {
- return mb_strlen((string)$b, 'UTF-8') <=> mb_strlen((string)$a, 'UTF-8');
- });
- return $out;
- }
- protected static function formatAdminWithGroup(string $group, string $name): string
- {
- $name = trim($name);
- $group = trim($group);
- if ($name === '') {
- return $group !== '' ? $group : '未知用户';
- }
- if ($group === '' || $group === '—' || $group === $name) {
- return $name;
- }
- // 已带组别则不再重复
- if (mb_strpos($name, $group, 0, 'UTF-8') === 0) {
- return $name;
- }
- return $group . ' ' . $name;
- }
- /**
- * @param array<string, string> $nameToLabeled
- */
- protected static function prefixNamesInText(string $text, array $nameToLabeled): string
- {
- foreach ($nameToLabeled as $name => $labeled) {
- if ($name === '' || $labeled === '' || $name === $labeled) {
- continue;
- }
- if (mb_strpos($text, $labeled, 0, 'UTF-8') !== false) {
- continue;
- }
- if (mb_strpos($text, $name, 0, 'UTF-8') === false) {
- continue;
- }
- $text = str_replace($name, $labeled, $text);
- }
- return $text;
- }
- /**
- * 还原说明里误拼的「组别 昵称」→ 原名(避免供应商名被改成「浙江新华管理员 新华印刷…」)
- *
- * @param array<string, string> $nameToLabeled
- */
- protected static function unprefixMistakenNamesInText(string $text, array $nameToLabeled): string
- {
- // 长标签优先替换
- $pairs = [];
- foreach ($nameToLabeled as $name => $labeled) {
- if ($name === '' || $labeled === '' || $name === $labeled) {
- continue;
- }
- $pairs[$labeled] = $name;
- }
- uksort($pairs, static function ($a, $b) {
- return mb_strlen((string)$b, 'UTF-8') <=> mb_strlen((string)$a, 'UTF-8');
- });
- foreach ($pairs as $labeled => $name) {
- if (mb_strpos($text, $labeled, 0, 'UTF-8') === false) {
- continue;
- }
- $text = str_replace($labeled, $name, $text);
- }
- return $text;
- }
- /**
- * @return array<int, array{nickname:string,group_name:string,username?:string}>
- */
- public static function loadAllAdminGroupNicknameMap(): array
- {
- try {
- $ids = Db::name('admin')->column('id');
- } catch (\Throwable $e) {
- $ids = [];
- }
- if (!is_array($ids) || $ids === []) {
- return [];
- }
- return self::loadAdminGroupNicknameMap(array_map('intval', $ids));
- }
- /**
- * @param array<int, int> $adminIds
- * @return array<int, array{nickname:string,group_name:string,username?:string}>
- */
- public static function loadAdminGroupNicknameMap(array $adminIds): array
- {
- $out = [];
- $adminIds = array_values(array_filter(array_map('intval', $adminIds)));
- if ($adminIds === []) {
- return $out;
- }
- try {
- $admins = Db::name('admin')->where('id', 'in', $adminIds)->field('id,nickname,username')->select();
- } catch (\Throwable $e) {
- $admins = [];
- }
- if (is_array($admins)) {
- foreach ($admins as $a) {
- if (!is_array($a)) {
- continue;
- }
- $id = (int)($a['id'] ?? 0);
- if ($id <= 0) {
- continue;
- }
- $nick = trim((string)($a['nickname'] ?? ''));
- if ($nick === '') {
- $nick = trim((string)($a['username'] ?? ''));
- }
- $out[$id] = ['nickname' => $nick, 'group_name' => '', 'username' => trim((string)($a['username'] ?? ''))];
- }
- }
- try {
- $rows = Db::name('auth_group_access')
- ->alias('aga')
- ->join('auth_group ag', 'aga.group_id = ag.id', 'LEFT')
- ->where('aga.uid', 'in', $adminIds)
- ->field('aga.uid,ag.name')
- ->select();
- } catch (\Throwable $e) {
- $rows = [];
- }
- if (is_array($rows)) {
- foreach ($rows as $r) {
- if (!is_array($r)) {
- continue;
- }
- $uid = (int)($r['uid'] ?? 0);
- $gname = trim((string)($r['name'] ?? ''));
- if ($uid <= 0 || $gname === '') {
- continue;
- }
- if (!isset($out[$uid])) {
- $out[$uid] = ['nickname' => '', 'group_name' => $gname];
- } elseif ($out[$uid]['group_name'] === '') {
- $out[$uid]['group_name'] = $gname;
- } elseif (strpos($out[$uid]['group_name'], $gname) === false) {
- $out[$uid]['group_name'] .= '、' . $gname;
- }
- }
- }
- return $out;
- }
- /**
- * 多工序短时间内相同操作合并为一条,并带上工序名称
- * 例:审核确认供应商:配套、快递已选定「xxx」
- *
- * @param array<int, array<string, mixed>> $logs
- * @param array<int, string> $processNameBySid
- * @return array<int, array<string, mixed>>
- */
- public static function mergeDuplicateProcessLogs(array $logs, array $processNameBySid = []): array
- {
- if ($logs === []) {
- return [];
- }
- $groups = [];
- $order = [];
- foreach ($logs as $idx => $lg) {
- if (!is_array($lg)) {
- continue;
- }
- $adminId = (int)($lg[self::COL_ADMIN_ID] ?? 0);
- $admin = trim((string)($lg[self::COL_ADMIN_NAME] ?? ''));
- $action = trim((string)($lg['action_text'] ?? $lg[self::COL_ACTION] ?? ''));
- $content = trim((string)($lg[self::COL_CONTENT] ?? ''));
- $contentCore = self::normalizeMergeContentCore($action, $content);
- $ts = self::logTimeTs($lg);
- // 60 秒内、同操作人+同操作类型+同说明核心 → 合并
- $bucket = null;
- foreach ($groups as $gk => $g) {
- if (($g['admin_id'] !== $adminId && $g['admin'] !== $admin)
- || $g['action'] !== $action
- || $g['content_core'] !== $contentCore) {
- continue;
- }
- if (abs($ts - (int)$g['ts']) <= 60) {
- $bucket = $gk;
- break;
- }
- }
- if ($bucket === null) {
- $bucket = 'g' . $idx;
- $groups[$bucket] = [
- 'admin_id' => $adminId,
- 'admin' => $admin,
- 'action' => $action,
- 'content_core' => $contentCore,
- 'ts' => $ts,
- 'row' => $lg,
- 'sids' => [],
- 'contents' => [],
- ];
- $order[] = $bucket;
- }
- $sid = (int)($lg[self::COL_SCYDGY_ID] ?? 0);
- if ($sid !== 0) {
- $groups[$bucket]['sids'][$sid] = true;
- }
- $groups[$bucket]['contents'][] = $content;
- // 保留最早一条作展示底稿,时间用最早
- $rowTs = self::logTimeTs($groups[$bucket]['row']);
- if ($ts > 0 && ($rowTs === 0 || $ts < $rowTs)) {
- $groups[$bucket]['row'] = $lg;
- $groups[$bucket]['ts'] = $ts;
- }
- }
- $out = [];
- foreach ($order as $gk) {
- $g = $groups[$gk];
- $row = $g['row'];
- $sids = array_keys($g['sids']);
- $processNames = [];
- foreach ($sids as $sid) {
- $nm = trim((string)($processNameBySid[$sid] ?? ''));
- if ($nm !== '' && !in_array($nm, $processNames, true)) {
- $processNames[] = $nm;
- }
- }
- $content = trim((string)($row[self::COL_CONTENT] ?? ''));
- if (count($sids) > 1 || count($processNames) > 1) {
- $content = self::buildMergedProcessContent(
- (string)($g['action'] ?? ''),
- $content,
- $processNames
- );
- }
- $row[self::COL_CONTENT] = $content;
- $row['action_text'] = (string)($g['action'] !== '' ? $g['action'] : ($row['action_text'] ?? ''));
- $out[] = $row;
- }
- return $out;
- }
- /**
- * 提取可合并的内容核心(去掉前缀动作名后的业务说明)
- */
- protected static function normalizeMergeContentCore(string $action, string $content): string
- {
- return self::stripLeadingActionPrefixes(trim($content), trim($action));
- }
- /**
- * @param array<int, string> $processNames
- */
- protected static function buildMergedProcessContent(string $action, string $content, array $processNames): string
- {
- $core = self::normalizeMergeContentCore($action, $content);
- $procText = implode('、', $processNames);
- if ($procText === '') {
- return $core !== '' ? $core : $content;
- }
- // 工序名放在操作说明里,不盖住操作类型
- if ($core !== '') {
- return $procText . ' ' . $core;
- }
- return $procText;
- }
- /**
- * @param array<string, mixed> $lg
- */
- protected static function logTimeTs(array $lg): int
- {
- $raw = trim((string)($lg['createtime_text'] ?? $lg[self::COL_TIME] ?? ''));
- if ($raw === '') {
- return 0;
- }
- $ts = strtotime(str_replace('T', ' ', $raw));
- return ($ts !== false && $ts > 0) ? (int)$ts : 0;
- }
- 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);
- }
- }
|