ProcuremenSchema.php 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519
  1. <?php
  2. namespace app\common\library;
  3. use think\Cache;
  4. use think\Config;
  5. use think\Db;
  6. use think\Log;
  7. /**
  8. * 协助采购 / 手机端报价相关表结构保障:
  9. * - 表不存在 → CREATE(完整字段)
  10. * - 表存在但缺列 → ALTER ADD(静默,不抛「字段不存在」给业务)
  11. */
  12. class ProcuremenSchema
  13. {
  14. public const CACHE_KEY = 'procuremen_quote_schema_ok_v2';
  15. /** @var bool */
  16. protected static $done = false;
  17. public static function ensureAll(): void
  18. {
  19. if (self::$done) {
  20. return;
  21. }
  22. self::$done = true;
  23. try {
  24. if (Cache::get(self::CACHE_KEY)) {
  25. return;
  26. }
  27. } catch (\Throwable $e) {
  28. }
  29. $changed = false;
  30. try {
  31. $changed = self::ensurePurchaseOrderTable() || $changed;
  32. $changed = self::ensurePurchaseOrderDetailTable() || $changed;
  33. $changed = self::ensurePurchaseOrderOperLogTable() || $changed;
  34. $changed = self::ensurePurchaseOrderBidOpenTable() || $changed;
  35. $changed = self::ensurePurchaseOrderInboundScoreTable() || $changed;
  36. $changed = self::ensurePurchaseEmailSendLogTable() || $changed;
  37. $changed = self::ensurePurchaseSmsTemplateTable() || $changed;
  38. try {
  39. ProcuremenSupplierScore::ensureSchema();
  40. } catch (\Throwable $e) {
  41. Log::write('ProcuremenSchema score: ' . $e->getMessage(), 'error');
  42. }
  43. if ($changed) {
  44. self::clearTableFieldsCache([
  45. 'purchase_order',
  46. 'purchase_order_detail',
  47. 'purchase_order_oper_log',
  48. 'purchase_order_bid_open',
  49. 'purchase_order_inbound_score',
  50. 'purchase_email_send_log',
  51. 'purchase_sms_template',
  52. ]);
  53. }
  54. try {
  55. Cache::set(self::CACHE_KEY, 1, 86400);
  56. } catch (\Throwable $e) {
  57. }
  58. } catch (\Throwable $e) {
  59. Log::write('ProcuremenSchema::ensureAll: ' . $e->getMessage(), 'error');
  60. }
  61. }
  62. /**
  63. * 强制下次请求重新探测(改库后可清 runtime cache 或调此方法)
  64. */
  65. public static function invalidateCache(): void
  66. {
  67. self::$done = false;
  68. try {
  69. Cache::rm(self::CACHE_KEY);
  70. } catch (\Throwable $e) {
  71. }
  72. }
  73. protected static function tableExists(string $table): bool
  74. {
  75. try {
  76. Db::query('SELECT 1 FROM `' . str_replace('`', '', $table) . '` LIMIT 1');
  77. return true;
  78. } catch (\Throwable $e) {
  79. return false;
  80. }
  81. }
  82. /**
  83. * @param array<string, string> $columns col => ADD COLUMN ... DDL fragment (without ADD COLUMN prefix ok if starts with ADD)
  84. */
  85. protected static function ensureColumns(string $table, array $columns): bool
  86. {
  87. $changed = false;
  88. $table = str_replace('`', '', $table);
  89. foreach ($columns as $col => $ddl) {
  90. $col = str_replace('`', '', (string)$col);
  91. if ($col === '') {
  92. continue;
  93. }
  94. try {
  95. $exists = Db::query("SHOW COLUMNS FROM `{$table}` LIKE '{$col}'");
  96. if (is_array($exists) && $exists !== []) {
  97. continue;
  98. }
  99. $ddl = trim($ddl);
  100. if (stripos($ddl, 'ADD ') !== 0) {
  101. $ddl = 'ADD COLUMN ' . $ddl;
  102. }
  103. Db::execute("ALTER TABLE `{$table}` {$ddl}");
  104. $changed = true;
  105. } catch (\Throwable $e) {
  106. Log::write("ProcuremenSchema {$table}.{$col}: " . $e->getMessage(), 'error');
  107. }
  108. }
  109. return $changed;
  110. }
  111. /**
  112. * @param string[] $tables
  113. */
  114. protected static function clearTableFieldsCache(array $tables): void
  115. {
  116. $db = '';
  117. try {
  118. $db = (string)Config::get('database.database');
  119. } catch (\Throwable $e) {
  120. }
  121. try {
  122. $ref = new \ReflectionProperty(\think\db\Query::class, 'info');
  123. $ref->setAccessible(true);
  124. $info = $ref->getValue();
  125. if (is_array($info)) {
  126. foreach ($tables as $t) {
  127. $key = ($db !== '' ? $db . '.' : '') . $t;
  128. unset($info[$key], $info[$t]);
  129. }
  130. $ref->setValue(null, $info);
  131. }
  132. } catch (\Throwable $e) {
  133. }
  134. if ($db === '') {
  135. return;
  136. }
  137. foreach ($tables as $t) {
  138. $schemaFile = RUNTIME_PATH . 'schema' . DIRECTORY_SEPARATOR . $db . '.' . $t . '.php';
  139. if (is_file($schemaFile)) {
  140. @unlink($schemaFile);
  141. }
  142. }
  143. }
  144. protected static function ensurePurchaseOrderTable(): bool
  145. {
  146. $changed = false;
  147. if (!self::tableExists('purchase_order')) {
  148. try {
  149. Db::execute("CREATE TABLE IF NOT EXISTS `purchase_order` (
  150. `id` int NOT NULL AUTO_INCREMENT,
  151. `scydgy_id` int DEFAULT NULL COMMENT '工序行ID(手工询价为负数)',
  152. `CCYDH` varchar(255) DEFAULT NULL COMMENT '订单号',
  153. `CYJMC` varchar(255) DEFAULT NULL COMMENT '印件名称',
  154. `CDXMC` varchar(255) DEFAULT NULL,
  155. `CGYBH` varchar(255) DEFAULT NULL,
  156. `CCLBMMC` varchar(255) DEFAULT NULL COMMENT '需求部门',
  157. `CGYMC` varchar(255) DEFAULT NULL COMMENT '工序名称',
  158. `CDW` varchar(255) DEFAULT NULL COMMENT '单位',
  159. `czlyq` varchar(64) NOT NULL DEFAULT '' COMMENT '类型',
  160. `NGZL` varchar(255) DEFAULT NULL COMMENT '工作量',
  161. `This_quantity` varchar(255) DEFAULT NULL COMMENT '本次数量',
  162. `ceilingPrice` varchar(255) DEFAULT NULL COMMENT '最高限价',
  163. `CDF` varchar(255) DEFAULT NULL COMMENT '订法',
  164. `cGzzxMc` varchar(255) DEFAULT NULL COMMENT '外厂单位',
  165. `MBZ` varchar(255) DEFAULT NULL COMMENT '备注',
  166. `bwjg` varchar(255) DEFAULT NULL,
  167. `iStatus` varchar(255) DEFAULT NULL,
  168. `dStamp` varchar(255) DEFAULT NULL,
  169. `dputrecord` varchar(255) DEFAULT NULL,
  170. `cywyxm` varchar(255) DEFAULT NULL COMMENT '业务员/需求发起人',
  171. `notify_salesman` varchar(500) NOT NULL DEFAULT '' COMMENT '通知业务员(可多选)',
  172. `rfq_salesman_notified` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '手工询价已通知业务员0否1是',
  173. `rfq_salesman_notify_time` datetime DEFAULT NULL COMMENT '手工询价通知业务员时间',
  174. `status` varchar(255) DEFAULT NULL COMMENT '空=未完结、0=进行中、1=已完结',
  175. `createtime` varchar(255) DEFAULT NULL COMMENT '录入时间',
  176. `pdf_url` varchar(255) DEFAULT NULL COMMENT 'pdf路径',
  177. `sys_rq` varchar(255) DEFAULT NULL COMMENT '招标截止日期',
  178. `delivery_deadline` date DEFAULT NULL COMMENT '交货截止日期',
  179. `wflow_status` varchar(255) DEFAULT NULL COMMENT '流程状态',
  180. `pick_company_name` varchar(255) DEFAULT NULL COMMENT '中标/选定供应商',
  181. `manual_pick` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '1=自选供应商(免开标)',
  182. `manual_pick_reason` varchar(500) NOT NULL DEFAULT '' COMMENT '自选供应商说明',
  183. `pick_time` datetime DEFAULT NULL COMMENT '选定时间',
  184. `score_rule_id` int unsigned NOT NULL DEFAULT 0 COMMENT '评分规则ID',
  185. `score_rule_name` varchar(100) NOT NULL DEFAULT '' COMMENT '规则名称快照',
  186. `score_quality_weight` int unsigned NOT NULL DEFAULT 0 COMMENT '质量权重快照%',
  187. `score_price_weight` int unsigned NOT NULL DEFAULT 0 COMMENT '价格权重快照%',
  188. `score_lead_weight` int unsigned NOT NULL DEFAULT 0 COMMENT '交货权重快照%',
  189. `order_batch_no` varchar(32) NOT NULL DEFAULT '' COMMENT '下发批次号',
  190. `mod_rq` varchar(255) DEFAULT NULL COMMENT '软删除日期',
  191. PRIMARY KEY (`id`),
  192. KEY `idx_scydgy_id` (`scydgy_id`),
  193. KEY `idx_CCYDH` (`CCYDH`(64)),
  194. KEY `idx_order_batch_no` (`order_batch_no`),
  195. KEY `idx_wflow_status` (`wflow_status`(32))
  196. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='协助采购订单(工序行)'");
  197. $changed = true;
  198. } catch (\Throwable $e) {
  199. Log::write('ProcuremenSchema create purchase_order: ' . $e->getMessage(), 'error');
  200. }
  201. }
  202. $changed = self::ensureColumns('purchase_order', [
  203. 'czlyq' => "`czlyq` varchar(64) NOT NULL DEFAULT '' COMMENT '类型'",
  204. 'notify_salesman' => "`notify_salesman` varchar(500) NOT NULL DEFAULT '' COMMENT '通知业务员(可多选)'",
  205. 'rfq_salesman_notified' => "`rfq_salesman_notified` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '手工询价已通知业务员0否1是'",
  206. 'rfq_salesman_notify_time' => "`rfq_salesman_notify_time` datetime DEFAULT NULL COMMENT '手工询价通知业务员时间'",
  207. 'manual_pick' => "`manual_pick` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '1=自选供应商(免开标)'",
  208. 'manual_pick_reason' => "`manual_pick_reason` varchar(500) NOT NULL DEFAULT '' COMMENT '自选供应商说明'",
  209. 'pick_time' => "`pick_time` datetime DEFAULT NULL COMMENT '选定时间'",
  210. 'delivery_deadline' => "`delivery_deadline` date DEFAULT NULL COMMENT '交货截止日期'",
  211. 'score_rule_id' => "`score_rule_id` int unsigned NOT NULL DEFAULT 0 COMMENT '评分规则ID'",
  212. 'score_rule_name' => "`score_rule_name` varchar(100) NOT NULL DEFAULT '' COMMENT '规则名称快照'",
  213. 'score_quality_weight' => "`score_quality_weight` int unsigned NOT NULL DEFAULT 0 COMMENT '质量权重快照%'",
  214. 'score_price_weight' => "`score_price_weight` int unsigned NOT NULL DEFAULT 0 COMMENT '价格权重快照%'",
  215. 'score_lead_weight' => "`score_lead_weight` int unsigned NOT NULL DEFAULT 0 COMMENT '交货权重快照%'",
  216. 'order_batch_no' => "`order_batch_no` varchar(32) NOT NULL DEFAULT '' COMMENT '下发批次号(如XH202608240001)'",
  217. 'This_quantity' => "`This_quantity` varchar(255) DEFAULT NULL COMMENT '本次数量'",
  218. 'ceilingPrice' => "`ceilingPrice` varchar(255) DEFAULT NULL COMMENT '最高限价'",
  219. 'pick_company_name' => "`pick_company_name` varchar(255) DEFAULT NULL COMMENT '中标/选定供应商'",
  220. 'wflow_status' => "`wflow_status` varchar(255) DEFAULT NULL COMMENT '流程状态'",
  221. 'mod_rq' => "`mod_rq` varchar(255) DEFAULT NULL COMMENT '软删除日期'",
  222. 'cywyxm' => "`cywyxm` varchar(255) DEFAULT NULL COMMENT '业务员/需求发起人'",
  223. 'CCLBMMC' => "`CCLBMMC` varchar(255) DEFAULT NULL COMMENT '需求部门'",
  224. 'MBZ' => "`MBZ` varchar(255) DEFAULT NULL COMMENT '备注'",
  225. 'createtime' => "`createtime` varchar(255) DEFAULT NULL COMMENT '录入时间'",
  226. 'pdf_url' => "`pdf_url` varchar(255) DEFAULT NULL COMMENT 'pdf路径'",
  227. 'sys_rq' => "`sys_rq` varchar(255) DEFAULT NULL COMMENT '招标截止日期'",
  228. ]) || $changed;
  229. return $changed;
  230. }
  231. protected static function ensurePurchaseOrderDetailTable(): bool
  232. {
  233. $changed = false;
  234. if (!self::tableExists('purchase_order_detail')) {
  235. try {
  236. Db::execute("CREATE TABLE IF NOT EXISTS `purchase_order_detail` (
  237. `id` int NOT NULL AUTO_INCREMENT,
  238. `scydgy_id` int DEFAULT NULL COMMENT '工序行ID',
  239. `CCYDH` varchar(255) DEFAULT NULL COMMENT '订单号',
  240. `CYJMC` varchar(255) DEFAULT NULL COMMENT '印件名称',
  241. `CGYMC` varchar(255) DEFAULT NULL COMMENT '工序名称',
  242. `amount` varchar(255) DEFAULT NULL COMMENT '单价/金额',
  243. `delivery` datetime DEFAULT NULL COMMENT '交货日期',
  244. `delivery_createtime` datetime DEFAULT NULL COMMENT '报价提交时间',
  245. `lead_days` int unsigned DEFAULT NULL COMMENT '预估工期(天)',
  246. `company_name` varchar(255) DEFAULT NULL COMMENT '供应商名称',
  247. `username` varchar(128) DEFAULT NULL COMMENT '供应商账号',
  248. `email` varchar(255) DEFAULT NULL COMMENT '邮箱',
  249. `phone` varchar(255) DEFAULT NULL COMMENT '手机号',
  250. `createtime` datetime DEFAULT NULL COMMENT '下发时间',
  251. `status` varchar(255) DEFAULT NULL COMMENT '数值状态',
  252. `status_name` varchar(255) DEFAULT NULL COMMENT '状态名',
  253. `remark` varchar(500) DEFAULT NULL COMMENT '供应商整单备注',
  254. PRIMARY KEY (`id`),
  255. KEY `idx_scydgy_id` (`scydgy_id`),
  256. KEY `idx_CCYDH` (`CCYDH`(64)),
  257. KEY `idx_company_name` (`company_name`(64)),
  258. KEY `idx_status_name` (`status_name`(32))
  259. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='协助采购报价明细'");
  260. $changed = true;
  261. } catch (\Throwable $e) {
  262. Log::write('ProcuremenSchema create purchase_order_detail: ' . $e->getMessage(), 'error');
  263. }
  264. }
  265. $changed = self::ensureColumns('purchase_order_detail', [
  266. 'amount' => "`amount` varchar(255) DEFAULT NULL COMMENT '单价/金额'",
  267. 'delivery' => "`delivery` datetime DEFAULT NULL COMMENT '交货日期'",
  268. 'delivery_createtime' => "`delivery_createtime` datetime DEFAULT NULL COMMENT '报价提交时间'",
  269. 'lead_days' => "`lead_days` int unsigned DEFAULT NULL COMMENT '预估工期(天)'",
  270. 'company_name' => "`company_name` varchar(255) DEFAULT NULL COMMENT '供应商名称'",
  271. 'username' => "`username` varchar(128) DEFAULT NULL COMMENT '供应商账号'",
  272. 'email' => "`email` varchar(255) DEFAULT NULL COMMENT '邮箱'",
  273. 'phone' => "`phone` varchar(255) DEFAULT NULL COMMENT '手机号'",
  274. 'status' => "`status` varchar(255) DEFAULT NULL COMMENT '数值状态'",
  275. 'status_name' => "`status_name` varchar(255) DEFAULT NULL COMMENT '状态名'",
  276. 'remark' => "`remark` varchar(500) DEFAULT NULL COMMENT '供应商整单备注'",
  277. 'CYJMC' => "`CYJMC` varchar(255) DEFAULT NULL COMMENT '印件名称'",
  278. 'CGYMC' => "`CGYMC` varchar(255) DEFAULT NULL COMMENT '工序名称'",
  279. 'CCYDH' => "`CCYDH` varchar(255) DEFAULT NULL COMMENT '订单号'",
  280. 'scydgy_id' => "`scydgy_id` int DEFAULT NULL COMMENT '工序行ID'",
  281. 'createtime' => "`createtime` datetime DEFAULT NULL COMMENT '下发时间'",
  282. ]) || $changed;
  283. return $changed;
  284. }
  285. protected static function ensurePurchaseOrderOperLogTable(): bool
  286. {
  287. if (self::tableExists('purchase_order_oper_log')) {
  288. return self::ensureColumns('purchase_order_oper_log', [
  289. 'scydgy_id' => "`scydgy_id` int NOT NULL DEFAULT 0 COMMENT '工序行ID'",
  290. 'purchase_order_id' => "`purchase_order_id` int unsigned DEFAULT NULL COMMENT 'purchase_order.id'",
  291. 'admin_id' => "`admin_id` int unsigned DEFAULT NULL COMMENT '操作人'",
  292. 'admin_name' => "`admin_name` varchar(64) NOT NULL DEFAULT '' COMMENT '操作人快照'",
  293. 'action' => "`action` varchar(64) NOT NULL DEFAULT '' COMMENT '动作码'",
  294. 'content' => "`content` varchar(1000) NOT NULL DEFAULT '' COMMENT '描述'",
  295. 'createtime' => "`createtime` int unsigned NOT NULL DEFAULT 0 COMMENT 'Unix时间戳'",
  296. ]);
  297. }
  298. try {
  299. Db::execute("CREATE TABLE IF NOT EXISTS `purchase_order_oper_log` (
  300. `id` int unsigned NOT NULL AUTO_INCREMENT,
  301. `scydgy_id` int NOT NULL DEFAULT 0 COMMENT '工序行 scydgy.ID',
  302. `purchase_order_id` int unsigned DEFAULT NULL COMMENT 'purchase_order.id',
  303. `admin_id` int unsigned DEFAULT NULL COMMENT '操作人 admin.id',
  304. `admin_name` varchar(64) NOT NULL DEFAULT '' COMMENT '操作人昵称/账号快照',
  305. `action` varchar(64) NOT NULL DEFAULT '' COMMENT '动作码',
  306. `content` varchar(1000) NOT NULL DEFAULT '' COMMENT '中文描述',
  307. `createtime` int unsigned NOT NULL DEFAULT 0 COMMENT 'Unix 时间戳',
  308. PRIMARY KEY (`id`),
  309. KEY `idx_scydgy_ct` (`scydgy_id`,`createtime`)
  310. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='协助采购操作日志'");
  311. return true;
  312. } catch (\Throwable $e) {
  313. Log::write('ProcuremenSchema create purchase_order_oper_log: ' . $e->getMessage(), 'error');
  314. return false;
  315. }
  316. }
  317. protected static function ensurePurchaseOrderBidOpenTable(): bool
  318. {
  319. if (self::tableExists('purchase_order_bid_open')) {
  320. return false;
  321. }
  322. try {
  323. Db::execute("CREATE TABLE IF NOT EXISTS `purchase_order_bid_open` (
  324. `id` int unsigned NOT NULL AUTO_INCREMENT,
  325. `ccydh` varchar(64) NOT NULL DEFAULT '' COMMENT '订单号',
  326. `verifier1_id` int unsigned NOT NULL DEFAULT 0,
  327. `verifier1_name` varchar(64) NOT NULL DEFAULT '',
  328. `verifier2_id` int unsigned NOT NULL DEFAULT 0,
  329. `verifier2_name` varchar(64) NOT NULL DEFAULT '',
  330. `operator_id` int unsigned NOT NULL DEFAULT 0,
  331. `operator_name` varchar(64) NOT NULL DEFAULT '',
  332. `createtime` datetime DEFAULT NULL,
  333. PRIMARY KEY (`id`),
  334. UNIQUE KEY `uk_ccydh` (`ccydh`)
  335. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='开标双重验证'");
  336. return true;
  337. } catch (\Throwable $e) {
  338. Log::write('ProcuremenSchema create purchase_order_bid_open: ' . $e->getMessage(), 'error');
  339. return false;
  340. }
  341. }
  342. protected static function ensurePurchaseOrderInboundScoreTable(): bool
  343. {
  344. $changed = false;
  345. if (!self::tableExists('purchase_order_inbound_score')) {
  346. try {
  347. Db::execute("CREATE TABLE IF NOT EXISTS `purchase_order_inbound_score` (
  348. `id` int unsigned NOT NULL AUTO_INCREMENT,
  349. `scydgy_id` int NOT NULL DEFAULT 0 COMMENT '工序ID',
  350. `purchase_order_id` int unsigned NOT NULL DEFAULT 0 COMMENT 'purchase_order.id',
  351. `ccydh` varchar(64) NOT NULL DEFAULT '' COMMENT '订单号',
  352. `cyjmc` varchar(255) NOT NULL DEFAULT '' COMMENT '印件名称',
  353. `cgymc` varchar(255) NOT NULL DEFAULT '' COMMENT '工序名称',
  354. `company_name` varchar(255) NOT NULL DEFAULT '' COMMENT '中标供应商',
  355. `result` varchar(16) NOT NULL DEFAULT '' COMMENT '合格/不合格',
  356. `delivery_status` varchar(16) NOT NULL DEFAULT '' COMMENT '交货情况 准时/滞后',
  357. `delivery_date` date DEFAULT NULL COMMENT '交货日期',
  358. `storage_location` varchar(64) NOT NULL DEFAULT '' COMMENT '库位',
  359. `customer_complaint` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '客户是否投诉 1=是',
  360. `complaint_remark` varchar(500) NOT NULL DEFAULT '' COMMENT '投诉说明',
  361. `order_interrupt` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '订单是否中断 1=是',
  362. `admin_id` int unsigned NOT NULL DEFAULT 0,
  363. `admin_name` varchar(64) NOT NULL DEFAULT '',
  364. `remark` varchar(500) NOT NULL DEFAULT '',
  365. `createtime` datetime DEFAULT NULL,
  366. `updatetime` datetime DEFAULT NULL,
  367. PRIMARY KEY (`id`),
  368. UNIQUE KEY `uk_scydgy_id` (`scydgy_id`),
  369. KEY `idx_ccydh` (`ccydh`),
  370. KEY `idx_result` (`result`),
  371. KEY `idx_updatetime` (`updatetime`)
  372. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='质量/交货评分'");
  373. $changed = true;
  374. } catch (\Throwable $e) {
  375. Log::write('ProcuremenSchema create inbound_score: ' . $e->getMessage(), 'error');
  376. }
  377. }
  378. return self::ensureColumns('purchase_order_inbound_score', [
  379. 'delivery_status' => "`delivery_status` varchar(16) NOT NULL DEFAULT '' COMMENT '交货情况 准时/滞后'",
  380. 'delivery_date' => "`delivery_date` date DEFAULT NULL COMMENT '交货日期'",
  381. 'storage_location' => "`storage_location` varchar(64) NOT NULL DEFAULT '' COMMENT '库位'",
  382. 'customer_complaint' => "`customer_complaint` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '客户是否投诉 1=是'",
  383. 'complaint_remark' => "`complaint_remark` varchar(500) NOT NULL DEFAULT '' COMMENT '投诉说明'",
  384. 'order_interrupt' => "`order_interrupt` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '订单是否中断 1=是'",
  385. 'result' => "`result` varchar(16) NOT NULL DEFAULT '' COMMENT '合格/不合格'",
  386. 'company_name' => "`company_name` varchar(255) NOT NULL DEFAULT '' COMMENT '中标供应商'",
  387. 'ccydh' => "`ccydh` varchar(64) NOT NULL DEFAULT '' COMMENT '订单号'",
  388. 'scydgy_id' => "`scydgy_id` int NOT NULL DEFAULT 0 COMMENT '工序ID'",
  389. ]) || $changed;
  390. }
  391. protected static function ensurePurchaseEmailSendLogTable(): bool
  392. {
  393. $changed = false;
  394. if (!self::tableExists('purchase_email_send_log')) {
  395. try {
  396. Db::execute("CREATE TABLE IF NOT EXISTS `purchase_email_send_log` (
  397. `id` int unsigned NOT NULL AUTO_INCREMENT,
  398. `from_email` varchar(128) NOT NULL DEFAULT '' COMMENT '发件邮箱',
  399. `to_email` varchar(128) NOT NULL DEFAULT '' COMMENT '收件邮箱',
  400. `send_time` datetime DEFAULT NULL COMMENT '发送时间',
  401. `scene` varchar(32) NOT NULL DEFAULT '' COMMENT '场景',
  402. `order_batch_no` varchar(32) NOT NULL DEFAULT '' COMMENT '下发批次号',
  403. `ccydh` varchar(500) NOT NULL DEFAULT '' COMMENT '订单号(多单逗号分隔)',
  404. `company_name` varchar(128) NOT NULL DEFAULT '' COMMENT '供应商名称',
  405. `mail_subject` varchar(255) NOT NULL DEFAULT '' COMMENT '邮件主题',
  406. `status` tinyint(1) NOT NULL DEFAULT 1 COMMENT '1成功 0失败',
  407. `fail_reason` varchar(500) NOT NULL DEFAULT '' COMMENT '失败原因',
  408. `admin_id` int unsigned NOT NULL DEFAULT 0 COMMENT '操作人ID',
  409. `admin_name` varchar(64) NOT NULL DEFAULT '' COMMENT '操作人',
  410. `scydgy_id` int NOT NULL DEFAULT 0 COMMENT '工序行ID',
  411. `purchase_order_id` int unsigned NOT NULL DEFAULT 0 COMMENT 'purchase_order.id',
  412. `createtime` datetime DEFAULT NULL COMMENT '记录创建时间',
  413. PRIMARY KEY (`id`),
  414. KEY `idx_send_time` (`send_time`),
  415. KEY `idx_to_email` (`to_email`),
  416. KEY `idx_ccydh` (`ccydh`(191)),
  417. KEY `idx_order_batch_no` (`order_batch_no`),
  418. KEY `idx_scene` (`scene`)
  419. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='协助采购邮件发送日志'");
  420. $changed = true;
  421. } catch (\Throwable $e) {
  422. Log::write('ProcuremenSchema create email_send_log: ' . $e->getMessage(), 'error');
  423. return false;
  424. }
  425. }
  426. $changed = self::ensureColumns('purchase_email_send_log', [
  427. 'order_batch_no' => "`order_batch_no` varchar(32) NOT NULL DEFAULT '' COMMENT '下发批次号' AFTER `scene`",
  428. ]) || $changed;
  429. try {
  430. $cols = Db::query("SHOW COLUMNS FROM `purchase_email_send_log` LIKE 'ccydh'");
  431. if (is_array($cols) && $cols !== []) {
  432. $type = strtolower((string)($cols[0]['Type'] ?? $cols[0]['type'] ?? ''));
  433. if (preg_match('/varchar\((\d+)\)/', $type, $m) && (int)$m[1] < 500) {
  434. Db::execute(
  435. "ALTER TABLE `purchase_email_send_log` MODIFY COLUMN `ccydh` varchar(500) NOT NULL DEFAULT '' COMMENT '订单号(多单逗号分隔)'"
  436. );
  437. $changed = true;
  438. }
  439. }
  440. } catch (\Throwable $e) {
  441. Log::write('ProcuremenSchema email_send_log.ccydh width: ' . $e->getMessage(), 'error');
  442. }
  443. try {
  444. $idx = Db::query("SHOW INDEX FROM `purchase_email_send_log` WHERE Key_name = 'idx_order_batch_no'");
  445. if (!is_array($idx) || $idx === []) {
  446. Db::execute('ALTER TABLE `purchase_email_send_log` ADD INDEX `idx_order_batch_no` (`order_batch_no`)');
  447. $changed = true;
  448. }
  449. } catch (\Throwable $e) {
  450. Log::write('ProcuremenSchema email_send_log idx_order_batch_no: ' . $e->getMessage(), 'error');
  451. }
  452. return $changed;
  453. }
  454. protected static function ensurePurchaseSmsTemplateTable(): bool
  455. {
  456. if (self::tableExists('purchase_sms_template')) {
  457. return self::ensureColumns('purchase_sms_template', [
  458. 'scene' => "`scene` varchar(32) DEFAULT NULL COMMENT '场景码'",
  459. 'title' => "`title` varchar(100) DEFAULT '' COMMENT '模版名称'",
  460. 'content' => "`content` text COMMENT '正文'",
  461. 'remark' => "`remark` varchar(255) DEFAULT '' COMMENT '备注'",
  462. 'status' => "`status` varchar(30) DEFAULT 'normal'",
  463. ]);
  464. }
  465. try {
  466. Db::execute("CREATE TABLE IF NOT EXISTS `purchase_sms_template` (
  467. `id` int unsigned NOT NULL AUTO_INCREMENT,
  468. `scene` varchar(32) DEFAULT NULL COMMENT '场景码',
  469. `title` varchar(100) DEFAULT '' COMMENT '模版名称',
  470. `content` text COMMENT '正文',
  471. `remark` varchar(255) DEFAULT '' COMMENT '备注',
  472. `status` varchar(30) DEFAULT 'normal',
  473. `createtime` datetime DEFAULT NULL,
  474. `updatetime` datetime DEFAULT NULL,
  475. PRIMARY KEY (`id`),
  476. UNIQUE KEY `uk_scene` (`scene`)
  477. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='协助采购短信/邮件模版'");
  478. return true;
  479. } catch (\Throwable $e) {
  480. Log::write('ProcuremenSchema create sms_template: ' . $e->getMessage(), 'error');
  481. return false;
  482. }
  483. }
  484. }