feat: 集成隐私小号功能保护用户手机号

新增隐私小号服务类,在订单创建时自动绑定隐私手机号,收货时自动解绑
在快递发货和收货流程中使用隐私小号替代真实手机号,保护用户隐私
添加数据库字段存储绑定关系,提供系统配置开关和测试脚本
This commit is contained in:
2026-04-01 00:43:51 +08:00
parent 03a6ba7961
commit 42d98407de
14 changed files with 1722 additions and 6 deletions

View File

@@ -0,0 +1,14 @@
<?php
date_default_timezone_set('Asia/Shanghai');
define('ROOT_PATH', dirname(__FILE__) . DIRECTORY_SEPARATOR);
require './lib/PrivateNumber.php';
$pn = new PrivateNumber();
// 这里设置appkey
$pn->setAppkey('');
// 这里设置秘钥
$pn->setSecret('');
$result = $pn->countNumber();
print_r($result);

View File

@@ -0,0 +1,457 @@
<?php
// 号码隐私保护类
class PrivateNumber
{
// 网站的appkey
private $appkey = "";
// appkey 对应的秘钥
private $secret = "";
public $apiurl = "https://api.mixcom.cn";
/**
* 设置appkey
* @param appkey
*/
public function setAppkey($appkey)
{
$this->appkey = $appkey;
}
/**
* 设置秘钥
* @param secret
*/
public function setSecret($secret)
{
$this->secret = $secret;
}
/**
* 获取服务器的时间
* @return
*/
public function getServerTime()
{
$url = $this->apiurl . "/virtual/getServerTime";
$json = $this->get($url);
return $json;
}
/**
* 获取可用的axb号码
* region_code 区号,如深圳区号为 0755, 通过这个参数查询指定地区的axb号码, 可选
* 获取号码的优先级: 绑定数量少优先 > 绑定总数少优先
* @return
*/
public function getAxbNumber($arr = array())
{
$url = $this->apiurl . "/virtual/getaxbnumber";
$params = array(
'region_code' => '',
);
$params = array_merge($params, $arr);
$json = $this->post($url, $params);
return $json;
}
/**
* axb号码绑定
* number 虚拟小号,必选
* aparty 呼叫方的号码,必选
* bparty 被叫方的号码,必选
* recording 是否开启录音,1开启, 0 不开启。使用前请先确定使用了通话录音功能
* recordhinttone 录音的提示音,可选
* maxduration 最大通话时长,单位为分钟,到了时间会自动挂断,可选
* lastminvoice 通话最后一分钟的提示音, 当最大通话时长不为0时有效,可选
* callerhinttone A拨打X通话前等待音,可选
* calleeHintTone B拨打X通话前等待音,可选
* direction 呼叫方向, 可选值0, 1, 2. 0代表可双向呼叫,可选
* enddate 解绑时间,时间戳格式,可选,0代表不设置解绑时间
* @return
*/
public function axbBind($arr = array())
{
$url = $this->apiurl . "/virtual/axbBind";
$params = array(
'number' => '',
'aparty' => '',
'bparty' => '',
'recording' => 0,
'recordhinttone' => '',
'maxduration' => 0,
'lastminvoice' => '',
'callerhinttone' => '',
'calleeHintTone' => '',
'direction' => 0,
'enddate' => 0,
);
$params = array_merge($params, $arr);
$json = $this->post($url, $params);
return $json;
}
/**
* axb号码解绑
* number 虚拟小号,必选
* aparty 呼叫方的号码,必选
* bparty 被叫方的号码,必选
* @return
*/
public function unAxbBind($arr = array())
{
$url = $this->apiurl . "/virtual/unAxbBind";
$params = array(
'number' => '',
'aparty' => '',
'bparty' => '',
);
$params = array_merge($params, $arr);
$json = $this->post($url, $params);
return $json;
}
/**
* axb号码自动绑定
* aparty 呼叫方的号码,必选
* bparty 被叫方的号码,必选
* recording 是否开启录音,1开启, 0 不开启。使用前请先确定使用了通话录音功能
* recordhinttone 录音的提示音,可选
* maxduration 最大通话时长,单位为分钟,到了时间会自动挂断,可选
* lastminvoice 通话最后一分钟的提示音, 当最大通话时长不为0时有效,可选
* callerhinttone A拨打X通话前等待音,可选
* calleeHintTone B拨打X通话前等待音,可选
* direction 呼叫方向, 可选值0, 1, 2. 0代表可双向呼叫,可选
* enddate 解绑时间,时间戳格式,可选,0代表不设置解绑时间
* @return
*/
public function axbAutoBind($arr = array())
{
$url = $this->apiurl . "/virtual/axbAutoBind";
$params = array(
'aparty' => '',
'bparty' => '',
'region_code' => '',
'defregioncode' => '',
'recording' => 0,
'recordhinttone' => '',
'maxduration' => 0,
'lastminvoice' => '',
'callerhinttone' => '',
'calleeHintTone' => '',
'direction' => 0,
'enddate' => 0,
);
$params = array_merge($params, $arr);
$json = $this->post($url, $params);
return $json;
}
/**
* 获取可用的ax号码
* region_code 区号,如深圳区号为 0755, 通过这个参数查询指定地区的ax号码, 可选
* @return
*/
public function getAxNumber($arr = array())
{
$url = $this->apiurl . "/virtual/getAxNumber";
$params = array(
'region_code' => '',
);
$params = array_merge($params, $arr);
$json = $this->post($url, $params);
return $json;
}
/**
* ax号码绑定
* number 虚拟小号,必选
* aparty 呼叫方的号码,必选
* recording 是否开启录音,1开启, 0 不开启。使用前请先确定使用了通话录音功能,可选
* recordhinttone 录音前播放的提示音,可选
* display_call 是否开启号码透传。默认为0,不开启。1为开启,开启后可能会导致部分通话无法接通,可选
* callerhinttone A拨打X通话前等待音,可选
* calleeHintTone B拨打X通话前等待音,可选
* enddate 解绑时间,时间戳格式,可选,0代表不设置解绑时间
* @return
*/
public function axBind($arr = array())
{
$url = $this->apiurl . "/virtual/axBind";
$params = array(
'number' => '',
'aparty' => '',
'recording' => 0,
'recordhinttone' => '',
'display_call' => 0,
'callerhinttone' => '',
'calleehinttone' => '',
'enddate' => 0,
);
$params = array_merge($params, $arr);
$json = $this->post($url, $params);
return $json;
}
/**
* ax号码解绑
* number 虚拟小号,必选
* aparty 呼叫方的号码,必选
* @return
*/
public function unAxBind($arr = array())
{
$url = $this->apiurl . "/virtual/unAxBind";
$params = array(
'number' => '',
'aparty' => '',
);
$params = array_merge($params, $arr);
$json = $this->post($url, $params);
return $json;
}
/**
* AX绑定呼出
* number 虚拟小号,必选
* aparty 呼叫方的号码,必选
* bparty 被叫方的号码,必选
* @return
*/
public function axDialNumber($arr = array())
{
$url = $this->apiurl . "/virtual/axdialnumber";
$params = array(
'number' => '',
'aparty' => '',
'bparty' => '',
);
$params = array_merge($params, $arr);
$json = $this->post($url, $params);
return $json;
}
/**
* ax号码自动绑定
* aparty 呼叫方的号码,必选
* region_code 需要的虚拟小号的区号, 可选
* display_call 是否开启号码透传。默认为0,不开启。1为开启,开启后可能会导致部分通话无法接通,可选
* recording 是否开启录音,1开启, 0 不开启。使用前请先确定使用了通话录音功能,可选
* recordhinttone 录音前播放的提示音,可选
* callerhinttone A拨打X通话前等待音,可选
* calleeHintTone B拨打X通话前等待音,可选
* enddate 解绑时间,时间戳格式,可选,0代表不设置解绑时间
* @return
*/
public function axAutoBind($arr = array())
{
$url = $this->apiurl . "/virtual/axAutoBind";
$params = array(
'aparty' => '',
'region_code' => '',
'defregioncode' => '',
'display_call' => 0,
'recording' => 0,
'recordhinttone' => '',
'callerhinttone' => '',
'calleehinttone' => '',
'enddate' => 0,
);
$params = array_merge($params, $arr);
$json = $this->post($url, $params);
return $json;
}
/**
* 挂断指定通话
* subscriptionid 该通话的绑定关系id
* @return
*/
public function callStop($arr)
{
$url = $this->apiurl . "/virtual/callStop";
$params = [
'subscriptionid' => '',
];
$params = array_merge($params, $arr);
$json = $this->post($url, $params);
return $json;
}
/**
* 获取小号的个数
* @return
*/
public function countNumber()
{
$url = $this->apiurl . "/virtual/countNumber";
$params = array();
$json = $this->post($url, $params);
return $json;
}
/**
* 查询所有的号码
* page 需要查询的页数,每页100条数据
* @return
*/
public function queryAllNumber($arr = array())
{
$url = $this->apiurl . "/virtual/queryAllNumber";
$params = array(
'page' => 1,
);
$params = array_merge($params, $arr);
$json = $this->post($url, $params);
return $json;
}
/**
* 查询号码的绑定关系
* number 虚拟小号,可选
* aparty 呼叫方的号码,可选
* bparty 被叫方的号码,可选
* @return
*/
public function queryBindRelation($arr = array())
{
$url = $this->apiurl . "/virtual/querybindrelation";
$params = array(
'number' => '',
'aparty' => '',
'bparty' => '',
);
$params = array_merge($params, $arr);
$json = $this->post($url, $params);
return $json;
}
/**
* 查询号码的绑定关系日志
* number 虚拟小号,可选
* aparty 呼叫方的号码,可选
* bparty 被叫方的号码,可选
* timestart 开始时间,可选
* timeend 结束时间,可选
* page 页数,可选
* @return
*/
public function queryRelationLog($arr = array())
{
$url = $this->apiurl . "/virtual/queryrelationlog";
$params = array(
'number' => '',
'aparty' => '',
'bparty' => '',
'timestart' => 0,
'timeend' => 0,
'page' => 1,
);
$params = array_merge($params, $arr);
$json = $this->post($url, $params);
return $json;
}
/**
* 查询通话记录
* number 虚拟小号,可选
* aparty 呼叫方的号码,可选
* bparty 被叫方的号码,可选
* timestart 开始时间,可选
* timeend 结束时间,可选
* page 页数,可选
* @return
*/
public function queryCallRecord($arr = array())
{
$url = $this->apiurl . "/virtual/querycallrecord";
$params = array(
'number' => '',
'aparty' => '',
'bparty' => '',
'timestart' => 0,
'timeend' => 0,
'page' => 1,
);
$params = array_merge($params, $arr);
$json = $this->post($url, $params);
return $json;
}
/**
* 返回录音的下载链接(在录音未删除之前有效)
* number 虚拟小号,必选
* callIdentifier 通话录音id,必选
*/
public function getRecordUrl($arr = array())
{
$url = $this->apiurl . "/virtual/getrecordurl";
$params = array(
'number' => '',
'callIdentifier' => '',
);
$params = array_merge($params, $arr);
$json = $this->post($url, $params);
return $json;
}
private function get($url)
{
$ch = curl_init();
//设置选项,包括URL
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
//执行并获取HTML文档内容
$data = curl_exec($ch);
//释放curl句柄
curl_close($ch);
return $data;
}
private function post($url, $params = array())
{
$params['time'] = time();
$params['appkey'] = $this->appkey;
$params['sign'] = $this->getSign($params);
$ch = curl_init(); //初始化curl
curl_setopt($ch, CURLOPT_URL, $url); //抓取指定网页
curl_setopt($ch, CURLOPT_HEADER, 0); //设置header
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //要求结果为字符串且输出到屏幕上
curl_setopt($ch, CURLOPT_POST, 1); //post提交方式
curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
$data = curl_exec($ch); //运行curl
curl_close($ch);
return $data;
}
private function getSign($params)
{
// key转换为小写
$params = array_change_key_case($params, CASE_LOWER);
// 排除不需要加密的参数
foreach ($params as $key => $val) {
if ($key == 'c' || $key == 'a' || $key == 'sign') {
// 从数组中移除
unset($params[$key]);
}
if ($val === '') {
// 空值从数组中移除
unset($params[$key]);
}
}
// 对key进行排序, 按名称升序
ksort($params);
// 将数据连接,进行加密
$data = '';
foreach ($params as $key => $val) {
$data .= $val;
}
$data .= $this->secret;
$sign = strtolower(sha1($data . sha1($this->secret)));
return $sign;
}
}

View File

@@ -0,0 +1 @@
隐私保护小号php sdk,不使用composer自动加载的方式执行

View File

@@ -0,0 +1,20 @@
-- 隐私手机号功能 - 数据库改动 (AX模式)
-- 执行时间: 2026-04-01
-- 说明: AX模式下 user_phone 直接存储隐私手机号
-- private_bind_id 存储真实手机号(用于解绑)
-- private_phone_status 存储绑定状态
-- 1. 订单表添加字段
ALTER TABLE `eb_store_order`
ADD COLUMN `private_bind_id` VARCHAR(18) NOT NULL DEFAULT '' COMMENT '隐私绑定真实手机号(用于解绑)' AFTER `user_address`,
ADD COLUMN `private_phone_status` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '隐私手机绑定状态:0未绑定,1已绑定' AFTER `private_bind_id`;
-- 2. 系统配置表添加隐私手机号配置
INSERT INTO `eb_system_config` (`id`, `is_store`, `menu_name`, `type`, `input_type`, `config_tab_id`, `parameter`, `upload_type`, `required`, `width`, `high`, `value`, `info`, `desc`, `sort`, `status`) VALUES
(NULL, 0, 'privacy_phone_status', 'radio', 'input', 1, '0=>关闭\n1=>开启', 0, '', 0, 0, '0', '隐私手机号功能', '开启后订单发货使用隐私手机号', 0, 1),
(NULL, 0, 'privacy_phone_appkey', 'text', 'input', 1, '', 0, 'required:true', 100, 0, '', '米糠云AppKey', '米糠云小号平台AppKey', 0, 1),
(NULL, 0, 'privacy_phone_appsecret', 'text', 'input', 1, '', 0, 'required:true', 100, 0, '', '米糠云AppSecret', '米糠云小号平台AppSecret', 0, 1);
-- 查询验证
-- SELECT id, order_id, user_phone, private_bind_id, private_phone_status FROM eb_store_order LIMIT 10;
-- SELECT * FROM eb_system_config WHERE menu_name LIKE 'privacy_phone%';

Binary file not shown.