一个基于spring boot + spring cloud的敏捷开发框架,一键生成CRUD及管理后台页面,后台管理系统使用Bootstrap开发,集成Redis实现分布式Session,集成Spring Cache,邮件,阿里OSS,极光推送,极光短信,微信公众号,微信硬件平台,Groovy,自动化部署服务器,微服务监控等Spring Cloud全家桶------可以关注微信公众号 Simba技术交流 不定期推送Simba框架的使用方法及设计思路,或者一些技术分享 如果有公司需要系统架构设计师证书挂靠的,也欢迎随时找我
克隆/下载 
master

simba
/

framework 

/

simba-alipay 

/

src 

/

main 

/

java 

/

com 

/

simba 

/

alipay 

/

util 

/

AliPayUtil.java

 AliPayUtil.java 18.00 KB →  loceme.student@163.com 提交于 23天前 . 支付宝企业支付
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
package com.simba.alipay.util;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.PostConstruct;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradeAppPayModel;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayDataDataserviceBillDownloadurlQueryRequest;
import com.alipay.api.request.AlipayFundTransOrderQueryRequest;
import com.alipay.api.request.AlipayFundTransToaccountTransferRequest;
import com.alipay.api.request.AlipayTradeAppPayRequest;
import com.alipay.api.request.AlipayTradeCancelRequest;
import com.alipay.api.request.AlipayTradeCloseRequest;
import com.alipay.api.request.AlipayTradeCreateRequest;
import com.alipay.api.request.AlipayTradeFastpayRefundQueryRequest;
import com.alipay.api.request.AlipayTradeOrderSettleRequest;
import com.alipay.api.request.AlipayTradePayRequest;
import com.alipay.api.request.AlipayTradePrecreateRequest;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.response.AlipayDataDataserviceBillDownloadurlQueryResponse;
import com.alipay.api.response.AlipayFundTransOrderQueryResponse;
import com.alipay.api.response.AlipayFundTransToaccountTransferResponse;
import com.alipay.api.response.AlipayTradeAppPayResponse;
import com.alipay.api.response.AlipayTradeCancelResponse;
import com.alipay.api.response.AlipayTradeCloseResponse;
import com.alipay.api.response.AlipayTradeCreateResponse;
import com.alipay.api.response.AlipayTradeFastpayRefundQueryResponse;
import com.alipay.api.response.AlipayTradeOrderSettleResponse;
import com.alipay.api.response.AlipayTradePayResponse;
import com.alipay.api.response.AlipayTradePrecreateResponse;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.alipay.api.response.AlipayTradeRefundResponse;
import com.simba.alipay.cosntantData.AliPayConstantData;
import com.simba.alipay.model.CreateOrder;
import com.simba.alipay.model.EnterprisePay;
import com.simba.alipay.model.PayOrder;
import com.simba.alipay.model.Precreate;
import com.simba.alipay.model.RoyaltyParameter;
import com.simba.exception.BussException;
import com.simba.framework.util.json.FastJsonUtil;
import com.simba.model.constant.ConstantData;
/**
* 阿里支付工具类
*
* @author caozhejun
*
*/
@Component
public class AliPayUtil {
private static final Log logger = LogFactory.getLog(AliPayUtil.class);
@Value("${alipay.appid}")
private String appId;
/**
* 应用的私钥
*/
@Value("${alipay.private.key}")
private String privateKey;
/**
* 支付宝的公钥
*/
@Value("${alipay.public.key}")
private String publicKey;
@Value("${alipay.domain}")
private String domain;
private AlipayClient alipayClient;
/**
* 回调通知url
*/
private String callbackUrl;
@PostConstruct
private void init() {
if (StringUtils.isEmpty(appId) || StringUtils.isEmpty(privateKey) || StringUtils.isEmpty(publicKey) || StringUtils.isEmpty(domain)) {
logger.warn("没有配置阿里支付相关信息,不能使用阿里支付功能");
return;
}
alipayClient = new DefaultAlipayClient(AliPayConstantData.payUrl, appId, privateKey, AliPayConstantData.format, ConstantData.DEFAULT_CHARSET, publicKey, AliPayConstantData.signType);
callbackUrl = domain + "/alipay/callback";
}
/**
* 交易查询(该接口提供所有支付宝支付订单的查询,商户可以通过该接口主动查询订单状态,完成下一步的业务逻辑。 需要调用查询接口的情况:
* 当商户后台、网络、服务器等出现异常,商户系统最终未接收到支付通知; 调用支付接口后,返回系统错误或未知交易状态情况;
* 调用alipay.trade.pay,返回INPROCESS的状态; 调用alipay.trade.cancel之前,需确认支付状态)
*
* @param out_trade_no
* 支付时传入的商户订单号,与trade_no必填一个
* @param trade_no
* 支付时返回的支付宝交易号,与out_trade_no必填一个
* @return
* @throws AlipayApiException
*/
public AlipayTradeQueryResponse query(String out_trade_no, String trade_no) throws AlipayApiException {
checkParams(out_trade_no, trade_no);
AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();// 创建API对应的request类
request.setBizContent("{" + " \"out_trade_no\":\"" + StringUtils.defaultString(out_trade_no) + "\"," + " \"trade_no\":\"" + StringUtils.defaultString(trade_no) + "\"" + " }");// 设置业务参数
AlipayTradeQueryResponse response = alipayClient.execute(request);// 通过alipayClient调用API,获得对应的response类
logger.info("交易查询返回结果:" + response.getBody());
return response;
}
private void checkParams(String out_trade_no, String trade_no) {
if (StringUtils.isEmpty(out_trade_no) && StringUtils.isEmpty(trade_no)) {
throw new BussException("支付时传入的商户订单号和支付时返回的支付宝交易号不能同时为空");
}
}
/**
* 交易退款(当交易发生之后一段时间内,由于买家或者卖家的原因需要退款时,卖家可以通过退款接口将支付款退还给买家,支付宝将在收到退款请求并且验证成功之后,按照退款规则将支付款按原路退到买家帐号上。
* 交易超过约定时间(签约时设置的可退款时间)的订单无法进行退款
* 支付宝退款支持单笔交易分多次退款,多次退款需要提交原支付订单的商户订单号和设置不同的退款单号。一笔退款失败后重新提交,要采用原来的退款单号。总退款金额不能超过用户实际支付金额)
*
* @param out_trade_no
* 支付时传入的商户订单号,与trade_no必填一个
* @param trade_no
* 支付时返回的支付宝交易号,与out_trade_no必填一个
* @param out_request_no
* 本次退款请求流水号,部分退款时必传
* @param refund_amount
* 本次退款金额
* @return
* @throws AlipayApiException
*/
public AlipayTradeRefundResponse refund(String out_trade_no, String trade_no, String out_request_no, String refund_amount) throws AlipayApiException {
checkParams(out_trade_no, trade_no);
AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();// 创建API对应的request类
request.setBizContent("{" + " \"out_trade_no\":\"" + StringUtils.defaultString(out_trade_no) + "\"," + " \"trade_no\":\"" + StringUtils.defaultString(trade_no) + "\","
+ " \"out_request_no\":\"" + StringUtils.defaultString(out_request_no) + "\"," + " \"refund_amount\":\"" + StringUtils.defaultString(refund_amount) + "\"" + " }");// 设置业务参数
AlipayTradeRefundResponse response = alipayClient.execute(request);// 通过alipayClient调用API,获得对应的response类
logger.info("交易退款返回结果:" + response.getBody());
return response;
}
/**
* 查询对账单下载地址
*
* @param bill_date
* 需要下载的账单日期,最晚是当期日期的前一天
* @return
* @throws AlipayApiException
*/
public AlipayDataDataserviceBillDownloadurlQueryResponse billDown(String bill_date) throws AlipayApiException {
AlipayDataDataserviceBillDownloadurlQueryRequest request = new AlipayDataDataserviceBillDownloadurlQueryRequest();// 创建API对应的request类
request.setBizContent("{" + " \"bill_type\":\"trade\"," + " \"bill_date\":\"" + bill_date + "\"" + " }");// 设置业务参数
AlipayDataDataserviceBillDownloadurlQueryResponse response = alipayClient.execute(request);
logger.info("查询对账单下载地址返回结果:" + response.getBody());
return response;
}
/**
* 统一收单交易退款查询(商户可使用该接口查询自已通过alipay.trade.refund提交的退款请求是否执行成功。
* 该接口的返回码10000,仅代表本次查询操作成功,不代表退款成功。如果该接口返回了查询数据,则代表退款成功,如果没有查询到则代表未退款成功,可以调用退款接口进行重试。重试时请务必保证退款请求号一致)
*
* @param trade_no
* 支付宝交易号,和商户订单号不能同时为空
* @param out_trade_no
* 订单支付时传入的商户订单号,和支付宝交易号不能同时为空。
* trade_no,out_trade_no如果同时存在优先取trade_no
* @param out_request_no
* 请求退款接口时,传入的退款请求号,如果在退款请求时未传入,则该值为创建交易时的外部交易号
* @return
* @throws AlipayApiException
*/
public AlipayTradeFastpayRefundQueryResponse refundQuery(String trade_no, String out_trade_no, String out_request_no) throws AlipayApiException {
AlipayTradeFastpayRefundQueryRequest request = new AlipayTradeFastpayRefundQueryRequest();
request.setBizContent("{" + "\"trade_no\":\"" + StringUtils.defaultString(trade_no) + "\"," + "\"out_trade_no\":\"" + StringUtils.defaultString(out_trade_no) + "\"," + "\"out_request_no\":\""
+ StringUtils.defaultString(out_request_no) + "\"" + " }");
AlipayTradeFastpayRefundQueryResponse response = alipayClient.execute(request);
logger.info("统一收单交易退款查询返回结果:" + response.getBody());
return response;
}
/**
* 统一收单交易结算(用于在线下场景交易支付后,进行结算)
*
* @param out_request_no
* 结算请求流水号 开发者自行生成并保证唯一性
* @param trade_no
* 支付宝订单号
* @param royalty_parameters
* 分账明细信息
* @param operator_id
* 操作员id
* @return
* @throws AlipayApiException
*/
public AlipayTradeOrderSettleResponse orderSettle(String out_request_no, String trade_no, List<RoyaltyParameter> royalty_parameters, String operator_id) throws AlipayApiException {
AlipayTradeOrderSettleRequest request = new AlipayTradeOrderSettleRequest();
request.setBizContent("{" + "\"out_request_no\":\"" + StringUtils.defaultString(out_request_no) + "\"," + "\"trade_no\":\"" + StringUtils.defaultString(trade_no) + "\","
+ " \"royalty_parameters\":" + FastJsonUtil.toJson(royalty_parameters) + "," + "\"operator_id\":\"" + StringUtils.defaultString(operator_id) + "\"" + " }");
AlipayTradeOrderSettleResponse response = alipayClient.execute(request);
logger.info("统一收单交易结算返回结果:" + response.getBody());
return response;
}
/**
* 统一收单交易关闭(用于交易创建后,用户在一定时间内未进行支付,可调用该接口直接将未付款的交易进行关闭)
*
* @param out_trade_no
* 订单支付时传入的商户订单号,和支付宝交易号不能同时为空。
* trade_no,out_trade_no如果同时存在优先取trade_no
* @param trade_no
* 该交易在支付宝系统中的交易流水号。最短 16 位,最长 64 位。和out_trade_no不能同时为空,如果同时传了
* out_trade_no和 trade_no,则以 trade_no为准
* @param operator_id
* 卖家端自定义的的操作员 ID
* @return
* @throws AlipayApiException
*/
public AlipayTradeCloseResponse close(String out_trade_no, String trade_no, String operator_id) throws AlipayApiException {
AlipayTradeCloseRequest request = new AlipayTradeCloseRequest();
request.setBizContent("{" + "\"trade_no\":\"" + StringUtils.defaultString(trade_no) + "\"," + "\"out_trade_no\":\"" + StringUtils.defaultString(out_trade_no) + "\"," + "\"operator_id\":\""
+ StringUtils.defaultString(operator_id) + "\"" + " }");
AlipayTradeCloseResponse response = alipayClient.execute(request);
logger.info("统一收单交易关闭返回结果:" + response.getBody());
return response;
}
/**
* 统一收单交易撤销(支付交易返回失败或支付系统超时,调用该接口撤销交易。如果此订单用户支付失败,支付宝系统会将此订单关闭;如果用户支付成功,支付宝系统会将此订单资金退还给用户。
* 注意:只有发生支付系统超时或者支付结果未知时可调用撤销,其他正常支付的单如需实现相同功能请调用申请退款API。提交支付交易后调用【查询订单API】,没有明确的支付结果再调用【撤销订单API】)
*
* @param out_trade_no
* 原支付请求的商户订单号,和支付宝交易号不能同时为空
* @param trade_no
* 支付宝交易号,和商户订单号不能同时为空
* @return
* @throws AlipayApiException
*/
public AlipayTradeCancelResponse cancel(String out_trade_no, String trade_no) throws AlipayApiException {
AlipayTradeCancelRequest request = new AlipayTradeCancelRequest();
request.setBizContent("{" + "\"out_trade_no\":\"" + StringUtils.defaultString(out_trade_no) + "\"," + "\"trade_no\":\"" + StringUtils.defaultString(trade_no) + "\"" + " }");
AlipayTradeCancelResponse response = alipayClient.execute(request);
logger.info("统一收单交易撤销返回结果:" + response.getBody());
return response;
}
/**
* 统一收单线下交易预创建(收银员通过收银台或商户后台调用支付宝接口,生成二维码后,展示给用户,由用户扫描二维码完成订单支付)
*
* @param precreate
* @return
* @throws AlipayApiException
*/
public AlipayTradePrecreateResponse precreate(Precreate precreate) throws AlipayApiException {
AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest();
request.setBizContent(FastJsonUtil.toJson(precreate));
AlipayTradePrecreateResponse response = alipayClient.execute(request);
logger.info("统一收单线下交易预创建返回结果:" + response.getBody());
return response;
}
/**
* 统一收单交易创建(商户通过该接口进行交易的创建下单)
*
* @param order
* @return
* @throws AlipayApiException
*/
public AlipayTradeCreateResponse create(CreateOrder order) throws AlipayApiException {
AlipayTradeCreateRequest request = new AlipayTradeCreateRequest();
request.setBizContent(FastJsonUtil.toJson(order));
AlipayTradeCreateResponse response = alipayClient.execute(request);
logger.info("统一收单交易创建返回结果:" + response.getBody());
return response;
}
/**
* 统一收单交易支付(收银员使用扫码设备读取用户手机支付宝“付款码”/声波获取设备(如麦克风)读取用户手机支付宝的声波信息后,将二维码或条码信息/声波信息通过本接口上送至支付宝发起支付)
*
* @param order
* @return
* @throws AlipayApiException
*/
public AlipayTradePayResponse pay(PayOrder order) throws AlipayApiException {
AlipayTradePayRequest request = new AlipayTradePayRequest();
request.setBizContent(FastJsonUtil.toJson(order));
AlipayTradePayResponse response = alipayClient.execute(request);
logger.info("统一收单交易支付返回结果:" + response.getBody());
return response;
}
/**
* 生成APP支付订单
*
* @param model
* @return
* @throws AlipayApiException
*/
public AlipayTradeAppPayResponse appPay(AlipayTradeAppPayModel model) throws AlipayApiException {
AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
request.setBizModel(model);
request.setNotifyUrl(callbackUrl);
// 这里和普通的接口调用不同,使用的是sdkExecute
AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
logger.info("生成APP支付订单返回结果:" + response.getBody());// 就是orderString,可以直接给客户端请求,无需再做处理。
return response;
}
/**
* 检查支付宝支付回调通知的参数是否正确
*
* @param params
* @throws AlipayApiException
*/
public void checkSign(Map<String, String> params) throws AlipayApiException {
logger.info("检查签名参数:" + params.toString());
boolean flag = AlipaySignature.rsaCheckV1(params, publicKey, ConstantData.DEFAULT_CHARSET, "RSA2");
if (!flag) {
throw new BussException("支付宝支付回调url签名错误,可能有人攻击");
}
}
/**
* 单笔转账到支付宝账户接口
*
* @param enterprisePay
* @return
* @throws AlipayApiException
*/
public AlipayFundTransToaccountTransferResponse enterprisePay(EnterprisePay enterprisePay) throws AlipayApiException {
String amount = enterprisePay.getAmount();
double money = NumberUtils.toDouble(amount) / 100;
amount = new java.text.DecimalFormat("0.00").format(money);
enterprisePay.setAmount(amount);
AlipayFundTransToaccountTransferRequest request = new AlipayFundTransToaccountTransferRequest();
request.setBizContent(FastJsonUtil.toJson(enterprisePay));
return alipayClient.execute(request);
}
/**
* 按支付宝转账单据号查询转账订单接口
*
* @param orderId
* 支付宝转账单据号
* @return
* @throws AlipayApiException
*/
public AlipayFundTransOrderQueryResponse searchEnterprisePayByOrderId(String orderId) throws AlipayApiException {
Map<String, String> param = new HashMap<>(1);
param.put("order_id", orderId);
AlipayFundTransOrderQueryRequest request = new AlipayFundTransOrderQueryRequest();
request.setBizContent(FastJsonUtil.toJson(param));
return alipayClient.execute(request);
}
/**
* 按商户转账唯一订单号查询转账订单接口
*
* @param outBizNo
* 商户转账唯一订单号
* @return
* @throws AlipayApiException
*/
public AlipayFundTransOrderQueryResponse searchEnterprisePayByOutBizNo(String outBizNo) throws AlipayApiException {
Map<String, String> param = new HashMap<>(1);
param.put("out_biz_no", outBizNo);
AlipayFundTransOrderQueryRequest request = new AlipayFundTransOrderQueryRequest();
request.setBizContent(FastJsonUtil.toJson(param));
return alipayClient.execute(request);
}
}

评论 ( 0 )

评论

mark 阿里支付的更多相关文章

  1. 阿里支付:User Notice: invalid [default store dir]: /tmp/

    主要是因为windows和linux文件系统不一致才导致此错误的.在linux系统上阿里提供的SDK没问题,但在windows上我们做测试或者开发的时候就会遇到这样的错误. 解决方法就是在alipay ...

  2. iOS开发总结--三方平台开发之微信支付

    1.前言 现在很多应用都有支付功能,支付也是开发中比较麻烦的一个部分.其实,最麻烦的部分是商户帐号的审核,如果没有商户帐号,就没有你要给钱的那个对公账户. 2.关于交易 在这个金融类项目的开发中,接触 ...

  3. iOS--->微信支付小结

    iOS--->微信支付小结 说起支付,除了支付宝支付之外,微信支付也是我们三方支付中最重要的方式之一,承接上面总结的支付宝,接下来把微信支付也总结了一下 ***那么首先还是由公司去创建并申请使用 ...

  4. iOS 微信支付总结

    1.支付流程 https://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=8_3 商户系统和微信支付系统主要交互说明: 步骤1:用户在商户APP中选择 ...

  5. iOS支付宝支付总结

    1.按照http://doc.open.alipay.com/doc2/detail?spm=0.0.0.0.SWdJgo&treeId=59&articleId=103676& ...

  6. iOS:集成支付宝支付

    一.介绍 支付宝的集成还是比较简单的,按照文档来一步步操作,基本上很顺利.不过,仍然有两个地方会是坑.这里我集成成功了,在此整理一下.说先说一下我遇到的坑如下: 第一个坑:下载的SDK文件AliPay ...

  7. iOS应用之微信支付集成-直接前端集成

    所有信息的生成都在前端完成,包括对订单进行sign签名以及MD5签名加密(此方法相对来说有些复杂,没有官方给的方法简单).注:官方给的是v3&v4支付流程,签名和加密都是在服务器端进行,由于没 ...

  8. iOS- 微信支付 (服务器调起支付 )以及回调不成功的原因 不看后悔

    写的不错,给留个言哈... 一. 支付准备工作 1. 微信相关准备工作 (1) 向微信官方开通支付功能. 这个不是前端的工作. (2) 导入官方下载的微信支付SDK包. 我用的是微信开放平台下载的SD ...

  9. 集成paypal支付

    https://developer.paypal.com cocoapods 管理 引入 pod 'PayPal-iOS-SDK' 1.在appdelegate #import <PayPalM ...

随机推荐

  1. 第三章XML简介

    概念:XML:提供数据交换.系统配置.内容管理等的功能,可跨平台.跨网络.跨程序的数据描述方式.XSL:依靠XPath定位,提供显示模板,且专门为了显示XML文件信息的语言.CSS(层叠样式表):在网 ...

  2. 008 Spark中standalone模式的HA(了解,知道怎么配置即可)

    standalone也存在单节点问题,这里主要是配置两个master. 1.官网 2.具体的配置 3.配置方式一(不是太理想) 这种知识基于未来可以重启,但是不能在宕机的时候提供服务. 方式一:Sin ...

  3. 洛谷 [P1024]一元三次方程求解【二分答案】

    题目链接:https://www.luogu.org/problemnew/show/P1024 题目描述 有形如:ax3+bx2+cx+d=0 这样的一个一元三次方程.给出该方程中各项的系数(a,b ...

  4. Angular 个人深究(五)【外部包引用 Leaflet 简单实用】

    Leaflet 使用 最近在Angular项目中,用到了地图,由于种种原因放弃了百度地图api使用,最后选择了leaflet,简单介绍一下. 介绍: Leaflet 是一个为移动设备设计的交互式地图的 ...

  5. Python常用模块--base64

    作用:对一些保密性不强的信息进行加密,变为人类不能直接理解的字符串,但是可以反向解密,是一种‘防君子,不防小人’的措施. 例如:在一些项目中,接口的报文是通过base64加密传输的,所以在进行接口自动 ...

  6. Java中递归和循环的优劣

    介绍: 你用你手中的钥匙打开一扇门,结果去发现前方还有一扇门,紧接着你又用钥匙打开了这扇门,然后你又看到一扇门......但是当你开到一扇门时,发现前方是一堵墙无路可走了,你选择原路返回--这就是递归 ...

  7. 模拟页面获取的php数据(三)

    <?php return array( "aData" => [//通勤方式 "trafficType" => [ 0 => [ &qu ...

  8. 安卓工作室 android studio 的 汉化 美化 定制 Android studio's Chinesization beautification customization

    安卓工作室 android studio 的 汉化 美化 定制 Android studio's Chinesization beautification customization 汉化包 百度云盘 ...

  9. 潭州课堂25班:Ph201805201 WEB 之 页面编写 第二课 (课堂笔记)

    index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset=&quo ...

  10. BZOJ4124 : [Baltic2015]Tug of war

    建立二分图,首先如果存在度数为$0$的点,那么显然无解. 如果存在度数为$1$的点,那么这个点的匹配方案固定,可以通过拓扑排序去掉所有这种点. 那么现在剩下的点度数都至少为$2$,因为左右点数相等,且 ...