ProcuremenOperLog.php 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. <?php
  2. namespace app\common\library;
  3. use think\Db;
  4. use think\Log;
  5. /**
  6. * 协助采购操作日志(purchase_order_oper_log)
  7. *
  8. * 表字段为英文;写入时「action」存中文操作类型,查询时兼容历史英文码。
  9. */
  10. class ProcuremenOperLog
  11. {
  12. public const COL_SCYDGY_ID = 'scydgy_id';
  13. public const COL_PO_ID = 'purchase_order_id';
  14. public const COL_ADMIN_ID = 'admin_id';
  15. public const COL_ADMIN_NAME = 'admin_name';
  16. public const COL_ACTION = 'action';
  17. public const COL_CONTENT = 'content';
  18. public const COL_TIME = 'createtime';
  19. /**
  20. * 英文码 => 中文操作类型(入库展示用;与库中已有中文值对齐)
  21. *
  22. * @return array<string, string>
  23. */
  24. public static function actionLabelMap(): array
  25. {
  26. return [
  27. 'issue_submit' => '下发通知',
  28. 'audit_select' => '确认供应商',
  29. 'purchase_confirm' => '审核确认供应商',
  30. 'purchase_reject' => '审核驳回',
  31. 'bid_open_verify' => '开标验证',
  32. 'audit_append_supplier' => '补加供应商',
  33. 'audit_resend_sms' => '重发短信',
  34. 'audit_resend_email' => '重发邮件',
  35. 'manual_add' => '手工新增',
  36. 'pick_soft_delete' => '初选删除',
  37. 'save_qty_price' => '保存数量限价',
  38. 'mark_complete' => '直接完结',
  39. 'audit_abandon' => '审批重新下发',
  40. 'archive_abandon' => '历史重新下发',
  41. ];
  42. }
  43. /**
  44. * 同一业务动作的可选别名(查询时一并匹配)
  45. *
  46. * @return array<string, string[]>
  47. */
  48. public static function actionAliases(): array
  49. {
  50. return [
  51. 'issue_submit' => ['下发通知', '下发', 'issue_submit'],
  52. 'audit_select' => ['确认供应商', 'audit_select'],
  53. 'purchase_confirm' => ['审核确认供应商', 'purchase_confirm'],
  54. 'purchase_reject' => ['审核驳回', 'purchase_reject'],
  55. 'bid_open_verify' => ['开标验证', 'bid_open_verify'],
  56. 'audit_append_supplier' => ['补加供应商', 'audit_append_supplier'],
  57. 'audit_resend_sms' => ['重发短信', 'audit_resend_sms'],
  58. 'audit_resend_email' => ['重发邮件', 'audit_resend_email'],
  59. 'manual_add' => ['手工新增', 'manual_add'],
  60. 'pick_soft_delete' => ['初选删除', 'pick_soft_delete'],
  61. 'save_qty_price' => ['保存数量限价', 'save_qty_price'],
  62. 'mark_complete' => ['直接完结', '完结', 'mark_complete'],
  63. 'audit_abandon' => ['审批重新下发', '重新下发', 'audit_abandon'],
  64. 'archive_abandon' => ['历史重新下发', '存证重新下发', 'archive_abandon'],
  65. ];
  66. }
  67. public static function toActionLabel(string $action): string
  68. {
  69. $action = trim($action);
  70. if ($action === '') {
  71. return '';
  72. }
  73. $map = self::actionLabelMap();
  74. if (isset($map[$action])) {
  75. return $map[$action];
  76. }
  77. // 已是中文别名时,归一到主标签
  78. foreach (self::actionAliases() as $code => $aliases) {
  79. if (in_array($action, $aliases, true)) {
  80. return $map[$code] ?? $action;
  81. }
  82. }
  83. return $action;
  84. }
  85. /**
  86. * 查询用:同时匹配中文标签、别名与历史英文码
  87. *
  88. * @param string|array<int, string> $actions
  89. * @return string[]
  90. */
  91. public static function expandActionQueryValues($actions): array
  92. {
  93. $aliases = self::actionAliases();
  94. $map = self::actionLabelMap();
  95. $out = [];
  96. foreach ((array)$actions as $a) {
  97. $a = trim((string)$a);
  98. if ($a === '') {
  99. continue;
  100. }
  101. $out[$a] = true;
  102. $code = $a;
  103. if (!isset($aliases[$code])) {
  104. foreach ($aliases as $c => $list) {
  105. if (in_array($a, $list, true) || (isset($map[$c]) && $map[$c] === $a)) {
  106. $code = $c;
  107. break;
  108. }
  109. }
  110. }
  111. if (isset($aliases[$code])) {
  112. foreach ($aliases[$code] as $v) {
  113. $out[$v] = true;
  114. }
  115. }
  116. if (isset($map[$code])) {
  117. $out[$map[$code]] = true;
  118. }
  119. }
  120. return array_keys($out);
  121. }
  122. /**
  123. * 组装入库行(英文字段,操作类型存中文)
  124. *
  125. * @return array<string, mixed>
  126. */
  127. public static function buildInsertRow(
  128. int $scydgyId,
  129. string $action,
  130. string $content,
  131. ?int $purchaseOrderId,
  132. int $adminId,
  133. string $adminName
  134. ): array {
  135. return [
  136. self::COL_SCYDGY_ID => $scydgyId,
  137. self::COL_PO_ID => $purchaseOrderId,
  138. self::COL_ADMIN_ID => $adminId,
  139. self::COL_ADMIN_NAME => self::cut($adminName !== '' ? $adminName : '未知用户', 64),
  140. self::COL_ACTION => self::cut(self::toActionLabel($action), 64),
  141. self::COL_CONTENT => self::cut($content, 1000),
  142. self::COL_TIME => ProcuremenTime::nowDateTime(),
  143. ];
  144. }
  145. /**
  146. * @param array{0:int,1:string} $admin [id, name]
  147. */
  148. public static function write(int $scydgyId, string $action, string $content, ?int $purchaseOrderId, array $admin): void
  149. {
  150. if (!ProcuremenGuard::isValidScydgyId($scydgyId) || $content === '') {
  151. return;
  152. }
  153. $adminId = (int)($admin[0] ?? 0);
  154. $adminName = (string)($admin[1] ?? '未知用户');
  155. try {
  156. Db::table('purchase_order_oper_log')->insert(
  157. self::buildInsertRow($scydgyId, $action, $content, $purchaseOrderId, $adminId, $adminName)
  158. );
  159. } catch (\Throwable $e) {
  160. Log::write('procuremen oper log: ' . $e->getMessage(), 'error');
  161. }
  162. }
  163. /**
  164. * 查询结果规范化(兼容偶发中文列名 → 英文键)
  165. *
  166. * @param array<string, mixed> $log
  167. * @return array<string, mixed>
  168. */
  169. public static function normalizeRow(array $log): array
  170. {
  171. $map = [
  172. '工序ID' => 'scydgy_id',
  173. '采购订单ID' => 'purchase_order_id',
  174. '操作人ID' => 'admin_id',
  175. '操作人' => 'admin_name',
  176. '操作类型' => 'action',
  177. '操作内容' => 'content',
  178. '创建时间' => 'createtime',
  179. ];
  180. foreach ($map as $cn => $en) {
  181. if (array_key_exists($cn, $log) && !array_key_exists($en, $log)) {
  182. $log[$en] = $log[$cn];
  183. }
  184. }
  185. if (isset($log['action'])) {
  186. $log['action'] = self::toActionLabel((string)$log['action']);
  187. }
  188. return $log;
  189. }
  190. /**
  191. * @param string|array<int, string> $actions
  192. */
  193. public static function resolveLatestTime(int $scydgyId, $actions): string
  194. {
  195. if (!ProcuremenGuard::isValidScydgyId($scydgyId)) {
  196. return '';
  197. }
  198. $acts = self::expandActionQueryValues($actions);
  199. if ($acts === []) {
  200. return '';
  201. }
  202. try {
  203. $log = Db::table('purchase_order_oper_log')
  204. ->where(self::COL_SCYDGY_ID, $scydgyId)
  205. ->where(self::COL_ACTION, 'in', $acts)
  206. ->order('id', 'desc')
  207. ->find();
  208. if (is_array($log)) {
  209. $log = self::normalizeRow($log);
  210. $text = ProcuremenTime::formatDisplayDateTime($log['createtime'] ?? null);
  211. if ($text !== '') {
  212. return $text;
  213. }
  214. }
  215. } catch (\Throwable $e) {
  216. }
  217. return '';
  218. }
  219. public static function resolveMarkCompleteTime(int $scydgyId): string
  220. {
  221. return self::resolveLatestTime($scydgyId, 'mark_complete');
  222. }
  223. /**
  224. * @param array<int, array<string, mixed>> $logs
  225. * @return array<int, array<string, mixed>>
  226. */
  227. public static function formatLogRowsForDisplay(array $logs): array
  228. {
  229. foreach ($logs as &$lg) {
  230. if (!is_array($lg)) {
  231. continue;
  232. }
  233. $lg = self::normalizeRow($lg);
  234. $lg['createtime_text'] = ProcuremenTime::formatDisplayDateTime($lg['createtime'] ?? null);
  235. $lg['content'] = ProcuremenTime::formatOperLogContent(
  236. (string)($lg['content'] ?? ''),
  237. (string)($lg['action'] ?? '')
  238. );
  239. }
  240. unset($lg);
  241. return $logs;
  242. }
  243. private static function cut(string $s, int $max): string
  244. {
  245. if (function_exists('mb_substr')) {
  246. return mb_substr($s, 0, $max, 'UTF-8');
  247. }
  248. return strlen($s) <= $max ? $s : substr($s, 0, $max);
  249. }
  250. }