訂單及其狀態機的設計實現

来源:https://www.cnblogs.com/wanglifeng717/archive/2022/05/29/16214122.html
-Advertisement-
Play Games

狀態機簡介: 狀態機是有限狀態自動機的簡稱,是現實事物運行規則抽象而成的一個數學模型。【規則的抽象】 有限狀態機一般都有以下特點: (1)可以用狀態來描述事物,並且任一時刻,事物總是處於一種狀態; (2)事物擁有的狀態總數是有限的; (3)通過觸發事物的某些行為,可以導致事物從一種狀態過渡到另一種狀 ...


 狀態機簡介:

 

狀態機是有限狀態自動機的簡稱,是現實事物運行規則抽象而成的一個數學模型。【規則的抽象】

有限狀態機一般都有以下特點:

(1)可以用狀態來描述事物,並且任一時刻,事物總是處於一種狀態;

(2)事物擁有的狀態總數是有限的;

(3)通過觸發事物的某些行為,可以導致事物從一種狀態過渡到另一種狀態;

(4)事物狀態變化是有規則的,A狀態可以變換到B,B可以變換到C,A卻不一定能變換到C;

(5)同一種行為,可以將事物從多種狀態變成同種狀態,但是不能從同種狀態變成多種狀態。

 

狀態機這種描述客觀世界的方式就是將事物抽象成若幹狀態,然後所有的事件和規則導致事物在這些狀態中游走。最終使得事物“自圓其說”。

很多通信協議的開發都必須用到狀態機;一個健壯的狀態機可以讓你的程式,不論發生何種突發事件都不會突然進入一個不可預知的程式分支。

 

  • 狀態機示例:

 

 

 

四大概念:

狀態(state)

一個狀態機至少要包含兩個狀態。

分為:現態(源狀態)、次態(目標狀態)

狀態可以理解為一種結果,一種穩態形式,沒有擾動會保持不變的。

 

狀態命名形式:

1.副詞+動詞;例如:待審批、待支付、待收貨

這種命名方式體現了:狀態機就是事件觸髮狀態不斷遷徙的本質。表達一種待觸發的感覺。

2.動詞+結果;例如:審批完成、支付完成

3.已+動詞形式;例如:已發貨、已付款

以上兩種命名方式體現了:狀態是一種結果或者穩態的本質。表達了一種已完成的感覺。

 

角色很多的時候,為了表示清晰,可以加上角色名:例如:待財務審批、主管已批准 

命名考慮從用戶好理解角度出發。

 

事件(event)

or

觸發條件

又稱為“條件”,就是某個操作動作的觸發條件或者口令。當一個條件滿足時,就會觸發一個動作,或者執行一次狀態遷徙

這個事件可以是外部調用、監聽到消息、或者各種定時到期等觸發的事件。

對於燈泡,“打開開關”就是一個事件。

條件命名形式:動詞+結果;例如:支付成功、下單時間>5分鐘

動作(action)

事件發生以後要執行動作。例如:事件=“打開開關指令”,動作=“開燈”。一般就對應一個函數。

條件滿足後執行動作。動作執行完畢後,可以遷移到新的狀態,也可以仍舊保持原狀態。

動作不是必需的,當條件滿足後,也可以不執行任何動作,直接遷移到新狀態。

那麼如何區分“動作”和“狀態”?

“動作”是不穩定的,即使沒有條件的觸發,“動作”一旦執行完畢就結束了;

而“狀態”是相對穩定的,如果沒有外部條件的觸發,一個狀態會一直持續下去。

 變換(transition)

即從一個狀態變化到另外一個狀態

例如:“開燈過程”就是一個變化

 

狀態機其他表達方式:

 

 

狀態機的設計:

 

信息系統中有很多狀態機,例如:業務訂單的狀態。 

狀態機的設計存在的問題:什麼是狀態?到底有多少個狀態?要細分到什麼程度?

  信息系統是現實世界一種抽象和描述。而業務領域中那些已經發生的事件就是事實信息系統就是將這些事實以信息的形式存儲到資料庫中,即:信息就是一組事實

信息系統就是存儲這些事實,對這些事實進行管理與追蹤,進而起到提供提高工作效率的作用。

信息系統就是記錄已經發生的事實,信息系統中的狀態機基本和事實匹配。即:標識某個事實的完成度。

業務系統,根據實際業務,具體會有哪些發生的事實需要記錄,基本這些事實就至少對應一個狀態。需要記錄的事實就是一種穩態,一種結果。

例如:【待支付】->【已支付】->【已收貨】->【已評價】

這些都是系統需要記錄的已發生的客觀事實。而這些事實就對應了狀態,而發生這些事實的事件就對應了觸髮狀態機的轉換的事件。

根據自己的業務實際進行分析,並畫出狀態圖即可。

 

狀態機實現方式:狀態模式

 

下麵使經典的自動販賣機例子來說明狀態模式的用法,狀態圖如下:

 

 

 

 

 

 分析一個這個狀態圖:

a、包含4個狀態(我們使用4個int型常量來表示)
b、包含3個暴露在外的方法(投幣、退幣、轉動手柄、(發貨動作是內部方法,售賣機未對外提供方法,售賣機自動調用))
c、我們需要處理每個狀態下,用戶都可以觸發這三個動作。

 

我們可以做沒有意義的事情,在【未投幣】狀態,試著退幣,或者同時投幣兩枚,此時機器會提示我們不能這麼做。


實現邏輯:

    任何一個可能的動作,我們都要檢查,看看我們所處的狀態和動作是否合適。

/**
 * 自動售貨機
 * 
 * 
 */
public class VendingMachine
{
 
    /**
     * 已投幣
     */
    private final static int HAS_MONEY = 0;
    /**
     * 未投幣
     */
    private final static int NO_MONEY = 1;
    /**
     * 售出商品
     */
    private final static int SOLD = 2;
    /**
     * 商品售罄
     */
    private final static int SOLD_OUT = 3;
    /**
     * 商品數量
     */
    private int count = 0;
    
         // 當前狀態,開機模式是沒錢
    private int currentStatus = NO_MONEY;
 
        // 開機設置商品數量,初始化狀態
    public VendingMachine(int count)
    {
        this.count = count;
        if (count > 0)
        {
            currentStatus = NO_MONEY;
        }
    }
 
    /**
     * 投入硬幣,任何狀態用戶都可能投幣
     */
    public void insertMoney()
    {
        switch (currentStatus)
        {
        case NO_MONEY:
            currentStatus = HAS_MONEY;
            System.out.println("成功投入硬幣");
            break;
        case HAS_MONEY:
            System.out.println("已經有硬幣,無需投幣");
            break;
        case SOLD:
            System.out.println("請稍等...");
            break;
        case SOLD_OUT:
            System.out.println("商品已經售罄,請勿投幣");
            break;
 
    }
    }
 
    /**
     * 退幣,任何狀態用戶都可能退幣
     */
    public void backMoney()
    {
        switch (currentStatus)
        {
        case NO_MONEY:
            System.out.println("您未投入硬幣");
            break;
        case HAS_MONEY:
            currentStatus = NO_MONEY;
            System.out.println("退幣成功");
            break;
        case SOLD:
            System.out.println("您已經買了糖果...");
            break;
        case SOLD_OUT:
            System.out.println("您未投幣...");
            break;
        }
    }
 
    /**
     * 轉動手柄購買,任何狀態用戶都可能轉動手柄
     */
    public void turnCrank()
    {
        switch (currentStatus)
        {
        case NO_MONEY:
            System.out.println("請先投入硬幣");
            break;
        case HAS_MONEY:
            System.out.println("正在出商品....");
            currentStatus = SOLD;
            dispense();
            break;
        case SOLD:
            System.out.println("連續轉動也沒用...");
            break;
        case SOLD_OUT:
            System.out.println("商品已經售罄");
            break;
 
    }
    }
 
    /**
     * 發放商品
     */
    private void dispense()
    {
 
    switch (currentStatus)
    {
    case NO_MONEY:
    case HAS_MONEY:
    case SOLD_OUT:
        throw new IllegalStateException("非法的狀態...");
    case SOLD:
        count--;
        System.out.println("發出商品...");
        if (count == 0)
        {
            System.out.println("商品售罄");
            currentStatus = SOLD_OUT;
        } else
        {
            currentStatus = NO_MONEY;
        }
        break;
 
    }
 
    }
}
狀態機使用if-else或switch實現
// 測試自動售賣機
class Test
{
    public static void main(String[] args)
    {
        VendingMachine machine = new VendingMachine(10);
        machine.insertMoney();
        machine.backMoney();
 
    System.out.println("-----------");
 
    machine.insertMoney();
    machine.turnCrank();
    
    System.out.println("-----------");
    machine.insertMoney();
    machine.insertMoney();
    machine.turnCrank();
    machine.turnCrank();
    machine.backMoney();
    machine.turnCrank();
 
    }
}
測試自動售賣機

使用if-else/switch的方式實現狀態有如下問題:

  • 沒有遵守【開閉】原則,沒有【封裝變化】,所以沒有彈性,應對需求變更非常吃力。

      例如:現在增加一個狀態。每個方法都需要添加if-else語句。

  • 狀態如何轉換看得不是很清楚,隱藏在if-else/switch邏輯中。

 

升級策略:

【封裝變化】,局部化每個狀態的行為,將每個狀態的行為放到各自類中,每個狀態只要實現自己的動作就可以了。

販賣機只要將動作委托給代表當前狀態的狀態對象即可。

 

 

public interface State
{
    /**
     * 放錢
     */
    public void insertMoney();
    /**
     * 退錢
     */
    public void backMoney();
    /**
     * 轉動曲柄
     */
    public void turnCrank();
    /**
     * 出商品
     */
    public void dispense();
}
State
public class NoMoneyState implements State
{
 
    private VendingMachine machine;
 
    public NoMoneyState(VendingMachine machine)
    {
        this.machine = machine;
        
    }
    
    @Override
    public void insertMoney()
    {
        System.out.println("投幣成功");
        machine.setState(machine.getHasMoneyState());
    }
 
    @Override
    public void backMoney()
    {
        System.out.println("您未投幣,想退錢?...");
    }
 
    @Override
    public void turnCrank()
    {
        System.out.println("您未投幣,想拿東西麽?...");
    }
 
    @Override
    public void dispense()
    {
        throw new IllegalStateException("非法狀態!");
    }
 
}
NoMoneyState
public class HasMoneyState implements State
{
 
    private VendingMachine machine;
 
    public HasMoneyState(VendingMachine machine)
    {
        this.machine = machine;
    }
 
    @Override
    public void insertMoney()
    {
        System.out.println("您已經投過幣了,無需再投....");
    }
 
    @Override
    public void backMoney()
    {
        System.out.println("退幣成功");
            machine.setState(machine.getNoMoneyState());
    }
 
    @Override
    public void turnCrank()
    {
        System.out.println("你轉動了手柄");
        machine.setState(machine.getSoldState());
    }
 
    @Override
    public void dispense()
    {
        throw new IllegalStateException("非法狀態!");
    }
 
}
    
HasMoneyState
public class SoldOutState implements State
{
 
    private VendingMachine machine;
 
    public SoldOutState(VendingMachine machine)
    {
        this.machine = machine;
    }
 
    @Override
    public void insertMoney()
    {
        System.out.println("投幣失敗,商品已售罄");
    }
 
    @Override
    public void backMoney()
    {
        System.out.println("您未投幣,想退錢麽?...");
    }
 
    @Override
    public void turnCrank()
    {
        System.out.println("商品售罄,轉動手柄也木有用");
    }
 
    @Override
    public void dispense()
    {
        throw new IllegalStateException("非法狀態!");
    }
 
}
SoldOutState
public class SoldState implements State
{
 
    private VendingMachine machine;
 
    public SoldState(VendingMachine machine)
    {
        this.machine = machine;
    }
 
    @Override
    public void insertMoney()
    {
        System.out.println("正在出貨,請勿投幣");
    }
 
    @Override
    public void backMoney()
    {
        System.out.println("正在出貨,沒有可退的錢");
    }
 
    @Override
    public void turnCrank()
    {
        System.out.println("正在出貨,請勿重覆轉動手柄");
    }
 
    @Override
    public void dispense()
    {
        machine.releaseBall();
        if (machine.getCount() > 0)
        {
            machine.setState(machine.getNoMoneyState());
        } else
        {
            System.out.println("商品已經售罄");
            machine.setState(machine.getSoldOutState());
        }
    }
}
SoldState
public class VendingMachine
{
    private State noMoneyState;
    private State hasMoneyState;
    private State soldState;
    private State soldOutState;
    private State winnerState ; 


    private int count = 0;
    private State currentState = noMoneyState;
 
    public VendingMachine(int count)
    {
        noMoneyState = new NoMoneyState(this);
        hasMoneyState = new HasMoneyState(this);
        soldState = new SoldState(this);
        soldOutState = new SoldOutState(this);
        winnerState = new WinnerState(this);
 
              if (count > 0)
             {
            this.count = count;
            currentState = noMoneyState;
             }
    }
 
       //將這些動作委托給當前狀態.
    public void insertMoney()
    {
        currentState.insertMoney();
    }
 
    public void backMoney()
    {
        currentState.backMoney();
    }
       
        // 機器不用提供dispense動作,因為這是一個內部動作.用戶不可以直 
        //接要求機器發放糖果.我們在狀態對象的turnCrank()方法中調用 
        //dispense方法;

       //dispense無論如何,即使在nomoney狀態也會被執行.
       //讓不合法的情形下,dispense拋出異常處理。
    public void turnCrank()
    {
        currentState.turnCrank();
            currentState.dispense();
    }
 

    public void releaseBall()
    {
        System.out.println("發出一件商品...");
        if (count != 0)
        {
            count -= 1;
        }
    }
 
    public void setState(State state)
    {
        this.currentState = state;
    }
 
    //getter setter omitted ...
 
}
VendingMachine

 

我們之前說過,if-else/switch實現方式沒有彈性,那現在按照這種實現模式,需求變更修改起來會輕鬆點嗎?

紅色部分標記了我們的需求變更:當用戶每次轉動手柄的時候,有10%的幾率贈送一瓶。

 

實現方式:

 

我們遵守了【開閉】原則,只要新建一個WinnerState的類即可。然後有限的修改has_money的轉向即可。

為什麼WinnerState要獨立成一個狀態,其實它和sold狀態一模一樣。我把代碼寫在SoldState中不行嗎?

  • 第一個原因就是上面說的信息系統的本質就是記錄【事實】,中獎是需要記錄的事實,它應該是一個狀態。
  • 第二個原因:【單一職責】問題,我們一個類的責任是明確的。

   如果sold需求變化不一定影響到winner代碼實現,winner需求變化時,也不一定要修改sold,比如促銷方案結束了,中獎概率變了等。

   如果他們的變化不是一定互相影響到彼此的,那我們就該將他們分離,即是【隔離變化】也是遵守【單一職責】的原則。

 

public class WinnerState implements State
{
 
    private VendingMachine machine;
 
    public WinnerState(VendingMachine machine)
    {
        this.machine = machine;
    }
 
    @Override
    public void insertMoney()
    {
        throw new IllegalStateException("非法狀態");
    }
 
    @Override
    public void backMoney()
    {
        throw new IllegalStateException("非法狀態");
    }
 
    @Override
    public void turnCrank()
    {
        throw new IllegalStateException("非法狀態");
    }
 
    @Override
    public void dispense()
    {
        System.out.println("你中獎了,恭喜你,將得到2件商品");
        machine.releaseBall();
 
    if (machine.getCount() == 0)
    {
        System.out.println("商品已經售罄");
        machine.setState(machine.getSoldOutState());
    } else
    {
        machine.releaseBall();
        if (machine.getCount() > 0)
        {
            machine.setState(machine.getNoMoneyState());
        } else
        {
            System.out.println("商品已經售罄");
            machine.setState(machine.getSoldOutState());
        }
        
    }
 
    }
 
}
WinnerState
public class HasMoneyState implements State
{
 
    private VendingMachine machine;
    private Random random = new Random();
 
    public HasMoneyState(VendingMachine machine)
    {
        this.machine = machine;
    }
 
    @Override
    public void insertMoney()
    {
        System.out.println("您已經投過幣了,無需再投....");
    }
 
    @Override
    public void backMoney()
    {
        System.out.println("退幣成功");
 
    machine.setState(machine.getNoMoneyState());
    }
 
    @Override
    public void turnCrank()
    {
        System.out.println("你轉動了手柄");
        int winner = random.nextInt(10);
        if (winner == 0 && machine.getCount() > 1)
        {
            machine.setState(machine.getWinnerState());
        } else
        {
            machine.setState(machine.getSoldState());
        }
    }
 
    @Override
    public void dispense()
    {
        throw new IllegalStateException("非法狀態!");
    }
 
}
HasMoneyState

 

總結狀態模式: 

狀態模式:允許對象在內部狀態改變時改變它的行為,對象看起來好像修改了他的類。

解釋:

狀態模式將狀態封裝成為獨立的類,並將動作委托到代表當前狀態的對象。
所以行為會隨著內部狀態改變而改變。
我們通過組合簡單引用不同狀態對象來造成類改變的假象.

狀態模式 策略模式

1.行為封裝的n個狀態中,不同狀態不用行為。

2.context的行為委托到不同狀態中。

3.[當前狀態]在n個狀態中游走,context的行為也隨之[當前狀態]的改變而改變。

4.用戶對context的狀態改變渾然不知。

5.客戶不會直接和state交互,只能通過context暴露的方法交互,state轉換是context內部事情。

6.state可以是介面也可以是抽象類,取決於有沒公共功能可以放進抽象類中。抽象類方便,因為可以後續加方法。

可以將重覆代碼放入抽象類中。例如:"你已投入25元,不能重覆投" 這種通用代碼放入抽象類中。

7.context可以決定狀態流轉,如果這個狀態流轉是固定的,就適合放在context中進行。但是如果狀態流轉是動態的就適合放在狀態中進行。

例如通過商品的剩餘數目來決定流向[已售完]或[等待投幣],這個時候放在狀態類中,因為dispense要根據狀態判斷流轉。

這個寫法決定了,有新需求時候,你是改context還是改state類。

8.可以共用所有的state對象,但是需要修改context的時候時候,需要handler中傳入context引用

 

1.context主動指定需要組合的策略對象是哪一個。

2.可以在啟動的時候通過工廠動態指定具體是哪個策略對象,但是沒有在策略對象之間游走,即:只組合了一個策略對象。

3.策略作為繼承之外一種彈性替代方案。因為繼承導致子類繼承不適用的方法,且每個類都要維護,策略模式通過不同對象組合來改變行為。

4.策略模式聚焦的是互換的演算法來創建業務。

 

 

 

狀態機典型應用:訂單狀態控制

 

 

 

/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET NAMES utf8 */;
/*!50503 SET NAMES utf8mb4 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;

CREATE TABLE IF NOT EXISTS `tbl_sapo_biz_param` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主鍵',
  `create_time` datetime(3) NOT NULL COMMENT '創建時間',
  `biz_type` varchar(255) NOT NULL COMMENT '業務類別(使用時定義)',
  `biz_code` varchar(255) NOT NULL COMMENT '業務數據編碼',
  `para_key` varchar(255) NOT NULL COMMENT '參數key',
  `para_value` varchar(255) NOT NULL COMMENT '參數值',
  `para_desc` varchar(255) NOT NULL COMMENT '參數描述',
  `level` varchar(64) DEFAULT NULL COMMENT '參數級別;ABCD',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uni_idx_biz_param_biz_type_biz_code_para_key` (`biz_type`,`biz_code`,`para_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='業務參數表';

CREATE TABLE IF NOT EXISTS `tbl_sapo_order` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主鍵',
  `order_no` varchar(100) NOT NULL COMMENT '訂單號',
  `create_time` datetime(3) NOT NULL COMMENT '創建時間',
  `last_update_time` datetime(3) DEFAULT NULL COMMENT '最後更新時間',
  `xx_code` varchar(100) NOT NULL COMMENT '業務實體code',
  `status` int(10) unsigned NOT NULL DEFAULT 3 COMMENT '狀態:3-已取消,4-已接單,5-待支付,6-已支付,7-待執行,8-執行中,9-執行完成',
  `cancel_reason` int(10) unsigned DEFAULT NULL COMMENT '訂單取消的原因。1-未支付(支付取消或過期),2-未準備(準備失敗),3-未執行(啟動過期),4-未完成(執行中取消)',
  `refund_count` int(10) unsigned NOT NULL DEFAULT 0 COMMENT '退款次數',
  `expire_time` datetime(3) DEFAULT NULL ON UPDATE current_timestamp(3) COMMENT '失效時間',
  `invoice_flag` int(10) unsigned NOT NULL DEFAULT 0 COMMENT '是否開過發票標識,0-未開發票,1-已開發票',
  `total_fee` int(10) unsigned NOT NULL DEFAULT 0 COMMENT '訂單總價,單位:分',
  `pay_fee` int(10) unsigned NOT NULL DEFAULT 0 COMMENT '最終支付金額(單位:分)',
  `trace_no` varchar(255) DEFAULT NULL COMMENT '日誌追蹤標識',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uni_idx_order_order_no` (`order_no`),
  KEY `idx_order_xx_code` (`xx_code`),
  KEY `idx_order_expire_time` (`expire_time`),
  CONSTRAINT `fk_order_xx_code` FOREIGN KEY (`xx_code`) REFERENCES `tbl_sapo_xx` (`code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='業務訂單'
              
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • jQuery jQuery是什麼 jQuery是一個快速、簡潔的JavaScript框架,是繼Prototype之後又一個優秀的JavaScript代碼庫(或JavaScript框架)。jQuery設計的宗旨是“write Less,Do More”,即倡導寫更少的代碼,做更多的事情。它封裝Java ...
  • 這幾天一直都在做項目,只是沒有上傳上來,即將把前臺項目完結了。現在開始更新整個前臺的部分 一.麵包屑處理 1.分類操作 點擊三級聯動進入搜索產生麵包屑,直接取參數中的name即可 點擊x怎麼幹掉這個麵包屑,直接讓其v-if為這個name,如果點擊x就把name清空 清空還沒完,清空應該再發一次請求, ...
  • 本章是系列文章的第五章,介紹了指針分析方法。指針分析在C/C++語言中非常重要,分析的結果可以有效提升指針的優化效率。 本文中的所有內容來自學習DCC888的學習筆記或者自己理解的整理,如需轉載請註明出處。周榮華@燧原科技 5.1 概念 指針是許多重要編程語言的特性之一 指針的使用,可以避免大量的數 ...
  • 第四章: 設計與聲明 ###18. 讓介面更容易被正確使用,不易被誤用 將你的class的public介面設計的符合class所扮演的角色,必要時不僅對傳參類型限制,還對傳參的值域進一步限制。 ###19. 設計class猶如設計type 內置類型如int、float等,本質也是一個class,用戶 ...
  • 前言 還記得你第一次遇到「線程安全」這個詞的時候嗎? 我第一次遇到線程安全這個詞是在學習多線程併發操作的時候,看到人家文章里出現這個詞,還有說各種線程安全的類,但是一開始並不理解線程安全是什麼意思,也沒去深究線程怎樣是安全的?怎樣是不安全的?只是腦子裡接收了這麼一個詞。 線程安全是多線程編程時的計算 ...
  • JVM 是Java的基石,Java從業者需要瞭解。JVM不是一個新的知識,網上文章很多,本篇的不同之處在於參考一手資料、內容經過反覆推敲。本文將會有篩選地研究JVM的精華部分,至少達到準系統架構師夠用的程度。本篇主要分享學習Java Class文件以及類載入器CLassLoader的知識。 ...
  • 資料庫表之間關係: 一對一 (可以看做一對多的特例) 一對多 多對多 下圖將涵蓋了所有關係。 根據restful介面風格,我們最終會落到一個實體上,示例按照b表。其他表同理。 GET https://ip:port/xx/xx/1/0/b 查詢的場景: 只需要b表的某些元素 需要b某些元素,及相關聯 ...
  • 前言 偶然一天把某項目文檔傳到手機上,用手機自帶的閱讀器方便隨時拿出來查閱。看著我那好久沒點開的閱讀器,再看著書架上擺著幾本不知道是多久之前導入的小說。 閉上眼,我仿佛看到那時候的自己。側躺著縮在被窩裡,亮度調到最低,看的津津有味。 睜開眼,一聲短嘆,心中五味雜陳,時間像箭一樣飛逝而去,過去靜止不動 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...