where(['id = :id'], [':id' => 1])->fetch(); } // 更新配置 public function updateConfig($data) { return $this->where(['id = :id'], [':id' => 1])->update($data); } /** * 获取代码前缀规则 * 格式: ['成品' => 'CP', '电芯' => 'DX', 'PCBA' => 'XLB'] */ public function getCodePrefixRules() { $config = $this->getConfig(); $raw = $config['code_prefix_rules'] ?? ''; if (empty($raw)) { // 返回默认规则 $rules = $this->getDefaultCodePrefixRules(); } else { $rules = json_decode($raw, true); if (!is_array($rules)) { $rules = $this->getDefaultCodePrefixRules(); } } // 产品类型别名对齐:确保同义类型能匹配到同一前缀规则 // 例如配置表中使用「电池」,而前缀规则可能使用旧名「电芯」 $aliases = [ '电池' => '电芯', '电芯' => '电池', '底壳' => '外壳', '外壳' => '底壳', '半成品' => '半成', '半成' => '半成品', ]; foreach ($aliases as $from => $to) { if (!isset($rules[$from]) && isset($rules[$to])) { $rules[$from] = $rules[$to]; } } return $rules; } /** * 获取默认代码前缀规则 */ public function getDefaultCodePrefixRules() { return [ '成品' => 'CP', '电池' => 'dc', 'PCBA' => 'xlb', '半成品' => '', '外壳' => '', '包装' => '', '配件' => '', ]; } /** * 根据产品类型获取代码前缀 * @param string $productType 产品类型(如:成品、电芯、PCBA) * @return string 前缀,无规则返回空字符串 */ public function getPrefixByType($productType) { $rules = $this->getCodePrefixRules(); $prefix = $rules[$productType] ?? ''; // 原则:所有工位序列号都受前缀管控。配置了该类型但前缀为空时, // 自动回退为「产品类型名拼音首字母」,避免无前缀类型被放行或卡死。 if ($prefix === '' && isset($rules[$productType])) { $prefix = $this->pinyinInitials($productType); } return $prefix; } /** * 取得字符串的拼音首字母(中文取首字拼音首字母,英文/数字原样保留)。 * 兼容「外壳」「包装」「半成品」「配件」等常见类型,英文部分(如 PCBA)原样输出。 * @param string $str * @return string */ public function pinyinInitials($str) { $str = trim($str); if ($str === '') return ''; // 常见产品类型精确映射(避免多音字/误判) $map = [ '外壳' => 'WK', '底壳' => 'WK', '包装' => 'BZ', '半成品' => 'BCP', '半成' => 'BCP', '配件' => 'PJ', '成品' => 'CP', '电池' => 'DC', '电芯' => 'DC', 'PCBA' => 'XLB', ]; if (isset($map[$str])) return $map[$str]; $first = mb_substr($str, 0, 1, 'UTF-8'); // 英文字母/数字:直接原样(大写) if (preg_match('/^[A-Za-z0-9]$/', $first)) { return strtoupper($first) . (mb_strlen($str, 'UTF-8') > 1 ? strtoupper(mb_substr($str, 1, 1, 'UTF-8')) : ''); } // 中文:取首字拼音首字母 return $this->chineseInitial($first); } /** * 单汉字 → 拼音首字母(GB2312 区位区间法,覆盖常用汉字) */ private function chineseInitial($char) { $py = 'A'; try { $gbk = iconv('UTF-8', 'GB2312//IGNORE', $char); } catch (\Throwable $e) { $gbk = ''; } if ($gbk === '' || strlen($gbk) < 2) { return $py; // 生僻字/无法转换时回退 } $hi = ord($gbk[0]); $lo = ord($gbk[1]); $code = $hi * 256 + $lo; // GB2312 拼音首字母分区(常用字) $ranges = [ [0xB0A1, 0xB0C4, 'A'], [0xB0C5, 0xB2C0, 'B'], [0xB2C1, 0xB4ED, 'C'], [0xB4EE, 0xB6E9, 'D'], [0xB6EA, 0xB7A1, 'E'], [0xB7A2, 0xB8C0, 'F'], [0xB8C1, 0xB9FD, 'G'], [0xB9FE, 0xBBF6, 'H'], [0xBBF7, 0xBFA5, 'J'], [0xBFA6, 0xC0AB, 'K'], [0xC0AC, 0xC2E7, 'L'], [0xC2E8, 0xC4C2, 'M'], [0xC4C3, 0xC5B5, 'N'], [0xC5B6, 0xC5BD, 'O'], [0xC5BE, 0xC6DA, 'P'], [0xC6DB, 0xC8BA, 'Q'], [0xC8BB, 0xC8F5, 'R'], [0xC8F6, 0xCBF0, 'S'], [0xCBF1, 0xCDDA, 'T'], [0xCDDB, 0xCEF3, 'W'], [0xCEF4, 0xD1B8, 'X'], [0xD1B9, 0xD4D0, 'Y'], [0xD4D1, 0xD7F9, 'Z'], ]; foreach ($ranges as $r) { if ($code >= $r[0] && $code <= $r[1]) { $py = $r[2]; break; } } return $py; } /** * 获取所有已配置的非空前缀规则(用于验证) * @return array [前缀 => 产品类型名] */ public function getActivePrefixMap() { $rules = $this->getCodePrefixRules(); $active = []; foreach ($rules as $type => $prefix) { if (!empty($prefix)) { $active[$prefix] = $type; } } return $active; } /** * 更新代码前缀规则(JSON 存储) * @param array $rules ['成品' => 'CP', '电池' => 'dc', ...] */ public function updateCodePrefixRules($rules) { $json = json_encode($rules, JSON_UNESCAPED_UNICODE); // 使用原生 PDO 直写,避免 Model::update 链式调用在二次写入时的潜在问题 $pdo = \core\db\Db::pdo(); $table = $this->getTable(); $stmt = $pdo->prepare("UPDATE {$table} SET code_prefix_rules = :v WHERE id = 1"); $stmt->execute([':v' => $json]); return true; } }