后端
libphonenumber
这是谷歌提供的一款用于解析、格式化和校验国际手机号码的软件库。它提供了三个包,分别对应不同的功能。
-
libphonenumber:用于校验手机号的正确性,提供了:getNumberType,isNumberMatch ,getExampleNumber 等方法。
-
carrier:用于获取手机号的供应商。通过初始化PhoneNumberToCarrierMapper ,调用getNameForNumber可获取运营商信息。
-
geocoder:用于获取手机号的归属地。通过初始化PhoneNumberOfflineGeocoder ,调用getDescriptionForNumber方法可获取手机归属地。
使用
导入maven坐标
<dependency>
<groupId>com.googlecode.libphonenumber</groupId>
<artifactId>libphonenumber</artifactId>
<version>8.13.26</version>
</dependency>
<dependency>
<groupId>com.googlecode.libphonenumber</groupId>
<artifactId>carrier</artifactId>
<version>1.210</version>
</dependency>
<dependency>
<groupId>com.googlecode.libphonenumber</groupId>
<artifactId>geocoder</artifactId>
<version>2.220</version>
</dependency>
编写工具类
public class PhoneToRegionUtil {
/**
* 手机号基本工具类
*/
private final static PhoneNumberUtil PHONE_NUMBER_UTIL = PhoneNumberUtil.getInstance();
/**
* 运营商
*/
private final static PhoneNumberToCarrierMapper CARRIER_MAPPER = PhoneNumberToCarrierMapper.getInstance();
/**
*
*/
private final static PhoneNumberOfflineGeocoder GEO_CODER = PhoneNumberOfflineGeocoder.getInstance();
/**
* 验证当前手机号是否有效
* @param phone 手机号
* @return 校验结果
*/
public static boolean isValidNumber(String phone){
return PHONE_NUMBER_UTIL.isValidNumber(getPhoneNumber(phone));
}
/**
* 获取手机号运营商
* @param phone 手机号
* @return 运营商
*/
public static String getPhoneCarrier(String phone){
return isValidNumber(phone) ? CARRIER_MAPPER.getNameForNumber(getPhoneNumber(phone), Locale.CHINA) : "";
}
/**
* 获取手机号归属地
* @param phone 手机号
* @return 归属地
*/
public static String getRegionInfoByPhone(String phone){
return isValidNumber(phone) ? GEO_CODER.getDescriptionForNumber(getPhoneNumber(phone),Locale.CHINESE) : "";
}
/**
* 生成PhoneNumber
* @param phone 手机号
* @return PhoneNumber
*/
private static Phonenumber.PhoneNumber getPhoneNumber(String phone){
Phonenumber.PhoneNumber phoneNumber = new Phonenumber.PhoneNumber();
phoneNumber.setCountryCode(86);
phoneNumber.setNationalNumber(Long.parseLong(phone));
return phoneNumber;
}
/**
* 获取手机号的归属信息:运营商,归属地
* @param phone 手机号
* @return 归属信息
*/
public static Map<String, Object> getPhoneAffiliationInfo(String phone) {
String fixNumber = phone;
// 号码长度不够自动补零
if (phone.length() < 11){
fixNumber = phone + "0".repeat(11 - phone.length());
}
Map<String, Object> affiliation = new HashMap<>();
affiliation.put("phone", phone);
affiliation.put("carrier", getPhoneCarrier(fixNumber));
affiliation.put("region", getRegionInfoByPhone(fixNumber));
return affiliation;
}
}
这段代码借鉴于网络,不过我在getPhoneAffiliationInfo()
方法中做了些许修改
public static Map<String, Object> getPhoneAffiliationInfo(String phone) {
String fixNumber = phone;
// 号码长度不够自动补零
if (phone.length() < 11){
fixNumber = phone + "0".repeat(11 - phone.length());
}
Map<String, Object> affiliation = new HashMap<>();
affiliation.put("phone", phone);
affiliation.put("carrier", getPhoneCarrier(fixNumber));
affiliation.put("region", getRegionInfoByPhone(fixNumber));
return affiliation;
}
我这里使用后补零的方式来确保电话号码的位数,以达到输入前三个数字即可查询到营业商的效果
对于超出长度的电话号码,getPhoneCarrier()
和getRegionInfoByPhone()
都可以查询,不过返回的是空结果,不会报错,所以我就没做错误处理。
/**
* 生成PhoneNumber
* @param phone 手机号
* @return PhoneNumber
*/
private static Phonenumber.PhoneNumber getPhoneNumber(String phone){
Phonenumber.PhoneNumber phoneNumber = new Phonenumber.PhoneNumber();
phoneNumber.setCountryCode(86);
phoneNumber.setNationalNumber(Long.parseLong(phone));
return phoneNumber;
}
由于这里规定了号码前缀为 +86 所以只能查询国内的电话号码归属地。
代码参考:SpringBoot通过手机号获取归属地,你应该知道的几种方式。_通过手机号码获取运营商归属地-CSDN博客
调用接口
@GetMapping("/getPhoneAffiliationInfo.do")
public Result getPhoneAffiliationInfo(@RequestParam("phone") String phone){
return Result.success(PhoneToRegionUtil.getPhoneAffiliationInfo(phone));
}
响应格式
{
"code": 1,
"msg": "success",
"data": {
"carrier": "中国联通",
"phone": "15566668888",
"region": "辽宁省大连市"
}
}
遇到的问题
在功能开发的时候遇到过下面这两个错误
-
其一:找不到对应的构造器
java: 无法将类 com.example.pojo.Result中的构造器 Result应用到给定类型;
需要: 没有参数
找到: int,java.lang.String,<nulltype>
原因: 实际参数列表和形式参数列表长度不同
这里排查出来时Lombok的@AllArgsConstructor
注解失效,我就取消了注解,手动定义了一个全参构造,问题解决。
-
其二:无法返回客户端要求的数据格式
[org.springframework.web.HttpMediaTypeNotAcceptableException: No acceptable representation]
一开始我以为这两个报错是不同的原因构成的,还花了好些时间去调整请求头,但都无济于事。后面在网上查资料发现是返回的对象没有getter和setter造成的,所以这里还是Lombok的问题。手动添加了getter和setter之后问题就解决了。至于为什么Lombok会失效我还在研究。
前端
主要设计
因为我的学习方向是Java后端,所以前端代码我是在网络上找的,手动做了一些功能上的调整。
<el-col :span="24" style="text-align: center;">
<el-input
v-model="phoneNumber"
placeholder="请输入要查询的手机号"
style="width: 80%;"
@input="onInputChange"
/>
</el-col>
这个输入框下面本来还有一个button
用来触发事件,我这里改成使用@input
来监听输入框值得变化并以此触发事件,即每输入一个字就调用一次onInputChange
,向后端发起一次请求,由于后端会自动将号码补齐,所以可以起到输入前2~3个数字即可查询到运营商等信息。
// 输入框变化时调用此方法
const onInputChange = async () => {
if (phoneNumber.value.length > 0) {
try {
// 调用接口请求数据
const response = await getPhoneAffiliationInfo(phoneNumber.value);
// 判断接口返回是否成功
if (response.code === 1) {
// 如果请求成功,更新表格数据
tableData.value = [{
phone: response.data.phone,
carrier: response.data.carrier,
region: response.data.region,
}];
} else {
// 如果请求失败,清空表格数据
tableData.value = [];
}
} catch (error) {
// 请求失败时处理错误并清空表格数据
console.error("请求失败:", error);
tableData.value = [];
}
}
};
最后通过对tableData
的双向数据绑定,实现查询结果的动态显示。
<el-col :span="24" style="margin-top: 30px;">
<el-table :data="tableData" style="width: 100%">
<el-table-column label="手机号码" prop="phone"></el-table-column>
<el-table-column label="运营商" prop="carrier"></el-table-column>
<el-table-column label="地区" prop="region"></el-table-column>
</el-table>
</el-col>
页面展示
-
输入号码前三个数字
-
输入完整电话号码