Java生鲜电商平台-提现模块的设计与架构
Java生鲜电商平台-提现模块的设计与架构
补充说明:生鲜电商平台-提现模块的设计与架构,提现功能指的卖家把在平台挣的钱提现到自己的支付宝或者银行卡的一个过程。
功能相对而言不算复杂,有以下几个功能需要处理。
业务逻辑如下;
1. 卖家登陆自己的B2B系统提交提现功能。
2. 如果没有绑定银行卡或者支付宝,则需要先绑定银行卡或者支付宝账户,以及填写提现密码
3. 支付宝或者银行卡需要跟用户的姓名所填一致,防止错误转账。
4. 后端需要记录所提现的记录,实际情况是支付宝提现需要收取手续费,这个也需要记录在内。
5. 需要形成一个审核机制,用户提现的状态有申请提现,审核成功,提现成功,提现失败四种可能状态。
6,每个提现的过程需要记录时间轴,如果有拒绝,用户需要查看拒绝的原因。
7.所有的提现到账后,需要平台短信通知用户申请了提现,提现成功,包括提现拒绝等等,都需要短信通知,给用户一个信任感。
8,每天晚上5:30之前提现当日到达,之后的次日早上10点钟到达。
9,系统自动审核提现的金额数据量的正确与否,来源于用户的订单以及账单数据。
相关的系统设计表如下:
1.提现信息表,为了便于大家理解,我详细的注释都写上了。
CREATE TABLE `withdrawal` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '自动增加ID',
`uid` bigint(20) NOT NULL COMMENT '提现申请人',
`withdraw_order` varchar(64) NOT NULL COMMENT '提现订单号,系统自动生成的.',
`withdraw_bank_id` bigint(20) NOT NULL COMMENT '用户对应的卡的编号',
`withdraw_charge` decimal(12,2) NOT NULL COMMENT '提现手续费',
`withdraw_reality_total` decimal(12,2) NOT NULL COMMENT '实际提现金额',
`withdraw_apply_total` decimal(12,2) NOT NULL COMMENT '申请提现的金额',
`withdraw_apply_time` datetime NOT NULL COMMENT '申请提现时间',
`status` int(11) NOT NULL COMMENT '提现状态,1表示申请提现,2表示审批通过,3,交易完成,-1审批不通过.',
`create_by` bigint(20) DEFAULT NULL COMMENT '创建人',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_by` bigint(20) DEFAULT NULL COMMENT '修改人',
`last_update_time` datetime DEFAULT NULL COMMENT '最后修改时间',
PRIMARY KEY (`id`),
KEY `unique_order` (`withdraw_order`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 COMMENT='提现信息表';
2. 卖家绑定卡的记录表
CREATE TABLE `withdrawal_bank` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '自动增加ID',
`uid` bigint(20) NOT NULL COMMENT '用户ID',
`cnname` varchar(8) NOT NULL COMMENT '中文姓名',
`bank_code` varchar(32) NOT NULL COMMENT '卡的缩写,例如:ICBC',
`bank_name` varchar(32) NOT NULL COMMENT '卡的名字',
`bank_number` varchar(64) NOT NULL COMMENT '卡号',
`sequence` tinyint(11) DEFAULT NULL COMMENT '排序用。按照小到大排序。',
`create_time` datetime NOT NULL COMMENT '创建时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8 COMMENT='用户绑定的银行';
补充说明:如果是支付宝,那么bank_code填写alipay,bank_name为支付宝,bank_number为支付宝卡号,cnname为提现的姓名
3. 卖家提现日志表。(会根据卖家的提现时间,形成时间轴)
CREATE TABLE `withdrawal_logs` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '自动增加ID',
`uid` bigint(20) NOT NULL COMMENT '提现申请人',
`withdraw_order` varchar(64) NOT NULL COMMENT '提现订单号,系统自动生成的.',
`remark` varchar(64) NOT NULL COMMENT '备注',
`status` int(11) DEFAULT NULL COMMENT '提现的状态',
`create_by` bigint(20) DEFAULT NULL COMMENT '创建人',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`),
KEY `unique_order` (`withdraw_order`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8 COMMENT='用户提现日志表';
补充说明:形成时间轴来显示。
4. 卖家提现密码:
CREATE TABLE `withdrawal_password` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '自动增加ID',
`uid` bigint(20) NOT NULL COMMENT '用户ID',
`password` varchar(32) NOT NULL COMMENT '密码,md5加密',
`create_time` datetime NOT NULL COMMENT '记录创建时间',
`last_update_time` datetime DEFAULT NULL COMMENT '最后一次更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `unique_key_uid` (`uid`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8 COMMENT='用户提现密码';
补充说明:由于设计到资金安全问题,提现需要设置提现密码,这个有别于用户的登陆密码。
整个业务比较简单,只是步骤比较多而已。
相关的业务核心代码如下:
2.1 卖家绑定自己的银行卡或者支付宝
/**
* 添加用户银行信息
* @param request
* @param response
* @return
*/
@RequestMapping(value = "/withdrawalBank/add", method = { RequestMethod.GET, RequestMethod.POST })
public JsonResult addWithdrawalBank(HttpServletRequest request, HttpServletResponse response,@RequestBody WithdrawalBank withdrawalBank) { try
{
if(withdrawalBank==null)
{
return new JsonResult(JsonResultCode.FAILURE, "传入对象有误", "");
} Long uid = withdrawalBank.getUid();
String bankCode = withdrawalBank.getBankCode();
if(uid == null)
{
return new JsonResult(JsonResultCode.FAILURE, "参数有误", "");
} //拿到当前银行卡的唯一编号
WithdrawalBank dbWithdrawalBank = withdrawalBankService.getWithdrawalBankByUidAndBankCode(uid, bankCode); if(dbWithdrawalBank != null){
return new JsonResult(JsonResultCode.FAILURE, "卡已存在,请重试",dbWithdrawalBank);
} int result = withdrawalBankService.insertWithdrawalBank(withdrawalBank); if (result>0)
{
return new JsonResult(JsonResultCode.SUCCESS, "添加用户银行成功", result);
}
return new JsonResult(JsonResultCode.FAILURE, "添加用户银行失败", "");
}catch(Exception e){ logger.error("[WithdrawalBankController][addWithdrawalBank] exception :",e);
return new JsonResult(JsonResultCode.FAILURE, "系统错误,请稍后重试","");
}
}
2.2 修改与管理自己的提现密码:
/**
* 提现金额计算
*/
@RequestMapping(value = "/withdrawal/count", method = { RequestMethod.GET, RequestMethod.POST })
public JsonResult countWithdraw(HttpServletRequest request, HttpServletResponse response,
BigDecimal withdrawApplyTotal, Long userId) {
logger.info("WithdrawalController.countWithdraw.start");
try {
Money m = new Money(withdrawApplyTotal);
Map<String, BigDecimal> result = new HashMap<String, BigDecimal>();
BigDecimal withdrawCharge = null;
BigDecimal withdrawRealityTotal = null;
if (m.compareTo(new Money(1500)) < 0) {
// 提现手续费
withdrawCharge = (new BigDecimal(2).add(withdrawApplyTotal.multiply(new BigDecimal(0.0055))))
.setScale(2, BigDecimal.ROUND_HALF_UP);
// 实际提现金额
withdrawRealityTotal = withdrawApplyTotal.subtract(withdrawCharge);
} else {
// 提现手续费
withdrawCharge = withdrawApplyTotal.multiply(new BigDecimal(0.007)).setScale(2,
BigDecimal.ROUND_HALF_UP);
// 实际提现金额
withdrawRealityTotal = withdrawApplyTotal.subtract(withdrawCharge);
} result.put("withdrawCharge", withdrawCharge);
result.put("withdrawRealityTotal", withdrawRealityTotal);
result.put("withdrawApplyTotal", withdrawApplyTotal);
return new JsonResult(JsonResultCode.SUCCESS, "计算成功", result);
} catch (Exception ex) {
logger.error("[WithdrawalController][countWithdraw]exception ", ex);
return new JsonResult(JsonResultCode.FAILURE, "系统异常,请稍后再试", "");
}
} /**
* 提现申请
*/
@RequestMapping(value = "/withdrawal/apply", method = { RequestMethod.GET, RequestMethod.POST })
public JsonResult applyWithDrawal(HttpServletRequest request, HttpServletResponse response,
@RequestBody Withdrawal withdrawal) {
logger.info("WithdrawalController.applyWithDrawal.start");
try
{
Long userId = withdrawal.getUid(); if (userId==null)
{
return new JsonResult(JsonResultCode.FAILURE, "参数异常!", "");
}
// 查询提现表中是否存在当前用户正在审核的提现,如果存在就不允许继续申请
Withdrawal withdrawalByUserId = withdrawalService.getWithdrawalByUserId(userId); if (withdrawalByUserId != null)
{
return new JsonResult(JsonResultCode.FAILURE, "已有提现记录,正在审核中!", withdrawalByUserId);
} //所属卖家
Seller seller = sellerService.getSellerById(userId); // 卖家可提现金额
BigDecimal billMoney = seller.getBillMoney(); logger.info("[WithdrawalController][applyWithDrawal]当前用户userId:" + userId + " 可提现金额:" + billMoney); // 卖家申请提现金额;
BigDecimal withdrawApplyTotal = withdrawal.getWithdrawApplyTotal(); logger.info("[WithdrawalController][applyWithDrawal]当前用户userId:" + userId + " 申请的提现金额:" + withdrawApplyTotal); // 如果申请的金额大于系统的金额,则返回1,同时不符合逻辑,直接返回error
if (withdrawApplyTotal.compareTo(billMoney) == 1) {
return new JsonResult(JsonResultCode.FAILURE, "申请金额错误,返回重试!", "");
} String orderNumber = OrderIDGenerator.getOrderNumber();
withdrawal.setWithdrawApplyTime(new Date());
withdrawal.setCreateTime(new Date());
withdrawal.setWithdrawOrder(orderNumber);
// 申请提现
withdrawal.setStatus(WithdrawalConstant.APPLY_WITHDRAWAL); WithdrawalLogs withdrawalLogs = new WithdrawalLogs();
withdrawalLogs.setWithdrawOrder(orderNumber);
withdrawalLogs.setCreateTime(new Date());
withdrawalLogs.setStatus(WithdrawalConstant.APPLY_WITHDRAWAL);
withdrawalLogs.setCreateBy(userId);
withdrawalLogs.setUid(userId);
withdrawalLogs.setRemark("提现已提交,审核中!");
withdrawalService.applyWithdrawal(withdrawal, withdrawalLogs); sendSmsNotice(withdrawal, userId, seller, billMoney, withdrawApplyTotal); return new JsonResult(JsonResultCode.SUCCESS, "申请提现成功", "");
} catch (Exception ex) {
logger.error("[WithdrawalController][applyWithDrawal]exception ", ex);
return new JsonResult(JsonResultCode.FAILURE, "申请失败,系统异常,请稍后再试", "");
}
} /**
* 发送短信通知给卖家
* @param withdrawal
* @param userId
* @param seller
* @param billMoney
* @param withdrawApplyTotal
*/
private void sendSmsNotice(Withdrawal withdrawal, Long userId, Seller seller, BigDecimal billMoney,BigDecimal withdrawApplyTotal)
{
try
{
// 发送短信给卖家
SmsMessage smsMessage = new SmsMessage();
smsMessage.setAccount(seller.getSellerAccount());
smsMessage.setAmount(withdrawal.getWithdrawApplyTotal());
smsMessage.setName(seller.getRealName() == null ? "" : seller.getRealName());
SmsMessageService smsMessageService = new SmsMessageServiceImpl();
smsMessageService.applicationWithdrawal(smsMessage, environment); // 保存信息到短信日志中
Sms sms = new Sms();
String msg = "当前用户userId:" + userId + ",申请的提现金额:" + withdrawApplyTotal + ",可提现金额:" + billMoney;
sms.setPhone(seller.getSellerAccount());
sms.setMessage(msg);
sms.setRemark("提现申请");
sms.setCreateTime(new Date());
smsService.saveSms(sms);
}catch(Exception ex)
{
logger.error("[WithdrawalController][sendSmsNotice]exception",ex);
}
} /**
* 提现申请 列表
* @param withdrawal
* 传递查询条件
* @param request
* @param response
* @return
*/
@RequestMapping(value = "/withdrawal/applyList", method = { RequestMethod.GET, RequestMethod.POST })
public JsonResult applyWithDrawalList(HttpServletRequest request, HttpServletResponse response, Long userId,
int currentPageNum, int currentPageSize) {
logger.info("WithdrawalController.applyWithDrawalList.start");
try
{
if (null == userId) {
return new JsonResult(JsonResultCode.FAILURE, "参数有误,userId不能为空", "");
} PageUtil pageUtil = withdrawalService.getPageResult(userId, currentPageNum, currentPageSize);
return new JsonResult(JsonResultCode.SUCCESS, "查询成功", pageUtil);
} catch (Exception ex) {
logger.error("[WithdrawalController][applyWithDrawalList]exception ", ex);
return new JsonResult(JsonResultCode.FAILURE, "申请失败,系统异常,请稍后再试", "");
}
} /**
* 根据提现订单号获取订单的详细情况
*
* @param request
* @param response
* @return
*/
@RequestMapping(value = "/withdrawal/order", method = { RequestMethod.GET, RequestMethod.POST })
public JsonResult withdrawalOrder(HttpServletRequest request, HttpServletResponse response,
String withdrawalOrder) {
logger.info("WithdrawalController.withdrawalOrder.start");
try {
if (StringUtils.isBlank(withdrawalOrder)) {
return new JsonResult(JsonResultCode.FAILURE, "提现订单号有误,请重新输入", "");
}
WithdrawalQuery withdrawal = withdrawalService.getWithdrawalByWithdrawalOrder(withdrawalOrder); if (withdrawal == null) {
return new JsonResult(JsonResultCode.FAILURE, "提现订单号不存在,请重新填写", "");
}
return new JsonResult(JsonResultCode.SUCCESS, "查询成功", withdrawal);
} catch (Exception ex) {
logger.error("[WithdrawalController][applyWithDrawalList]exception ", ex);
return new JsonResult(JsonResultCode.FAILURE, "系统异常,请稍后再试", "");
}
}
3. 提现记录表核心代码
/**
* 卖家提现功能---提现银行设置
*/
@RestController
@RequestMapping("/seller")
public class WithdrawalBankController extends BaseController
{ private static final Logger logger = LoggerFactory.getLogger(WithdrawalBankController.class); @Autowired
private WithdrawalBankService withdrawalBankService; /**
* 根据用户Uid查询用户绑定的银行卡信息;
* @param request
* @param response
* @param withdrawal 条件查询
* @return
*/
@RequestMapping(value = "/withdrawalBank/list", method = { RequestMethod.GET, RequestMethod.POST })
public JsonResult withdrawalBankList(HttpServletRequest request, HttpServletResponse response,Long userId,Model model)
{ try
{
List<WithdrawalBank> withdrawalBankList = withdrawalBankService.getWithdrawalBankByUid(userId); if(CollectionUtils.isEmpty(withdrawalBankList))
{
return new JsonResult(JsonResultCode.SUCCESS, "用户未绑定银行卡", withdrawalBankList);
}
return new JsonResult(JsonResultCode.SUCCESS, "查询用户银行卡信息", withdrawalBankList);
}catch(Exception ex){
logger.error("[WithdrawalBankController][withdrawalBankList] exception :",ex);
return new JsonResult(JsonResultCode.FAILURE, "系统错误,请稍后重试","");
}
} /**
* 添加用户银行信息
* @param request
* @param response
* @return
*/
@RequestMapping(value = "/withdrawalBank/add", method = { RequestMethod.GET, RequestMethod.POST })
public JsonResult addWithdrawalBank(HttpServletRequest request, HttpServletResponse response,@RequestBody WithdrawalBank withdrawalBank) { try
{
if(withdrawalBank==null)
{
return new JsonResult(JsonResultCode.FAILURE, "传入对象有误", "");
} Long uid = withdrawalBank.getUid();
String bankCode = withdrawalBank.getBankCode();
if(uid == null)
{
return new JsonResult(JsonResultCode.FAILURE, "参数有误", "");
} //拿到当前银行卡的唯一编号
WithdrawalBank dbWithdrawalBank = withdrawalBankService.getWithdrawalBankByUidAndBankCode(uid, bankCode); if(dbWithdrawalBank != null){
return new JsonResult(JsonResultCode.FAILURE, "卡已存在,请重试",dbWithdrawalBank);
} int result = withdrawalBankService.insertWithdrawalBank(withdrawalBank); if (result>0)
{
return new JsonResult(JsonResultCode.SUCCESS, "添加用户银行成功", result);
}
return new JsonResult(JsonResultCode.FAILURE, "添加用户银行失败", "");
}catch(Exception e){ logger.error("[WithdrawalBankController][addWithdrawalBank] exception :",e);
return new JsonResult(JsonResultCode.FAILURE, "系统错误,请稍后重试","");
}
}
}
4. 卖家提现日志表:
/**
* 卖家提现功能---提现日志记录
*/
@RestController
@RequestMapping("/seller")
public class WithdrawalLogsController extends BaseController { private static final Logger logger = LoggerFactory.getLogger(WithdrawalLogsController.class); @Autowired
private WithdrawalLogsService withdrawalLogsService; /**
* 根据Uid和withdrawOrder查询单个提现详情
*
* @param userId
* @param withdrawOrder
* @return
*/
@RequestMapping(value = "/withdrawalLogs/getLogsByWithdrawOrder", method = { RequestMethod.GET,RequestMethod.POST })
public JsonResult getWithdrawalLogsByUidAndWithdrawOrder(HttpServletRequest request, HttpServletResponse response,
Long userId, String withdrawOrder) { try
{
if (StringUtils.isBlank(withdrawOrder)) {
return new JsonResult(JsonResultCode.FAILURE, "请求参数异常", "");
}
List<WithdrawalLogs> withdrawalLogs = withdrawalLogsService.getWithdrawalLogsByWithdrawOrder(withdrawOrder);
return new JsonResult(JsonResultCode.SUCCESS, "订单详情", withdrawalLogs);
} catch (Exception ex) {
logger.error("[WithdrawalLogsController][getWithdrawalLogsByUidAndWithdrawOrder]", ex);
return new JsonResult(JsonResultCode.FAILURE, "系统错误,请稍后重试", "");
}
}
}
相关的运营截图如下:
Java生鲜电商平台-提现模块的设计与架构的更多相关文章
- Java生鲜电商平台-供应链模块的设计与架构
Java生鲜电商平台-供应链模块的设计与架构 说明:Java开源生鲜电商平台中供应链模块属于卖家的行为,也就是卖家如何管理他们自己的供应商,包括结算方式,压款方式,结算周期等等,超出了我这个B2B平台 ...
- Java开源生鲜电商平台-账单模块的设计与架构(源码可下载)
Java开源生鲜电商平台-账单模块的设计与架构(源码可下载) 补充说明:Java开源生鲜电商平台-账单模块的设计与架构,即用户的账单形成过程. 由于系统存在一个押账功能的需求,(何为押账,就是形成公司 ...
- Java开源生鲜电商平台-支付模块的设计与架构(源码可下载)
Java开源生鲜电商平台-支付模块的设计与架构(源码可下载) 开源生鲜电商平台支付目前支持支付宝与微信.针对的是APP端(android or IOS) 1. 数据库表设计. 说明:无论是支付宝还 ...
- Java开源生鲜电商平台-购物车模块的设计与架构(源码可下载)
ava开源生鲜电商平台-购物车模块的设计与架构(源码可下载) 说明:任何一个电商无论是B2C还是B2B都有一个购物车模块,其中最重要的原因就是客户需要的东西放在一起,形成一个购物清单,确认是否有问题, ...
- Java开源生鲜电商平台-推荐系统模块的设计与架构(源码可下载)
Java开源生鲜电商平台-推荐系统模块的设计与架构(源码可下载) 业务需求: 对于一个B2B的生鲜电商平台,对于买家而言,他需要更加快速的购买到自己的产品,跟自己的餐饮店不相关的东西,他是不关心的,而 ...
- Java开源生鲜电商平台-搜索模块的设计与架构(源码可下载)
Java开源生鲜电商平台-搜索模块的设计与架构(源码可下载) 说明:搜索模块针对的是买家用户,在找菜品找的很费劲下的一种查询方面.目前也是快速的检索商品. 对于移动端的APP买家用户而言,要求的速度在 ...
- Java开源生鲜电商平台-售后模块的设计与架构(源码可下载)
Java开源生鲜电商平台-售后模块的设计与架构(源码可下载) 说明:任何一个的电商平台都有售后服务系统,那么对于我们这个生鲜的电商平台,售后系统需要思考以下几个维度. 1. 买家的需求维度 说明:买家 ...
- Java开源生鲜电商平台-异常模块的设计与架构(源码可下载)
Java开源生鲜电商平台-异常模块的设计与架构(源码可下载) 说明:任何一个软件系统都会出现各式各样的异常与错误,我们需要根据异常的情况进行捕获与分析,改善自己的代码,让其更加的稳定的,快速的运行,那 ...
- Java生鲜电商平台-系统异常状态的设计与架构(APP应用或者生鲜小程序)
Java生鲜电商平台-系统异常状态的设计与架构 说明:在实际开发Java生鲜电商平台的时候,异常状态的设计关系着整体系统的性能问题,架构设计,以及稳定性方面,对此,我根据实际的业务场景,进行了系统设计 ...
随机推荐
- Fiddler使用技巧:强大的数据文本编解码功能
- 总览 打开Fiddler后,使用快捷键Ctrl+e或者点击菜单Tools-->TextWizard...即可进入TextWizard界面. 界面上方为输入框,用来输入需要编码或解码的数据. ...
- 剑指offer 14:调整数组顺序使奇数位于偶数前边
题目描述 输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变. 解题思路 题目中要求 ...
- oracle通过impdp导入不同表用户、不同表空间的数据
原创 tegwy 发布于2013-04-21 17:34:39 阅读数 46592 收藏 展开 impdp smtj2012/密码@localhost/devdb DIRECTORY=dump_dir ...
- HOW TO: Setting up Encrypted Communications Channels in Oracle Databas
access_timeSeptember 22, 2015 person_outlineMartin Rakhmanov share In this article, I will explain h ...
- 渗透测试学习 二十、 其他漏洞汇总之PHP相关漏洞
大纲: PHP相关漏洞 JSP相关漏洞 其他漏洞汇总 PHP相关漏洞 文件包含漏洞 php://input等伪协议利用 代码执行漏洞 变量覆盖漏洞 文件包含漏洞 程序开发人员一般会把重复使用的函数写到 ...
- fiddler---Fiddler工具栏功能介绍
前几篇简单的介绍了Fiddler的功能,对于工具栏的操作只是简单了解,今天写一篇工具栏功能介绍和操作. 工具栏功能介绍 工具栏内容 工具栏依次是:会话保存,数据重放(Replay),转到(GO),数据 ...
- 最长上升子序列(LIS: Longest Increasing Subsequence)
示例: 输入: [10,9,2,5,3,7,101,18] 输出: 4 解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4. 从网上找的一段代码(我由java改为了C++版本),原作者 ...
- springmvc+strut2比较
常见web框架中Struts2和SpringMVC独占鳌头,SpringMVC和Struts有什么不同? 我们可以从各个方面进行对比: 一:框架的思想设计上 SpringMVC控制器是基于方法上拦截, ...
- 【洛谷P3329】 [ZJOI2011]最小割(最小割树)
洛谷 题意: 给出一个无向图,之后有\(q,q\leq 30\)组询问,每组询问有一个\(x\),回答有多少点对\((a,b)\)其\(a-b\)最小割不超过\(x\). 思路: 这个题做法要最小割树 ...
- 字典与json转化
json.dumps(字典) #转成json格式 json.loads(json格式) #转成字典格式