|
|
@@ -239,9 +239,10 @@ class ProcuremenOperLog
|
|
|
|
|
|
/**
|
|
|
* @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
|
|
|
+ public static function formatLogRowsForDisplay(array $logs, array $processNameBySid = []): array
|
|
|
{
|
|
|
foreach ($logs as &$lg) {
|
|
|
if (!is_array($lg)) {
|
|
|
@@ -253,12 +254,546 @@ class ProcuremenOperLog
|
|
|
(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')) {
|