java开发-微信支付
一、最近又做了微信公众号支付,前一次做支付没有好好记录,这次又浪费了不少时间,故完整的记录下,下次就可以直接用了。
1、准备工作(微信公众号、微信商户号申请)
2、域名购买、域名备案(微信支付必须是备案的域名,测试环境支付测试不了)
测试环境能测试授权等功能,扫描关注可获得微信管方测试、app_id、app_secret 有了两个就可以了
二、准备工作续
第一步:开通微信公众号支付功能后,就可以获得app_id、app_secret,这个地方还需要设置一个ip白名单,代码所放置的服务器ip地址
第二步:设置回调域名,必须是备案的域名,域名去掉 http
第三步:设置调起微信支付h5页面的地址,在微信商户平台设置
以上工作准备好了,接下来开始上代码。
微信支付重点在签名的生成,这块错一点就很麻烦。
主代码:
复制代码
package com.snp.app.controller;
import com.alibaba.fastjson.JSONObject;
import com.snp.app.domain.VO.PayVO;
import com.snp.common.controller.BaseController;
import com.snp.common.utils.ConfigUtil;
import com.snp.common.utils.StringUtil;
import com.snp.order.dao.FinancialClaimOrderDao;
import com.snp.order.domain.FinancialClaimOrderDO;
import com.snp.userManager.domain.UserWechatDO;
import com.snp.userManager.service.UserWechatService;
import com.snp.wechat.config.WechatConfig;
import com.snp.wechat.model.bean.*;
import com.snp.wechat.utils.AdvancedUtil;
import com.snp.wechat.utils.PayUtils;
import com.snp.wechat.utils.WeiXinOrderUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;
/**
支付控制器
*/
@RestController
@RequestMapping("/interface/pay")
public class PayController extends BaseController{
private static Logger logger = LoggerFactory.getLogger(PayController.class);
@Autowired
private WeiXinOrderUtil weiXinOrderUtil;
@Autowired
private FinancialClaimOrderDao financialClaimOrderDao;
@Autowired
private WechatConfig wechatConfig;
@Autowired
private UserWechatService userWechatService;@ResponseBody
@RequestMapping(value = “/createWechatOrder”, method ={ RequestMethod.GET,RequestMethod.POST})
public JSONObject createWechatOrder(PayVO payVO, HttpServletRequest request, HttpServletResponse response)
throws Exception{
payVO.setPayAmount(“1”);//1分钱
payVO.setRobbingOrderNo(PayUtils.getTransferNo16());
//ResultBody result = new ResultBody();
logger.info(“saveFinancialClaimOrder{}支付金额:”+payVO.getPayAmount());
logger.info(“saveFinancialClaimOrder{}订单号:”+payVO.getRobbingOrderNo());
UserWechatDO userWechatDO = userWechatService.getUserWechatByUserId(ConfigUtil.getUserId(request));
WechatOrder wechatOrder = new WechatOrder();
wechatOrder.setOpenid(userWechatDO.getWechatOpenid());
wechatOrder.setOrderName(“押金”);
//PayUtils.getTransferNo16()
wechatOrder.setOut_trade_no(payVO.getRobbingOrderNo());
wechatOrder.setNonce_str(PayUtils.getNonceStr());
//wechatOrder.setTotal_fee(MoneyUtil.getOrderMoney(payVO.getPayAmount()));
wechatOrder.setTotal_fee(Integer.parseInt(payVO.getPayAmount()));
//请求微信下单 并返回参数
String resultOrderStr = weiXinOrderUtil.buildWechatOrderParam(wechatOrder,request);
JSONObject returnJson=JSONObject.parseObject(resultOrderStr);
//更新 openID prepay_id 到抢单表中
FinancialClaimOrderDO finaClaimDO = new FinancialClaimOrderDO();
finaClaimDO.setOpenId(userWechatDO.getWechatOpenid());
finaClaimDO.setPrepayId(returnJson.get(“pg”).toString());
//financialClaimOrderDao.update(finaClaimDO);
logger.info(“createWechatOrder{}支付结果:”+resultOrderStr);
//result.setData(resultOrderStr);
return returnJson;
}//支付之前先去下单 获取用户openID
@RequestMapping(value = “/getWechatOpenId”, method = RequestMethod.GET)
public void getWechatOpenId(PayVO payVO,HttpServletRequest request,HttpServletResponse response) throws IOException{
//静默授权 只能获得openid
//获得微信公众号的唯一标识
String appID = wechatConfig.getWechatAppId();
String appSecret = wechatConfig.getWechatAppSecret();// 用户同意授权后,能获取到codeString code = request.getParameter("code");WeixinOauth2Token weixinOauth2Token = AdvancedUtil.getOauth2AccessToken(appID, appSecret, code);String openId = null;String accessToken = null;if(null!=weixinOauth2Token){openId = weixinOauth2Token.getOpenId();accessToken = weixinOauth2Token.getAccessToken();}UserWechatDO userWechatDO = new UserWechatDO();userWechatDO.setUserId(ConfigUtil.getUserId(request));userWechatDO.setWechatOpenid(openId);userWechatDO.setAccessToken(accessToken);userWechatService.save(userWechatDO);//获取回调域名String url = wechatConfig.getRedirectUri();url=url+"/index.html";System.out.println(url);response.sendRedirect(url);
}
/**
支付下单之前 先去判断是否需要静默授权获取openID
@param request
@param response
@throws IOException
*/
@RequestMapping(value = “/saveFinancialClaimOrder”, method = {RequestMethod.GET,RequestMethod.POST})
public void saveFinancialClaimOrder(PayVO payVO,HttpServletRequest request,HttpServletResponse response)
throws Exception{
//查询数据库 如果没有记录就是第一次登录,如果有数据判断token有没有失效
if(ConfigUtil.getUserId(request) == 0L){
throw new Exception(“用户id为空”);
}
UserWechatDO userWechatDO = userWechatService.getUserWechatByUserId(ConfigUtil.getUserId(request));
int isLoginFirst = 0;
if(StringUtil.isEmpty(userWechatDO)){//首次支付 静默登录
isLoginFirst = 1;
}else{
//第二次登录 支付这里不用检查token失效 openid唯一的不会失效
isLoginFirst = 0;
}
//获取回调域名
String url = wechatConfig.getRedirectUri();
//获得微信公众号的唯一标识
String appID = wechatConfig.getWechatAppId();
//微信本地中转站 去获取token
String reUrl = wechatConfig.getReUrl();
reUrl = reUrl+"?userId="+ConfigUtil.getUserId(request);
//微信静默授权地址
String accreditUrlBase = wechatConfig.getAccreditUrlBase();if(isLoginFirst == 1){
accreditUrlBase=accreditUrlBase.replace(“Redirect_UI”, reUrl).replace(“appId”, appID);
response.sendRedirect(accreditUrlBase);//未授权的用户继续授权
}
//token effective 支付页面
url=url+"/index.html";
System.out.println(url);
response.sendRedirect(url);
}
/**提交支付后的微信异步返回接口
@throws IOException
*/
@RequestMapping(value="/weixinNotify")
public void weixinNotify(HttpServletRequest request, HttpServletResponse response) throws IOException{
String out_trade_no=null;
String return_code =null;
try {
String resultNotify = weiXinOrderUtil.getOrderReturnStream(request);
Map<String, String> resultMap = PayUtils.getH5PayMap(resultNotify,request);
System.out.println(“支付回调参数:”+resultMap);
out_trade_no = resultMap.get(“out_trade_no”);
return_code = resultMap.get(“return_code”);//通知微信.异步确认成功.必写.不然微信会一直通知后台.八次之后就认为交易失败了.response.getWriter().write(PayUtils.setXML("SUCCESS", ""));
} catch (Exception e) {
logger.error(“微信回调接口出现错误:”,e);
try {
response.getWriter().write(PayUtils.setXML(“FAIL”, “error”));
} catch (IOException e1) {
e1.printStackTrace();
}
}
FinancialClaimOrderDO fClaimDO = financialClaimOrderDao.getFinanClaimByOrderNo(out_trade_no);
if(return_code.equals(“SUCCESS”)){
//支付成功的业务逻辑
fClaimDO.setStatus(1);
}else{
//支付失败的业务逻辑
fClaimDO.setStatus(-1);
}
financialClaimOrderDao.update(fClaimDO);
}
}
复制代码
页面:
复制代码
微信安全支付 复制代码 工具类:
复制代码
package com.snp.wechat.utils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.snp.wechat.config.WechatConfig;
import com.snp.wechat.model.bean.WechatOrder;
import com.snp.wechat.model.bean.WeixinOauth2Token;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
/**
微信公用工具类
*/
@Component
public class WeiXinOrderUtil {@Autowired
private WechatConfig wechatConfig;/**
- 组装统一下单参数
- @return
*/
public String buildWechatOrderParam(WechatOrder wechatOrder,HttpServletRequest request) throws Exception{
String payKey = wechatConfig.getPaykey();
String signType = wechatConfig.getSignType();
SortedMap<String, String> signMap = new TreeMap<String, String>();
long timeStamp = PayUtils.getUnixTime(new Date());
wechatOrder.setAppid(wechatConfig.getWechatAppId());
wechatOrder.setTrade_type(wechatConfig.getTradeType());
//构建参数返回前台 请求支付接口
String prepayId = createWechatOrder(wechatOrder,request);
signMap.put(“appId”,wechatOrder.getAppid());
signMap.put(“timeStamp”, timeStamp+"");
signMap.put(“nonceStr”, wechatOrder.getNonce_str());
signMap.put(“package”, “prepay_id=”+prepayId);
signMap.put(“signType”, signType);
String paySign = PayUtils.getSign(signMap, payKey);
signMap.put(“pg”, prepayId);
signMap.put(“paySign”, paySign);
signMap.put(“result”, “success”);
String json = JSON.toJSONString(signMap);
JSONObject returnJson=JSONObject.parseObject(json);
return returnJson.toJSONString();
}
/**
创建下单签名 包含商品信息
@return
*/
public String createWechatSign(WechatOrder wechatOrder){
String mch_id = wechatConfig.getMchId();
String notify_url = wechatConfig.getNotifyUrl();
String device_info = wechatConfig.getDeviceInfo();
String payKey = wechatConfig.getPaykey();//将商品信息打包
SortedMap<String, String> parameters = new TreeMap<String, String>();
parameters.put(“appid”, wechatOrder.getAppid());//公众号id 这地方一定要小写并跟下面xml文件对应都是小写
parameters.put(“mch_id”,mch_id);//商户ID
parameters.put(“device_info”, device_info);
parameters.put(“body”, wechatOrder.getOrderName());//名称
parameters.put(“trade_type”, wechatOrder.getTrade_type());
parameters.put(“nonce_str”, wechatOrder.getNonce_str());//随机数
parameters.put(“notify_url”, notify_url);
parameters.put(“out_trade_no”, wechatOrder.getOut_trade_no());
parameters.put(“total_fee”, wechatOrder.getTotal_fee()+"");
// parameters.put(“spbill_create_ip”, spbill_create_ip );
parameters.put(“openid”, wechatOrder.getOpenid());
//根据上述的数据生成预支付订单号的前面sign
return PayUtils.getSign(parameters, payKey);
}
/**
创建微信订单 请求微信接口下单
@return
*/
public String createWechatOrder(WechatOrder wechatOrder,HttpServletRequest request) throws Exception{
String mch_id = wechatConfig.getMchId();
String notify_url = wechatConfig.getNotifyUrl();
String device_info = wechatConfig.getDeviceInfo();
String createOrderURL = wechatConfig.getCreateOrderUrl();//生成统一支付接口数据
String xml = “”+
“”+wechatOrder.getAppid()+""+
“”+wechatOrder.getOrderName()+""+
“<device_info>”+device_info+"</device_info>"+
“<mch_id>”+mch_id+"</mch_id>"+
“<nonce_str>”+wechatOrder.getNonce_str()+"</nonce_str>"+
“<notify_url>”+notify_url+"</notify_url>"+
“”+wechatOrder.getOpenid()+""+
“<out_trade_no>”+wechatOrder.getOut_trade_no()+"</out_trade_no>"+
“<total_fee>”+wechatOrder.getTotal_fee()+"</total_fee>"+
“<trade_type>”+wechatOrder.getTrade_type()+"</trade_type>"+
“”+createWechatSign(wechatOrder)+""+
“”;
//调用统一支付接口
String result = PayUtils.httpsRequest(createOrderURL, “POST”, xml);
System.out.println("-----------------------------统一下单结果---------------------------");
System.out.println(result);
Map<String, String> resultMap = null;
resultMap=PayUtils.getH5PayMap(result,request);
return resultMap.get(“prepay_id”); //预支付ID,保存到数据库中
}
/**
- 解析微信支付回调的结果
- @return
*/
public static String getOrderReturnStream(HttpServletRequest request) throws IOException {
InputStream inStream = request.getInputStream();
ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
while ((len = inStream.read(buffer)) != -1) {
outSteam.write(buffer, 0, len);
}
outSteam.close();
inStream.close();
return new String(outSteam.toByteArray(),“utf-8”);
}
/**
- 获取 WeixinOauth2Token
- @return
*/
public WeixinOauth2Token getWeixinOauth2Token(String code){
//获得微信公众号的唯一标识
String appId = wechatConfig.getWechatAppId();
String appSecret = wechatConfig.getWechatAppSecret();
return AdvancedUtil.getOauth2AccessToken(appId, appSecret, code);
}
}
复制代码
复制代码
package com.snp.wechat.utils;
import com.snp.common.utils.StringUtil;
import com.snp.userManager.domain.UserWechatDO;
import com.snp.wechat.model.bean.WeixinOauth2Token;
import org.apache.commons.lang3.StringUtils;
import com.alibaba.fastjson.JSONObject;
import java.util.Date;
/**
@author yuwei
工具类
2016年12月21日 下午1:58:38
*/
public class AdvancedUtil {public AdvancedUtil() {
super();
// TODO Auto-generated constructor stub
}/**
获取网页授权凭证
@param appId 公众账号的唯一标识
@param appSecret 公众账号的密钥
@param code
@return
@return WeixinAouth2Token
/
public static WeixinOauth2Token getOauth2AccessToken(String appId, String appSecret, String code) {
WeixinOauth2Token wat = null;
// 拼接请求地址
String requestUrl = “https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code”;
requestUrl = requestUrl.replace(“APPID”, appId);
requestUrl = requestUrl.replace(“SECRET”, appSecret);
requestUrl = requestUrl.replace(“CODE”, code);
// 获取网页授权凭证
JSONObject jsonObject = CommonUtil.httpsRequest(requestUrl, “GET”, null);
if (null != jsonObject) {
try {
wat = new WeixinOauth2Token();
wat.setAccessToken(jsonObject.getString(“access_token”));
wat.setExpiresIn(jsonObject.getInteger(“expires_in”));
wat.setRefreshToken(jsonObject.getString(“refresh_token”));
wat.setOpenId(jsonObject.getString(“openid”));
wat.setScope(jsonObject.getString(“scope”));
} catch (Exception e) {
wat = null;
int errorCode = jsonObject.getInteger(“errcode”);
String errorMsg = jsonObject.getString(“errmsg”);
System.out.println(errorCode);
System.out.println(errorMsg);
// log.error(“获取网页授权凭证失败 errcode:{} errmsg:{}”, errorCode, errorMsg);
}
}
return wat;
}
/*通过网页授权获取用户信息
@param accessToken 网页授权接口调用凭证
@param openId 用户标识
@return SNSUserInfo
*/
public static UserWechatDO getWechatInfo(String accessToken, String openId,String refreshToken) {
UserWechatDO userWechatDO = null;
// 拼接请求地址
String requestUrl = “https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID”;
requestUrl = requestUrl.replace(“ACCESS_TOKEN”, accessToken).replace(“OPENID”, openId);
// 通过网页授权获取用户信息
JSONObject jsonObject = CommonUtil.httpsRequest(requestUrl, “GET”, null);if (!StringUtil.isEmpty(jsonObject)) {
try {
userWechatDO = new UserWechatDO();
// 用户的标识
userWechatDO.setWechatOpenid(jsonObject.getString(“openid”));
// 昵称
String nickname = jsonObject.getString(“nickname”);
nickname = filterEmoji(nickname);
userWechatDO.setNickName(nickname);
// 性别(1是男性,2是女性,0是未知)
userWechatDO.setSex(jsonObject.getInteger(“sex”));
// 用户所在国家
userWechatDO.setCountry(jsonObject.getString(“country”));
// 用户所在省份
userWechatDO.setProvince(jsonObject.getString(“province”));
// 用户所在城市
userWechatDO.setCity(jsonObject.getString(“city”));
// 用户头像
userWechatDO.setHeadImgUrl(jsonObject.getString(“headimgurl”));
//UnicodeID
userWechatDO.setWechatUnionid(jsonObject.getString(“unionid”));
//userWechatDO.setWin(0);
//userWechatDO.setLose(0);
//查询时间
// Date date = new Date();
// player.setJoinTime(date);
//首次授权时间
userWechatDO.setCreateTime(new Date());
//更新时间
userWechatDO.setUpdateTime(new Date());
userWechatDO.setLasttIme(new Date());
//凭证保存
userWechatDO.setAccessToken(accessToken);
//刷新凭证
userWechatDO.setRefreshToken(refreshToken);
// 用户特权信息
// snsUserInfo.setPrivilegeList(JSONArray.parseObject(jsonObject.getJSONArray(“privilege”), List.class));
} catch (Exception e) {
userWechatDO = null;
int errorCode = jsonObject.getInteger(“errcode”);
String errorMsg = jsonObject.getString(“errmsg”);
System.out.println(errorCode);
System.out.println(errorMsg);
// log.error(“获取用户信息失败 errcode:{} errmsg:{}”, errorCode, errorMsg);
}
}
return userWechatDO;
}
//检验凭证是否失效
@SuppressWarnings(“unused”)
public static boolean judgeToken(String accessToken, String openId){
// 拼接请求地址
String requestUrl = “https://api.weixin.qq.com/sns/auth?access_token=ACCESS_TOKEN&openid=OPENID”;
requestUrl = requestUrl.replace(“ACCESS_TOKEN”, accessToken).replace(“OPENID”, openId);
// 通过网页授权获取用户信息
JSONObject jsonObject = CommonUtil.httpsRequest(requestUrl, “GET”, null);
int errorCode = jsonObject.getInteger(“errcode”);
String errorMsg = jsonObject.getString(“errmsg”);//正确返回OK
errorMsg = errorMsg.toUpperCase();
if(errorMsg.equals(“OK”)){
return true;
}
return false;
}//去掉ios特殊字符
public static String filterEmoji(String source) {
if (StringUtils.isBlank(source)) {
return source;
}
StringBuilder buf = null;
int len = source.length();
for (int i = 0; i < len; i++) {
char codePoint = source.charAt(i);
if (isNotEmojiCharacter(codePoint)) {
if (buf == null) {
buf = new StringBuilder(source.length());
}
buf.append(codePoint);
}
}
if (buf == null) {
return source;
} else {
if (buf.length() == len) {
buf = null;
return source;
} else {
return buf.toString();
}
}
}
//判断特殊字符串
private static boolean isNotEmojiCharacter(char codePoint) {
return (codePoint == 0x0) ||
(codePoint == 0x9) ||
(codePoint == 0xA) ||
(codePoint == 0xD) ||
((codePoint >= 0x20) && (codePoint <= 0xD7FF)) ||
((codePoint >= 0xE000) && (codePoint <= 0xFFFD)) ||
((codePoint >= 0x10000) && (codePoint <= 0x10FFFF));
}
}
java开发-微信支付相关推荐
- JAVA开发微信支付(JSAPI支付)
最近开发完了微信支付功能模块,下面就趁热打铁整理下微信支付(JSAPI支付)相关的知识. 1.JSAPI支付 JSAPI支付是用户在微信中打开商户的H5页面,商户在H5页面通过调用微信支付提供的JSA ...
- Java教程:如何申请Java开发微信支付中的必要参数(appid,appsecret,商户号,微信支付密钥)
这一章我们来说下,微信开发中的一些必要参数申请,如appid,appsecret,商户号,微信支付密钥 第一步: 我们要申请微信公众服务号 链接:https://mp.weixin.qq.com/ 第 ...
- 微信小程序开发-微信支付之免密支付(自动扣费)一 小程序+java接口
微信小程序开发-微信支付之免密支付(自动扣费)一 小程序+java接口 链接: 点击进入
- java开发微信公众号支付
这篇文章主要给大家结合微信支付接口开发的实践,从获取用户授权到各主要接口的使用方法等方面介绍微信支付的关键点技术,有需要的小伙伴可以参考下 最近做了微信公众号支付的开发,由于是第一次做也摸索了几天的时 ...
- java对接微信支付收不到支付通知问题(亲身实践)
问题描述: 用java对接微信支付时,统一下单接口正常.但是用户扫码付款成功后,设置用于回调的notify_url对应的接口并没有收到请求(这个url测试过,是正常的且外网能访问的). 由于官方文档没 ...
- 微信小程序-JAVA实现微信支付功能(微信支付2.0)
微信小程序-JAVA实现微信支付功能(微信支付2.0) 一.前言 本博客主要介绍JAVA后台与微信小程序(UNI-APP或者原生微信小程序)的微信支付的实现,如果是APP或者H5的开发暂时不支持,具体 ...
- springboot快速开发微信支付
springboot快速开发微信支付 1. POM文件添加依赖 <properties><wechat.sdk.version>3.3.7.B</wechat.sdk.v ...
- Java开发微信公众号(四)---微信服务器post消息体的接收及消息的处理
在前几节文章中我们讲述了微信公众号环境的搭建.如何接入微信公众平台.以及微信服务器请求消息,响应消息,事件消息以及工具处理类的封装:接下来我们重点说一下-微信服务器post消息体的接收及消息的处理,这 ...
- Java开发微信公众号(二)---开启开发者模式,接入微信公众平台开发
接入微信公众平台开发,开发者需要按照如下步骤完成: 1.填写服务器配置 2.验证服务器地址的有效性 3.依据接口文档实现业务逻辑 资料准备: 1.一个可以访问的外网,即80的访问端口,因为微信公众号接 ...
- 如何使用easywechat开发微信支付功能
easywechat是神一样的存在.非常好用.希望大家都能转到这上面来. 用easywechat来开发微信支付功能,步骤如下: 一,需要有一个商品下单页面,页面上有你的商品的信息,还要有购买数量,和一 ...
最新文章
- 在Cloudshare上使用PAL=〉系统检查及初始化
- 如何应用AutoML加速图机器学习任务的处理?
- git checkout之一 HEAD基本和detached 状态
- 【QGIS入门实战精品教程】1.1:QGIS与GIS的区别和联系
- 【R】语言第四课----读取文件
- python列表元组_Python列表元组操作
- HDU2002 计算球体积【入门】
- git revert 之后怎么撤销_git撤销操作
- [转]网络编程学习指南
- Linux设备驱动模型一 sysfs
- 苹果笔记本摄像头Linux驱动,苹果发布Macbook摄像头驱动更新 更适配window10
- 云服务器上安装jboss_jboss下载_精通jboss下载_下载与安装jboss-华军软件园
- 基于docker实现人人影视CVNT虚拟化多开,基于selinum实现自动化点击程序
- PdfSharp -- 根据PDF模板导出PDF
- 橘子学ES11之URI搜索方式
- 5种数值评分标准总结 - 为预测模型找到正确的度量标准
- 搞 AI 建模预测都在用 Python,其实入门用 SPL 也不错
- adb是什么?如何安装配置adb?如何检验是否成功安装adb?
- 目标检测论文解读复现之五:改进YOLOv5的SAR图像舰船目标检测
- 智能卡水表管理系统功能介绍