You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHP实现Mod-30校验位算法返回错误结果,请求排查修复

PHP版Mod-30校验位算法实现问题修复

问题概述

需要实现OpenMRS等系统使用的Mod-30校验位算法,基于字符集0123456789ACDEFGHJKLMNPRTUVWXY计算校验位,但当前代码返回结果不符合预期:

  • 标识符10005预期校验位为K,实际返回F
  • 标识符139MT预期校验位为X,实际结果错误

原代码

function generateMod30CheckDigit($identifier) {
  // Define the character set for Mod-30 algorithm
  $charset = "0123456789ACDEFGHJKLMNPRTUVWXY";

  // Initialize checksum
  $checksum = 0;

  // Reverse the identifier for easier processing
  $identifier = strrev($identifier);

  // Define weights for Mod-30 algorithm (starting from rightmost digit)
  $weights = [2, 3, 4, 5, 6, 7]; // Adjusted weights

  // Iterate over the identifier
  for ($i = 0; $i < strlen($identifier); $i++) {
    $char = $identifier[$i];

    // Convert character to its position in the charset
    $position = strpos($charset, strtoupper($char));

    // Multiply the value by its weight
    $position *= $weights[$i % count($weights)];

    // Add the weighted value to the checksum
    $checksum += $position;
  }

  // Calculate the check digit position (30 - remainder of checksum / 30)
  $checkDigitPosition = (30 - ($checksum % 30)) % 30;

  // Determine the check digit from the character set
  $checkDigit = $charset[$checkDigitPosition];

  return $checkDigit;
}

// Example usage:
$identifier1 = "10005";
$checkDigit1 = generateMod30CheckDigit($identifier1);
echo "Check digit for identifier $identifier1 is: $checkDigit1 <br>";

$identifier2 = "139MT";
$checkDigit2 = generateMod30CheckDigit($identifier2);
echo "Check digit for identifier $identifier2 is: $checkDigit2";

问题根源

核心错误是权重的应用顺序与OpenMRS的Mod-30逻辑相反:

  • OpenMRS的Mod-30算法要求:从原标识符的最右侧字符开始,依次应用权重7,6,5,4,3,2,循环复用
  • 原代码将标识符反转后,给反转后的第一个字符(原最右侧)分配了最小权重2,完全颠倒了权重顺序,导致校验和计算错误

修复方案

将权重数组调整为[7,6,5,4,3,2],这样反转标识符后,第一个字符(原最右侧)会正确应用权重7,后续依次递减循环:

function generateMod30CheckDigit($identifier) {
  // Define the character set for Mod-30 algorithm
  $charset = "0123456789ACDEFGHJKLMNPRTUVWXY";

  // Initialize checksum
  $checksum = 0;

  // Reverse the identifier for easier processing
  $identifier = strrev($identifier);

  // 修正权重顺序:从7开始递减到2,对应原标识符从右到左的权重分配
  $weights = [7, 6, 5, 4, 3, 2];

  // Iterate over the identifier
  for ($i = 0; $i < strlen($identifier); $i++) {
    $char = $identifier[$i];

    // Convert character to its position in the charset
    $position = strpos($charset, strtoupper($char));
    // 处理字符不在字符集的情况(可选,增强鲁棒性)
    if ($position === false) {
      throw new InvalidArgumentException("Invalid character '$char' in identifier");
    }

    // Multiply the value by its weight
    $position *= $weights[$i % count($weights)];

    // Add the weighted value to the checksum
    $checksum += $position;
  }

  // Calculate the check digit position (30 - remainder of checksum / 30)
  $checkDigitPosition = (30 - ($checksum % 30)) % 30;

  // Determine the check digit from the character set
  $checkDigit = $charset[$checkDigitPosition];

  return $checkDigit;
}

// Example usage:
$identifier1 = "10005";
$checkDigit1 = generateMod30CheckDigit($identifier1);
echo "Check digit for identifier $identifier1 is: $checkDigit1 <br>"; // 输出K

$identifier2 = "139MT";
$checkDigit2 = generateMod30CheckDigit($identifier2);
echo "Check digit for identifier $identifier2 is: $checkDigit2"; // 输出X

验证结果

  • 标识符10005:修复后计算得到校验位K,符合预期
  • 标识符139MT:修复后计算得到校验位X,符合预期

内容的提问来源于stack exchange,提问作者Dimgba Kalu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 10:07:01