-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcontent.json
1 lines (1 loc) · 21.3 KB
/
content.json
1
{"posts":[{"title":"【版本号比较工具-(算法版)】","text":"1.版本号工具之前在网上看到版本号工具,觉得还行,就一直用这个。代码如下,写的也挺简洁的 123456789101112131415161718192021public static int compareAppVersion(String version1, String version2) { if (version1 == null || version2 == null) { throw new RuntimeException("版本号不能为空"); } // 注意此处为正则匹配,不能用. String[] versionArray1 = version1.split("\\\\."); String[] versionArray2 = version2.split("\\\\."); int idx = 0; // 取数组最小长度值 int minLength = Math.min(versionArray1.length, versionArray2.length); int diff = 0; // 先比较长度,再比较字符 while (idx < minLength && (diff = versionArray1[idx].length() - versionArray2[idx].length()) == 0 && (diff = versionArray1[idx].compareTo(versionArray2[idx])) == 0) { ++idx; } // 如果已经分出大小,则直接返回,如果未分出大小,则再比较位数,有子版本的为大 diff = (diff != 0) ? diff : versionArray1.length - versionArray2.length; return diff;} 2.算法版本不过最近发现,leetcode大神们写的算法,比较版本号。真的是简洁优雅高效,而且逻辑上更加完善。这边版本号是(忽略任何前导零后的整数值 例如1.01和1.001是一样的)。而且没有对string进行转换成数组,整体运行时间和空间都更佳。真正的简洁、优雅高效 1234567891011121314151617181920212223public int compareVersion(String version1, String version2) { if (version1 == null || version2 == null) { throw new RuntimeException("版本号不能为空"); } int i = 0, j = 0; int n = version1.length(), m = version2.length(); while (i < n || j < m) { int a = 0; for (; i < n && version1.charAt(i) != '.'; i++) { a = a * 10 + version1.charAt(i) - '0'; } i++; int b = 0; for (; j < m && version2.charAt(j) != '.'; j++) { b = b * 10 + version2.charAt(j) - '0'; } j++; if (a != b) { return a > b ? 1 : -1; } } return 0;} 3.结论看了大神们写的代码,发现和人的差距太大了。算法真的太神奇了。","link":"/2023/11/14/%E3%80%90%E7%89%88%E6%9C%AC%E5%8F%B7%E6%AF%94%E8%BE%83%E5%B7%A5%E5%85%B7-%EF%BC%88%E7%AE%97%E6%B3%95%E7%89%88%EF%BC%89%E3%80%91/"},{"title":"统一支付接口,无限扩展不同支付服务工具包实现。【开箱即用】(可用于生产环境中)","text":"前言在实际生产环境中,我们经常面临多个支付厂商的选择和变更,这可能导致支付代码的不断修改和维护。为了更好地应对产品需求的变化,我们采用了策略模式,通过统一的支付接口将各个支付服务进行实现。这样一来,支付厂商的变化对业务代码没有任何影响,使得我们能够更灵活地应对支付服务的变化和新增,而无需频繁修改核心业务逻辑。这种设计不仅提高了代码的可维护性,还为未来的扩展和变更提供了便利。 实现方案支付 PayService(统一的支付Service方便将来扩展)12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061package com.github.ji.service;import com.github.ji.dto.PayDTO;import com.github.ji.dto.RefundDTO;import com.github.ji.enums.PayCode;import com.github.ji.vo.PayVO;/** * 支付 PayService(统一的支付Service方便将来扩展) * * @author jishenglong on 2023/3/27 10:02 **/public interface PayService { PayCode getPayCode(); /** * 支付 * * @param payDTO payDTO * @return com.github.ji.vo.PayVO * @author jisl on 2023/11/16 11:18 **/ PayVO pay(PayDTO payDTO); /** * 交易是否成功 * * @param merchantId 门店id * @param outTradeNo 交易订单号 * @param payAmount 支付金额 */ Boolean isPaySuccess(Integer merchantId, String outTradeNo, Integer payAmount); /** * 退款 * * @param refundDTO RefundDTO * @author jishenglong on 2023/4/19 15:23 **/ void refund(RefundDTO refundDTO); /** * 退款是否成功 * * @param outRefundNo 退款单号 * @param refundAmount 退款金额 */ Boolean isRefundSuccess(Integer merchantId, String outRefundNo, Integer refundAmount); /** * 关闭订单 * * @param outTradeNo 交易订单号 */ void payClose(Integer merchantId, String outTradeNo);} 微信支付抽象类引入抽象类是为了更好地封装微信支付的退款 SDK,尤其是在不同的微信支付方式下,调用方式保持一致。通过这一抽象,我们能够将共同的退款操作逻辑集中到一个代码库中,从而降低了具体实现类的重复程度。这种设计不仅提高了代码的重用性,也使得对微信支付退款的变更更为集中和方便。通过抽象类,我们有效地管理了微信支付的共享代码,简化了具体实现类的维护工作,确保了系统的一致性和可维护性。 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859package com.github.ji.service.impl;import com.github.ji.config.MerchantMatchWeChatConfig;import com.github.ji.config.WxPayProperties;import com.github.ji.dto.RefundDTO;import com.github.ji.service.PayService;import com.wechat.pay.java.service.refund.RefundService;import com.wechat.pay.java.service.refund.model.*;import lombok.extern.slf4j.Slf4j;import org.springframework.stereotype.Component;import javax.annotation.Resource;import java.util.Objects;/** * 微信支付抽象类(退款是相同的sdk放在这里) * * @author jishenglong on 2023/3/27 10:02 **/@Component@Slf4jpublic abstract class WeChatPayService implements PayService { @Resource private MerchantMatchWeChatConfig matchWeChatConfig; @Resource private WxPayProperties wxPayProperties; @Override public void refund(RefundDTO refundDTO) { final RefundService refundService = new RefundService.Builder().config(matchWeChatConfig.getCertificateConfig(refundDTO.getMerchantId())).build(); CreateRequest request = new CreateRequest(); request.setOutTradeNo(refundDTO.getOutTradeNo()); request.setOutRefundNo(refundDTO.getOutRefundNo()); request.setNotifyUrl(wxPayProperties.getDomainUrl() + "/payNotify/weChatPay/refundOrder/" + refundDTO.getMerchantId()); final AmountReq amountReq = new AmountReq(); amountReq.setRefund(Long.valueOf(refundDTO.getRefundAmount())); amountReq.setTotal(Long.valueOf(refundDTO.getAmount())); amountReq.setCurrency("CNY"); request.setAmount(amountReq); refundService.create(request); } @Override public Boolean isRefundSuccess(Integer merchantId, String outRefundNo, Integer refundAmount) { final RefundService refundService = new RefundService.Builder().config(matchWeChatConfig.getCertificateConfig(merchantId)).build(); QueryByOutRefundNoRequest request = new QueryByOutRefundNoRequest(); request.setOutRefundNo(outRefundNo); final Refund refund = refundService.queryByOutRefundNo(request); return Objects.equals(refund.getStatus(), Status.SUCCESS) && Objects.equals(refund.getAmount().getRefund().intValue(), refundAmount); }} 支付厂商实现123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106package com.github.ji.service.impl;import com.github.ji.config.MerchantMatchWeChatConfig;import com.github.ji.config.WxPayProperties;import com.github.ji.dto.PayDTO;import com.github.ji.enums.PayCode;import com.github.ji.service.IMerchantMatchService;import com.github.ji.vo.MerchantMatchVO;import com.github.ji.vo.PayVO;import com.wechat.pay.java.core.RSAAutoCertificateConfig;import com.wechat.pay.java.core.util.NonceUtil;import com.wechat.pay.java.service.payments.app.AppService;import com.wechat.pay.java.service.payments.app.model.Amount;import com.wechat.pay.java.service.payments.app.model.CloseOrderRequest;import com.wechat.pay.java.service.payments.app.model.PrepayRequest;import com.wechat.pay.java.service.payments.app.model.QueryOrderByOutTradeNoRequest;import com.wechat.pay.java.service.payments.model.Transaction;import lombok.extern.slf4j.Slf4j;import org.springframework.stereotype.Component;import javax.annotation.Resource;import java.time.Instant;import java.util.Objects;/** * 微信APP支付服务实现 * * @author jishenglong on 2023/3/27 10:02 **/@Component@Slf4jpublic class WeChatAppPayServiceImpl extends WeChatPayService { @Resource private MerchantMatchWeChatConfig matchWeChatConfig; @Resource private IMerchantMatchService merchantMatchService; @Resource private WxPayProperties wxPayProperties; @Override public PayCode getPayCode() { return PayCode.WX_APP; } @Override public PayVO pay(PayDTO payDTO) { final MerchantMatchVO merchantMatchVO = merchantMatchService.selectByMerchantId(payDTO.getMerchantId()); final RSAAutoCertificateConfig certificateConfig = matchWeChatConfig.getCertificateConfig(merchantMatchVO); PrepayRequest request = new PrepayRequest(); Amount amount = new Amount(); amount.setTotal(payDTO.getAmount()); request.setAmount(amount); request.setAppid(wxPayProperties.getAppid()); request.setMchid(merchantMatchVO.getMchId()); request.setDescription(payDTO.getDescription()); request.setNotifyUrl(wxPayProperties.getDomainUrl() + "/payNotify/weChatPay/notifyOrder/" + payDTO.getMerchantId()); request.setOutTradeNo(payDTO.getOutTradeNo()); String prepayId = getAppService(payDTO.getMerchantId()).prepay(request).getPrepayId(); long timestamp = Instant.now().getEpochSecond(); String nonceStr = NonceUtil.createNonce(32); String message = request.getAppid() + "\\n" + timestamp + "\\n" + nonceStr + "\\n" + prepayId + "\\n"; log.debug("Message for RequestPayment signatures is[{}]", message); String sign = certificateConfig.createSigner().sign(message).getSign(); PayVO response = new PayVO(); response.setAppid(request.getAppid()); response.setPartnerId(request.getMchid()); response.setPrepayId(prepayId); response.setPackageVal("Sign=WXPay"); response.setNonceStr(nonceStr); response.setTimestamp(String.valueOf(timestamp)); response.setPaySign(sign); return response; } private AppService getAppService(Integer merchantId) { return new AppService.Builder().config(matchWeChatConfig.getCertificateConfig(merchantId)).build(); } @Override public Boolean isPaySuccess(Integer merchantId, String outTradeNo, Integer payAmount) { final QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest(); request.setMchid(merchantMatchService.selectByMerchantId(merchantId).getMchId()); request.setOutTradeNo(outTradeNo); final Transaction transaction = getAppService(merchantId).queryOrderByOutTradeNo(request); return Objects.equals(transaction.getTradeState(), Transaction.TradeStateEnum.SUCCESS) && Objects.equals(transaction.getAmount().getTotal(), payAmount); } @Override public void payClose(Integer merchantId, String outTradeNo) { final CloseOrderRequest request = new CloseOrderRequest(); request.setMchid(merchantMatchService.selectByMerchantId(merchantId).getMchId()); request.setOutTradeNo(outTradeNo); getAppService(merchantId).closeOrder(request); }} PayContext通过创建 PayContext,我们成功将所有支付实现类汇聚到一个中心化的管理器中。这种设计使得支付方式的选择和切换变得更为简洁和方便。业务代码只需与 PayContext 进行交互,而无需直接与具体的支付实现类打交道。这不仅提高了代码的可读性,也使得系统更易于维护和扩展。通过 PayContext,我们实现了一种灵活且高度可扩展的支付体系,使得系统能够轻松适应支付方式的变化,同时确保业务逻辑的清晰和解耦 123456789101112131415161718192021222324252627282930313233343536373839404142434445package com.github.ji;import com.github.ji.service.PayService;import lombok.extern.slf4j.Slf4j;import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;import javax.annotation.Resource;import java.util.List;import java.util.Map;import java.util.function.Function;import java.util.stream.Collectors;/** * 支付方式的PayContext,自动加入到这个PayContext */@Slf4j@Componentpublic class PayContext { private Map<Integer, PayService> payServiceMap; // spring中,在使用@Autowired注解注入list集合的时候,并不会根据List类型去容器中查找,而是根据list集合的元素类型,从spring容器中找到所有的实现类,放在list集合中,然后注入到bean中 @Resource private List<PayService> payServiceList; @PostConstruct public void buildMap() {// 将来完善下,可以加入是否禁用等 payServiceMap = payServiceList.stream() .collect(Collectors.toMap(operationStrategy -> operationStrategy.getPayCode().getCode(), Function.identity())); } public PayService getPayService(Integer code) { if (!payServiceMap.containsKey(code)) { throw new RuntimeException("没有该支付方式"); } return payServiceMap.get(code); }} 支付调用通过使用 PayCode 来选择支付方式,我们实现了对不同支付厂商的调用,从而将支付的变化抽象到了一个统一的接口中。这种设计使得业务代码与支付实现之间完全解耦,业务逻辑无需关心具体的支付厂商,只需通过 PayCode 来选择支付方式即可。这不仅提高了代码的可扩展性,使得支付方式的变化对业务代码没有任何影响,而且使系统更容易适应未来的扩展和变更。这种解耦的设计方式为我们提供了更大的灵活性和可维护性,确保了系统的健壮性。 12345678910111213@ApiOperation("订单支付")@PostMapping("/orderPay")public CommonResult<PayVO> orderPay(@Validated @RequestBody OrderPayDTO orderDTO) { final PayService payService = payContext.getPayService(orderDTO.getPayCode()); Order order = new Order(); final PayDTO payDTO = PayDTO.builder() .description(order.getDescription()).outTradeNo(order.getOutTradeNo()) .openid(orderDTO.getOpenid()).amount(order.getAmount()) .payCode(orderDTO.getPayCode()).merchantId(order.getMerchantId()) .build(); final PayVO payVO = payService.pay(payDTO); return CommonResult.success(payVO);} GitHub源码源码获取:JCommon 总结通过策略的封装,我们成功将支付功能解耦,摆脱了硬编码的束缚。这意味着不再需要为产品的每一个支付需求而硬编码,从而大大提高了系统的灵活性。现在,我们可以通过简单地切换策略,适应产品支付方式的变化,而不必担心影响到业务代码。这种解耦的设计使得支付模块更为可维护和可扩展,为未来的需求变更提供了更为便捷的途径","link":"/2023/11/16/%E7%BB%9F%E4%B8%80%E6%94%AF%E4%BB%98%E6%8E%A5%E5%8F%A3%EF%BC%8C%E6%97%A0%E9%99%90%E6%89%A9%E5%B1%95%E4%B8%8D%E5%90%8C%E6%94%AF%E4%BB%98%E6%9C%8D%E5%8A%A1%E5%B7%A5%E5%85%B7%E5%8C%85%E5%AE%9E%E7%8E%B0%E3%80%82%E3%80%90%E5%BC%80%E7%AE%B1%E5%8D%B3%E7%94%A8%E3%80%91%EF%BC%88%E5%8F%AF%E7%94%A8%E4%BA%8E%E7%94%9F%E4%BA%A7%E7%8E%AF%E5%A2%83%E4%B8%AD%EF%BC%89/"},{"title":"Java手动实现一个双色球游戏,每天自己买个彩票玩","text":"前言之前购买彩票时很少中奖,实际上彩票的中奖概率非常低。在多次尝试后,只中过一次。考虑到中奖机会有限,我决定尝试实现一个彩票自动购买程序,类似于机选功能。这样,我可以更加灵活地进行尝试,而不受实际彩票中奖概率的限制。 代码实现123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146package demo.game;import cn.hutool.core.util.RandomUtil;import com.alibaba.fastjson.JSON;import java.util.*;/** * @author jisl on 2023/7/31 15:52 * 双色球彩票 */public class DoubleColorBallLottery { // 彩票期数 private int lotteryNumber = 0; // 开奖号码 private Set<Integer> winRedNum = new HashSet<>(); private int winBlueNum; // 用户购买的彩票 private final List<List<Integer>> userLottery = new ArrayList<>(); // 用户中奖的彩票 private final Map<Integer, List<String>> userPrizeLottery = new HashMap<>(); public int[] lotteryAmount = new int[]{0, 5000000, 1250000, 3000, 200, 10, 5}; private static final int MAX_PRIZE_LEVEL = 6; Random random = new Random(); /** * 彩票开奖 * * @author jisl on 2023/7/31 16:06 **/ public void lotteryDrawing() { winRedNum = buildRedNum(); winBlueNum = buildBlueNum(); System.out.printf(\"[第%s期双色球]开奖结果:红球:%s,蓝球:%s%n\", lotteryNumber, JSON.toJSONString(winRedNum), winBlueNum); } private int buildBlueNum() { return random.nextInt(16) + 1; } private HashSet<Integer> buildRedNum() { final HashSet<Integer> set = new HashSet<>(); while (set.size() < 6) { final int randomNum = random.nextInt(33) + 1; set.add(randomNum); } return set; } /** * 买彩票 * * @author jisl on 2023/7/31 16:07 **/ public void buy(int count) { lotteryNumber = RandomUtil.randomInt(999); for (int i = 0; i < count; i++) { final ArrayList<Integer> lottery = new ArrayList<>(buildRedNum()); lottery.add(buildBlueNum()); userLottery.add(lottery); } } public void redemption() {// 一到六等奖 for (int i = 1; i <= MAX_PRIZE_LEVEL; i++) { userPrizeLottery.put(i, new ArrayList<>()); } for (List<Integer> lottery : userLottery) { int readBallMatches = countMatchingNumbers(lottery); boolean blueBallMatch = lottery.get(6) == winBlueNum; int prizeItem = calculatePrize(readBallMatches, blueBallMatch); if (prizeItem != 0) {// 中奖了 final List<String> numbers = userPrizeLottery.get(prizeItem); numbers.add(JSON.toJSONString(lottery)); } } System.out.println(String.format(\"[第%s期双色球]中奖结果:\", lotteryNumber)); int userPrizeCount = 0; int userPrizeAmount = 0; for (int i = 1; i <= MAX_PRIZE_LEVEL; i++) { final int userPrizeItemCount = userPrizeLottery.get(i).size(); userPrizeCount += userPrizeItemCount; userPrizeAmount += lotteryAmount[i] * userPrizeItemCount; if (i <= 3) { System.out.println(String.format(\"%s等奖中奖个数:%s,中奖号码:%s\", i, userPrizeItemCount, userPrizeLottery.get(i))); } else { System.out.println(String.format(\"%s等奖中奖个数:%s,中奖号码:-\", i, userPrizeItemCount)); } }// 共计中奖率:6.71% System.out.println(String.format(\"一共买了[%s张]彩票,中奖个数=%s,中奖概率=%s;购买彩票费用=%s,奖金=%s,盈利=%s\" , userLottery.size(), userPrizeCount, 1.0 * userPrizeCount / userLottery.size(), userLottery.size() * 2, userPrizeAmount, userPrizeAmount - userLottery.size() * 2)); } private int calculatePrize(int readBallMatches, boolean blueBallMatch) { if (readBallMatches == 6 && blueBallMatch) { return 1; } else if (readBallMatches == 6) { return 2; } else if (readBallMatches == 5 && blueBallMatch) { return 3; } else if (readBallMatches == 5 || (readBallMatches == 4 && blueBallMatch)) { return 4; } else if (readBallMatches == 4 || (readBallMatches == 3 && blueBallMatch)) { return 5; } else if (blueBallMatch) { return 6; } return 0; } private int countMatchingNumbers(List<Integer> lottery) { int count = 0; for (int i = 0; i < 6; i++) { if (winRedNum.contains(lottery.get(i))) { count += 1; } } return count; } public void game(int n) { buy(n); lotteryDrawing(); redemption(); } public static void main(String[] args) { final DoubleColorBallLottery lottery = new DoubleColorBallLottery(); lottery.game(100000); }} 执行效果12345678910[第88期双色球]开奖结果:红球:[2,3,19,23,13,29],蓝球:15[第88期双色球]中奖结果:1等奖中奖个数:0,中奖号码:[]2等奖中奖个数:0,中奖号码:[]3等奖中奖个数:0,中奖号码:[]4等奖中奖个数:43,中奖号码:-5等奖中奖个数:752,中奖号码:-6等奖中奖个数:5957,中奖号码:-一共买了[100000张]彩票,中奖个数=6752,中奖概率=0.06752;购买彩票费用=200000,奖金=45905,盈利=-154095 概率计算一等奖(6+1)中奖概率为:红球33选6乘以蓝球16选1=== 二等奖(6+0)中奖概率为:红球33选6乘以蓝球16选15(红色都中了,蓝色没中是)= 三等奖(5+1)中奖概率为:红球33选5乘以蓝球16选1(红色剩下27个数字可以选没中,任意一个球)== 四等奖(5+0、4+1)中奖概率为:红球33选5乘以蓝球16选15+红球33选4乘以蓝球16选1=$27615+65/227*26/2$=7695/17721088 (4+1,从27个数可以随机选出两个数,选出的两个数和顺序无关) 五等奖(4+0、3+1)中奖概率为:红球33选4乘以蓝球16选15+红球33选3乘以蓝球16选1=2726/265/215+654/62726*25/6=137475/17721088 六等奖(2+1、1+1、0+1)中奖概率为:红球33选0乘以蓝球16选1= 6543/(432)27262524/(432)+65432/(5432)2726252423/(5432)+654321/(65432)272625242322/(65432)=1043640/17721088 买了所有的彩票17721088组合,花费177210882=35442176=3500万,一共中奖金额=10436405+13747510+7695200+1623000+155106+5*106=88617950。共计中奖率:6.71%","link":"/2023/11/17/Java%E6%89%8B%E5%8A%A8%E5%AE%9E%E7%8E%B0%E4%B8%80%E4%B8%AA%E5%8F%8C%E8%89%B2%E7%90%83%E6%B8%B8%E6%88%8F%EF%BC%8C%E6%AF%8F%E5%A4%A9%E8%87%AA%E5%B7%B1%E4%B9%B0%E4%B8%AA%E5%BD%A9%E7%A5%A8%E7%8E%A9/"}],"tags":[{"name":"算法","slug":"算法","link":"/tags/%E7%AE%97%E6%B3%95/"},{"name":"Java","slug":"Java","link":"/tags/Java/"}],"categories":[{"name":"算法","slug":"算法","link":"/categories/%E7%AE%97%E6%B3%95/"},{"name":"Java","slug":"Java","link":"/categories/Java/"}],"pages":[]}