記錄--前端小票列印、網頁列印

来源:https://www.cnblogs.com/smileZAZ/archive/2023/05/26/17435607.html
-Advertisement-
Play Games

這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 一、小票列印 目前市面上的小票印表機大多採用的列印指令集為ESC/POS指令,它可以使用ASCII碼、十進位、十六進位來控制列印,我們可以使用它來控制字體大小、列印排版、字體加粗、下劃線、走紙、切紙、控制錢箱等,下麵以初始化印表機為例: ...


這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助

 一、小票列印

目前市面上的小票印表機大多採用的列印指令集為ESC/POS指令,它可以使用ASCII碼、十進位、十六進位來控制列印,我們可以使用它來控制字體大小、列印排版、字體加粗、下劃線、走紙、切紙、控制錢箱等,下麵以初始化印表機為例:

ASCII碼  ESC  @
十進位碼  27  64
十六進位  1B  40

小票列印紙的寬度一般可分58mm和80mm,這裡指的是列印紙的寬度,但是在實際列印的時候,有效列印區域並沒有這麼寬。

 印表機紙寬58mm,頁的寬度384,字元寬度為1,每行最多盛放32個字元
 印表機紙寬80mm,頁的寬度576,字元寬度為1,每行最多盛放48個字元

上面說的字元指的是列印到小票上的內容,其中數字和字母占1個字元,中文占2個字元,也就是說,如果使用58mm的列印紙,一行最多可以列印16個漢字或者32個數字 。

當然這是在不改變字體大小的情況下,如果我們改變了字體大小,那麼一行盛放的內容也會改變。

//控制字元大小

ASCII碼   GS  !   n
十進位碼  29  33  n
十六進位  1D  21  n

1.這裡的n是一個變數, 0 ≤ n ≤ 255 

2.用二進位表示,n的取值範圍就是00000000到11111111,其中二進位的前四位用來控制寬度,後四位用來控制高度。0000表示不變0001表示放大2倍0002表示放大3倍,以此類推

3.該命令對所有字元(英數字元和漢字) 有效。

4.預設值:n = 0

 下麵我們來看一下字元的不同放大倍數(這裡的1倍,表示使用預設大小):

放大倍數n(二進位)n(十進位)
寬度1倍,高度1倍 00000000 0
寬度1倍,高度2倍 00000001 1
寬度1倍,高度3倍 00000002 2
寬度2倍,高度1倍 00010000 16
寬度2倍,高度2倍 00010001 17
寬度2倍,高度3倍 00010002 18
寬度3倍,高度1倍 00020000 32
寬度3倍,高度2倍 00020001 33
寬度3倍,高度3倍 00020002 34

 

PS:列印紙時間有些長,字跡有些模糊,見諒

 列印指令封裝

// 印表機紙寬58mm,頁的寬度384,字元寬度為1,每行最多盛放32個字元
// 印表機紙寬80mm,頁的寬度576,字元寬度為1,每行最多盛放48個字元
const PAGE_WIDTH = 576;
const MAX_CHAR_COUNT_EACH_LINE = 48;

//字元串轉位元組序列
function stringToByte(str) {
  var bytes = new Array();
  var len, c;
  len = str.length;
  for (var i = 0; i < len; i++) {
    c = str.charCodeAt(i);
    if (c >= 0x010000 && c <= 0x10FFFF) {
      bytes.push(((c >> 18) & 0x07) | 0xF0);
      bytes.push(((c >> 12) & 0x3F) | 0x80);
      bytes.push(((c >> 6) & 0x3F) | 0x80);
      bytes.push((c & 0x3F) | 0x80);
    } else if (c >= 0x000800 && c <= 0x00FFFF) {
      bytes.push(((c >> 12) & 0x0F) | 0xE0);
      bytes.push(((c >> 6) & 0x3F) | 0x80);
      bytes.push((c & 0x3F) | 0x80);
    } else if (c >= 0x000080 && c <= 0x0007FF) {
      bytes.push(((c >> 6) & 0x1F) | 0xC0);
      bytes.push((c & 0x3F) | 0x80);
    } else {
      bytes.push(c & 0xFF);
    }
  }
  return bytes;
}

//位元組序列轉ASCII碼
//[0x24, 0x26, 0x28, 0x2A] ==> "$&C*"
function byteToString(arr) {
  if (typeof arr === 'string') {
    return arr;
  }
  var str = '',
    _arr = arr;
  for (var i = 0; i < _arr.length; i++) {
    var one = _arr[i].toString(2),
      v = one.match(/^1+?(?=0)/);
    if (v && one.length == 8) {
      var bytesLength = v[0].length;
      var store = _arr[i].toString(2).slice(7 - bytesLength);
      for (var st = 1; st < bytesLength; st++) {
        store += _arr[st + i].toString(2).slice(2);
      }
      str += String.fromCharCode(parseInt(store, 2));
      i += bytesLength - 1;
    } else {
      str += String.fromCharCode(_arr[i]);
    }
  }
  return str;
}
//居中
function Center() {
  var Center = [];
  Center.push(27);
  Center.push(97);
  Center.push(1);
  var strCenter = byteToString(Center);
  return strCenter;
}

//居左
function Left() {
  var Left = [];
  Left.push(27);
  Left.push(97);
  Left.push(0);
  var strLeft = byteToString(Left);
  return strLeft;
}
//居右
function Right() {
  var right = [];
  Left.push(27);
  Left.push(97);
  Left.push(2);
  var strRight = byteToString(right);
  return strRight;
}
//標準字體
function Size1() {
  var Size1 = [];
  Size1.push(29);
  Size1.push(33);
  Size1.push(0);
  var strSize1 = byteToString(Size1);
  return strSize1;
}
//大號字體
/*  放大1倍  n = 0
 *  長寬各放大2倍  n = 17 */
function Size2(n) {
  var Size2 = [];
  Size2.push(29);
  Size2.push(33);
  Size2.push(n);
  var strSize2 = byteToString(Size2);
  return strSize2;
}

// 字體加粗
function boldFontOn() {
  var arr = []
  arr.push(27)
  arr.push(69)
  arr.push(1)
  var cmd = byteToString(arr);
  return cmd
}
// 取消字體加粗
function boldFontOff() {
  var arr = []
  arr.push(27)
  arr.push(69)
  arr.push(0)
  var cmd = byteToString(arr);
  return cmd
}
// 列印並走紙n行
function feedLines(n = 1) {
  var feeds = []
  feeds.push(27)
  feeds.push(100)
  feeds.push(n)
  var printFeedsLines = byteToString(feeds);
  return printFeedsLines
}
// 切紙
function cutPaper() {
  var cut = []
  cut.push(29)
  cut.push(86)
  cut.push(49)
  var cutType = byteToString(cut);
  return cutType
}

// 開錢箱
function open_money_box() {
  var open = []
  open.push(27)
  open.push(112)
  open.push(0)
  open.push(60)
  open.push(255)
  var openType = byteToString(open)
  return openType
}

// 初始化印表機
function init() {
  var arr = []
  arr.push(27)
  arr.push(68)
  arr.push(0)
  var str = byteToString(arr)
  return str
}
/* 
 設置左邊距
 len:
 */

function setLeftMargin(len = 1) {
  var arr = []
  arr.push(29)
  arr.push(76)
  arr.push(len)
  var str = byteToString(arr)
  return str
}

// 設置列印區域寬度
function setPrintAreaWidth(width) {
  var arr = []
  arr.push(29)
  arr.push(87)
  arr.push(width)
  var str = byteToString(arr)
  return str
}

/**
 * @param str
 * @returns {boolean} str是否全是中文
 */
function isChinese(str) {
  return /^[\u4e00-\u9fa5]$/.test(str);
}

// str是否全含中文或者中文標點
function isHaveChina(str) {
  if (escape(str).indexOf("%u") < 0) {
    return 0
  } else {
    return 1
  }
}
/**
 * 返回字元串寬度(1個中文=2個英文字元)
 * @param str
 * @returns {number}
 */
function getStringWidth(str) {
  let width = 0;
  for (let i = 0, len = str.length; i < len; i++) {
    width += isHaveChina(str.charAt(i)) ? 2 : 1;
  }
  return width;
}

/**
 * 同一行輸出str1, str2,str1居左, str2居右
 * @param {string} str1 內容1
 * @param {string} str2 內容2
 * @param {string} fillWith str1 str2之間的填充字元
 * @param {number} fontWidth 字元寬度 1/2
 *
 */
function inline(str1, str2, fillWith = ' ', fontWidth = 1) {
  const lineWidth = MAX_CHAR_COUNT_EACH_LINE / fontWidth;
  // 需要填充的字元數量
  let fillCount = lineWidth - (getStringWidth(str1) + getStringWidth(str2)) % lineWidth;
  let fillStr = new Array(fillCount).fill(fillWith.charAt(0)).join('');
  return str1 + fillStr + str2;
}
/**
 * 用字元填充一整行
 * @param {string} fillWith 填充字元
 * @param {number} fontWidth 字元寬度 1/2
 */
function fillLine(fillWith = '-', fontWidth = 1) {
  const lineWidth = MAX_CHAR_COUNT_EACH_LINE / fontWidth;
  return new Array(lineWidth).fill(fillWith.charAt(0)).join('');
}

/**
 * 文字內容居中,左右用字元填充
 * @param {string} str 文字內容
 * @param {number} fontWidth 字元寬度 1/2
 * @param {string} fillWith str1 str2之間的填充字元
 */
function fillAround(str, fillWith = '-', fontWidth = 1) {
  const lineWidth = MAX_CHAR_COUNT_EACH_LINE / fontWidth;
  let strWidth = getStringWidth(str);
  // 內容已經超過一行了,沒必要填充
  if (strWidth >= lineWidth) {
    return str;
  }
  // 需要填充的字元數量
  let fillCount = lineWidth - strWidth;
  // 左側填充的字元數量
  let leftCount = Math.round(fillCount / 2);
  // 兩側的填充字元,需要考慮左邊需要填充,右邊不需要填充的情況
  let fillStr = new Array(leftCount).fill(fillWith.charAt(0)).join('');
  return fillStr + str + fillStr.substr(0, fillCount - leftCount);
}

也就是說,如果我們使用的印表機採用的是ESC/POS指令集(我這裡使用過佳博、芯燁、斯普瑞特印表機),只要我們想辦法把列印指令發送給印表機,印表機就可以識別到並且進行列印等操作。那麼我們該如何發送呢?

1.藍牙印表機

參考掘金 zgt_不夢的文章 微信小程式連接藍牙印表機列印圖片示例

  • 初始化藍牙模塊 wx.openBluetoothAdapter()
  • 初始化完成後搜尋附近的藍牙設備 wx.startBluetoothDevicesDiscovery()
  • 監聽尋找到新設備的事件 wx.onBluetoothDeviceFound()
  • 在監聽尋找到新設備的事件回調中獲取所有藍牙設備列表 wx.getBluetoothDevices()
  • 連接低功耗藍牙設備 wx.createBLEConnection()
  • 連接成功後獲取藍牙設備服務 wx.getBLEDeviceServices()
  • 在服務中取(notify=true || indicate=true) && write=true 的特征值的 uuid: wx.getBLEDeviceCharacteristics()
  • 完成後停止搜尋 wx.stopBluetoothDevicesDiscovery()
  • 向低功耗藍牙設備特征值中寫入二進位數據 wx.writeBLECharacteristicValue()
  • 離開頁面時取消藍牙連接 wx.closeBLEConnection()
  • 關閉藍牙模塊 wx.closeBluetoothAdapter()

親測,好使!在uniapp也可以,只需替換對應的API即可

2.網口印表機

這裡我使用的scoket連接,相比於USB列印,這裡需要保證印表機和安卓設備在同一區域網下。好處是安卓設備可以和印表機距離較遠(比如廚房列印)。這裡以斯普瑞特印表機為例:[斯普瑞特官網 www.sprinter.com.cn/ 在進行數據通信之前,我們需要知道印表機在此區域網下的 IP, 下圖為“一鍵配網”工具

image.png  通過這個工具我們可以方便快捷的查詢到印表機的IP,或者可以根據空閑的網段來修改預設分配的IP,斯普瑞特POS印表機的埠是9100。

image.png

如果是其他品牌的印表機,我們也可以使用arp命令來查看當前區域網下的IP

拿到印表機的IP之後我們怎麼來測試一下印表機呢?

我們可以使用telnet命令(這個在Windows系統一般預設是關閉的,需要我們手動打開)

//telnet + 空格 + ip + 空格 + 埠號
telnet 192.168.5.6 9100

打開命令行視窗輸入telnet命令,按下回車

image.png

 如果埠關閉或者無法連接,則顯示不能打開到主機的鏈接,鏈接失敗;埠打開的情況下,鏈接成功,則進入telnet頁面(全黑的),證明埠可用。

連接成功後,我們輸入任何內容後,按下回車,印表機就會列印我們剛纔輸入的內容。

接下來我們要使用scoket來連接安卓設備和印表機,這裡我使用的是uniapp

/**
 * 調用tcp通信進行列印
 * @param {buffer}  buffer       列印數據
 * @param {object}  printerInfo  印表機對象{IP:'',PORT:''}
 */
function tcpWrite(buffer, printerInfo) {
  var Socket = plus.android.importClass("java.net.Socket");
  var PrintWriter = plus.android.importClass("java.io.PrintWriter");
  var BufferedWriter = plus.android.importClass("java.io.BufferedWriter");
  var OutputStreamWriter = plus.android.importClass("java.io.OutputStreamWriter");
  var BufferedReader = plus.android.importClass("java.io.BufferedReader");
  var InputStreamReader = plus.android.importClass("java.io.InputStreamReader");
  var InetSocketAddress = plus.android.importClass("java.net.InetSocketAddress");
  //連接  註意:這裡的埠一定是數字類型
  var sk = null
  try {
    sk = new Socket(printerInfo.IP, Number(printerInfo.PORT));
    sk.setSoTimeout(5000);
  } catch (e) {
    console.log(e, 'ee')
    uni.showToast({
      icon: 'none',
      title: '印表機連接失敗'
    })
  }
  //發送
  try {
    var outputStreamWriter = new OutputStreamWriter(sk.getOutputStream(), "GBK");
    var bufferWriter = new BufferedWriter(outputStreamWriter);
    var out = new PrintWriter(bufferWriter, true);
    out.println(buffer);
    //關閉tcp連接
    out.close();
  } catch (e) {
    console.log(e, 'ee')
    uni.showToast({
      icon: 'none',
      title: '印表機數據傳輸失敗'
    })
  }
}

列印小票

目前我們已經可以開心的使用列印功能了,只需要組合一下列印指令即可。這裡需要註意的是,如果我們在此之前設置了字元大小寬高均放大2倍,那麼後面列印的字元都會被放大,所以如果後面我們想使用預設字元大小,我們還需要再次設置字元大小為預設來覆蓋之前的指令

//這裡的EscPosUtil.js就是上面封裝的列印指令

import Esc from './EscPosUtil.js';

// 列印文字格式
let strCenter = Esc.Center(); //文字居中
let strLeft = Esc.Left(); //文字靠左
let strSize1 = Esc.Size1(); //預設文字
let strSize2 = Esc.Size2(17); //文字放大兩倍(長寬均為兩倍)

let printerInfo = {
  IP:'192.168.5.6',
  PORT: 9100
}


let strCmd = strCenter + Esc.Size2(17) + Esc.boldFontOn() + '測試門店'+ "\n";
  strCmd += strSize1 + Esc.fillLine(' ') + "\n"
  strCmd += strCenter + Esc.Size2(17) + Esc.boldFontOn() + '結賬單-堂食'  + "\n";
  strCmd += strSize1 + Esc.fillLine(' ') + "\n"
  strCmd += strLeft + Esc.Size2(17) + "取餐號:" + '62' + "\n";
  strCmd += Esc.inline('桌號:' + '牡丹廳', '人數:' + '6', ' ', 2) + "\n"
  strCmd += Esc.boldFontOff() + strSize1 + Esc.fillLine(' ') + "\n"
  strCmd += strLeft + strSize1 + "訂單號:" + '202305171749110001' + "\n";

  // 商品信息
  strCmd += Esc.fillAround('商品') + "\n"
  // 票尾
  strCmd += Esc.fillLine(' ') + "\n"
  strCmd += strCenter + '歡迎下次光臨!' + "\n";
  strCmd += Esc.feedLines(4) + "\n"
  // 切紙
  strCmd += Esc.cutPaper()

tcpWrite(strCmd, printerInfo)

列印效果(這裡僅為展示,非上述代碼列印)

image.png

3.USB印表機

這裡我使用的是uniapp插件市場的插件,如果你瞭解安卓原生開發,你也可以自己製作一個原生插件,或者使用Native.js開發。使用原生插件在本地調試需要先打包“自定義調試基座”,在本地測試後再打正式包。

uni-app基於nativejs實現USB-OTG通訊 - 簡書1,監聽USB拔出連接,判斷是否含有許可權 2,獲取許可權後,打開設備實現連接 3,讀寫發送接受數據 轉存失敗,建議直接上傳圖片文件https://www.jianshu.com/p/7c308ffcd789

uni-app官網uni-app,uniCloud,serverless https://uniapp.dcloud.net.cn/plugin/native-plugin.html#%E6%9C%AC%E5%9C%B0%E6%8F%92%E4%BB%B6-%E9%9D%9E%E5%86%85%E7%BD%AE%E5%8E%9F%E7%94%9F%E6%8F%92%E4%BB%B6

Android USB介面熱敏小票印表機插件usbPrinter - DCloud 插件市場本插件提供安卓手機通過USB介面連接熱敏小票印表機進行列印的相關功能。通過USB連接相比使用藍牙連接更穩定。 https://ext.dcloud.net.cn/plugin?id=7757

在使用USB插件後,我們可以監聽USB設備的插入和拔出,在初始化之後,我們可以進行數據通信,將上面封裝的列印指令傳給印表機即可

 二、網頁列印

由於是網頁運行在瀏覽器中,所以我們只能使用瀏覽器給我們提供的API

1.windows.print()

這個API在不同的瀏覽器中會有差異,其作用就是可以把網頁中的body元素列印出來,如果我們不想列印整個body元素,則需要將body的innerHTML替換。使用這種方式有時有些頁面樣式會和列印出來的不一樣,那麼我們就要使用其他方式來優化。

//使用方法

document.body.innerHTML = newstr;  // 把需要列印的指定內容賦給body
window.print();

1.1使用媒體查詢

@media print {
  //把需要列印時才用到的樣式寫到這裡
  p{
    font-size:16px;
  }
}
同理,你也可以直接在CSS文件或者style標簽中加上 media="print"
<style media="print">
//CSS代碼
</style>

1.2監聽列印事件

//監聽列印之前的事件
window.onbeforeprint = function() {
  //可以修改元素樣式
}


//監聽列印之後的事件
window.onafterprint = function() {
   //恢復之前的樣式
}

1.3分頁符

  1.3.1 page-break-before  指定元素前插入分頁符

  1.3.2 page-break-after  指定元素後插入分頁符

描述
auto 預設。如果必要則在元素後插入分頁符。
always 在元素後插入分頁符。
avoid 避免在元素後插入分頁符。
left 在元素之後足夠的分頁符,一直到一張空白的左頁為止。
right 在元素之後足夠的分頁符,一直到一張空白的右頁為止。
inherit 規定應該從父元素繼承 page-break-after 屬性的設置。

1. 您不能對絕對定位的元素使用此屬性。

2. 請儘可能少地使用分頁屬性,並且避免在表格、浮動元素、帶有邊框的塊元素中使用分頁屬性。

3. 任何版本的Internet Explorer(包括IE8)支持屬性值"left","right",和"inherit"。

4. Firefox,Chrome和Safari不支持屬性值"avoid","left"和"right"。.

@media print {
    footer {page-break-after: always;}
}

1.3.3 page-break-inside  設置是否在指定元素中插入分頁符 

描述
auto 預設。如果必要則在元素內部插入分頁符。
avoid 避免在元素內部插入分頁符。
inherit 規定應該從父元素繼承 page-break-inside 屬性的設置。
  1. 您不能對絕對定位的元素使用此屬性。
  2. 請儘可能少地使用分頁屬性,並且避免在表格、浮動元素、帶有邊框的塊元素中使用分頁屬性。
  3. IE8 及更早IE版本不支持 "inherit" 屬性。
  4. Firefox, Chrome, 以及 Safari 不支持屬性值 "avoid".
//避免在 <pre> 與 <blockquote> 元素中插入分頁符:

@media print {
    pre, blockquote {page-break-inside: avoid;}
}

1.4設置紙張

@page:  用來設置頁面大小、邊距、方向等

//portrait:縱向;  landscape: 橫向

@page {
    size: A4 portrait;  //設置紙張及其方向    這裡表示使用A4紙張,列印方向為縱向 
    margin: 3.7cm 2.6cm 3.5cm; //設置紙張外邊距
}
 
// 去除頁眉
@page { margin-top: 0; }
 
// 去除頁腳
@page { margin-bottom: 0; }

 值得註意的是,如果我們使用的印表機是黑白列印的,比如針式印表機,那麼我們使用的顏色最好是 #000,如果使用 #999這種灰色,列印效果會很不清晰

本文轉載於:

https://juejin.cn/post/7237316724739457061

如果對您有所幫助,歡迎您點個關註,我會定時更新技術文檔,大家一起討論學習,一起進步。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 近萬條一級分類經典簡訊大全ACCESS資料庫收集的是近萬條常用經典簡訊,之所以稱“一級分類”(意思是只有一個大類沒有子類),原因是為了區別另外一個有二級分類的簡訊資料庫。近萬條一級分類經典簡訊大全ACCESS資料庫中的簡訊都是經過索引沒有收錄重覆的記錄。 大類分類情況是:愛情簡訊(1730)、搞笑短 ...
  • 《5284個中醫葯基本名詞中醫名詞ACCESS資料庫》共收集中醫葯基本名詞5284個,分類情況統計:01.總論(45)、02.醫史文獻(275)、03.中醫基礎理論(804)、04.診斷學(930)、05.治療學(399)、06.中藥學(640)、07.方劑學(560)、08.針灸學(708)、09 ...
  • 摘要:本文模擬一下在主庫查詢訂單信息查詢不到的時候,切換數據源去歷史庫裡面查詢。 本文分享自華為雲社區《springboot動態切換數據源》,作者:小陳沒煩惱 。 前言 在公司的系統里,由於數據量較大,所以配置了多個數據源,它會根據用戶所在的地區去查詢那一個資料庫,這樣就產生了動態切換數據源的場景。 ...
  • 準備測試表,先跟著執行下麵的SQL ```sql #1.登錄MySQL後 #2.創建test_database資料庫,不存在則創建 create database if not exists test_database; #2.1.如果test_database庫存在,可以根據自己意願刪除或換個名稱 ...
  • 這是從一個完整的茶文化網站採集下來的完整的內容,2萬多條的記錄數幾乎包含了關於茶信息的所有內容,而且內容格式經過嚴謹的整理不會顯示亂七八遭而是統一干爽的格式。憑此資料庫可以創建一個內容建全的茶網站。 模塊包含:茶的種類(497)、茶具知識(139)、茶農資料(1568)、茶葉技術(3437)、茶藝茶 ...
  • 關於菜譜類的數據這個博客里已經發現過幾次,但是關於數據量多一點的飲食方面的數據似乎沒有見過,即使有也是千把條數據,而今天採集的就是一個關於飲食方面網站的大量數據。 分類是根據模塊來區分的所以類別不是很多,詳細情況是:廚房寶典(969)、美食笑話(175)、新鮮食報(5770)、飲食文化(980)、營 ...
  • APP發佈到市場後,難免會遇到嚴重的BUG阻礙用戶使用,因此有在不發佈新版本APP的情況下使用熱更新技術立即修複BUG需求。原生APP(例如:Android & IOS)的熱更新需求已經比較成熟,但Flutter技術棧目前還缺少類似的技術方案,因此Flutter研發團隊,也需要類似的熱更新技術。 ...
  • 一說到創建桌面應用,就不得不提及Electron和Tauri框架。這次給大家主要分享的是基於electron最新版本整合vite4.x構建vue3桌面端應用程式。 之前也有使用vite2+vue3+electronc創建桌面端項目,不過 vue-cli-plugin-electron-builder ...
一周排行
    -Advertisement-
    Play Games
  • GoF之工廠模式 @目錄GoF之工廠模式每博一文案1. 簡單說明“23種設計模式”1.2 介紹工廠模式的三種形態1.3 簡單工廠模式(靜態工廠模式)1.3.1 簡單工廠模式的優缺點:1.4 工廠方法模式1.4.1 工廠方法模式的優缺點:1.5 抽象工廠模式1.6 抽象工廠模式的優缺點:2. 總結:3 ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 本章將和大家分享ES的數據同步方案和ES集群相關知識。廢話不多說,下麵我們直接進入主題。 一、ES數據同步 1、數據同步問題 Elasticsearch中的酒店數據來自於mysql資料庫,因此mysql數據發生改變時,Elasticsearch也必須跟著改變,這個就是Elasticsearch與my ...
  • 引言 在我們之前的文章中介紹過使用Bogus生成模擬測試數據,今天來講解一下功能更加強大自動生成測試數據的工具的庫"AutoFixture"。 什麼是AutoFixture? AutoFixture 是一個針對 .NET 的開源庫,旨在最大程度地減少單元測試中的“安排(Arrange)”階段,以提高 ...
  • 經過前面幾個部分學習,相信學過的同學已經能夠掌握 .NET Emit 這種中間語言,並能使得它來編寫一些應用,以提高程式的性能。隨著 IL 指令篇的結束,本系列也已經接近尾聲,在這接近結束的最後,會提供幾個可供直接使用的示例,以供大伙分析或使用在項目中。 ...
  • 當從不同來源導入Excel數據時,可能存在重覆的記錄。為了確保數據的準確性,通常需要刪除這些重覆的行。手動查找並刪除可能會非常耗費時間,而通過編程腳本則可以實現在短時間內處理大量數據。本文將提供一個使用C# 快速查找並刪除Excel重覆項的免費解決方案。 以下是實現步驟: 1. 首先安裝免費.NET ...
  • C++ 異常處理 C++ 異常處理機制允許程式在運行時處理錯誤或意外情況。它提供了捕獲和處理錯誤的一種結構化方式,使程式更加健壯和可靠。 異常處理的基本概念: 異常: 程式在運行時發生的錯誤或意外情況。 拋出異常: 使用 throw 關鍵字將異常傳遞給調用堆棧。 捕獲異常: 使用 try-catch ...
  • 優秀且經驗豐富的Java開發人員的特征之一是對API的廣泛瞭解,包括JDK和第三方庫。 我花了很多時間來學習API,尤其是在閱讀了Effective Java 3rd Edition之後 ,Joshua Bloch建議在Java 3rd Edition中使用現有的API進行開發,而不是為常見的東西編 ...
  • 框架 · 使用laravel框架,原因:tp的框架路由和orm沒有laravel好用 · 使用強制路由,方便介面多時,分多版本,分文件夾等操作 介面 · 介面開發註意欄位類型,欄位是int,查詢成功失敗都要返回int(對接java等強類型語言方便) · 查詢介面用GET、其他用POST 代碼 · 所 ...
  • 正文 下午找企業的人去鎮上做貸後。 車上聽同事跟那個司機對罵,火星子都快出來了。司機跟那同事更熟一些,連我在內一共就三個人,同事那一手指桑罵槐給我都聽愣了。司機也是老社會人了,馬上聽出來了,為那個無辜的企業經辦人辯護,實際上是為自己辯護。 “這個事情你不能怪企業。”“但他們總不能讓銀行的人全權負責, ...