主頁 > 後端開發 > 暢購商城(十二):接入微信掃碼支付

暢購商城(十二):接入微信掃碼支付

2020-09-11 14:38:02 後端開發

好好學習,天天向上

本文已收錄至我的Github倉庫DayDayUP:github.com/RobodLee/DayDayUP,歡迎Star

  • 暢購商城(一):環境搭建
  • 暢購商城(二):分布式檔案系統FastDFS
  • 暢購商城(三):商品管理
  • 暢購商城(四):Lua、OpenResty、Canal實作廣告快取與同步
  • 暢購商城(五):Elasticsearch實作商品搜索
  • 暢購商城(六):商品搜索
  • 暢購商城(七):Thymeleaf實作靜態頁
  • 暢購商城(八):微服務網關和JWT令牌
  • 暢購商城(九):Spring Security Oauth2
  • 暢購商城(十):購物車
  • 暢購商城(十一):訂單
  • 暢購商城(十二):接入微信支付

支付流程

為了實作支付的功能,這里選擇接入微信支付,流程就是我們通過訂單系統下單,然后訂單系統呼叫支付系統去向微信支付的服務器發送請求,然后獲取二維碼回傳給用戶,然后訂單系統就開始監聽MQ,用戶掃碼支付后,支付系統將支付狀態存進MQ中,訂單系統檢測到用戶已經付錢了,就將訂單設為已支付,然后存進MySQL中,可能會因為網路問題導致訂單系統獲取不到支付狀態,所以訂單系統會定時向微信支付服務器發送請求去查詢訂單狀態,

微信支付簡介

要想接入微信支付,就得有認證過的服務號,這個我沒有,所以申請不了,就用黑馬提供的賬號吧,我試了一下,可以用,

appid(公眾賬號ID):wx8397f8696b538317
mch_id(商戶號):1473426802
key(商戶密鑰):T6m9iK73b0kn9g5v426MKfHQH7X8rKwb

微信支付的開發檔案:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_1

在這個官方的開發檔案里面介紹了微信支付相關的API并且提供了SDK,

這個SDK的內容不多,只有幾個類,我稍微介紹一下:

  • WXPayConfig:這是個抽象類,里面有幾個方法,是用來獲取核心引數的,比如公眾號id,商戶號,密鑰等,所以在使用的時候要先去實體化這個類將幾個重要引數配置進去,

  • WXPay:和訂單相關的方法都封裝在這個類里面,比如下單,查詢訂單,取消訂單等,里面有個方法fillRequestData(),每次執行下單等操作的時候都會去呼叫這個方法將WXPayConfig中的幾個核心引數封裝到請求引數的Map集合里,

  • WXPayRequest:這個是負責請求服務器的,WXPay也都是通過呼叫這個類中的方法去請求服務器的,執行相應方法的時候,會將WXPay傳過來的Map集合轉換成XML格式的字串,然后使用HttpClient向服務器發送請求,沒錯,微信支付是通過XML進行資料傳輸的,

  • WXPayUtil:這是個工具類,封裝了一些常用方法,比如Map轉XML,XML轉Map等,

在這個專案中用到的微信的Native支付,也就是掃碼支付,有兩種模式,我們用到的是模式二,

首先在暢購的訂單系統中生成訂單,然后將一些必要的引數傳入到微信支付的后臺,然后就會產生一個預支付的訂單,將支付鏈接回傳給我們,我們再根據支付鏈接生成二維碼傳給用戶,用戶掃碼支付后再將支付結果傳到我們的后臺,這樣整個支付的流程就結束了,

準備作業

介紹完了微信支付后,就來說一下專案中該怎么去集成微信支付,視頻中用的是第三方的依賴,我用的是官方的,微信支付的SDK在Maven的遠程倉庫里是沒有的,所以需要自己下載然后手動匯入,這里面有兩個坑有必要說一下,前面不是提到WXPayConfig是個抽象類么,那么用的時候肯定得去繼承才能實體化吧,但是里面的抽象方法都沒有權限修飾符,所以默認是包訪問權限,我們既然是Maven依賴這個SDK,那么我們寫的代碼自然不會和它在同一個包下,所以要先在這幾個抽象方法前面添加public修飾符,而且,微信提供的sdk檔案里還寫成了implements抽象類,真搞不懂微信怎么會犯這種錯~~~

現在就可以將這個SDK添加到我們本地的Maven倉庫里了,在解壓后的sdk的根目錄下執行mvn install命令,

當出現BUILD SUCCESS的字樣的時候,就說明已經成功添加到本地的Maven倉庫了,這時候第二個坑就來了,如果就這么添加到我們的專案中就有可能會出現Maven依賴沖突:

可以看到,出現沖突的包是slf4j-simple,而微信支付sdk恰好依賴了這個包,所以在匯入微信支付的時候把這個依賴排除掉即可,

<dependency>
    <groupId>com.github.wxpay</groupId>
    <artifactId>wxpay-sdk</artifactId>
    <version>3.0.9</version>
    <exclusions>
        <exclusion>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
        </exclusion>
    </exclusions>
</dependency>

然后就可以創建支付的工程了,在changgou-service下創建一個Moudle名為changgou-service-wechatpay,然后將微信支付的依賴添加到這個工程下,啟動類沒啥好說的,組態檔如下:

server:
  port: 18090
spring:
  application:
    name: wechatpay
  main:
    allow-bean-definition-overriding: true
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:7001/eureka
  instance:
    prefer-ip-address: true
feign:
  hystrix:
    enabled: true
#hystrix 配置
hystrix:
  command:
    default:
      execution:
        timeout:
          #如果enabled設定為false,則請求超時交給ribbon控制
          enabled: true
        isolation:
          strategy: SEMAPHORE

#微信支付資訊配置
wechat:
  # 應用id
  app_id: wx8397f8696b538317
  # 商戶號id
  mch_id: 1473426802
  # 密鑰
  key: T6m9iK73b0kn9g5v426MKfHQH7X8rKwb
  # 支付回呼地址
  notify_url: http://www.itcast.cn

這里配置了appid,mch_id,key,notify_url這幾個引數,用的時候直接讀取就可以了,這樣支付微服務就搭建好了,

微信支付二維碼生成

添加一個Controller層的類WeChatPayController,然后再創建一個入口方法,因為二維碼支付叫做Native支付,所以這里就起名為createNative了,接收一個Order引數,視頻中用的是Map接收引數,但是既然是創建訂單,直接用Order作為引數可讀性不是好很多嗎,

//創建二維碼
@RequestMapping(value = https://www.cnblogs.com/robod/p/"/create/native")
public Result createNative(@RequestBody Order order){
    Map resultMap = weChatPayService.createNative(order);
    return new Result(true, StatusCode.OK,"創建二維碼預付訂單成功!",resultMap);
}

寫完了Controller就可以寫Service層了,視頻中是用HttpClient呼叫微信支付的遠程介面,既然微信已經提供了sdk,為什么還要重復造輪子呢?所以我就沒和視頻中寫的一樣,而是直接使用微信的sdk,雖然底層用的也是HttpClient,

首先需要將WXPayConfig給實作一下,

public class MyWXPayConfig extends WXPayConfig {

    private String appId;

    private String mchId;

    private String key;

    public MyWXPayConfig(String appId, String mchId, String key) {
        this.appId = appId;
        this.mchId = mchId;
        this.key = key;
    }

    @Override
    public String getAppID() {
        return appId;
    }

    @Override
    public String getMchID() {
        return mchId;
    }

    @Override
    public String getKey() {
        return key;
    }

    @Override
    public InputStream getCertStream() {
        return null;
    }

    @Override
    public int getHttpConnectTimeoutMs() {
        return 8000;
    }

    @Override
    public int getHttpReadTimeoutMs() {
        return 10000;
    }

    @Override
    public IWXPayDomain getWXPayDomain() {
        IWXPayDomain iwxPayDomain = new IWXPayDomain() {
            @Override
            public void report(String domain, long elapsedTimeMillis, Exception ex) {
            }
            @Override
            public DomainInfo getDomain(WXPayConfig config) {
                return new IWXPayDomain.DomainInfo(WXPayConstants.DOMAIN_API, true);
            }
        };
        return iwxPayDomain;
    }

}

然后在WeChatPayServiceImpl撰寫相應的代碼,

@Value("${wechat.appid}")
private String appId;

@Value("${wechat.mch_id}")
private String mcnId;

@Value("${wechat.key}")
private String key;

@Value("${wechat.notify_url}")
private String notifyUrl;

@Override
public Map<String, String> createNative(Order order) {
    try {
        Map<String, String> map = new HashMap<>(16);
        map.put("body", "騰訊充值中心-QQ會員充值");    //商品描述
        map.put("out_trade_no", order.getId());      //商戶訂單號
        map.put("total_fee", String.valueOf((int)(order.getTotalMoney() * 100))); //標價金額,單位為分
        map.put("spbill_create_ip", "127.0.0.1");    //終端IP
        map.put("trade_type", "NATIVE ");    //交易型別,JSAPI -JSAPI支付,NATIVE -Native支付,APP -APP支付

        Map<String, String> response = wxpay.unifiedOrder(map);
        if (response == null || response.size() == 0) {
        	throw new RuntimeException("下單失敗");
        }
        return response;
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

這里用到了**@Value注解將組態檔中的幾個引數注入了進來,然后將創建訂單的幾個引數添加到Map集合中,鍵的名稱在檔案中有說明,不能改變,金額的單位是分,我們傳過來的是元,所以需要乘100,微信要求金額是整數,所以再強轉成int,然后就去創建MyWXPayConfig的實體,將三個引數傳入進入,然后去創建WXPay的物件,將config和notifyUrl傳入進去,然后呼叫unifiedOrder()**方法把map傳入進去就可以創建訂單了,其實內部就是把appid這幾個引數放在了我們傳入的map中,所以在這里放入到map中也是OK的,

測驗一下,OK了,成功創建了訂單,也拿到了二維碼的地址,接下來只要把code_url轉換成二維碼圖片就可以了,

如果是做測驗,隨便在網上找個二維碼生成器,然后把code_url復制過去就行了,在專案中是用qrious生成,

<html>
<head>
<title>二維碼入門小demo</title>
<!--1.引入js  2. 創建一個img標簽 用來存盤顯示二維碼的圖片 3.創建js物件 4.設定js物件的配置項-->
<script src=https://www.cnblogs.com/robod/p/"qrious.js"> </script>	

<body>


<script>
   var qrious = new QRious({
   		 element:document.getElementById("myqrious"),// 指定的是圖片所在的DOM物件
   		 size:250,//指定圖片的像素大小
		 level:'H',//指定二維碼的容錯級別(H:可以恢復30%的資料)
		 value:'weixin://wxpay/bizpayurl?pr=xKlU7lD'//指定二維碼圖片代表的真正的值
   })
</script>
</html>

查詢訂單狀態

有時候可能因為網路原因導致支付狀態沒有及時回傳到我們的服務器中,這個時候就要手動地去查詢了,代碼很簡單,我就不多說了,

//WeChatPayController
@GetMapping(value = https://www.cnblogs.com/robod/p/"/status/query")
public Result> queryPayStatus(@RequestParam String outTradeNo) {
    Map resultMap = weChatPayService.queryPayStatus(outTradeNo);
    return new Result<>(true, StatusCode.OK,"訂單查詢成功",resultMap);
}
--------------------------------------------------------------------------------------
//WeChatPayServiceImpl
@Override
public Map queryPayStatus(String outTradeNo) {
    try {
        Map map = new HashMap<>();
        map.put("out_trade_no", outTradeNo);      //商戶訂單號
        MyWXPayConfig config = new MyWXPayConfig(appId,mcnId, key);
        WXPay wxpay = new WXPay(config,notifyUrl);
        Map response = wxpay.orderQuery(map);
        if (response == null || response.size() == 0) {
            throw new RuntimeException("訂單查詢失敗");
        }
        return response;
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

測驗一下:

支付結果回呼通知及RabbitMQ監聽

要實作支付結果回呼通知,首先得告訴微信支付的服務器我們的支付結果回呼通知的地址,因為我們沒有公網IP,所以微信服務器沒辦法主動發訊息到我們的服務器,所以可以使用內網穿透,我選擇的是uTools的一個內網穿透插件,不用注冊賬號就可以直接使用,很方便,

然后將內網穿透的地址配置到之前配置notify_url的地方就可以了,

# 支付回呼地址
notify_url: http://robod123.cn1.utools.club/wechat/pay/notify/url

接下來就該配置RabbitMQ了,首先得在虛擬機中安裝RabbitMQ

docker pull docker.io/rabbitmq:3.7-management			# 下載rabbitmq的鏡像
docker run --name rabbitmq -d -p 15672:15672 -p 5672:5672 4b23cfb64730	# 安裝rabbitmq,最后面的id是docker images查出來的
docker exec -it rabbitmq /bin/bash						# 進入到rabbitmq中
rabbitmqctl add_user root 123456						# 添加一個名為root的用戶,密碼為123456
rabbitmqctl set_permissions -p / root ".*" ".*" ".*"	# 賦予root用戶所有權限
rabbitmqctl set_user_tags root administrator			# 賦予root用戶administrator角色

因為支付微服務和訂單微服務都用到了RabbitMQ,所以在這兩個微服務中添加mq的依賴

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

然后在這兩個微服務中添加rabbitmq的配置資訊:

spring:			# 配置rabbitmq的ip和埠
  rabbitmq:
    host: 192.168.31.200
    port: 5672
    username: root
    password: 123456

接著就去添加exchange和queue了,視頻中是用程式自動創建的,但是我遇到了一個問題,就是訂單微服務啟動的時候報了一個錯,導致服務啟動不了:

org.springframework.amqp.rabbit.listener.QueuesNotAvailableException: Cannot prepare queue for listener. Either the queue doesn't exist or the broker will not allow us to use it.

所以我就在網頁端手動創建exchange和queue了,并指定routing key將exchange和queue系結起來,

配置的部分到這里就已經完成了,接下來就可以去寫代碼了,為了方便理解,我畫了一張流程圖:

代碼我就不去解釋了,直接貼了,對照著這張流程圖應該就能看懂了,視頻中提到的兩個小作業我也寫了,

WeChatPayController

/**
 * 支付結果回呼通知
 * @param request
 * @return
 * @throws Exception
 */
@RequestMapping("/notify/url")
public String notifyUrl(HttpServletRequest request) throws Exception {
    ServletInputStream inputStream = request.getInputStream();
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    byte[] buffer = new byte[1024];
    int len = 0;
    while ((len = inputStream.read(buffer))!=-1) {
        outputStream.write(buffer,0,len);
    }
    String xmlString = outputStream.toString("UTF-8");

    //將java物件轉換成amqp訊息發送出去,呼叫的是send方法
    rabbitTemplate.convertAndSend("exchange.order","routing.order", xmlString);

    return  "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
}

OrderMessageListener

@Component
@RabbitListener(queues = {"queue.order"})
public class OrderMessageListener {

    private final static String SUCCESS = "SUCCESS";

    @Autowired
    private OrderService orderService;

    @Autowired
    private WeChatPayFeign weChatPayFeign;

    /**
     * 監聽mq的訊息
     * @param message xml格式的訊息
     */
    @RabbitHandler
    public void getMessage(String message) throws Exception {
        Map<String, String> map = WXPayUtil.xmlToMap(message);
        String returnCode = map.getOrDefault("return_code","");     //回傳狀態碼
        String resultCode = map.getOrDefault("result_code","");     //業務結果
        if (SUCCESS.equals(returnCode)) {   //支付成功,修改訂單狀態
            String outTradeNo = map.get("out_trade_no");    //商戶訂單號
            if (! SUCCESS.equals(resultCode)) { //交易失敗,關閉訂單,從資料庫中將訂單狀態修改為支付失敗,回滾庫存
                Map<String, String> closeResult = weChatPayFeign.closeOrder(outTradeNo).getData();  //關閉訂單時服務器回傳的資料
                //如果錯誤代碼為ORDERPAID則說明訂單已經支付,當作正常訂單處理,反之 回滾庫存
                if (!("FAIL".equals(closeResult.get("result_code")) && "ORDERPAID".equals(closeResult.get("err_code")))) {
                    orderService.deleteOrder(outTradeNo);
                    return;
                }
            }
            String transactionId = map.get("transaction_id");   //微信支付訂單號
            String timeEnd = map.get("time_end");               //支付完成時間
            orderService.updateStatus(outTradeNo,timeEnd,transactionId);
        }
    }

}

OrderServiceImpl

@Override
public void updateStatus(String outTradeNo,String timeEnd,String transactionId) {
    Order order = orderMapper.findById(outTradeNo);
    LocalDateTime payTime = LocalDateTime.parse(timeEnd, DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));

    order.setPayStatus("1");                //支付狀態修改為1表示已支付
    order.setTransactionId(transactionId);  //交易流水號
    order.setPayTime(payTime);              //交易時間

    orderMapper.updateByPrimaryKey(order);
}

@Override
public void deleteOrder(String outTradeNo) {
    Order order = orderMapper.findById(outTradeNo);
    LocalDateTime time = LocalDateTime.now();
    //修改狀態
    order.setPayStatus("2");    //交易失敗
    order.setUpdateTime(time);
    //提交到資料庫
    orderMapper.updateByPrimaryKey(order);
    //回滾庫存
    List<OrderItem> orderItems = orderItemMapper.findByOrderId(order.getId());
    List<Long> skuIds = new ArrayList<>();
    for (OrderItem orderItem : orderItems) {
        skuIds.add(orderItem.getSkuId());
    }
    List<Sku> skuList = skuFeign.findBySkuIds(order.getSkuIds()).getData(); //資料庫中對應的sku集合
    Map<Long, Sku> skuMap = skuList.stream().collect(Collectors.toMap(Sku::getId, a -> a));
    for (OrderItem orderItem : orderItems) {
        Sku sku = skuMap.get(orderItem.getSkuId());
        sku.setNum(sku.getNum()+orderItem.getNum());    //加庫存
    }
    skuFeign.updateMap(skuMap);
}

定時處理訂單狀態

當創建一個訂單后,可能用戶并不會去支付,或者是因為網路的原因導致我們獲取不到用戶支付的結果,所以可以采用定時處理的方式,具體的操作就是下單成功后就往mq的佇列1中發送一條訊息,設定30分鐘過期,過期后將訊息發送給佇列2,然后我們監聽佇列2,這樣一旦監聽到了佇列2的訊息,則說明離下單已經過去了30分鐘,這時候我們去查詢一下訂單狀態,如果是已支付就不去管它,要是未支付的話就通知微信服務器關閉訂單,然后從資料庫中洗掉訂單,為了方便理解,我畫了一張流程圖:

代碼挺簡單的,首先將添加一個配置類去把佇列創建出來,直接在web頁面上配置也是OK的,不過上一節是在web頁面配置的,這個就在程式中添加吧,

@Configuration
public class MqConfig {

    //佇列1,延時佇列,訊息過期后發送給佇列2
    @Bean
    public Queue orderDelayQueue() {
        return QueueBuilder
                .durable("orderDelayQueue")
                //orderDelayQueue佇列資訊會過期,過期之后,進入到死信佇列,死信佇列資料系結到其他交換機
                .withArgument("x-dead-letter-exchange","orderListenerExchange")
                .withArgument("x-dead-letter-routing-key","orderListenerRoutingKey") //系結指定的routing-key
                .build();
    }

    //佇列2
    @Bean(name = "orderListenerQueue")  //名稱不寫默認就是方法名
    public Queue orderListenerQueue() {
        return new Queue("orderListenerQueue",true);
    }

    //創建交換機
    @Bean
    public Exchange orderListenerExchange() {
        return new DirectExchange("orderListenerExchange");
    }

    //佇列queue2系結exchange
    @Bean
    public Binding orderListenerBinding(Queue orderListenerQueue,Exchange orderListenerExchange) {
        return BindingBuilder
                .bind(orderListenerQueue)
                .to(orderListenerExchange)
                .with("orderListenerRoutingKey")
                .noargs();
    }

}

在創建佇列1的時候,配置了將死信佇列的資料系結到佇列2的交換機上,這樣資料過期后就會被發送到佇列2中,

然后在創建訂單的時候將訂單號發送到佇列1中

//OrderServiceImpl
@Override
public synchronized void add(Order order) {
	order.setId(String.valueOf(idWorker.nextId()));
    …………
    rabbitTemplate.convertAndSend("orderDelayQueue", (Object)order.getId(), new MessagePostProcessor() {
        @Override
        public Message postProcessMessage(Message message) throws AmqpException {
            message.getMessageProperties().setExpiration("30*60*1000");   //定時30min后過時
            return message;
        }
    });
}

監聽佇列2然后執行相應的操作

@Component
@RabbitListener(queues = "orderListenerQueue")
public class DelayMessageQueueListener {

    @Autowired
    private OrderService orderService;

    @Autowired
    private WeChatPayFeign weChatPayFeign;

    @RabbitHandler
    public void getMessage(String orderId) throws Exception {
        Order order = orderService.findById(orderId);
        if ("0".equals(order.getPayStatus())) {
            //0表示未支付,通知微信服務器取消訂單,從資料庫中洗掉訂單,回滾庫存
            Map<String, String> closeResult = weChatPayFeign.closeOrder(orderId).getData();
            //如果錯誤代碼為ORDERPAID則說明訂單已經支付,當作正常訂單處理,反之 回滾庫存
            if (!("FAIL".equals(closeResult.get("result_code")) && "ORDERPAID".equals(closeResult.get("err_code")))) {
                orderService.deleteOrder(orderId);
            }
        }
    }

}

OK,這樣就可以定時去處理訂單資料了,

總結

這篇文章主要就是實作了微信掃碼支付,因為微信提供的sdk有點問題,還是花了一些時間的,不過最后還是完成了,然后又實作了支付結果回呼通知和rabbitmq監聽的功能,用到了內網穿透,最后使用兩個佇列來實作了定時處理訂單,還有一個問題就是我沒系統地學過RabbitMQ,所以對于很多操作都是一知半解的,都是參考著視頻中講的然后再自己摸索摸索web頁面的配置,后期我會系統地學習一下RabbitMQ然后再總結出一篇文章,

如果覺得我寫的還可以,請不要吝嗇你的

代碼:https://github.com/RobodLee/changgou

本文已收錄至我的Github倉庫DayDayUP:github.com/RobodLee/DayDayUP,歡迎Star

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/5950.html

標籤:Java

上一篇:[Go]GO語言實戰-開源WEB客服GO-FLY-gorm下分頁的實作

下一篇:為什么 Redis 要比 Memcached 更火?

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more