[C#.NET][SMS] 使用 Every8D Web Service API 2.0 查詢 Batch ID

  • 4113
  • 0
  • C#
  • 2014-03-04

[C#.NET][SMS] 使用 Every8D Web Service API 2.0 查詢 Batch ID

上篇文章[C#.NET][Every8D] 使用 Every8D Web Service API 2.1 發送簡訊寫到,要查詢簡訊發送/簡訊回傳資料必須要有有Batch ID,Batch ID 可以在發送簡訊後自己寫code記下來,也可以透過Web Service API 2.0版本去取得,我在API 2.1版本沒有找到查詢 Batch ID 的功能,只在 API 2.0 版本找到。

image

 

 

API 2.0版本的登錄機制與2.1版本不同,以下兩個WS擇一加入,然後調用Login方法

http://tw.every8d.com/API20/Security.asmx?wsdl

https://oms.every8d.com/API20/Security.asmx?wsdl

image

 

調用Login方法後會回傳Xml格式。

image

 

看到Xml後,定義相關的Entity Class:


/// <summary>
/// 登入後回傳的欄位
/// </summary>
[Serializable, XmlRoot("USER")]
public class LoginResponse
{
    [XmlElement("ERROR_CODE")]
    public string ErrorCode { get; set; }

    [XmlElement("DESC")]
    public string Description { get; set; }

    [XmlElement("COMPANY_NO")]
    public string CompanyNo { get; set; }

    [XmlElement("USER_NO")]
    public string UserNo { get; set; }

    [XmlElement("USER_NAME")]
    public string UserName { get; set; }

    [XmlElement("CREDIT")]
    public string Credit { get; set; }

    [XmlElement("MOBILE")]
    public string Mobile { get; set; }

    [XmlElement("SPECIAL_ACCOUNT")]
    public string SpecialAccount { get; set; }

    [XmlElement("DUE")]
    public LoginDUE DUE { get; set; }

    [XmlElement("UPLOAD_FILE_URL")]
    public string UploadFileURL { get; set; }

    [XmlElement("APPLY_SERVICE")]
    public string ApplyService { get; set; }

    [XmlElement("CHARGE")]
    public string Charge { get; set; }

    [XmlElement("EDM_COMMON")]
    public string EDMCommand { get; set; }

    [XmlElement("EDM_PRIVATE")]
    public string EDMPrivate { get; set; }

    [XmlElement("TEMPLATE")]
    public LoginTemplate Template { get; set; }
}

[Serializable, XmlRoot("DUE")]
public class LoginDUE
{
    [XmlElement("TW")]
    public string TW { get; set; }

    [XmlElement("OTHER")]
    public string Other { get; set; }
}

[Serializable, XmlRoot("TEMPLATE")]
public class LoginTemplate
{
    [XmlElement("ITEM")]
    public List<LoginItem> Item { get; set; }
}

[Serializable, XmlRoot("ITEM")]
public class LoginItem
{
    [XmlAttribute("FILENAME")]
    public string FileName { get; set; }

    [XmlAttribute("DESC")]
    public string Description { get; set; }
}


@LoginRequest 登入參數類別:

/// 登入參數
/// <summary>
/// 登入參數
/// </summary>
public class LoginRequest
{
    public string UserID { get; set; }

    public string Password { get; set; }

    public string APIType { get; set; }

    public string Version { get; set; }
}


@globalParameter共用常數:

internal class globalParameter
{
    public readonly static string SendMessageTimeFormat = "yyyyMMddHHmmss";
    public readonly static string GetReportTimeFormat = "yyyyMMdd";
    public readonly static string CustID = "av8d20";//這是固定的,文件有寫
}


@ReportFactory Class,這是用來存放實作AIP 2.0 方法的類別

SecurityWebService.Security _security = new SecurityWebService.Security();private LoginRequest _LoginRequest;

public LoginRequest LoginRequest
{
    get { return _LoginRequest; }
    set { _LoginRequest = value; }
}

private LoginResponse _LoginResponse;

public LoginResponse LoginResponse
{
    get { return _LoginResponse; }
    private set
    {
        _LoginResponse = value;
    }
}

private string _UserNo;

public string UserNo
{
    get { return _UserNo; }
    private set { _UserNo = value; }
}
/// 建構子
/// <summary>
/// 建構子
/// </summary>
/// <param name="LoginRequest"></param>
public ReportFactory(LoginRequest LoginRequest)
{
    if (LoginRequest == null)
    {
        throw new ArgumentNullException("LoginRequest");
    }
    this.LoginRequest = LoginRequest;
}

Login方法調用了API 2.0的Login方法,再透過反序列化將Login的資料轉成Class。


/// 登入
/// <summary>
/// 登入
/// </summary>
/// <returns></returns>
public string Login()
{
    string data = this._security.Login(globalParameter.CustID, this.LoginRequest.UserID, this.LoginRequest.Password, this.LoginRequest.APIType, this.LoginRequest.Version);
    if (string.IsNullOrEmpty(data))
    {
        this.LoginResponse = null;
        this.UserNo = null;
        return null;
    }
    this.LoginResponse = this._deSerial.FromXmlString<LoginResponse>(data);
    this.UserNo = this.LoginResponse.UserNo;

    return this.UserNo;
}

PS.FromXmlString是我自己寫的反序列化類別,若不知如合反序列化,請參考以下:
[C#.NET] 利用 泛型方法 重構 反序列化

[XML][C#.NET] 處理 ezTRACK 的 EPCIS Xml文件

[XML][C#.NET] 忽略XML宣告及XML命名空間


Login單元測試這樣寫:

[TestMethod()]
public void LoginTest()
{
    LoginRequest LoginRequest = new LoginRequest()
    {
        UserID = "xxx",帳號
        Password = "xxx",密碼
    };
    ReportFactory target = new ReportFactory(LoginRequest);
    string expected = "xxx";//期望客戶編號
    var actual = target.Login();
    Assert.AreEqual(expected, actual);
}

下圖是中斷觀察,因為UserNo是後面的查詢必要條件,所以我的Login是回傳UserNo並驗証測試結果。
image

 

 


拿到了UserID後,便可查詢Batch ID了,這時後就需要另一組WS,以下則一加入。

http://tw.every8d.com/API20/Report.asmx?wsdl

https://oms.every8d.com/API20/Report.asmx?wsdl

image

 

查詢 Batch ID 的時間格式是"yyyyMMdd",這部份文件沒寫,是我try出來的


/// 取得所有的 BatchID 網址
/// <summary>
/// 取得所有的 BatchID 網址
/// </summary>
/// <param name="StartTime">開始時間</param>
/// <param name="EndTime">結束時間</param>
/// <returns>回傳網址</returns>
public string GetBatchIDsUri(DateTime StartTime, DateTime EndTime)
{
    if (string.IsNullOrEmpty(this.UserNo))
    {
        throw new ArgumentNullException("UserNo", "UserID No Exist");
    }

    string startTime = StartTime.ToString(globalParameter.GetReportTimeFormat);
    string endTime = EndTime.ToString(globalParameter.GetReportTimeFormat);
    string result = this._report.getMessageStatusList(globalParameter.CustID, this.LoginResponse.UserNo, startTime, endTime);
    return result;
}


image

 

 

很好,到目前為止都很順,讚,資料拿到後就打完收工了,嘿嘿。


/// <summary>
/// A test for GetBatchIDsUri
/// </summary>
[TestMethod()]
public void GetBatchIDsUriTest()
{
    LoginRequest LoginRequest = new LoginRequest()
    {
        UserID = "0956778187",
        Password = "1980690911",
    };
    ReportFactory target = new ReportFactory(LoginRequest);
    DateTime StartTime = new DateTime(2012, 04, 24);
    DateTime EndTime = new DateTime(2012, 04, 26);
    target.Login();
    bool expected = true;
    string actual;
    actual = target.GetBatchIDsUri(StartTime, EndTime);
    Assert.AreEqual(expected, actual.IsUri());
}


設定中斷觀察一下結果,什麼!!!是Link Zip檔!!!(『想打完,嘿嘿,沒有那麼容易』every8d API 2.0 竊笑著)
image
PS.當初知道後,右手食指,抖了好幾下,咆哮了好幾聲(抱頭);不過,食指抖歸抖,遇到了還是要做;接續著把單元測試寫完,IsUri()是我自己寫的string擴充方法,用來判斷字串是否為Uri。
PS.提醒下,別忘了測試失敗結果是否如預期,這裡就不多描述了;失敗會回傳-1,-2。
1.把StartTime跟EndTime 參數教換或輪流帶空值進去
2.UserID帶空字串/null
3.全部帶空字串/null

變數越多測試矩陣就越大,這時候就可以借助驅動測試來處理。參考 [TFS] 資料驅動單元測試


有點扯遠了,拉回來。既然已經知道是網路連結,就把它抓下來吧。


/// 下載網址資料,存放在Memory
/// <summary>
/// 下載網址資料,存放在Memory
/// </summary>
/// <param name="Uri">網址</param>
/// <returns>回傳網址串流</returns>
public Stream DownloadUri(string Uri)
{
    if (string.IsNullOrEmpty(this.UserNo))
    {
        throw new ArgumentNullException("UserNo", "UserID No Exist");
    }
    if (string.IsNullOrEmpty(Uri))
    {
        throw new ArgumentNullException(Uri);
    }
    if (!Uri.IsUri())
    {
        throw new NotSupportedException("Uri");
    }

    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Uri);
    using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
    using (Stream dataStream = response.GetResponseStream())
    {
        MemoryStream memory = new MemoryStream();
        byte[] buffer = new byte[1024];
        int size = 0;
        do
        {
            size = dataStream.Read(buffer, 0, buffer.Length);
            if (size > 0)
                memory.Write(buffer, 0, size);
        } while (size > 0);
        memory.Position = 0;
        return memory;
    }
}

/// 下載網址資料,存放在實體IO
/// <summary>
/// 下載網址資料,存放在實體IO 
/// </summary>
/// <param name="Uri">網址</param>
/// <param name="OutputFileName">輸出檔案名稱</param>
public void DownloadUri(string Uri, string OutputFileName)
{
    if (string.IsNullOrEmpty(OutputFileName))
    {
        throw new ArgumentNullException("OutputFileName");
    }
    var input = this.DownloadUri(Uri);
    using (FileStream output = new FileStream(OutputFileName, FileMode.Create, FileAccess.Write))
    {
        byte[] buffer = new byte[1024];
        int size = 0;
        do
        {
            size = input.Read(buffer, 0, buffer.Length);
            if (size > 0)
                output.Write(buffer, 0, size);
        } while (size > 0);
    }
}


檔案已經知道是Zip了,當然還需要解壓縮,用法請參考以下。

[C#.NET] 使用 7-zip 解壓縮檔案

[.NET] 解決 單元測試 Can not load 7-zip library or internal COM error! Message: failed to load library. 問題

 

我已經觀察出解壓縮後是一個csv的格式檔案

用法請參考,是為它撰寫處理它而寫的 [.Net Tools][C#.NET] 使用 LinqToCSV 處理 CSV 檔案格式;不過這裡的解壓縮方式有點不一樣,是因為後來才發現解壓檔案可能會有一個以上,原本的寫法只能解第一個檔案,所以我把它改成回傳IEnumerable<T>。




public class DecompressionContent
{
    public string FileName { get; set; }

    public Stream DecompressionStream { get; set; }
}
/// 解壓縮到串流集合
/// <summary>
/// 解壓縮到串流集合
/// </summary>
/// <param name="CompressionFileName">壓縮檔檔案名稱</param>
/// <returns>回傳解壓縮串流集合</returns>
public IEnumerable<DecompressionContent> Decompression(string CompressionFileName)
{
    using (FileStream fileStream = new FileStream(CompressionFileName, FileMode.Open, FileAccess.Read))
    {
        return Decompression(fileStream);
    }
}

/// 解壓縮到串流集合
/// <summary>
/// 解壓縮到串流集合
/// </summary>
/// <param name="CompressionFileStream">壓縮檔串流</param>
/// <returns>回傳解壓縮串流集合</returns>
public IEnumerable<DecompressionContent> Decompression(Stream CompressionFileStream)
{
    List<DecompressionContent> streamList = new List<DecompressionContent>();
    using (SevenZipExtractor zip = new SevenZipExtractor(CompressionFileStream))
    {
        for (int i = 0; i < zip.ArchiveFileData.Count; i++)
        {
            DecompressionContent content = new DecompressionContent();
            MemoryStream decompressionStream = new MemoryStream();
            zip.ExtractFile(i, decompressionStream);

            decompressionStream.Position = 0;

            content.FileName = zip.ArchiveFileData[i].FileName;
            content.DecompressionStream = decompressionStream;
            streamList.Add(content);
        }

        return streamList;
    }
}

GetBatchIDs方法就是上述方法的集成,下載→解壓→處理CSV

/// 取得Batch ID集合
/// <summary>
/// 取得Batch ID集合
/// </summary>
/// <param name="StartTime">開始時間</param>
/// <param name="EndTime">結束時間</param>
/// <returns>返回Batch ID 集合</returns>
public IEnumerable<BatchIdResponse> GetBatchIDs(DateTime StartTime, DateTime EndTime)
{
    if (string.IsNullOrEmpty(this.UserNo))
    {
        throw new ArgumentNullException("UserNo", "UserID No Exist");
    }
    var uri = this.GetBatchIDsUri(StartTime, EndTime);
    var stream = this.DownloadUri(uri);

    var decompression = this._zip.Decompression(stream);//參考http://www.dotblogs.com.tw/yc421206/archive/2012/04/30/71911.aspx
    IEnumerable<BatchIdResponse> content = null;
    //解壓檔案可能有兩個以上    foreach (var item in decompression)
    {
        //資料編碼是UTF8
        using (StreamReader reader = new StreamReader(item.DecompressionStream, Encoding.UTF8))
        {
            CsvContext csv = new CsvContext();
            content = csv.Read<BatchIdResponse>(reader).ToList();
        }
    }
    return content;
}


GetBatchIDs的單元測試程式碼

/// <summary>
///A test for GetBatchIDs
///</summary>
[TestMethod()]
public void GetBatchIDsTest()
{
    LoginRequest LoginRequest = new LoginRequest()
    {
        UserID = "0956778187",
        Password = "1980690911",
    };
    ReportFactory target = new ReportFactory(LoginRequest);
    target.Login();

    DateTime StartTime = new DateTime(2012, 04, 24);
    DateTime EndTime = new DateTime(2012, 04, 26);
    int expected = 1;
    var actual = target.GetBatchIDs(StartTime, EndTime);

    Assert.IsTrue(actual.Count() >= expected);
}


設定中斷觀察,確定有查詢到Batch ID

image


真的打完收工,有了Batch ID我們便能進一步的取得我們想要的資料,今天就到此先告一段落,後面再寫有哪些資料可以拿。

若有謬誤,煩請告知,新手發帖請多包涵


Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET

Image result for microsoft+mvp+logo