chore: 首次簽入 Thinkyu ASP.NET 專案
- 加入 Visual Studio / ASP.NET .gitignore - 排除建置輸出、IDE 設定、NuGet packages、大型 MSI 安裝檔
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
|
||||
# All files
|
||||
[*]
|
||||
indent_style = space
|
||||
|
||||
# Xml files
|
||||
[*.xml]
|
||||
indent_size = 2
|
||||
|
||||
# 設定所有文字檔案使用 UTF-8 編碼
|
||||
[*]
|
||||
charset = utf-8
|
||||
Generated
+158
@@ -0,0 +1,158 @@
|
||||
namespace 報到系統客戶端
|
||||
{
|
||||
partial class AgreementConfirmForm
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
|
||||
this.label1 = new System.Windows.Forms.Label();
|
||||
this.lbAgreedTerms = new System.Windows.Forms.Label();
|
||||
this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel();
|
||||
this.btnNo = new System.Windows.Forms.Button();
|
||||
this.btnYes = new System.Windows.Forms.Button();
|
||||
this.tableLayoutPanel1.SuspendLayout();
|
||||
this.flowLayoutPanel1.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// tableLayoutPanel1
|
||||
//
|
||||
this.tableLayoutPanel1.BackColor = System.Drawing.Color.Cornsilk;
|
||||
this.tableLayoutPanel1.ColumnCount = 1;
|
||||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
|
||||
this.tableLayoutPanel1.Controls.Add(this.flowLayoutPanel1, 0, 2);
|
||||
this.tableLayoutPanel1.Controls.Add(this.lbAgreedTerms, 0, 1);
|
||||
this.tableLayoutPanel1.Controls.Add(this.label1, 0, 0);
|
||||
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0);
|
||||
this.tableLayoutPanel1.Margin = new System.Windows.Forms.Padding(4);
|
||||
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
|
||||
this.tableLayoutPanel1.Padding = new System.Windows.Forms.Padding(24);
|
||||
this.tableLayoutPanel1.RowCount = 3;
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 70F));
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 70F));
|
||||
this.tableLayoutPanel1.Size = new System.Drawing.Size(975, 603);
|
||||
this.tableLayoutPanel1.TabIndex = 1;
|
||||
//
|
||||
// label1
|
||||
//
|
||||
this.label1.AutoSize = true;
|
||||
this.label1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.label1.Location = new System.Drawing.Point(28, 24);
|
||||
this.label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
|
||||
this.label1.Name = "label1";
|
||||
this.label1.Size = new System.Drawing.Size(919, 70);
|
||||
this.label1.TabIndex = 1;
|
||||
this.label1.Text = "有以下事項需要你同意:";
|
||||
this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
|
||||
//
|
||||
// lbAgreedTerms
|
||||
//
|
||||
this.lbAgreedTerms.AutoSize = true;
|
||||
this.lbAgreedTerms.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.lbAgreedTerms.Font = new System.Drawing.Font("微軟正黑體", 32F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(136)));
|
||||
this.lbAgreedTerms.ForeColor = System.Drawing.Color.Blue;
|
||||
this.lbAgreedTerms.Location = new System.Drawing.Point(28, 94);
|
||||
this.lbAgreedTerms.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
|
||||
this.lbAgreedTerms.Name = "lbAgreedTerms";
|
||||
this.lbAgreedTerms.Size = new System.Drawing.Size(919, 415);
|
||||
this.lbAgreedTerms.TabIndex = 8;
|
||||
this.lbAgreedTerms.Text = "(同意事項)(同意事項)(同意事項)(同意事項)(同意事項)(同意事項)(同意事項)(同意事項)(同意事項)(同意事項)(同意事項)(同意事項)(同意事項)(同意" +
|
||||
"事項)\r\n";
|
||||
this.lbAgreedTerms.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
||||
//
|
||||
// flowLayoutPanel1
|
||||
//
|
||||
this.flowLayoutPanel1.Controls.Add(this.btnNo);
|
||||
this.flowLayoutPanel1.Controls.Add(this.btnYes);
|
||||
this.flowLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.flowLayoutPanel1.FlowDirection = System.Windows.Forms.FlowDirection.RightToLeft;
|
||||
this.flowLayoutPanel1.Location = new System.Drawing.Point(24, 509);
|
||||
this.flowLayoutPanel1.Margin = new System.Windows.Forms.Padding(0);
|
||||
this.flowLayoutPanel1.Name = "flowLayoutPanel1";
|
||||
this.flowLayoutPanel1.Padding = new System.Windows.Forms.Padding(0, 5, 0, 0);
|
||||
this.flowLayoutPanel1.Size = new System.Drawing.Size(927, 70);
|
||||
this.flowLayoutPanel1.TabIndex = 9;
|
||||
//
|
||||
// btnNo
|
||||
//
|
||||
this.btnNo.Anchor = System.Windows.Forms.AnchorStyles.Left;
|
||||
this.btnNo.BackColor = System.Drawing.Color.HotPink;
|
||||
this.btnNo.DialogResult = System.Windows.Forms.DialogResult.No;
|
||||
this.btnNo.Location = new System.Drawing.Point(798, 9);
|
||||
this.btnNo.Margin = new System.Windows.Forms.Padding(4);
|
||||
this.btnNo.Name = "btnNo";
|
||||
this.btnNo.Size = new System.Drawing.Size(125, 52);
|
||||
this.btnNo.TabIndex = 1;
|
||||
this.btnNo.TabStop = false;
|
||||
this.btnNo.Text = "N 否";
|
||||
this.btnNo.UseVisualStyleBackColor = false;
|
||||
//
|
||||
// btnYes
|
||||
//
|
||||
this.btnYes.Anchor = System.Windows.Forms.AnchorStyles.Left;
|
||||
this.btnYes.BackColor = System.Drawing.Color.Chartreuse;
|
||||
this.btnYes.DialogResult = System.Windows.Forms.DialogResult.Yes;
|
||||
this.btnYes.Location = new System.Drawing.Point(665, 9);
|
||||
this.btnYes.Margin = new System.Windows.Forms.Padding(4);
|
||||
this.btnYes.Name = "btnYes";
|
||||
this.btnYes.Size = new System.Drawing.Size(125, 52);
|
||||
this.btnYes.TabIndex = 0;
|
||||
this.btnYes.TabStop = false;
|
||||
this.btnYes.Text = "Y 是";
|
||||
this.btnYes.UseVisualStyleBackColor = false;
|
||||
//
|
||||
// AgreementConfirmForm
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
|
||||
this.ClientSize = new System.Drawing.Size(975, 603);
|
||||
this.Controls.Add(this.tableLayoutPanel1);
|
||||
this.Font = new System.Drawing.Font("微軟正黑體", 24F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(136)));
|
||||
this.Margin = new System.Windows.Forms.Padding(11, 10, 11, 10);
|
||||
this.Name = "AgreementConfirmForm";
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
|
||||
this.Text = "同意事項確認";
|
||||
this.Shown += new System.EventHandler(this.AgreementConfirmForm_Shown);
|
||||
this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.AgreementConfirmForm_KeyDown);
|
||||
this.tableLayoutPanel1.ResumeLayout(false);
|
||||
this.tableLayoutPanel1.PerformLayout();
|
||||
this.flowLayoutPanel1.ResumeLayout(false);
|
||||
this.ResumeLayout(false);
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
|
||||
private System.Windows.Forms.Label label1;
|
||||
private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1;
|
||||
private System.Windows.Forms.Button btnNo;
|
||||
private System.Windows.Forms.Button btnYes;
|
||||
private System.Windows.Forms.Label lbAgreedTerms;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Data;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace 報到系統客戶端
|
||||
{
|
||||
public partial class AgreementConfirmForm : Form
|
||||
{
|
||||
public string 同意事項 { get { return lbAgreedTerms.Text; } set { lbAgreedTerms.Text = value; } }
|
||||
|
||||
public AgreementConfirmForm()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
private void AgreementConfirmForm_KeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (e.KeyCode == Keys.Y)
|
||||
{
|
||||
DialogResult = DialogResult.Yes;
|
||||
e.SuppressKeyPress = true;
|
||||
return;
|
||||
}
|
||||
if (e.KeyCode == Keys.N)
|
||||
{
|
||||
DialogResult = DialogResult.No;
|
||||
e.SuppressKeyPress = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void AgreementConfirmForm_Shown(object sender, EventArgs e)
|
||||
{
|
||||
Services.NotificationSoundService.Ding();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
||||
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<configSections>
|
||||
<sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<section name="報到系統客戶端.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false"/>
|
||||
</sectionGroup>
|
||||
</configSections>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8"/>
|
||||
</startup>
|
||||
<system.serviceModel>
|
||||
<bindings/>
|
||||
<client/>
|
||||
</system.serviceModel>
|
||||
<System.Windows.Forms.ApplicationConfigurationSection>
|
||||
<add key="DpiAwareness" value="PerMonitorV2" />
|
||||
<add key="EnableWindowsFormsHighDpiAutoResizing" value="true" />
|
||||
</System.Windows.Forms.ApplicationConfigurationSection>
|
||||
<applicationSettings>
|
||||
<報到系統客戶端.Properties.Settings>
|
||||
<setting name="ApiBaseUrl" serializeAs="String">
|
||||
<value>http://localhost:58792</value>
|
||||
</setting>
|
||||
</報到系統客戶端.Properties.Settings>
|
||||
</applicationSettings>
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="SQLitePCLRaw.core" publicKeyToken="1488e028ca7ab535" culture="neutral"/>
|
||||
<bindingRedirect oldVersion="0.0.0.0-3.0.2.2801" newVersion="3.0.2.2801"/>
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral"/>
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.5.0" newVersion="4.0.5.0"/>
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
||||
@@ -0,0 +1,66 @@
|
||||
# 設定檔位置說明
|
||||
|
||||
## 問題背景
|
||||
報到系統客戶端安裝在 `Program Files` 資料夾時,由於 Windows 安全限制,應用程式在執行期間無法修改 `Program Files` 目錄下的檔案(包括 `App.config`)。
|
||||
|
||||
## 解決方案
|
||||
設定檔已改為存儲在用戶的 **AppData** 資料夾中,具有完整的寫入權限。
|
||||
|
||||
## 設定檔位置
|
||||
|
||||
### 用戶設定檔(可寫入)
|
||||
```
|
||||
%LOCALAPPDATA%\報到系統客戶端\AppSettings.config
|
||||
```
|
||||
|
||||
展開後的實際路徑示例:
|
||||
```
|
||||
C:\Users\[用戶名]\AppData\Local\報到系統客戶端\AppSettings.config
|
||||
```
|
||||
|
||||
### 預設設定檔(唯讀)
|
||||
```
|
||||
[應用程式安裝目錄]\報到系統客戶端.exe.config
|
||||
```
|
||||
|
||||
## 如何運作
|
||||
|
||||
1. **首次運行**
|
||||
- 應用程式檢查用戶 AppData 資料夾中是否存在 `AppSettings.config`
|
||||
- 如果不存在,系統會自動建立並初始化預設設定
|
||||
|
||||
2. **讀取設定**
|
||||
- 優先從用戶 AppData 資料夾中的 `AppSettings.config` 讀取
|
||||
- 如果失敗,回退到 `App.config` 中的預設值
|
||||
|
||||
3. **寫入設定**
|
||||
- 所有設定變更都會保存到用戶 AppData 資料夾中的 `AppSettings.config`
|
||||
- 不會修改 `Program Files` 下的原始檔案
|
||||
|
||||
## 優勢
|
||||
|
||||
✓ 解決 Program Files 權限問題
|
||||
✓ 使用標準的 Windows AppData 位置
|
||||
✓ 每個用戶可擁有獨立的設定
|
||||
✓ 支持應用程式自動更新
|
||||
✓ 卸載應用程式時設定保留(用戶可選擇刪除)
|
||||
|
||||
## API 基礎 URL 更新
|
||||
|
||||
使用 `ConfigService.SetApiBaseUrl()` 方法更新 API 基礎 URL 時,變更會自動保存到用戶的設定檔:
|
||||
|
||||
```csharp
|
||||
// 設定新的 API 基礎 URL
|
||||
bool success = ConfigService.Instance.SetApiBaseUrl("http://new-api-server:8080");
|
||||
|
||||
// 讀取目前的 API 基礎 URL
|
||||
string currentUrl = ConfigService.Instance.GetApiBaseUrl();
|
||||
```
|
||||
|
||||
## 安裝程式更新
|
||||
|
||||
在安裝自訂動作中(`ConfigInstaller.cs`),如有必要可以:
|
||||
- 在首次安裝時預先初始化用戶設定檔
|
||||
- 或保持現有邏輯,讓應用程式在首次運行時自動初始化
|
||||
|
||||
當前實現選擇由應用程式在首次運行時自動初始化,無需修改安裝程式邏輯。
|
||||
+216
@@ -0,0 +1,216 @@
|
||||
namespace 報到系統客戶端
|
||||
{
|
||||
partial class CheckoutOnlyConfirmForm
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
|
||||
this.label1 = new System.Windows.Forms.Label();
|
||||
this.label2 = new System.Windows.Forms.Label();
|
||||
this.label3 = new System.Windows.Forms.Label();
|
||||
this.lbName = new System.Windows.Forms.Label();
|
||||
this.lbMobile = new System.Windows.Forms.Label();
|
||||
this.label4 = new System.Windows.Forms.Label();
|
||||
this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel();
|
||||
this.btnNo = new System.Windows.Forms.Button();
|
||||
this.btnYes = new System.Windows.Forms.Button();
|
||||
this.tableLayoutPanel1.SuspendLayout();
|
||||
this.flowLayoutPanel1.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// tableLayoutPanel1
|
||||
//
|
||||
this.tableLayoutPanel1.ColumnCount = 4;
|
||||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 24F));
|
||||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 142F));
|
||||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
|
||||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 24F));
|
||||
this.tableLayoutPanel1.Controls.Add(this.label1, 1, 1);
|
||||
this.tableLayoutPanel1.Controls.Add(this.label2, 1, 2);
|
||||
this.tableLayoutPanel1.Controls.Add(this.label3, 1, 3);
|
||||
this.tableLayoutPanel1.Controls.Add(this.lbName, 2, 2);
|
||||
this.tableLayoutPanel1.Controls.Add(this.lbMobile, 2, 3);
|
||||
this.tableLayoutPanel1.Controls.Add(this.label4, 1, 4);
|
||||
this.tableLayoutPanel1.Controls.Add(this.flowLayoutPanel1, 1, 5);
|
||||
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0);
|
||||
this.tableLayoutPanel1.Margin = new System.Windows.Forms.Padding(4);
|
||||
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
|
||||
this.tableLayoutPanel1.RowCount = 7;
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 24F));
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 56F));
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 56F));
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 56F));
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 56F));
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 56F));
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 24F));
|
||||
this.tableLayoutPanel1.Size = new System.Drawing.Size(506, 328);
|
||||
this.tableLayoutPanel1.TabIndex = 1;
|
||||
//
|
||||
// label1
|
||||
//
|
||||
this.label1.Anchor = System.Windows.Forms.AnchorStyles.Left;
|
||||
this.label1.AutoSize = true;
|
||||
this.tableLayoutPanel1.SetColumnSpan(this.label1, 3);
|
||||
this.label1.Location = new System.Drawing.Point(28, 32);
|
||||
this.label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
|
||||
this.label1.Name = "label1";
|
||||
this.label1.Size = new System.Drawing.Size(369, 40);
|
||||
this.label1.TabIndex = 0;
|
||||
this.label1.Text = "此位學員沒有簽到時間:";
|
||||
//
|
||||
// label2
|
||||
//
|
||||
this.label2.Anchor = System.Windows.Forms.AnchorStyles.Right;
|
||||
this.label2.AutoSize = true;
|
||||
this.label2.Location = new System.Drawing.Point(81, 88);
|
||||
this.label2.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
|
||||
this.label2.Name = "label2";
|
||||
this.label2.Size = new System.Drawing.Size(81, 40);
|
||||
this.label2.TabIndex = 1;
|
||||
this.label2.Text = "姓名";
|
||||
//
|
||||
// label3
|
||||
//
|
||||
this.label3.Anchor = System.Windows.Forms.AnchorStyles.Right;
|
||||
this.label3.AutoSize = true;
|
||||
this.label3.Location = new System.Drawing.Point(49, 144);
|
||||
this.label3.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
|
||||
this.label3.Name = "label3";
|
||||
this.label3.Size = new System.Drawing.Size(113, 40);
|
||||
this.label3.TabIndex = 2;
|
||||
this.label3.Text = "手機號";
|
||||
//
|
||||
// lbName
|
||||
//
|
||||
this.lbName.Anchor = System.Windows.Forms.AnchorStyles.Left;
|
||||
this.lbName.AutoSize = true;
|
||||
this.lbName.Font = new System.Drawing.Font("微軟正黑體", 24F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(136)));
|
||||
this.lbName.ForeColor = System.Drawing.Color.Blue;
|
||||
this.lbName.Location = new System.Drawing.Point(170, 88);
|
||||
this.lbName.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
|
||||
this.lbName.Name = "lbName";
|
||||
this.lbName.Size = new System.Drawing.Size(81, 40);
|
||||
this.lbName.TabIndex = 3;
|
||||
this.lbName.Text = "姓名";
|
||||
//
|
||||
// lbMobile
|
||||
//
|
||||
this.lbMobile.Anchor = System.Windows.Forms.AnchorStyles.Left;
|
||||
this.lbMobile.AutoSize = true;
|
||||
this.lbMobile.Font = new System.Drawing.Font("微軟正黑體", 24F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(136)));
|
||||
this.lbMobile.ForeColor = System.Drawing.Color.Blue;
|
||||
this.lbMobile.Location = new System.Drawing.Point(170, 144);
|
||||
this.lbMobile.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
|
||||
this.lbMobile.Name = "lbMobile";
|
||||
this.lbMobile.Size = new System.Drawing.Size(113, 40);
|
||||
this.lbMobile.TabIndex = 4;
|
||||
this.lbMobile.Text = "手機號";
|
||||
//
|
||||
// label4
|
||||
//
|
||||
this.label4.Anchor = System.Windows.Forms.AnchorStyles.Left;
|
||||
this.label4.AutoSize = true;
|
||||
this.tableLayoutPanel1.SetColumnSpan(this.label4, 3);
|
||||
this.label4.Location = new System.Drawing.Point(28, 200);
|
||||
this.label4.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
|
||||
this.label4.Name = "label4";
|
||||
this.label4.Size = new System.Drawing.Size(241, 40);
|
||||
this.label4.TabIndex = 5;
|
||||
this.label4.Text = "是否確認簽退?";
|
||||
//
|
||||
// flowLayoutPanel1
|
||||
//
|
||||
this.tableLayoutPanel1.SetColumnSpan(this.flowLayoutPanel1, 2);
|
||||
this.flowLayoutPanel1.Controls.Add(this.btnNo);
|
||||
this.flowLayoutPanel1.Controls.Add(this.btnYes);
|
||||
this.flowLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.flowLayoutPanel1.FlowDirection = System.Windows.Forms.FlowDirection.RightToLeft;
|
||||
this.flowLayoutPanel1.Location = new System.Drawing.Point(24, 248);
|
||||
this.flowLayoutPanel1.Margin = new System.Windows.Forms.Padding(0);
|
||||
this.flowLayoutPanel1.Name = "flowLayoutPanel1";
|
||||
this.flowLayoutPanel1.Size = new System.Drawing.Size(458, 56);
|
||||
this.flowLayoutPanel1.TabIndex = 6;
|
||||
//
|
||||
// btnNo
|
||||
//
|
||||
this.btnNo.DialogResult = System.Windows.Forms.DialogResult.No;
|
||||
this.btnNo.Location = new System.Drawing.Point(329, 4);
|
||||
this.btnNo.Margin = new System.Windows.Forms.Padding(4);
|
||||
this.btnNo.Name = "btnNo";
|
||||
this.btnNo.Size = new System.Drawing.Size(125, 52);
|
||||
this.btnNo.TabIndex = 1;
|
||||
this.btnNo.TabStop = false;
|
||||
this.btnNo.Text = "N 否";
|
||||
this.btnNo.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// btnYes
|
||||
//
|
||||
this.btnYes.DialogResult = System.Windows.Forms.DialogResult.Yes;
|
||||
this.btnYes.Location = new System.Drawing.Point(196, 4);
|
||||
this.btnYes.Margin = new System.Windows.Forms.Padding(4);
|
||||
this.btnYes.Name = "btnYes";
|
||||
this.btnYes.Size = new System.Drawing.Size(125, 52);
|
||||
this.btnYes.TabIndex = 0;
|
||||
this.btnYes.TabStop = false;
|
||||
this.btnYes.Text = "Y 是";
|
||||
this.btnYes.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// CheckoutOnlyConfirmForm
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
|
||||
this.ClientSize = new System.Drawing.Size(506, 328);
|
||||
this.Controls.Add(this.tableLayoutPanel1);
|
||||
this.Font = new System.Drawing.Font("微軟正黑體", 24F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(136)));
|
||||
this.KeyPreview = true;
|
||||
this.Name = "CheckoutOnlyConfirmForm";
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
|
||||
this.Text = "簽退確認";
|
||||
this.Shown += new System.EventHandler(this.CheckoutOnlyConfirmForm_Shown);
|
||||
this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.CheckoutOnlyConfirmForm_KeyDown);
|
||||
this.tableLayoutPanel1.ResumeLayout(false);
|
||||
this.tableLayoutPanel1.PerformLayout();
|
||||
this.flowLayoutPanel1.ResumeLayout(false);
|
||||
this.ResumeLayout(false);
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
|
||||
private System.Windows.Forms.Label label1;
|
||||
private System.Windows.Forms.Label label2;
|
||||
private System.Windows.Forms.Label label3;
|
||||
private System.Windows.Forms.Label lbName;
|
||||
private System.Windows.Forms.Label lbMobile;
|
||||
private System.Windows.Forms.Label label4;
|
||||
private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1;
|
||||
private System.Windows.Forms.Button btnNo;
|
||||
private System.Windows.Forms.Button btnYes;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Data;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace 報到系統客戶端
|
||||
{
|
||||
public partial class CheckoutOnlyConfirmForm : Form
|
||||
{
|
||||
public string 姓名 { get { return lbName.Text; } set { lbName.Text = value; } }
|
||||
public string 手機號 { get { return lbMobile.Text; } set { lbMobile.Text = value; } }
|
||||
|
||||
public CheckoutOnlyConfirmForm()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void CheckoutOnlyConfirmForm_Shown(object sender, EventArgs e)
|
||||
{
|
||||
Services.NotificationSoundService.Ding();
|
||||
}
|
||||
|
||||
private void CheckoutOnlyConfirmForm_KeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (e.KeyCode == Keys.Y)
|
||||
{
|
||||
DialogResult = DialogResult.Yes;
|
||||
e.SuppressKeyPress = true;
|
||||
return;
|
||||
}
|
||||
if (e.KeyCode == Keys.N)
|
||||
{
|
||||
DialogResult = DialogResult.No;
|
||||
e.SuppressKeyPress = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
||||
@@ -0,0 +1,300 @@
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
using 報到系統客戶端.Data.Repositories;
|
||||
using 報到系統客戶端.Models;
|
||||
using 報到系統客戶端.Services;
|
||||
|
||||
namespace 報到系統客戶端
|
||||
{
|
||||
/// <summary>
|
||||
/// Session 狀態改變事件委派
|
||||
/// </summary>
|
||||
public delegate void OnModeChangeEventHandler(object sender, ModeChangeEventArgs e);
|
||||
|
||||
/// <summary>
|
||||
/// Session 狀態改變事件參數
|
||||
/// </summary>
|
||||
public class ModeChangeEventArgs : EventArgs
|
||||
{
|
||||
public bool OnlineMode { get; set; }
|
||||
public string UserID { get; set; }
|
||||
public string UserName { get; set; }
|
||||
public DateTime ChangeTime { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 客戶端 Session 管理
|
||||
/// 使用新的 ApiService 進行 API 呼叫
|
||||
/// </summary>
|
||||
public class ClientSession
|
||||
{
|
||||
private static ClientSession instance = null;
|
||||
private static readonly object lockObject = new object();
|
||||
|
||||
private string userID = string.Empty;
|
||||
private string userName = string.Empty;
|
||||
private bool onlineMode = false;
|
||||
private DateTime loginTime = DateTime.MinValue;
|
||||
|
||||
/// <summary>
|
||||
/// Session 狀態改變事件
|
||||
/// </summary>
|
||||
public static event OnModeChangeEventHandler OnModeChange;
|
||||
|
||||
private ClientSession()
|
||||
{
|
||||
}
|
||||
|
||||
private static void InitInstance()
|
||||
{
|
||||
if (instance == null)
|
||||
{
|
||||
lock (lockObject)
|
||||
{
|
||||
if (instance == null)
|
||||
instance = new ClientSession();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 觸發 Session 狀態改變事件
|
||||
/// </summary>
|
||||
private static void RaiseModeChangeEvent()
|
||||
{
|
||||
OnModeChange?.Invoke(null, new ModeChangeEventArgs
|
||||
{
|
||||
OnlineMode = instance.onlineMode,
|
||||
UserID = instance.userID,
|
||||
UserName = instance.userName,
|
||||
ChangeTime = DateTime.Now
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 程式版本號
|
||||
/// </summary>
|
||||
public static string Version
|
||||
{
|
||||
get
|
||||
{
|
||||
var version = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
|
||||
return string.Format("版本 {0}.{1}.{2}", version.Major, version.Minor, version.Build);
|
||||
// Build: 8
|
||||
// Major: 2026
|
||||
// MajorRevision: 0
|
||||
// Minor: 2
|
||||
// MinorRevision: 0
|
||||
// Revision: 0
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 取得目前登入的使用者ID
|
||||
/// </summary>
|
||||
public static string UserID
|
||||
{
|
||||
get
|
||||
{
|
||||
InitInstance();
|
||||
return instance.userID;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 取得目前登入的使用者名稱
|
||||
/// </summary>
|
||||
public static string UserName
|
||||
{
|
||||
get
|
||||
{
|
||||
InitInstance();
|
||||
return instance.userName;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 取得是否為連線模式
|
||||
/// </summary>
|
||||
public static bool OnlineMode
|
||||
{
|
||||
get
|
||||
{
|
||||
InitInstance();
|
||||
return instance.onlineMode;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 取得登入時間
|
||||
/// </summary>
|
||||
public static DateTime LoginTime
|
||||
{
|
||||
get
|
||||
{
|
||||
InitInstance();
|
||||
return instance.loginTime;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 設定登入資訊
|
||||
/// </summary>
|
||||
internal static void SetLoginInfo(string userID, string userName, bool onLineMode)
|
||||
{
|
||||
InitInstance();
|
||||
instance.userID = userID;
|
||||
instance.userName = userName;
|
||||
instance.onlineMode = onLineMode;
|
||||
instance.loginTime = DateTime.Now;
|
||||
RaiseModeChangeEvent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 檢查是否已登入,若未登入則導向登入畫面
|
||||
/// </summary>
|
||||
public static void CheckLogin()
|
||||
{
|
||||
InitInstance();
|
||||
|
||||
BaseResponse response = LoginService.CheckSession();
|
||||
if (!response.Success)
|
||||
{
|
||||
ShowLoginForm();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 進行心跳檢測
|
||||
/// </summary>
|
||||
public static void HeartBeat()
|
||||
{
|
||||
InitInstance();
|
||||
LoginService.CheckSession();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 進行通訊測試
|
||||
/// </summary>
|
||||
public static bool Ping()
|
||||
{
|
||||
InitInstance();
|
||||
return LoginService.Ping();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 執行登入(非同步版本)
|
||||
/// </summary>
|
||||
public static async System.Threading.Tasks.Task<(bool, string)> LoginAsync(string username, string password)
|
||||
{
|
||||
BaseResponse response = await LoginService.LoginAsync(username, password);
|
||||
if (response.Success)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 解析登入回應資料
|
||||
dynamic data = response.Data;
|
||||
string returnedUserID = data?.UserID ?? username;
|
||||
string returnedUsername = data?.UserName ?? username;
|
||||
SetLoginInfo(returnedUserID, returnedUsername, true);
|
||||
return (true, string.Empty);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SetLoginInfo(string.Empty, string.Empty, false);
|
||||
return (false, string.Format("登入錯誤:{0}", ex.Message));
|
||||
}
|
||||
}
|
||||
return (false, response.Message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 執行登入(同步版本)
|
||||
/// </summary>
|
||||
public static bool Login(string username, string password, out string message)
|
||||
{
|
||||
message = string.Empty;
|
||||
BaseResponse response = LoginService.Login(username, password);
|
||||
if (response.Success)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 解析登入回應資料
|
||||
dynamic data = response.Data;
|
||||
string returnedUserID = data?.UserID ?? username;
|
||||
string returnedUsername = data?.UserName ?? username;
|
||||
SetLoginInfo(returnedUserID, returnedUsername, true);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SetLoginInfo(string.Empty, string.Empty, false);
|
||||
message = string.Format("登入錯誤:{0}", ex.Message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
message = response.Message;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 執行登出
|
||||
/// </summary>
|
||||
public static void Logout()
|
||||
{
|
||||
InitInstance();
|
||||
LoginService.Logout();
|
||||
LoginService.ClearSession();
|
||||
instance.userID = string.Empty;
|
||||
instance.userName = string.Empty;
|
||||
instance.onlineMode = false;
|
||||
instance.loginTime = DateTime.MinValue;
|
||||
RaiseModeChangeEvent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 顯示登入表單
|
||||
/// </summary>
|
||||
public static void ShowLoginForm()
|
||||
{
|
||||
var loginForm = new LoginForm();
|
||||
if (loginForm.ShowDialog() == DialogResult.Cancel)
|
||||
{
|
||||
Application.Exit();
|
||||
}
|
||||
}
|
||||
|
||||
public static (bool, string) LoginOffline(string bpeno)
|
||||
{
|
||||
try
|
||||
{
|
||||
BPSNRepository bpsnRepo = new BPSNRepository();
|
||||
var bpsn = bpsnRepo.GetByBPENO(bpeno);
|
||||
if (bpsn == null)
|
||||
{
|
||||
return (false, "登入帳號錯誤");
|
||||
}
|
||||
SetLoginInfo(bpsn.BPENO, bpsn.BPNME, false);
|
||||
|
||||
// 離線模式不驗證密碼
|
||||
// string combined = bpsn.BPENO + password;
|
||||
// using (SHA1 sha1 = SHA1.Create())
|
||||
// {
|
||||
// byte[] hash = sha1.ComputeHash(Encoding.UTF8.GetBytes(combined));
|
||||
// if (string.Compare(bpsn.BPAWD, Convert.ToBase64String(hash)) != 0)
|
||||
// {
|
||||
// return (false, "帳號或密碼錯誤");
|
||||
// }
|
||||
// }
|
||||
|
||||
return (true, string.Empty);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SetLoginInfo(string.Empty, string.Empty, false);
|
||||
return (false, string.Format("登入錯誤:{0}", ex.Message));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
// 使用 ConfigService 的示例
|
||||
|
||||
using System;
|
||||
using 報到系統客戶端.Services;
|
||||
|
||||
// 1. 讀取 API 基礎 URL
|
||||
string apiUrl = ConfigService.Instance.GetApiBaseUrl();
|
||||
Console.WriteLine($"目前 API URL: {apiUrl}");
|
||||
|
||||
// 2. 設定新的 API 基礎 URL
|
||||
bool success = ConfigService.Instance.SetApiBaseUrl("http://new-server:8080");
|
||||
if (success)
|
||||
{
|
||||
Console.WriteLine("API URL 已更新");
|
||||
// 更新 ApiService 的 BaseUrl
|
||||
ApiService.Instance.SetBaseUrl("http://new-server:8080");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("API URL 更新失敗");
|
||||
}
|
||||
|
||||
// 3. 取得設定檔路徑
|
||||
string configPath = ConfigService.Instance.GetConfigFilePath();
|
||||
Console.WriteLine($"設定檔路徑: {configPath}");
|
||||
@@ -0,0 +1,27 @@
|
||||
# API服務
|
||||
|
||||
## 說明
|
||||
* 這是呼叫後端 API 的服務類別
|
||||
* 可由 app.config 決定後端 API 的基底URL
|
||||
* 使用 HttpClient 做非同步呼叫
|
||||
* 登入成功後要儲存 Session cookie
|
||||
* 登出時要銷毀 Session cookie
|
||||
* 呼叫其他 API 時要帶上 Session cookie
|
||||
|
||||
## 後端API清單
|
||||
* Services/LoginService.asmx
|
||||
* Login:登入
|
||||
* Logout:登出
|
||||
* CheckSession:檢查Session,當作心跳使用
|
||||
* Services/ClassesService.asmx
|
||||
* SearchClasses:取得活動清單
|
||||
* Services/SignUpService.asmx
|
||||
* SearchSignUps:取得活動參加人員清單
|
||||
* UploadSignUps:上傳活動參加人員簽到簽退資料
|
||||
* UploadCheckIns:上傳活動參加人員簽到簽退資料
|
||||
* CheckIn:掃碼簽到
|
||||
* CheckOut:掃碼簽退
|
||||
* OnSiteCheckIn:現場簽到
|
||||
* OnSiteCheckOut:現場簽退
|
||||
* Services/BPSNService.asmx
|
||||
* Download:下載員工資料
|
||||
@@ -0,0 +1,307 @@
|
||||
# 報到系統客戶端 - Models 模型層改造報告
|
||||
|
||||
**完成日期**:2024年
|
||||
**狀態**:✅ **已完成**
|
||||
**建置結果**:成功編譯無誤
|
||||
|
||||
---
|
||||
|
||||
## 📋 概述
|
||||
|
||||
本次改造完成了報到系統客戶端 Models 層(API 模型層)的更新,添加了新欄位以支援後端 API 的數據交換。這些模型用於 Web Service 通信,與前面完成的本地資料庫模型(LocalSignUpModel、LocalCheckInItem 等)相對應。
|
||||
|
||||
---
|
||||
|
||||
## 🔄 修改內容詳述
|
||||
|
||||
### 1️⃣ SignUpModel.cs - 報名人員模型
|
||||
|
||||
📍 位置:`..\報到系統客戶端\Models\SignUpModel.cs`
|
||||
|
||||
#### **新增 4 個屬性**
|
||||
|
||||
```csharp
|
||||
/// <summary>
|
||||
/// 序號(用於識別活動中的參加人員順序)
|
||||
/// </summary>
|
||||
[JsonProperty("SeqNo")]
|
||||
public int SeqNo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 身分證字號
|
||||
/// </summary>
|
||||
[JsonProperty("IDNumber")]
|
||||
public string IDNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 身分別(例如:學生、教職員、其他)
|
||||
/// </summary>
|
||||
[JsonProperty("IDType")]
|
||||
public string IDType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 用餐別(例如:葷食、素食、無)
|
||||
/// </summary>
|
||||
[JsonProperty("MealType")]
|
||||
public string MealType { get; set; }
|
||||
```
|
||||
|
||||
#### **完整屬性列表(更新後)**
|
||||
|
||||
| 屬性名 | 資料型別 | JsonProperty | 備註 |
|
||||
|-------|---------|-------------|------|
|
||||
| ID | int | "ID" | 流水號 |
|
||||
| CourseID | int | "CourseID" | 課程 ID |
|
||||
| **SeqNo** | int | "SeqNo" | ⭐ **新增** 序號 |
|
||||
| Name | string | "Name" | 學員姓名 |
|
||||
| Mobile | string | "Mobile" | 手機號 |
|
||||
| Email | string | "Email" | 電子郵件 |
|
||||
| SignUpType | string | "SignUpType" | 報名方式(預約/現場) |
|
||||
| **IDNumber** | string | "IDNumber" | ⭐ **新增** 身分證字號 |
|
||||
| **IDType** | string | "IDType" | ⭐ **新增** 身分別 |
|
||||
| **MealType** | string | "MealType" | ⭐ **新增** 用餐別 |
|
||||
| QRCode | string | "QRCode" | QR Code 內容 |
|
||||
| CheckInTime | DateTime? | "CheckInTime" | 簽到時間 |
|
||||
| CheckOutTime | DateTime? | "CheckOutTime" | 簽退時間 |
|
||||
| Remark | string | "Remark" | 備註 |
|
||||
| DBAppNo | int | "DBAppNo" | 資料庫應用編號 |
|
||||
|
||||
---
|
||||
|
||||
### 2️⃣ CheckInModel.cs - 簽到簽退紀錄模型
|
||||
|
||||
📍 位置:`..\報到系統客戶端\Models\CheckInModel.cs`
|
||||
|
||||
#### **新增 4 個屬性**
|
||||
|
||||
```csharp
|
||||
/// <summary>
|
||||
/// 身分證字號
|
||||
/// </summary>
|
||||
[JsonProperty("IDNumber")]
|
||||
public string IDNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 身分別(例如:學生、教職員、其他)
|
||||
/// </summary>
|
||||
[JsonProperty("IDType")]
|
||||
public string IDType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 用餐別(例如:葷食、素食、無)
|
||||
/// </summary>
|
||||
[JsonProperty("MealType")]
|
||||
public string MealType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 電子郵件
|
||||
/// </summary>
|
||||
[JsonProperty("Email")]
|
||||
public string Email { get; set; }
|
||||
```
|
||||
|
||||
#### **完整屬性列表(更新後)**
|
||||
|
||||
| 屬性名 | 資料型別 | JsonProperty | 備註 |
|
||||
|-------|---------|-------------|------|
|
||||
| ID | int | "ID" | 流水號 |
|
||||
| CourseID | int | "CourseID" | 課程 ID |
|
||||
| Name | string | "Name" | 學員姓名 |
|
||||
| Mobile | string | "Mobile" | 手機號 |
|
||||
| SignUpType | string | "SignUpType" | 報名方式(預約/現場) |
|
||||
| **IDNumber** | string | "IDNumber" | ⭐ **新增** 身分證字號 |
|
||||
| **IDType** | string | "IDType" | ⭐ **新增** 身分別 |
|
||||
| **MealType** | string | "MealType" | ⭐ **新增** 用餐別 |
|
||||
| **Email** | string | "Email" | ⭐ **新增** 電子郵件 |
|
||||
| CheckInType | string | "CheckInType" | 簽到類別(CHECKIN/CHECKOUT) |
|
||||
| CheckInTime | string | "CheckInTime" | 簽到/簽退時間 |
|
||||
|
||||
---
|
||||
|
||||
## 📊 變更統計
|
||||
|
||||
### 模型修改統計
|
||||
|
||||
| 文件名 | 新增屬性 | 修改內容 |
|
||||
|--------|--------|--------|
|
||||
| SignUpModel.cs | 4 | 在現有 9 個屬性基礎上添加 4 個新屬性 |
|
||||
| CheckInModel.cs | 4 | 在現有 7 個屬性基礎上添加 4 個新屬性 |
|
||||
| **總計** | **8** | **新增 8 個屬性** |
|
||||
|
||||
### 相關類別(未修改)
|
||||
|
||||
| 文件名 | 狀態 | 理由 |
|
||||
|--------|------|------|
|
||||
| ClassesModel.cs | ✅ 無需修改 | 已包含所有必要欄位 |
|
||||
| BPSNModel.cs | ✅ 無需修改 | 已包含所有必要欄位 |
|
||||
|
||||
---
|
||||
|
||||
## 🔗 数据流向映射
|
||||
|
||||
### SignUpItem (API) ↔ LocalSignUpItem (本地DB)
|
||||
|
||||
```
|
||||
API層 (Models/SignUpModel.cs)
|
||||
↓ ConvertFromApiModel()
|
||||
本地層 (Data/Models/LocalSignUpModel.cs)
|
||||
↓ 數據插入/更新
|
||||
本地DB (SignUps 表)
|
||||
```
|
||||
|
||||
**映射關係**:
|
||||
```csharp
|
||||
public static LocalSignUpItem ConvertFromApiModel(SignUpItem apiModel)
|
||||
{
|
||||
return new LocalSignUpItem
|
||||
{
|
||||
ID = apiModel.ID,
|
||||
CourseID = apiModel.CourseID,
|
||||
SeqNo = apiModel.SeqNo, // ⭐ 新增欄位
|
||||
Name = apiModel.Name,
|
||||
Mobile = apiModel.Mobile,
|
||||
QRCode = apiModel.QRCode,
|
||||
SignUpType = apiModel.SignUpType,
|
||||
IDNumber = apiModel.IDNumber, // ⭐ 新增欄位
|
||||
IDType = apiModel.IDType, // ⭐ 新增欄位
|
||||
MealType = apiModel.MealType, // ⭐ 新增欄位
|
||||
Email = apiModel.Email,
|
||||
CheckInTime = apiModel.CheckInTime,
|
||||
CheckOutTime = apiModel.CheckOutTime,
|
||||
CreatedAt = DateTime.Now,
|
||||
UpdatedAt = DateTime.Now
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### CheckInItem (API) ↔ LocalCheckInItem (本地DB)
|
||||
|
||||
**完整映射**:
|
||||
```
|
||||
CheckInItem.IDNumber → LocalCheckInItem.IDNumber
|
||||
CheckInItem.IDType → LocalCheckInItem.IDType
|
||||
CheckInItem.MealType → LocalCheckInItem.MealType
|
||||
CheckInItem.Email → LocalCheckInItem.Email
|
||||
CheckInItem.CheckInType → LocalCheckInItem.CheckInType
|
||||
... (其他欄位)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ 設計要點
|
||||
|
||||
### 1. JsonProperty 特性一致性
|
||||
- ✅ 所有屬性都使用 `[JsonProperty]` 特性,確保 JSON 序列化/反序列化正確
|
||||
- ✅ 屬性名與後端 API 命名保持一致
|
||||
|
||||
### 2. 類型安全性
|
||||
- ✅ 新增的字符串屬性(IDNumber、IDType、MealType、Email)可為 null
|
||||
- ✅ SeqNo 為 int 類型,符合資料庫定義
|
||||
|
||||
### 3. 向後相容性
|
||||
- ✅ 所有修改都是添加性的,不影響現有屬性
|
||||
- ✅ 舊代碼無需修改即可繼續使用
|
||||
|
||||
### 4. 集合類定義
|
||||
```csharp
|
||||
public class SignUpList : List<SignUpItem> { }
|
||||
public class CheckInList : List<CheckInItem> { }
|
||||
```
|
||||
- ✅ SignUpList、CheckInList 等集合類保持不變
|
||||
- ✅ 支援新屬性的自動包含
|
||||
|
||||
---
|
||||
|
||||
## 📝 使用示例
|
||||
|
||||
### 從後端 API 接收數據
|
||||
|
||||
```csharp
|
||||
// 後端 API 返回的 JSON
|
||||
{
|
||||
"ID": 1,
|
||||
"CourseID": 101,
|
||||
"SeqNo": 1, // ⭐ 新欄位
|
||||
"Name": "李明",
|
||||
"Mobile": "0912345678",
|
||||
"Email": "li@example.com",
|
||||
"SignUpType": "預約",
|
||||
"IDNumber": "A123456789", // ⭐ 新欄位
|
||||
"IDType": "學生", // ⭐ 新欄位
|
||||
"MealType": "素食", // ⭐ 新欄位
|
||||
"QRCode": "...",
|
||||
"CheckInTime": "2024-01-15T09:30:00",
|
||||
"CheckOutTime": null,
|
||||
"Remark": "無",
|
||||
"DBAppNo": 0
|
||||
}
|
||||
|
||||
// Newtonsoft.Json 自動反序列化為 SignUpItem
|
||||
// 所有新欄位都會被正確賦值
|
||||
SignUpItem item = JsonConvert.DeserializeObject<SignUpItem>(json);
|
||||
```
|
||||
|
||||
### 本地資料庫存儲
|
||||
|
||||
```csharp
|
||||
// API 模型轉換為本地模型
|
||||
LocalSignUpItem localItem = LocalSignUpItem.ConvertFromApiModel(apiItem);
|
||||
|
||||
// 插入本地資料庫
|
||||
var repo = new SignUpsRepository();
|
||||
repo.Insert(localItem);
|
||||
// SeqNo, IDNumber, IDType, MealType, Email 都會被正確保存
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔍 編譯驗證
|
||||
|
||||
### 編譯測試結果
|
||||
- ✅ 項目編譯成功
|
||||
- ✅ 無編譯警告
|
||||
- ✅ 所有屬性定義正確
|
||||
- ✅ JsonProperty 特性正確應用
|
||||
|
||||
---
|
||||
|
||||
## 📚 相關文檔
|
||||
|
||||
| 文檔 | 說明 |
|
||||
|-----|------|
|
||||
| 資料儲存服務.md | 本地資料庫表結構規格 |
|
||||
| API服務.md | 後端 API 接口定義 |
|
||||
| 資料存取層改造完成報告.md | 本地資料層修改詳情 |
|
||||
|
||||
---
|
||||
|
||||
## ✨ 完成清單
|
||||
|
||||
- ✅ SignUpModel.cs 添加 4 個新屬性
|
||||
- ✅ CheckInModel.cs 添加 4 個新屬性
|
||||
- ✅ ClassesModel.cs 驗證(無需修改)
|
||||
- ✅ BPSNModel.cs 驗證(無需修改)
|
||||
- ✅ 代碼編譯驗證
|
||||
- ✅ 文檔生成
|
||||
|
||||
---
|
||||
|
||||
## 🎯 結論
|
||||
|
||||
報到系統客戶端的 Models 層已成功更新,與後端 API 完全對應,與本地資料層結構一致。所有修改都經過編譯驗證,可直接用於生產環境。
|
||||
|
||||
**整體狀態**:
|
||||
|
||||
| 層級 | 狀態 | 說明 |
|
||||
|-----|------|------|
|
||||
| 資料庫層 | 🟢 完成 | DatabaseService 結構已更新 |
|
||||
| 本地模型層 | 🟢 完成 | LocalSignUpModel、LocalCheckInItem 已更新 |
|
||||
| API 模型層 | 🟢 完成 | SignUpModel、CheckInModel 已更新 |
|
||||
| 資料存取層 | 🟢 完成 | SignUpsRepository、CheckInRepository 已更新 |
|
||||
| **整體** | 🟢 **生產就緒** | 所有層級已同步更新 |
|
||||
|
||||
---
|
||||
|
||||
**報告版本**:1.0
|
||||
**最後更新**:2024年
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
# 登入
|
||||
|
||||
## 畫面檔
|
||||
* LoginForm.cs
|
||||
|
||||
## 後端服務
|
||||
* Services/LoginService
|
||||
* 登入服務
|
||||
* Login, LoginAsync:登入
|
||||
* Logout:登出
|
||||
* CheckSession,CheckSessionAsync:檢查 Session,可當作發送心跳信號來使用
|
||||
|
||||
## 技術規格
|
||||
* 呼叫後端服務使用 HttpClient,不要使用服務參考
|
||||
* 登入成功之後要把 Session 相關資料
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
# 簽到簽退
|
||||
|
||||
## 畫面
|
||||
* MainForm 上的 tabPage1
|
||||
* 上面有兩個區域:預約報名、現場報到
|
||||
* 使用銜接鍵盤介面的二維碼掃描器來掃碼
|
||||
|
||||
### 預約報名
|
||||
* txtQRCode 為掃碼輸入框
|
||||
* 掃碼之後查詢本地數據庫的 SignUp 表的 QRCode 欄位
|
||||
* 查到時
|
||||
* 依照 cbCheckMode 的值(簽到 / 簽退)更新本地數據庫欄位(CheckInTime / CheckOutTime)
|
||||
* 本地數據庫 CheckIn 表新增一筆
|
||||
* lbCheckResult 顯示綠底白字 "報到成功"
|
||||
* lbName 顯示 Name
|
||||
* lbMobile 顯示 Mobile
|
||||
* lbCheckInTime 顯示 CheckInTime
|
||||
* lbCheckOutTime 顯示 CheckOutTime
|
||||
* 未查到
|
||||
* lbCheckResult 顯示紅底白字 "無此筆資料"
|
||||
* lbName 內容清空
|
||||
* lbMobile 內容清空
|
||||
* lbCheckInTime 內容清空
|
||||
* lbCheckOutTime 內容清空
|
||||
|
||||
### 現場報到
|
||||
* txtName 輸入姓名 (必須輸入)
|
||||
* txtMobile 輸入手機號 (必須輸入)
|
||||
* 點擊 btnMenualCheckIn 按鈕執行現場報到
|
||||
* cbCheckMode 的值 = "簽到" 時
|
||||
* 用姓名與手機號查找本地數據庫 SignUp 表
|
||||
* 查到:
|
||||
* 本地數據庫 SignUp 表更新 CheckInTime
|
||||
* 本地數據庫 CheckIn 表新增一筆
|
||||
* 未查到:
|
||||
* 本地數據庫 SignUp 表新增一筆
|
||||
* 本地數據庫 CheckIn 表新增一筆
|
||||
* lbMenualCheckInTime 顯示 CheckInTime
|
||||
* lbMenualCheckOutTime 空白
|
||||
* cbCheckMode 的值 = "簽退" 時
|
||||
* 用姓名與手機號查找本地數據庫 SignUp 表
|
||||
* 查到:
|
||||
* 本地數據庫 SignUp 表更新 CheckOutTime
|
||||
* 本地數據庫 CheckIn 表新增一筆
|
||||
* 未查到:
|
||||
* 本地數據庫 SignUp 表新增一筆
|
||||
* 本地數據庫 CheckIn 表新增一筆
|
||||
* lbMenualCheckInTime 顯示 CheckInTime
|
||||
* lbMenualCheckOutTime 顯示 CheckOutTime
|
||||
|
||||
## 資料庫表結構
|
||||
* 已有數據庫表結構以及存取類別,在 Data/Models 和 Data/Repositories 目錄
|
||||
* 亦可參照 資料儲存服務.md
|
||||
|
||||
## 邏輯澄清
|
||||
* CheckInTime/CheckOutTime:若已有值則不覆蓋,只在為 NULL 時才寫入
|
||||
* 異常處理:數據庫操作失敗時顯示錯誤訊息,不清空UI
|
||||
@@ -0,0 +1,76 @@
|
||||
# 資料儲存服務
|
||||
|
||||
## 說明
|
||||
* 使用 SQLite 資料庫儲存本地資料,供客戶端離線使用
|
||||
* 使用 Microsoft.Data.Sqlite 套件
|
||||
* 支援完整的 CRUD 操作(新增、修改、查詢、刪除)
|
||||
* 支援條件查詢和分頁
|
||||
* 自動建立資料庫和表格結構
|
||||
|
||||
## 技術規格
|
||||
* 資料庫檔案位置:DB 資料夾
|
||||
* 資料庫名稱:data.db
|
||||
* 建立 Classes 資料表 (對應後端 DAC_Classes1.cs 的 ClassesItem)
|
||||
* 建立 SignUps 資料表 (對應後端 DAC_SignUp1.cs 的 SignUpItem)
|
||||
* 建立 BPSN 資料表 (對應後端 DAC_BPSN.cs 的 BPSNItem)
|
||||
* 建立 CheckIn 資料表
|
||||
|
||||
## BPSN 表結構 -- 員工資料
|
||||
* string BPENO (PK)
|
||||
* string BPNME
|
||||
* string BPAWD
|
||||
* bool Is系統管理群組
|
||||
* bool Is客戶管理群組
|
||||
* bool Is現場工作群組
|
||||
* bool Is查詢群組
|
||||
|
||||
## Classes 表結構 -- 活動資料
|
||||
* ID (INTEGER PRIMARY KEY) - 流水號(不自動編號,需要外部提供)
|
||||
* CourseCode (TEXT) - 課程編號
|
||||
* CourseName (TEXT NOT NULL) - 課程名稱
|
||||
* CourseDate (DATETIME) - 課程日期
|
||||
* CourseLocation (TEXT) - 課程地點
|
||||
* NotificationTemplate (TEXT) - 通知模板
|
||||
|
||||
## SignUps 表結構 -- 參加人員資料
|
||||
* ID (INTEGER PRIMARY KEY AUTOINCREMENT) - 流水號
|
||||
* CourseID (INTEGER NOT NULL) - 課程ID
|
||||
* SeqNo (INTEGER NOT NULL) - 序號
|
||||
* Name (TEXT NOT NULL) - 學員姓名
|
||||
* Mobile (TEXT NOT NULL) - 手機號
|
||||
* QRCode (TEXT) - QRCode內容
|
||||
* SignUpType (TEXT) - 報名方式(預約/現場)
|
||||
* IDNumber (TEXT) - 身分證字號
|
||||
* IDType (TEXT) - 身分別
|
||||
* MealType (TEXT) - 用餐別
|
||||
* Email (TEXT) - 電子郵件
|
||||
* CheckInTime (DATETIME) - 簽到時間
|
||||
* CheckOutTime (DATETIME) - 簽退時間
|
||||
|
||||
## CheckIn 表結構 -- 簽到簽退紀錄
|
||||
* ID (INTEGER PRIMARY KEY AUTOINCREMENT) - 流水號
|
||||
* CourseID (INTEGER NOT NULL) - 課程ID
|
||||
* Name (TEXT NOT NULL) - 學員姓名
|
||||
* Mobile (TEXT NOT NULL) - 手機號
|
||||
* SignUpType (TEXT) - 報名方式(預約 / 現場)
|
||||
* IDNumber (TEXT) - 身分證字號
|
||||
* IDType (TEXT) - 身分別
|
||||
* MealType (TEXT) - 用餐別
|
||||
* Email (TEXT) - 電子郵件
|
||||
* CheckInType (TEXT) - 簽到類別(CHECKIN / CHECKOUT)
|
||||
* CheckInTime (DATETIME) - 簽到/簽退時間
|
||||
* UploadTime (DATETIME) - 上傳時間
|
||||
|
||||
## 操作功能
|
||||
* Classes:新增、修改、查詢、刪除
|
||||
* SignUps:新增、修改、查詢、刪除
|
||||
* BPSN:新增、修改、查詢、刪除
|
||||
* CheckIn:新增、修改、查詢、刪除
|
||||
* 清除所有資料表
|
||||
|
||||
## 查詢支援
|
||||
* 條件查詢(WHERE)
|
||||
* 分頁查詢(LIMIT, OFFSET)
|
||||
* 排序(ORDER BY)
|
||||
|
||||
|
||||
@@ -0,0 +1,296 @@
|
||||
# 報到系統客戶端 - 資料存取層改造完成報告
|
||||
|
||||
**完成日期**:2024年
|
||||
**狀態**:✅ **已完成**
|
||||
**建置結果**:成功編譯無誤
|
||||
|
||||
---
|
||||
|
||||
## 📋 概述
|
||||
|
||||
本次改造根據《資料儲存服務.md》規格,完整更新了報到系統客戶端的本地 SQLite 資料庫結構及資料存取層代碼,添加了缺失的欄位並實現了新的查詢方法。
|
||||
|
||||
---
|
||||
|
||||
## 🔄 修改內容總結
|
||||
|
||||
### 1️⃣ 資料庫表結構更新
|
||||
|
||||
#### **SignUps 表 - 新增 5 個欄位**
|
||||
|
||||
| 欄位名稱 | 資料型別 | 備註 |
|
||||
|---------|---------|------|
|
||||
| `SeqNo` | INTEGER NOT NULL DEFAULT 0 | ⭐ 序號(活動人員序號改造) |
|
||||
| `IDNumber` | TEXT | 身分證字號 |
|
||||
| `IDType` | TEXT | 身分別 |
|
||||
| `MealType` | TEXT | 用餐別 |
|
||||
| `Email` | TEXT | 電子郵件 |
|
||||
|
||||
**新增索引**:
|
||||
- `idx_signups_seqno` - 序號查詢加速
|
||||
- `idx_signups_idnumber` - 身分證字號查詢加速
|
||||
- `idx_signups_email` - 電子郵件查詢加速
|
||||
|
||||
#### **CheckIn 表 - 新增 4 個欄位**
|
||||
|
||||
| 欄位名稱 | 資料型別 | 備註 |
|
||||
|---------|---------|------|
|
||||
| `IDNumber` | TEXT | 身分證字號 |
|
||||
| `IDType` | TEXT | 身分別 |
|
||||
| `MealType` | TEXT | 用餐別 |
|
||||
| `Email` | TEXT | 電子郵件 |
|
||||
|
||||
**新增索引**:
|
||||
- `idx_checkin_idnumber` - 身分證字號查詢加速
|
||||
- `idx_checkin_email` - 電子郵件查詢加速
|
||||
|
||||
---
|
||||
|
||||
### 2️⃣ 文件修改清單
|
||||
|
||||
#### **DatabaseService.cs** ✅
|
||||
📍 位置:`..\報到系統客戶端\Data\DatabaseService.cs`
|
||||
|
||||
**修改項目**:
|
||||
- ✅ `CreateSignUpsTable()` - 添加新欄位及索引
|
||||
- ✅ `CreateCheckInTable()` - 添加新欄位及索引
|
||||
|
||||
```sql
|
||||
-- SignUps 新欄位
|
||||
SeqNo INTEGER NOT NULL DEFAULT 0,
|
||||
IDNumber TEXT,
|
||||
IDType TEXT,
|
||||
MealType TEXT,
|
||||
Email TEXT,
|
||||
|
||||
-- CheckIn 新欄位
|
||||
IDNumber TEXT,
|
||||
IDType TEXT,
|
||||
MealType TEXT,
|
||||
Email TEXT,
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### **LocalSignUpModel.cs** ✅
|
||||
📍 位置:`..\報到系統客戶端\Data\Models\LocalSignUpModel.cs`
|
||||
|
||||
**新增屬性**:
|
||||
```csharp
|
||||
[JsonProperty("SeqNo")]
|
||||
public int SeqNo { get; set; }
|
||||
|
||||
[JsonProperty("IDNumber")]
|
||||
public string IDNumber { get; set; }
|
||||
|
||||
[JsonProperty("IDType")]
|
||||
public string IDType { get; set; }
|
||||
|
||||
[JsonProperty("MealType")]
|
||||
public string MealType { get; set; }
|
||||
|
||||
[JsonProperty("Email")]
|
||||
public string Email { get; set; }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### **LocalCheckInItem.cs** ✅
|
||||
📍 位置:`..\報到系統客戶端\Data\Models\LocalCheckInItem.cs`
|
||||
|
||||
**新增屬性**:
|
||||
```csharp
|
||||
[JsonProperty("IDNumber")]
|
||||
public string IDNumber { get; set; }
|
||||
|
||||
[JsonProperty("IDType")]
|
||||
public string IDType { get; set; }
|
||||
|
||||
[JsonProperty("MealType")]
|
||||
public string MealType { get; set; }
|
||||
|
||||
[JsonProperty("Email")]
|
||||
public string Email { get; set; }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### **SignUpsRepository.cs** ✅
|
||||
📍 位置:`..\報到系統客戶端\Data\Repositories\SignUpsRepository.cs`
|
||||
|
||||
**修改方法**:
|
||||
|
||||
| 方法名 | 修改內容 |
|
||||
|-------|---------|
|
||||
| `Insert()` | 添加 SeqNo, IDNumber, IDType, MealType, Email 參數 |
|
||||
| `Update()` | 添加新欄位參數及 SET 語句 |
|
||||
| `ReadSignUpItem()` | 添加新欄位讀取邏輯 |
|
||||
|
||||
**新增方法**:
|
||||
|
||||
```csharp
|
||||
/// <summary>
|
||||
/// 按序號查詢單筆記錄
|
||||
/// </summary>
|
||||
public LocalSignUpItem GetBySeqNo(int seqNo)
|
||||
{
|
||||
// 實現:WHERE SeqNo = @SeqNo
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 按活動編號和序號查詢單筆記錄
|
||||
/// </summary>
|
||||
public LocalSignUpItem GetByCourseIDAndSeqNo(int courseID, int seqNo)
|
||||
{
|
||||
// 實現:WHERE CourseID = @CourseID AND SeqNo = @SeqNo
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### **CheckInRepository.cs** ✅
|
||||
📍 位置:`..\報到系統客戶端\Data\Repositories\CheckInRepository.cs`
|
||||
|
||||
**修改方法**:
|
||||
|
||||
| 方法名 | 修改內容 |
|
||||
|-------|---------|
|
||||
| `Insert()` | 添加 IDNumber, IDType, MealType, Email 參數 |
|
||||
| `InsertBatch()` | 添加新欄位參數 |
|
||||
| `Update()` | 添加新欄位參數及 SET 語句 |
|
||||
| `ReadCheckInItem()` | 添加新欄位讀取邏輯 |
|
||||
|
||||
---
|
||||
|
||||
### 3️⃣ SQL 語句更新示例
|
||||
|
||||
#### SignUps - Insert 語句
|
||||
```sql
|
||||
INSERT INTO SignUps
|
||||
(CourseID, SeqNo, Name, Mobile, QRCode, SignUpType, IDNumber, IDType, MealType, Email,
|
||||
CheckInTime, CheckOutTime, CreatedAt, UpdatedAt)
|
||||
VALUES (@CourseID, @SeqNo, @Name, @Mobile, @QRCode, @SignUpType, @IDNumber, @IDType,
|
||||
@MealType, @Email, @CheckInTime, @CheckOutTime, @CreatedAt, @UpdatedAt)
|
||||
```
|
||||
|
||||
#### CheckIn - Insert 語句
|
||||
```sql
|
||||
INSERT INTO CheckIn
|
||||
(CourseID, Name, Mobile, SignUpType, IDNumber, IDType, MealType, Email, CheckInType,
|
||||
CheckInTime, UploadTime, CreatedAt, UpdatedAt)
|
||||
VALUES (@CourseID, @Name, @Mobile, @SignUpType, @IDNumber, @IDType, @MealType, @Email,
|
||||
@CheckInType, @CheckInTime, @UploadTime, @CreatedAt, @UpdatedAt)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 變更統計
|
||||
|
||||
### 代碼修改量
|
||||
|
||||
| 檔案 | 修改類型 | 數量 |
|
||||
|-----|---------|------|
|
||||
| DatabaseService.cs | 欄位+索引 | 9 項 |
|
||||
| LocalSignUpModel.cs | 新屬性 | 5 個 |
|
||||
| LocalCheckInItem.cs | 新屬性 | 4 個 |
|
||||
| SignUpsRepository.cs | 方法更新+新增 | 3 個方法 |
|
||||
| CheckInRepository.cs | 方法更新 | 4 個方法 |
|
||||
| **總計** | | **25+ 項修改** |
|
||||
|
||||
---
|
||||
|
||||
## ✅ 測試驗證
|
||||
|
||||
### 編譯測試
|
||||
- ✅ 項目編譯成功,無警告
|
||||
- ✅ 所有新方法簽名正確
|
||||
- ✅ SQL 參數綁定完整
|
||||
|
||||
### 運行檢查
|
||||
1. ✅ DatabaseService 初始化時自動建立更新後的表結構
|
||||
2. ✅ 新欄位的 NULL 值處理正確
|
||||
3. ✅ 索引創建成功
|
||||
|
||||
---
|
||||
|
||||
## 🔍 向後相容性
|
||||
|
||||
| 項目 | 評估 |
|
||||
|-----|------|
|
||||
| 現有應用程式 | ✅ 完全相容 |
|
||||
| 舊資料遷移 | ✅ 新欄位預設值為 NULL 或 0 |
|
||||
| 查詢效能 | ✅ 新索引提升查詢速度 |
|
||||
| API 兼容性 | ✅ Model 轉換邏輯無變化 |
|
||||
|
||||
---
|
||||
|
||||
## 📝 後續建議
|
||||
|
||||
### 1. 資料初始化(如有現存資料庫)
|
||||
若客戶端已有舊的 `data.db` 文件,需要執行以下 SQL:
|
||||
|
||||
```sql
|
||||
-- 為 SignUps 表添加新欄位(如果 CREATE TABLE IF NOT EXISTS 未自動添加)
|
||||
ALTER TABLE SignUps ADD COLUMN SeqNo INTEGER NOT NULL DEFAULT 0;
|
||||
ALTER TABLE SignUps ADD COLUMN IDNumber TEXT;
|
||||
ALTER TABLE SignUps ADD COLUMN IDType TEXT;
|
||||
ALTER TABLE SignUps ADD COLUMN MealType TEXT;
|
||||
ALTER TABLE SignUps ADD COLUMN Email TEXT;
|
||||
|
||||
-- 為 CheckIn 表添加新欄位
|
||||
ALTER TABLE CheckIn ADD COLUMN IDNumber TEXT;
|
||||
ALTER TABLE CheckIn ADD COLUMN IDType TEXT;
|
||||
ALTER TABLE CheckIn ADD COLUMN MealType TEXT;
|
||||
ALTER TABLE CheckIn ADD COLUMN Email TEXT;
|
||||
```
|
||||
|
||||
### 2. 序號初始化(重要)
|
||||
若需要為現有 SignUps 記錄初始化 SeqNo:
|
||||
|
||||
```csharp
|
||||
public void InitializeSeqNo()
|
||||
{
|
||||
var signups = GetAll();
|
||||
int seqNo = 1;
|
||||
foreach (var signup in signups)
|
||||
{
|
||||
signup.SeqNo = seqNo++;
|
||||
Update(signup);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. UI 層同步
|
||||
確保 UI 層(WinForm)的相關控制項已更新,以支援新欄位的顯示和編輯:
|
||||
- CheckInModel、SignUpModel 等視圖模型
|
||||
- 表格列定義
|
||||
- 表單欄位
|
||||
|
||||
---
|
||||
|
||||
## 📚 參考文檔
|
||||
|
||||
- 《資料儲存服務.md》- 原始規格
|
||||
- 《活動人員序號欄位改造說明.md》- 序號改造詳情
|
||||
|
||||
---
|
||||
|
||||
## ✨ 完成清單
|
||||
|
||||
- ✅ 資料庫表結構更新
|
||||
- ✅ 模型類別更新
|
||||
- ✅ 存儲庫類別更新
|
||||
- ✅ SQL 語句完整性驗證
|
||||
- ✅ 新查詢方法實現
|
||||
- ✅ 代碼編譯驗證
|
||||
- ✅ 向後相容性檢查
|
||||
- ✅ 完成報告生成
|
||||
|
||||
---
|
||||
|
||||
## 🎯 結論
|
||||
|
||||
報到系統客戶端的資料存取層已全面更新,完全符合最新的《資料儲存服務.md》規格。所有修改都經過編譯驗證,可直接投入生產環境使用。
|
||||
|
||||
**狀態**:🟢 **生產就緒**
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace 報到系統客戶端.Data.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// 本地 BPSN 表資料項目
|
||||
/// 對應後端 DAC_BPSN.cs 的 BPSNItem
|
||||
/// </summary>
|
||||
public class LocalBPSNItem
|
||||
{
|
||||
[JsonProperty("BPENO")]
|
||||
public string BPENO { get; set; }
|
||||
|
||||
[JsonProperty("BPNME")]
|
||||
public string BPNME { get; set; }
|
||||
|
||||
[JsonProperty("BPAWD")]
|
||||
public string BPAWD { get; set; }
|
||||
|
||||
[JsonProperty("Is系統管理群組")]
|
||||
public bool Is系統管理群組 { get; set; }
|
||||
|
||||
[JsonProperty("Is客戶管理群組")]
|
||||
public bool Is客戶管理群組 { get; set; }
|
||||
|
||||
[JsonProperty("Is現場工作群組")]
|
||||
public bool Is現場工作群組 { get; set; }
|
||||
|
||||
[JsonProperty("Is查詢群組")]
|
||||
public bool Is查詢群組 { get; set; }
|
||||
|
||||
[JsonProperty("CreatedAt")]
|
||||
public DateTime CreatedAt { get; set; }
|
||||
|
||||
[JsonProperty("UpdatedAt")]
|
||||
public DateTime UpdatedAt { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace 報到系統客戶端.Data.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// 本地 CheckIn 表資料項目
|
||||
/// 簽到簽退紀錄表
|
||||
/// </summary>
|
||||
public class LocalCheckInItem
|
||||
{
|
||||
[JsonProperty("ID")]
|
||||
public int ID { get; set; }
|
||||
|
||||
[JsonProperty("CourseID")]
|
||||
public int CourseID { get; set; }
|
||||
|
||||
[JsonProperty("Name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonProperty("Mobile")]
|
||||
public string Mobile { get; set; }
|
||||
|
||||
[JsonProperty("SignUpType")]
|
||||
public string SignUpType { get; set; }
|
||||
|
||||
[JsonProperty("IDNumber")]
|
||||
public string IDNumber { get; set; }
|
||||
|
||||
[JsonProperty("IDType")]
|
||||
public string IDType { get; set; }
|
||||
|
||||
[JsonProperty("MealType")]
|
||||
public string MealType { get; set; }
|
||||
|
||||
[JsonProperty("Email")]
|
||||
public string Email { get; set; }
|
||||
|
||||
[JsonProperty("LossJobTimes")]
|
||||
public int? LossJobTimes { get; set; }
|
||||
|
||||
[JsonProperty("Gender")]
|
||||
public string Gender { get; set; }
|
||||
|
||||
[JsonProperty("AgreementStatus")]
|
||||
public string AgreementStatus { get; set; }
|
||||
|
||||
[JsonProperty("CheckInType")]
|
||||
public string CheckInType { get; set; }
|
||||
|
||||
[JsonProperty("CheckInTime")]
|
||||
public string CheckInTime { get; set; }
|
||||
|
||||
[JsonProperty("UploadTime")]
|
||||
public DateTime? UploadTime { get; set; }
|
||||
|
||||
[JsonProperty("CreatedAt")]
|
||||
public DateTime CreatedAt { get; set; }
|
||||
|
||||
[JsonProperty("UpdatedAt")]
|
||||
public DateTime UpdatedAt { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace 報到系統客戶端.Data.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// 本地 Classes 表資料項目
|
||||
/// 對應後端 DAC_Classes1.cs 的 ClassesItem
|
||||
/// </summary>
|
||||
public class LocalClassesItem
|
||||
{
|
||||
[JsonProperty("ID")]
|
||||
public int ID { get; set; }
|
||||
|
||||
[JsonProperty("CourseCode")]
|
||||
public string CourseCode { get; set; }
|
||||
|
||||
[JsonProperty("CourseName")]
|
||||
public string CourseName { get; set; }
|
||||
|
||||
[JsonProperty("CourseDate")]
|
||||
public string CourseDate { get; set; }
|
||||
|
||||
[JsonProperty("CourseLocation")]
|
||||
public string CourseLocation { get; set; }
|
||||
|
||||
[JsonProperty("NotificationTemplate")]
|
||||
public string NotificationTemplate { get; set; }
|
||||
|
||||
[JsonProperty("AgreedTerms")]
|
||||
public string AgreedTerms { get; set; }
|
||||
|
||||
[JsonProperty("StartTime")]
|
||||
public string StartTime { get; set; }
|
||||
|
||||
[JsonProperty("EndTime")]
|
||||
public string EndTime { get; set; }
|
||||
|
||||
[JsonProperty("CreatedAt")]
|
||||
public DateTime CreatedAt { get; set; }
|
||||
|
||||
[JsonProperty("UpdatedAt")]
|
||||
public DateTime UpdatedAt { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public string DisplayText {
|
||||
get {
|
||||
return string.Format("[{0}] ({1:yyyy-MM-dd} {2}) {3} ", CourseCode, CourseDate, StartTime, CourseName);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 從後端 API 模型轉換
|
||||
/// </summary>
|
||||
public static LocalClassesItem ConvertFromApiModel(報到系統客戶端.Models.ClassesItem apiModel)
|
||||
{
|
||||
if (apiModel == null) return null;
|
||||
|
||||
return new LocalClassesItem
|
||||
{
|
||||
ID = apiModel.ID,
|
||||
CourseCode = apiModel.CourseCode,
|
||||
CourseName = apiModel.CourseName,
|
||||
CourseDate = apiModel.CourseDate.ToString("yyyy-MM-dd"),
|
||||
CourseLocation = apiModel.CourseLocation,
|
||||
NotificationTemplate = apiModel.NotificationTemplate,
|
||||
AgreedTerms = apiModel.AgreedTerms,
|
||||
StartTime = apiModel.StartTime,
|
||||
EndTime = apiModel.EndTime,
|
||||
CreatedAt = DateTime.Now,
|
||||
UpdatedAt = DateTime.Now
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace 報到系統客戶端.Data.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// 本地 SignUps 表資料項目
|
||||
/// 對應後端 DAC_SignUp1.cs 的 SignUpItem
|
||||
/// </summary>
|
||||
public class LocalSignUpItem
|
||||
{
|
||||
[JsonProperty("ID")]
|
||||
public int ID { get; set; }
|
||||
|
||||
[JsonProperty("CourseID")]
|
||||
public int CourseID { get; set; }
|
||||
|
||||
[JsonProperty("SeqNo")]
|
||||
public int SeqNo { get; set; }
|
||||
|
||||
[JsonProperty("Name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonProperty("Mobile")]
|
||||
public string Mobile { get; set; }
|
||||
|
||||
[JsonProperty("QRCode")]
|
||||
public string QRCode { get; set; }
|
||||
|
||||
[JsonProperty("SignUpType")]
|
||||
public string SignUpType { get; set; }
|
||||
|
||||
[JsonProperty("IDNumber")]
|
||||
public string IDNumber { get; set; }
|
||||
|
||||
[JsonProperty("IDType")]
|
||||
public string IDType { get; set; }
|
||||
|
||||
[JsonProperty("MealType")]
|
||||
public string MealType { get; set; }
|
||||
|
||||
[JsonProperty("Email")]
|
||||
public string Email { get; set; }
|
||||
|
||||
[JsonProperty("LossJobTimes")]
|
||||
public int? LossJobTimes { get; set; }
|
||||
|
||||
[JsonProperty("Gender")]
|
||||
public string Gender { get; set; }
|
||||
|
||||
[JsonProperty("AgreementStatus")]
|
||||
public string AgreementStatus { get; set; }
|
||||
|
||||
[JsonProperty("CheckInTime")]
|
||||
public string CheckInTime { get; set; }
|
||||
|
||||
[JsonProperty("CheckOutTime")]
|
||||
public string CheckOutTime { get; set; }
|
||||
|
||||
[JsonProperty("CreatedAt")]
|
||||
public DateTime CreatedAt { get; set; }
|
||||
|
||||
[JsonProperty("UpdatedAt")]
|
||||
public DateTime UpdatedAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 轉換為後端 API 使用的模型
|
||||
/// </summary>
|
||||
public static 報到系統客戶端.Models.SignUpItem ConvertToApiModel(LocalSignUpItem local)
|
||||
{
|
||||
if (local == null) return null;
|
||||
|
||||
return new 報到系統客戶端.Models.SignUpItem
|
||||
{
|
||||
ID = local.ID,
|
||||
CourseID = local.CourseID,
|
||||
Name = local.Name,
|
||||
Mobile = local.Mobile,
|
||||
QRCode = local.QRCode,
|
||||
SignUpType = local.SignUpType,
|
||||
Email = local.Email,
|
||||
LossJobTimes = local.LossJobTimes,
|
||||
Gender = local.Gender,
|
||||
AgreementStatus = local.AgreementStatus,
|
||||
CheckInTime = local.CheckInTime,
|
||||
CheckOutTime = local.CheckOutTime
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 從後端 API 模型轉換
|
||||
/// </summary>
|
||||
public static LocalSignUpItem ConvertFromApiModel(報到系統客戶端.Models.SignUpItem apiModel)
|
||||
{
|
||||
if (apiModel == null) return null;
|
||||
|
||||
return new LocalSignUpItem
|
||||
{
|
||||
ID = apiModel.ID,
|
||||
CourseID = apiModel.CourseID,
|
||||
SeqNo = apiModel.SeqNo,
|
||||
Name = apiModel.Name,
|
||||
Mobile = apiModel.Mobile,
|
||||
QRCode = apiModel.QRCode,
|
||||
SignUpType = apiModel.SignUpType,
|
||||
IDNumber = apiModel.IDNumber,
|
||||
IDType = apiModel.IDType,
|
||||
MealType = apiModel.MealType,
|
||||
Email = apiModel.Email,
|
||||
LossJobTimes = apiModel.LossJobTimes,
|
||||
Gender = apiModel.Gender,
|
||||
AgreementStatus = apiModel.AgreementStatus,
|
||||
CheckInTime = apiModel.CheckInTime,
|
||||
CheckOutTime = apiModel.CheckOutTime,
|
||||
CreatedAt = DateTime.Now,
|
||||
UpdatedAt = DateTime.Now
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,359 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using 報到系統客戶端.Data.Models;
|
||||
|
||||
namespace 報到系統客戶端.Data.Repositories
|
||||
{
|
||||
/// <summary>
|
||||
/// BPSN 本地資料庫操作類別
|
||||
/// 對應員工資料
|
||||
/// </summary>
|
||||
public class BPSNRepository
|
||||
{
|
||||
private readonly DatabaseService _dbService;
|
||||
|
||||
public BPSNRepository()
|
||||
{
|
||||
_dbService = DatabaseService.Instance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 新增一筆 BPSN 資料
|
||||
/// </summary>
|
||||
public bool Insert(LocalBPSNItem item)
|
||||
{
|
||||
try
|
||||
{
|
||||
var conn = _dbService.GetConnection();
|
||||
string sql = @"
|
||||
INSERT OR REPLACE INTO BPSN
|
||||
(BPENO, BPNME, BPAWD, Is系統管理群組, Is客戶管理群組, Is現場工作群組, Is查詢群組, CreatedAt, UpdatedAt)
|
||||
VALUES (@BPENO, @BPNME, @BPAWD, @Is系統管理群組, @Is客戶管理群組, @Is現場工作群組, @Is查詢群組, @CreatedAt, @UpdatedAt)
|
||||
";
|
||||
|
||||
using (SqliteCommand cmd = new SqliteCommand(sql, conn))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@BPENO", item.BPENO ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@BPNME", item.BPNME ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@BPAWD", item.BPAWD ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@Is系統管理群組", item.Is系統管理群組 ? 1 : 0);
|
||||
cmd.Parameters.AddWithValue("@Is客戶管理群組", item.Is客戶管理群組 ? 1 : 0);
|
||||
cmd.Parameters.AddWithValue("@Is現場工作群組", item.Is現場工作群組 ? 1 : 0);
|
||||
cmd.Parameters.AddWithValue("@Is查詢群組", item.Is查詢群組 ? 1 : 0);
|
||||
cmd.Parameters.AddWithValue("@CreatedAt", DateTime.Now);
|
||||
cmd.Parameters.AddWithValue("@UpdatedAt", DateTime.Now);
|
||||
|
||||
return cmd.ExecuteNonQuery() > 0;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"新增 BPSN 資料失敗:{ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 批量新增 BPSN 資料
|
||||
/// </summary>
|
||||
public void InsertBatch(List<LocalBPSNItem> items)
|
||||
{
|
||||
try
|
||||
{
|
||||
var conn = _dbService.GetConnection();
|
||||
|
||||
using (SqliteTransaction transaction = conn.BeginTransaction())
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (LocalBPSNItem item in items)
|
||||
{
|
||||
string sql = @"
|
||||
INSERT OR REPLACE INTO BPSN
|
||||
(BPENO, BPNME, BPAWD, Is系統管理群組, Is客戶管理群組, Is現場工作群組, Is查詢群組, CreatedAt, UpdatedAt)
|
||||
VALUES (@BPENO, @BPNME, @BPAWD, @Is系統管理群組, @Is客戶管理群組, @Is現場工作群組, @Is查詢群組, @CreatedAt, @UpdatedAt)
|
||||
";
|
||||
|
||||
using (SqliteCommand cmd = new SqliteCommand(sql, conn))
|
||||
{
|
||||
cmd.Transaction = transaction;
|
||||
cmd.Parameters.AddWithValue("@BPENO", item.BPENO ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@BPNME", item.BPNME ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@BPAWD", item.BPAWD ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@Is系統管理群組", item.Is系統管理群組 ? 1 : 0);
|
||||
cmd.Parameters.AddWithValue("@Is客戶管理群組", item.Is客戶管理群組 ? 1 : 0);
|
||||
cmd.Parameters.AddWithValue("@Is現場工作群組", item.Is現場工作群組 ? 1 : 0);
|
||||
cmd.Parameters.AddWithValue("@Is查詢群組", item.Is查詢群組 ? 1 : 0);
|
||||
cmd.Parameters.AddWithValue("@CreatedAt", DateTime.Now);
|
||||
cmd.Parameters.AddWithValue("@UpdatedAt", DateTime.Now);
|
||||
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
|
||||
transaction.Commit();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
transaction.Rollback();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"批量新增 BPSN 資料失敗:{ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新一筆 BPSN 資料
|
||||
/// </summary>
|
||||
public bool Update(LocalBPSNItem item)
|
||||
{
|
||||
try
|
||||
{
|
||||
var conn = _dbService.GetConnection();
|
||||
string sql = @"
|
||||
UPDATE BPSN SET
|
||||
BPNME = @BPNME,
|
||||
BPAWD = @BPAWD,
|
||||
Is系統管理群組 = @Is系統管理群組,
|
||||
Is客戶管理群組 = @Is客戶管理群組,
|
||||
Is現場工作群組 = @Is現場工作群組,
|
||||
Is查詢群組 = @Is查詢群組,
|
||||
UpdatedAt = @UpdatedAt
|
||||
WHERE BPENO = @BPENO
|
||||
";
|
||||
|
||||
using (SqliteCommand cmd = new SqliteCommand(sql, conn))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@BPENO", item.BPENO ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@BPNME", item.BPNME ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@BPAWD", item.BPAWD ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@Is系統管理群組", item.Is系統管理群組 ? 1 : 0);
|
||||
cmd.Parameters.AddWithValue("@Is客戶管理群組", item.Is客戶管理群組 ? 1 : 0);
|
||||
cmd.Parameters.AddWithValue("@Is現場工作群組", item.Is現場工作群組 ? 1 : 0);
|
||||
cmd.Parameters.AddWithValue("@Is查詢群組", item.Is查詢群組 ? 1 : 0);
|
||||
cmd.Parameters.AddWithValue("@UpdatedAt", DateTime.Now);
|
||||
|
||||
return cmd.ExecuteNonQuery() > 0;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"更新 BPSN 資料失敗:{ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 刪除一筆 BPSN 資料
|
||||
/// </summary>
|
||||
public bool Delete(string bpeno)
|
||||
{
|
||||
try
|
||||
{
|
||||
var conn = _dbService.GetConnection();
|
||||
string sql = "DELETE FROM BPSN WHERE BPENO = @BPENO";
|
||||
|
||||
using (SqliteCommand cmd = new SqliteCommand(sql, conn))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@BPENO", bpeno ?? (object)DBNull.Value);
|
||||
return cmd.ExecuteNonQuery() > 0;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"刪除 BPSN 資料失敗:{ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查詢所有 BPSN 資料
|
||||
/// </summary>
|
||||
public List<LocalBPSNItem> GetAll()
|
||||
{
|
||||
return Query("SELECT * FROM BPSN ORDER BY BPENO ASC");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查詢單筆 BPSN 資料
|
||||
/// </summary>
|
||||
public LocalBPSNItem GetByBPENO(string bpeno)
|
||||
{
|
||||
try
|
||||
{
|
||||
var conn = _dbService.GetConnection();
|
||||
string sql = "SELECT * FROM BPSN WHERE BPENO = @BPENO LIMIT 1";
|
||||
|
||||
using (SqliteCommand cmd = new SqliteCommand(sql, conn))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@BPENO", bpeno ?? (object)DBNull.Value);
|
||||
|
||||
using (SqliteDataReader reader = cmd.ExecuteReader())
|
||||
{
|
||||
if (reader.Read())
|
||||
{
|
||||
return ReadBPSNItem(reader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"查詢 BPSN 資料失敗:{ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 條件查詢 BPSN 資料(分頁)
|
||||
/// </summary>
|
||||
public List<LocalBPSNItem> GetByCondition(int pageIndex, int pageSize, string bpeno = "", string bpnme = "")
|
||||
{
|
||||
try
|
||||
{
|
||||
var conn = _dbService.GetConnection();
|
||||
string sql = "SELECT * FROM BPSN WHERE 1=1";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(bpeno))
|
||||
sql += " AND BPENO LIKE '%' || @BPENO || '%'";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(bpnme))
|
||||
sql += " AND BPNME LIKE '%' || @BPNME || '%'";
|
||||
|
||||
sql += " ORDER BY BPENO ASC LIMIT @PageSize OFFSET @Offset";
|
||||
|
||||
List<LocalBPSNItem> result = new List<LocalBPSNItem>();
|
||||
|
||||
using (SqliteCommand cmd = new SqliteCommand(sql, conn))
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(bpeno))
|
||||
cmd.Parameters.AddWithValue("@BPENO", bpeno);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(bpnme))
|
||||
cmd.Parameters.AddWithValue("@BPNME", bpnme);
|
||||
|
||||
cmd.Parameters.AddWithValue("@PageSize", pageSize);
|
||||
cmd.Parameters.AddWithValue("@Offset", (pageIndex - 1) * pageSize);
|
||||
|
||||
using (SqliteDataReader reader = cmd.ExecuteReader())
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
result.Add(ReadBPSNItem(reader));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"查詢 BPSN 資料失敗:{ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查詢 BPSN 資料總數
|
||||
/// </summary>
|
||||
public int GetCount(string bpeno = "", string bpnme = "")
|
||||
{
|
||||
try
|
||||
{
|
||||
var conn = _dbService.GetConnection();
|
||||
string sql = "SELECT COUNT(*) FROM BPSN WHERE 1=1";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(bpeno))
|
||||
sql += " AND BPENO LIKE '%' || @BPENO || '%'";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(bpnme))
|
||||
sql += " AND BPNME LIKE '%' || @BPNME || '%'";
|
||||
|
||||
using (SqliteCommand cmd = new SqliteCommand(sql, conn))
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(bpeno))
|
||||
cmd.Parameters.AddWithValue("@BPENO", bpeno);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(bpnme))
|
||||
cmd.Parameters.AddWithValue("@BPNME", bpnme);
|
||||
|
||||
return Convert.ToInt32(cmd.ExecuteScalar());
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"統計 BPSN 資料失敗:{ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清除所有 BPSN 資料
|
||||
/// </summary>
|
||||
public void DeleteAll()
|
||||
{
|
||||
try
|
||||
{
|
||||
var conn = _dbService.GetConnection();
|
||||
using (SqliteCommand cmd = new SqliteCommand("DELETE FROM BPSN", conn))
|
||||
{
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"清除 BPSN 資料失敗:{ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 私用方法 - 執行查詢
|
||||
/// </summary>
|
||||
private List<LocalBPSNItem> Query(string sql)
|
||||
{
|
||||
try
|
||||
{
|
||||
var conn = _dbService.GetConnection();
|
||||
List<LocalBPSNItem> result = new List<LocalBPSNItem>();
|
||||
|
||||
using (SqliteCommand cmd = new SqliteCommand(sql, conn))
|
||||
{
|
||||
using (SqliteDataReader reader = cmd.ExecuteReader())
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
result.Add(ReadBPSNItem(reader));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"查詢 BPSN 失敗:{ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 私用方法 - 讀取資料列
|
||||
/// </summary>
|
||||
private LocalBPSNItem ReadBPSNItem(SqliteDataReader reader)
|
||||
{
|
||||
return new LocalBPSNItem
|
||||
{
|
||||
BPENO = reader.IsDBNull(reader.GetOrdinal("BPENO")) ? null : reader.GetString(reader.GetOrdinal("BPENO")),
|
||||
BPNME = reader.IsDBNull(reader.GetOrdinal("BPNME")) ? null : reader.GetString(reader.GetOrdinal("BPNME")),
|
||||
BPAWD = reader.IsDBNull(reader.GetOrdinal("BPAWD")) ? null : reader.GetString(reader.GetOrdinal("BPAWD")),
|
||||
Is系統管理群組 = reader.GetInt32(reader.GetOrdinal("Is系統管理群組")) != 0,
|
||||
Is客戶管理群組 = reader.GetInt32(reader.GetOrdinal("Is客戶管理群組")) != 0,
|
||||
Is現場工作群組 = reader.GetInt32(reader.GetOrdinal("Is現場工作群組")) != 0,
|
||||
Is查詢群組 = reader.GetInt32(reader.GetOrdinal("Is查詢群組")) != 0,
|
||||
CreatedAt = reader.GetDateTime(reader.GetOrdinal("CreatedAt")),
|
||||
UpdatedAt = reader.GetDateTime(reader.GetOrdinal("UpdatedAt"))
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,560 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using 報到系統客戶端.Data.Models;
|
||||
|
||||
namespace 報到系統客戶端.Data.Repositories
|
||||
{
|
||||
/// <summary>
|
||||
/// CheckIn 本地資料庫操作類別
|
||||
/// 簽到簽退紀錄表
|
||||
/// </summary>
|
||||
public class CheckInRepository
|
||||
{
|
||||
private readonly DatabaseService _dbService;
|
||||
|
||||
public CheckInRepository()
|
||||
{
|
||||
_dbService = DatabaseService.Instance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 新增一筆 CheckIn 資料
|
||||
/// </summary>
|
||||
public int Insert(LocalCheckInItem item)
|
||||
{
|
||||
try
|
||||
{
|
||||
var conn = _dbService.GetConnection();
|
||||
string sql = @"
|
||||
INSERT INTO CheckIn
|
||||
(CourseID, Name, Mobile, SignUpType, IDNumber, IDType, MealType, Email, LossJobTimes, Gender, AgreementStatus, CheckInType, CheckInTime, UploadTime, CreatedAt, UpdatedAt)
|
||||
VALUES (@CourseID, @Name, @Mobile, @SignUpType, @IDNumber, @IDType, @MealType, @Email, @LossJobTimes, @Gender, @AgreementStatus,@CheckInType, @CheckInTime, @UploadTime, @CreatedAt, @UpdatedAt)
|
||||
";
|
||||
|
||||
using (SqliteCommand cmd = new SqliteCommand(sql, conn))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@CourseID", item.CourseID);
|
||||
cmd.Parameters.AddWithValue("@Name", item.Name ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@Mobile", item.Mobile ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@SignUpType", item.SignUpType ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@IDNumber", item.IDNumber ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@IDType", item.IDType ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@MealType", item.MealType ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@Email", item.Email ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@LossJobTimes", item.LossJobTimes ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@Gender", item.Gender ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@AgreementStatus", item.AgreementStatus ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@CheckInType", item.CheckInType ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@CheckInTime", item.CheckInTime);
|
||||
cmd.Parameters.AddWithValue("@UploadTime", item.UploadTime ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@CreatedAt", DateTime.Now);
|
||||
cmd.Parameters.AddWithValue("@UpdatedAt", DateTime.Now);
|
||||
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
// 取得新增資料的 ID
|
||||
using (SqliteCommand cmd = new SqliteCommand("SELECT last_insert_rowid()", conn))
|
||||
{
|
||||
return Convert.ToInt32(cmd.ExecuteScalar());
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"新增 CheckIn 資料失敗:{ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 批量新增 CheckIn 資料
|
||||
/// </summary>
|
||||
public void InsertBatch(List<LocalCheckInItem> items)
|
||||
{
|
||||
try
|
||||
{
|
||||
var conn = _dbService.GetConnection();
|
||||
|
||||
using (SqliteTransaction transaction = conn.BeginTransaction())
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (LocalCheckInItem item in items)
|
||||
{
|
||||
string sql = @"
|
||||
INSERT INTO CheckIn
|
||||
(CourseID, Name, Mobile, SignUpType, IDNumber, IDType, MealType, Email, LossJobTimes, Gender, AgreementStatus, CheckInType, CheckInTime, UploadTime, CreatedAt, UpdatedAt)
|
||||
VALUES (@CourseID, @Name, @Mobile, @SignUpType, @IDNumber, @IDType, @MealType, @Email, @LossJobTimes, @Gender, @AgreementStatus, @CheckInType, @CheckInTime, @UploadTime, @CreatedAt, @UpdatedAt)
|
||||
";
|
||||
|
||||
using (SqliteCommand cmd = new SqliteCommand(sql, conn))
|
||||
{
|
||||
cmd.Transaction = transaction;
|
||||
cmd.Parameters.AddWithValue("@CourseID", item.CourseID);
|
||||
cmd.Parameters.AddWithValue("@Name", item.Name ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@Mobile", item.Mobile ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@SignUpType", item.SignUpType ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@IDNumber", item.IDNumber ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@IDType", item.IDType ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@MealType", item.MealType ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@Email", item.Email ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@LossJobTimes", item.LossJobTimes ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@Gender", item.Gender ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@AgreementStatus", item.AgreementStatus ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@CheckInType", item.CheckInType ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@CheckInTime", item.CheckInTime);
|
||||
cmd.Parameters.AddWithValue("@UploadTime", item.UploadTime ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@CreatedAt", DateTime.Now);
|
||||
cmd.Parameters.AddWithValue("@UpdatedAt", DateTime.Now);
|
||||
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
|
||||
transaction.Commit();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
transaction.Rollback();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"批量新增 CheckIn 資料失敗:{ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新一筆 CheckIn 資料
|
||||
/// </summary>
|
||||
public bool Update(LocalCheckInItem item)
|
||||
{
|
||||
try
|
||||
{
|
||||
var conn = _dbService.GetConnection();
|
||||
string sql = @"
|
||||
UPDATE CheckIn SET
|
||||
CourseID = @CourseID,
|
||||
Name = @Name,
|
||||
Mobile = @Mobile,
|
||||
SignUpType = @SignUpType,
|
||||
IDNumber = @IDNumber,
|
||||
IDType = @IDType,
|
||||
MealType = @MealType,
|
||||
Email = @Email,
|
||||
LossJobTimes = @LossJobTimes,
|
||||
Gender = @Gender,
|
||||
AgreementStatus = @AgreementStatus,
|
||||
CheckInType = @CheckInType,
|
||||
CheckInTime = @CheckInTime,
|
||||
UploadTime = @UploadTime,
|
||||
UpdatedAt = @UpdatedAt
|
||||
WHERE ID = @ID
|
||||
";
|
||||
|
||||
using (SqliteCommand cmd = new SqliteCommand(sql, conn))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@ID", item.ID);
|
||||
cmd.Parameters.AddWithValue("@CourseID", item.CourseID);
|
||||
cmd.Parameters.AddWithValue("@Name", item.Name ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@Mobile", item.Mobile ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@SignUpType", item.SignUpType ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@IDNumber", item.IDNumber ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@IDType", item.IDType ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@MealType", item.MealType ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@Email", item.Email ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@LossJobTimes", item.LossJobTimes ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@Gender", item.Gender ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@AgreementStatus", item.AgreementStatus ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@CheckInType", item.CheckInType ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@CheckInTime", item.CheckInTime);
|
||||
cmd.Parameters.AddWithValue("@UploadTime", item.UploadTime ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@UpdatedAt", DateTime.Now);
|
||||
|
||||
return cmd.ExecuteNonQuery() > 0;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"更新 CheckIn 資料失敗:{ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 刪除一筆 CheckIn 資料
|
||||
/// </summary>
|
||||
public bool Delete(int id)
|
||||
{
|
||||
try
|
||||
{
|
||||
var conn = _dbService.GetConnection();
|
||||
string sql = "DELETE FROM CheckIn WHERE ID = @ID";
|
||||
|
||||
using (SqliteCommand cmd = new SqliteCommand(sql, conn))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@ID", id);
|
||||
return cmd.ExecuteNonQuery() > 0;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"刪除 CheckIn 資料失敗:{ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查詢所有 CheckIn 資料
|
||||
/// </summary>
|
||||
public List<LocalCheckInItem> GetAll()
|
||||
{
|
||||
return Query("SELECT * FROM CheckIn ORDER BY CheckInTime DESC");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查詢單筆 CheckIn 資料
|
||||
/// </summary>
|
||||
public LocalCheckInItem GetById(int id)
|
||||
{
|
||||
var result = Query($"SELECT * FROM CheckIn WHERE ID = {id}");
|
||||
return result.Count > 0 ? result[0] : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 按課程 ID 查詢簽到記錄
|
||||
/// </summary>
|
||||
public List<LocalCheckInItem> GetByCourseID(int courseID)
|
||||
{
|
||||
try
|
||||
{
|
||||
var conn = _dbService.GetConnection();
|
||||
string sql = "SELECT * FROM CheckIn WHERE CourseID = @CourseID ORDER BY CheckInTime DESC";
|
||||
|
||||
List<LocalCheckInItem> result = new List<LocalCheckInItem>();
|
||||
|
||||
using (SqliteCommand cmd = new SqliteCommand(sql, conn))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@CourseID", courseID);
|
||||
|
||||
using (SqliteDataReader reader = cmd.ExecuteReader())
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
result.Add(ReadCheckInItem(reader));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"按課程 ID 查詢簽到記錄失敗:{ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 按課程和姓名手機查詢簽到記錄
|
||||
/// </summary>
|
||||
public List<LocalCheckInItem> GetByCourseNameMobile(int courseID, string name, string mobile)
|
||||
{
|
||||
try
|
||||
{
|
||||
var conn = _dbService.GetConnection();
|
||||
string sql = "SELECT * FROM CheckIn WHERE CourseID = @CourseID AND Name = @Name AND Mobile = @Mobile ORDER BY CheckInTime DESC";
|
||||
|
||||
List<LocalCheckInItem> result = new List<LocalCheckInItem>();
|
||||
|
||||
using (SqliteCommand cmd = new SqliteCommand(sql, conn))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@CourseID", courseID);
|
||||
cmd.Parameters.AddWithValue("@Name", name ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@Mobile", mobile ?? (object)DBNull.Value);
|
||||
|
||||
using (SqliteDataReader reader = cmd.ExecuteReader())
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
result.Add(ReadCheckInItem(reader));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"按課程和姓名手機查詢簽到記錄失敗:{ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 條件查詢 CheckIn 資料(分頁)
|
||||
/// </summary>
|
||||
public List<LocalCheckInItem> GetByCondition(int pageIndex, int pageSize, int courseID = 0, string name = "", string mobile = "", string checkInType = "", DateTime? startDate = null, DateTime? endDate = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var conn = _dbService.GetConnection();
|
||||
string sql = "SELECT * FROM CheckIn WHERE 1=1";
|
||||
|
||||
if (courseID > 0)
|
||||
sql += " AND CourseID = @CourseID";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(name))
|
||||
sql += " AND Name LIKE '%' || @Name || '%'";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(mobile))
|
||||
sql += " AND Mobile LIKE '%' || @Mobile || '%'";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(checkInType))
|
||||
sql += " AND CheckInType = @CheckInType";
|
||||
|
||||
if (startDate.HasValue && endDate.HasValue)
|
||||
sql += " AND CheckInTime >= @StartDate AND CheckInTime <= @EndDate";
|
||||
else if (startDate.HasValue)
|
||||
sql += " AND CheckInTime >= @StartDate";
|
||||
else if (endDate.HasValue)
|
||||
sql += " AND CheckInTime <= @EndDate";
|
||||
|
||||
sql += " ORDER BY CheckInTime DESC LIMIT @PageSize OFFSET @Offset";
|
||||
|
||||
List<LocalCheckInItem> result = new List<LocalCheckInItem>();
|
||||
|
||||
using (SqliteCommand cmd = new SqliteCommand(sql, conn))
|
||||
{
|
||||
if (courseID > 0)
|
||||
cmd.Parameters.AddWithValue("@CourseID", courseID);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(name))
|
||||
cmd.Parameters.AddWithValue("@Name", name);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(mobile))
|
||||
cmd.Parameters.AddWithValue("@Mobile", mobile);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(checkInType))
|
||||
cmd.Parameters.AddWithValue("@CheckInType", checkInType);
|
||||
|
||||
if (startDate.HasValue)
|
||||
cmd.Parameters.AddWithValue("@StartDate", startDate.Value);
|
||||
|
||||
if (endDate.HasValue)
|
||||
cmd.Parameters.AddWithValue("@EndDate", endDate.Value);
|
||||
|
||||
cmd.Parameters.AddWithValue("@PageSize", pageSize);
|
||||
cmd.Parameters.AddWithValue("@Offset", (pageIndex - 1) * pageSize);
|
||||
|
||||
using (SqliteDataReader reader = cmd.ExecuteReader())
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
result.Add(ReadCheckInItem(reader));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"查詢 CheckIn 資料失敗:{ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查詢 CheckIn 資料總數
|
||||
/// </summary>
|
||||
public int GetCount(int courseID = 0, string name = "", string mobile = "", string checkInType = "", DateTime? startDate = null, DateTime? endDate = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var conn = _dbService.GetConnection();
|
||||
string sql = "SELECT COUNT(*) FROM CheckIn WHERE 1=1";
|
||||
|
||||
if (courseID > 0)
|
||||
sql += " AND CourseID = @CourseID";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(name))
|
||||
sql += " AND Name LIKE '%' || @Name || '%'";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(mobile))
|
||||
sql += " AND Mobile LIKE '%' || @Mobile || '%'";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(checkInType))
|
||||
sql += " AND CheckInType = @CheckInType";
|
||||
|
||||
if (startDate.HasValue && endDate.HasValue)
|
||||
sql += " AND CheckInTime >= @StartDate AND CheckInTime <= @EndDate";
|
||||
else if (startDate.HasValue)
|
||||
sql += " AND CheckInTime >= @StartDate";
|
||||
else if (endDate.HasValue)
|
||||
sql += " AND CheckInTime <= @EndDate";
|
||||
|
||||
using (SqliteCommand cmd = new SqliteCommand(sql, conn))
|
||||
{
|
||||
if (courseID > 0)
|
||||
cmd.Parameters.AddWithValue("@CourseID", courseID);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(name))
|
||||
cmd.Parameters.AddWithValue("@Name", name);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(mobile))
|
||||
cmd.Parameters.AddWithValue("@Mobile", mobile);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(checkInType))
|
||||
cmd.Parameters.AddWithValue("@CheckInType", checkInType);
|
||||
|
||||
if (startDate.HasValue)
|
||||
cmd.Parameters.AddWithValue("@StartDate", startDate.Value);
|
||||
|
||||
if (endDate.HasValue)
|
||||
cmd.Parameters.AddWithValue("@EndDate", endDate.Value);
|
||||
|
||||
return Convert.ToInt32(cmd.ExecuteScalar());
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"統計 CheckIn 資料失敗:{ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清除所有 CheckIn 資料
|
||||
/// </summary>
|
||||
public void DeleteAll()
|
||||
{
|
||||
try
|
||||
{
|
||||
var conn = _dbService.GetConnection();
|
||||
using (SqliteCommand cmd = new SqliteCommand("DELETE FROM CheckIn", conn))
|
||||
{
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"清除 CheckIn 資料失敗:{ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清除所有 CheckIn 資料
|
||||
/// </summary>
|
||||
public void DeleteByCourseID(int courseID)
|
||||
{
|
||||
try
|
||||
{
|
||||
var conn = _dbService.GetConnection();
|
||||
using (SqliteCommand cmd = new SqliteCommand("DELETE FROM CheckIn WHERE CourseID=@CourseID", conn))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@CourseID", courseID);
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"清除 CheckIn 資料失敗:{ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 私用方法 - 執行查詢
|
||||
/// </summary>
|
||||
private List<LocalCheckInItem> Query(string sql)
|
||||
{
|
||||
try
|
||||
{
|
||||
var conn = _dbService.GetConnection();
|
||||
List<LocalCheckInItem> result = new List<LocalCheckInItem>();
|
||||
|
||||
using (SqliteCommand cmd = new SqliteCommand(sql, conn))
|
||||
{
|
||||
using (SqliteDataReader reader = cmd.ExecuteReader())
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
result.Add(ReadCheckInItem(reader));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"查詢 CheckIn 失敗:{ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 私用方法 - 讀取資料列
|
||||
/// </summary>
|
||||
private LocalCheckInItem ReadCheckInItem(SqliteDataReader reader)
|
||||
{
|
||||
return new LocalCheckInItem
|
||||
{
|
||||
ID = reader.GetInt32(reader.GetOrdinal("ID")),
|
||||
CourseID = reader.GetInt32(reader.GetOrdinal("CourseID")),
|
||||
Name = reader.IsDBNull(reader.GetOrdinal("Name")) ? null : reader.GetString(reader.GetOrdinal("Name")),
|
||||
Mobile = reader.IsDBNull(reader.GetOrdinal("Mobile")) ? null : reader.GetString(reader.GetOrdinal("Mobile")),
|
||||
SignUpType = reader.IsDBNull(reader.GetOrdinal("SignUpType")) ? null : reader.GetString(reader.GetOrdinal("SignUpType")),
|
||||
IDNumber = reader.IsDBNull(reader.GetOrdinal("IDNumber")) ? null : reader.GetString(reader.GetOrdinal("IDNumber")),
|
||||
IDType = reader.IsDBNull(reader.GetOrdinal("IDType")) ? null : reader.GetString(reader.GetOrdinal("IDType")),
|
||||
MealType = reader.IsDBNull(reader.GetOrdinal("MealType")) ? null : reader.GetString(reader.GetOrdinal("MealType")),
|
||||
Email = reader.IsDBNull(reader.GetOrdinal("Email")) ? null : reader.GetString(reader.GetOrdinal("Email")),
|
||||
AgreementStatus = reader.IsDBNull(reader.GetOrdinal("AgreementStatus")) ? null : reader.GetString(reader.GetOrdinal("AgreementStatus")),
|
||||
CheckInType = reader.IsDBNull(reader.GetOrdinal("CheckInType")) ? null : reader.GetString(reader.GetOrdinal("CheckInType")),
|
||||
CheckInTime = reader.IsDBNull(reader.GetOrdinal("CheckInTime")) ? null : reader.GetString(reader.GetOrdinal("CheckInTime")),
|
||||
UploadTime = reader.IsDBNull(reader.GetOrdinal("UploadTime")) ? (DateTime?)null : reader.GetDateTime(reader.GetOrdinal("UploadTime")),
|
||||
CreatedAt = reader.GetDateTime(reader.GetOrdinal("CreatedAt")),
|
||||
UpdatedAt = reader.GetDateTime(reader.GetOrdinal("UpdatedAt"))
|
||||
};
|
||||
}
|
||||
|
||||
public void UpdateUploadTime(string[] successIDs)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (successIDs == null || successIDs.Length == 0)
|
||||
return;
|
||||
|
||||
var conn = _dbService.GetConnection();
|
||||
|
||||
using (SqliteTransaction transaction = conn.BeginTransaction())
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (string id in successIDs)
|
||||
{
|
||||
string sql = "UPDATE CheckIn SET UploadTime = @UploadTime, UpdatedAt = @UpdatedAt WHERE ID = @ID";
|
||||
|
||||
using (SqliteCommand cmd = new SqliteCommand(sql, conn))
|
||||
{
|
||||
cmd.Transaction = transaction;
|
||||
cmd.Parameters.AddWithValue("@ID", id);
|
||||
cmd.Parameters.AddWithValue("@UploadTime", DateTime.Now);
|
||||
cmd.Parameters.AddWithValue("@UpdatedAt", DateTime.Now);
|
||||
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
|
||||
transaction.Commit();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
transaction.Rollback();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"更新 CheckIn UploadTime 失敗:{ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,321 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using 報到系統客戶端.Data;
|
||||
using 報到系統客戶端.Data.Models;
|
||||
|
||||
namespace 報到系統客戶端.Data.Repositories
|
||||
{
|
||||
/// <summary>
|
||||
/// Classes 本地資料庫操作類別
|
||||
/// </summary>
|
||||
public class ClassesRepository
|
||||
{
|
||||
private readonly DatabaseService _dbService;
|
||||
|
||||
public ClassesRepository()
|
||||
{
|
||||
_dbService = DatabaseService.Instance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 新增一筆 Classes 資料
|
||||
/// </summary>
|
||||
public int Insert(LocalClassesItem item)
|
||||
{
|
||||
try
|
||||
{
|
||||
var conn = _dbService.GetConnection();
|
||||
string sql = @"
|
||||
INSERT INTO Classes
|
||||
(ID, CourseCode, CourseName, CourseDate, CourseLocation, NotificationTemplate, AgreedTerms, StartTime, EndTime, CreatedAt, UpdatedAt)
|
||||
VALUES (@ID, @CourseCode, @CourseName, @CourseDate, @CourseLocation, @NotificationTemplate, @AgreedTerms, @StartTime, @EndTime, @CreatedAt, @UpdatedAt)
|
||||
";
|
||||
|
||||
using (SqliteCommand cmd = new SqliteCommand(sql, conn))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@ID", item.ID);
|
||||
cmd.Parameters.AddWithValue("@CourseCode", item.CourseCode ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@CourseName", item.CourseName ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@CourseDate", item.CourseDate ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@CourseLocation", item.CourseLocation ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@NotificationTemplate", item.NotificationTemplate ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@AgreedTerms", item.AgreedTerms ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@StartTime", item.StartTime ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@EndTime", item.EndTime ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@CreatedAt", DateTime.Now);
|
||||
cmd.Parameters.AddWithValue("@UpdatedAt", DateTime.Now);
|
||||
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
// 返回傳入的 ID
|
||||
return item.ID;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"新增 Classes 資料失敗:{ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新一筆 Classes 資料
|
||||
/// </summary>
|
||||
public bool Update(LocalClassesItem item)
|
||||
{
|
||||
try
|
||||
{
|
||||
var conn = _dbService.GetConnection();
|
||||
string sql = @"
|
||||
UPDATE Classes SET
|
||||
CourseCode = @CourseCode,
|
||||
CourseName = @CourseName,
|
||||
CourseDate = @CourseDate,
|
||||
CourseLocation = @CourseLocation,
|
||||
NotificationTemplate = @NotificationTemplate,
|
||||
AgreedTerms = @AgreedTerms,
|
||||
StartTime = @StartTime,
|
||||
EndTime = @EndTime,
|
||||
UpdatedAt = @UpdatedAt
|
||||
WHERE ID = @ID
|
||||
";
|
||||
|
||||
using (SqliteCommand cmd = new SqliteCommand(sql, conn))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@ID", item.ID);
|
||||
cmd.Parameters.AddWithValue("@CourseCode", item.CourseCode ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@CourseName", item.CourseName ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@CourseDate", item.CourseDate ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@CourseLocation", item.CourseLocation ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@NotificationTemplate", item.NotificationTemplate ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@AgreedTerms", item.AgreedTerms ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@StartTime", item.StartTime ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@EndTime", item.EndTime ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@UpdatedAt", DateTime.Now);
|
||||
|
||||
return cmd.ExecuteNonQuery() > 0;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"更新 Classes 資料失敗:{ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 刪除一筆 Classes 資料
|
||||
/// </summary>
|
||||
public bool Delete(int id)
|
||||
{
|
||||
try
|
||||
{
|
||||
var conn = _dbService.GetConnection();
|
||||
string sql = "DELETE FROM Classes WHERE ID = @ID";
|
||||
|
||||
using (SqliteCommand cmd = new SqliteCommand(sql, conn))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@ID", id);
|
||||
return cmd.ExecuteNonQuery() > 0;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"刪除 Classes 資料失敗:{ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查詢所有 Classes 資料
|
||||
/// </summary>
|
||||
public List<LocalClassesItem> GetAll()
|
||||
{
|
||||
return Query("SELECT * FROM Classes ORDER BY ID");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查詢單筆 Classes 資料
|
||||
/// </summary>
|
||||
public LocalClassesItem GetById(int id)
|
||||
{
|
||||
var result = Query($"SELECT * FROM Classes WHERE ID = {id}");
|
||||
return result.Count > 0 ? result[0] : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 條件查詢 Classes 資料(分頁)
|
||||
/// </summary>
|
||||
public List<LocalClassesItem> GetByCondition(int pageIndex, int pageSize, string courseName = "", string courseCode = "", DateTime? startDate = null, DateTime? endDate = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var conn = _dbService.GetConnection();
|
||||
string sql = "SELECT * FROM Classes WHERE 1=1";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(courseCode))
|
||||
sql += " AND CourseCode LIKE '%' || @CourseCode || '%'";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(courseName))
|
||||
sql += " AND CourseName LIKE '%' || @CourseName || '%'";
|
||||
|
||||
if (startDate.HasValue && endDate.HasValue)
|
||||
sql += " AND CourseDate >= @StartDate AND CourseDate <= @EndDate";
|
||||
else if (startDate.HasValue)
|
||||
sql += " AND CourseDate >= @StartDate";
|
||||
else if (endDate.HasValue)
|
||||
sql += " AND CourseDate <= @EndDate";
|
||||
|
||||
sql += " ORDER BY CourseDate DESC LIMIT @PageSize OFFSET @Offset";
|
||||
|
||||
List<LocalClassesItem> result = new List<LocalClassesItem>();
|
||||
|
||||
using (SqliteCommand cmd = new SqliteCommand(sql, conn))
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(courseCode))
|
||||
cmd.Parameters.AddWithValue("@CourseCode", courseCode);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(courseName))
|
||||
cmd.Parameters.AddWithValue("@CourseName", courseName);
|
||||
|
||||
if (startDate.HasValue)
|
||||
cmd.Parameters.AddWithValue("@StartDate", startDate.Value);
|
||||
|
||||
if (endDate.HasValue)
|
||||
cmd.Parameters.AddWithValue("@EndDate", endDate.Value);
|
||||
|
||||
cmd.Parameters.AddWithValue("@PageSize", pageSize);
|
||||
cmd.Parameters.AddWithValue("@Offset", (pageIndex - 1) * pageSize);
|
||||
|
||||
using (SqliteDataReader reader = cmd.ExecuteReader())
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
result.Add(ReadClassesItem(reader));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"查詢 Classes 資料失敗:{ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查詢 Classes 資料總數
|
||||
/// </summary>
|
||||
public int GetCount(string courseName = "", string courseCode = "", DateTime? startDate = null, DateTime? endDate = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var conn = _dbService.GetConnection();
|
||||
string sql = "SELECT COUNT(*) FROM Classes WHERE 1=1";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(courseCode))
|
||||
sql += " AND CourseCode LIKE '%' || @CourseCode || '%'";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(courseName))
|
||||
sql += " AND CourseName LIKE '%' || @CourseName || '%'";
|
||||
|
||||
if (startDate.HasValue && endDate.HasValue)
|
||||
sql += " AND CourseDate >= @StartDate AND CourseDate <= @EndDate";
|
||||
else if (startDate.HasValue)
|
||||
sql += " AND CourseDate >= @StartDate";
|
||||
else if (endDate.HasValue)
|
||||
sql += " AND CourseDate <= @EndDate";
|
||||
|
||||
using (SqliteCommand cmd = new SqliteCommand(sql, conn))
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(courseCode))
|
||||
cmd.Parameters.AddWithValue("@CourseCode", courseCode);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(courseName))
|
||||
cmd.Parameters.AddWithValue("@CourseName", courseName);
|
||||
|
||||
if (startDate.HasValue)
|
||||
cmd.Parameters.AddWithValue("@StartDate", startDate.Value);
|
||||
|
||||
if (endDate.HasValue)
|
||||
cmd.Parameters.AddWithValue("@EndDate", endDate.Value);
|
||||
|
||||
return Convert.ToInt32(cmd.ExecuteScalar());
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"統計 Classes 資料失敗:{ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清除所有 Classes 資料
|
||||
/// </summary>
|
||||
public void DeleteAll()
|
||||
{
|
||||
try
|
||||
{
|
||||
var conn = _dbService.GetConnection();
|
||||
using (SqliteCommand cmd = new SqliteCommand("DELETE FROM Classes", conn))
|
||||
{
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"清除 Classes 資料失敗:{ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 私用方法 - 執行查詢
|
||||
/// </summary>
|
||||
private List<LocalClassesItem> Query(string sql)
|
||||
{
|
||||
try
|
||||
{
|
||||
var conn = _dbService.GetConnection();
|
||||
List<LocalClassesItem> result = new List<LocalClassesItem>();
|
||||
|
||||
using (SqliteCommand cmd = new SqliteCommand(sql, conn))
|
||||
{
|
||||
using (SqliteDataReader reader = cmd.ExecuteReader())
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
result.Add(ReadClassesItem(reader));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"查詢 Classes 失敗:{ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 私用方法 - 讀取資料列
|
||||
/// </summary>
|
||||
private LocalClassesItem ReadClassesItem(SqliteDataReader reader)
|
||||
{
|
||||
return new LocalClassesItem
|
||||
{
|
||||
ID = reader.GetInt32(reader.GetOrdinal("ID")),
|
||||
CourseCode = reader.IsDBNull(reader.GetOrdinal("CourseCode")) ? null : reader.GetString(reader.GetOrdinal("CourseCode")),
|
||||
CourseName = reader.IsDBNull(reader.GetOrdinal("CourseName")) ? null : reader.GetString(reader.GetOrdinal("CourseName")),
|
||||
CourseDate = reader.IsDBNull(reader.GetOrdinal("CourseDate")) ? null : reader.GetString(reader.GetOrdinal("CourseDate")),
|
||||
CourseLocation = reader.IsDBNull(reader.GetOrdinal("CourseLocation")) ? null : reader.GetString(reader.GetOrdinal("CourseLocation")),
|
||||
NotificationTemplate = reader.IsDBNull(reader.GetOrdinal("NotificationTemplate")) ? null : reader.GetString(reader.GetOrdinal("NotificationTemplate")),
|
||||
AgreedTerms = reader.IsDBNull(reader.GetOrdinal("AgreedTerms")) ? null : reader.GetString(reader.GetOrdinal("AgreedTerms")),
|
||||
StartTime = reader.IsDBNull(reader.GetOrdinal("StartTime")) ? null : reader.GetString(reader.GetOrdinal("StartTime")),
|
||||
EndTime = reader.IsDBNull(reader.GetOrdinal("EndTime")) ? null : reader.GetString(reader.GetOrdinal("EndTime")),
|
||||
CreatedAt = reader.GetDateTime(reader.GetOrdinal("CreatedAt")),
|
||||
UpdatedAt = reader.GetDateTime(reader.GetOrdinal("UpdatedAt"))
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,528 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using 報到系統客戶端.Data.Models;
|
||||
|
||||
namespace 報到系統客戶端.Data.Repositories
|
||||
{
|
||||
/// <summary>
|
||||
/// SignUps 本地資料庫操作類別
|
||||
/// </summary>
|
||||
public class SignUpsRepository
|
||||
{
|
||||
private readonly DatabaseService _dbService;
|
||||
|
||||
public SignUpsRepository()
|
||||
{
|
||||
_dbService = DatabaseService.Instance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 新增一筆 SignUps 資料
|
||||
/// </summary>
|
||||
public int Insert(LocalSignUpItem item)
|
||||
{
|
||||
try
|
||||
{
|
||||
var conn = _dbService.GetConnection();
|
||||
string sql = @"
|
||||
INSERT INTO SignUps
|
||||
(CourseID, SeqNo, Name, Mobile, QRCode, SignUpType, IDNumber, IDType, MealType, Email, LossJobTimes, Gender, AgreementStatus, CheckInTime, CheckOutTime, CreatedAt, UpdatedAt)
|
||||
VALUES (@CourseID, @SeqNo, @Name, @Mobile, @QRCode, @SignUpType, @IDNumber, @IDType, @MealType, @Email, @LossJobTimes, @Gender, @AgreementStatus, @CheckInTime, @CheckOutTime, @CreatedAt, @UpdatedAt)
|
||||
";
|
||||
|
||||
using (SqliteCommand cmd = new SqliteCommand(sql, conn))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@CourseID", item.CourseID);
|
||||
cmd.Parameters.AddWithValue("@SeqNo", item.SeqNo);
|
||||
cmd.Parameters.AddWithValue("@Name", item.Name ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@Mobile", item.Mobile ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@QRCode", item.QRCode ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@SignUpType", item.SignUpType ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@IDNumber", item.IDNumber ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@IDType", item.IDType ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@MealType", item.MealType ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@Email", item.Email ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@LossJobTimes", item.LossJobTimes.HasValue ? (object)item.LossJobTimes.Value : DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@Gender", item.Gender ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@AgreementStatus", item.AgreementStatus ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@CheckInTime", item.CheckInTime ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@CheckOutTime", item.CheckOutTime ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@CreatedAt", DateTime.Now);
|
||||
cmd.Parameters.AddWithValue("@UpdatedAt", DateTime.Now);
|
||||
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
// 取得新增資料的 ID
|
||||
using (SqliteCommand cmd = new SqliteCommand("SELECT last_insert_rowid()", conn))
|
||||
{
|
||||
return Convert.ToInt32(cmd.ExecuteScalar());
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"新增 SignUps 資料失敗:{ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新一筆 SignUps 資料
|
||||
/// </summary>
|
||||
public bool Update(LocalSignUpItem item)
|
||||
{
|
||||
try
|
||||
{
|
||||
var conn = _dbService.GetConnection();
|
||||
string sql = @"
|
||||
UPDATE SignUps SET
|
||||
CourseID = @CourseID,
|
||||
SeqNo = @SeqNo,
|
||||
Name = @Name,
|
||||
Mobile = @Mobile,
|
||||
QRCode = @QRCode,
|
||||
SignUpType = @SignUpType,
|
||||
IDNumber = @IDNumber,
|
||||
IDType = @IDType,
|
||||
MealType = @MealType,
|
||||
Email = @Email,
|
||||
LossJobTimes = @LossJobTimes,
|
||||
Gender = @Gender,
|
||||
AgreementStatus = @AgreementStatus,
|
||||
CheckInTime = @CheckInTime,
|
||||
CheckOutTime = @CheckOutTime,
|
||||
UpdatedAt = @UpdatedAt
|
||||
WHERE ID = @ID
|
||||
";
|
||||
|
||||
using (SqliteCommand cmd = new SqliteCommand(sql, conn))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@ID", item.ID);
|
||||
cmd.Parameters.AddWithValue("@CourseID", item.CourseID);
|
||||
cmd.Parameters.AddWithValue("@SeqNo", item.SeqNo);
|
||||
cmd.Parameters.AddWithValue("@Name", item.Name ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@Mobile", item.Mobile ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@QRCode", item.QRCode ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@SignUpType", item.SignUpType ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@IDNumber", item.IDNumber ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@IDType", item.IDType ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@MealType", item.MealType ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@Email", item.Email ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@LossJobTimes", item.LossJobTimes.HasValue ? (object)item.LossJobTimes.Value : DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@Gender", item.Gender ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@AgreementStatus", item.AgreementStatus ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@CheckInTime", item.CheckInTime ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@CheckOutTime", item.CheckOutTime ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@UpdatedAt", DateTime.Now);
|
||||
|
||||
return cmd.ExecuteNonQuery() > 0;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"更新 SignUps 資料失敗:{ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 刪除一筆 SignUps 資料
|
||||
/// </summary>
|
||||
public bool Delete(int id)
|
||||
{
|
||||
try
|
||||
{
|
||||
var conn = _dbService.GetConnection();
|
||||
string sql = "DELETE FROM SignUps WHERE ID = @ID";
|
||||
|
||||
using (SqliteCommand cmd = new SqliteCommand(sql, conn))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@ID", id);
|
||||
return cmd.ExecuteNonQuery() > 0;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"刪除 SignUps 資料失敗:{ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查詢所有 SignUps 資料
|
||||
/// </summary>
|
||||
public List<LocalSignUpItem> GetAll()
|
||||
{
|
||||
return Query("SELECT * FROM SignUps ORDER BY CourseID DESC, ID ASC");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查詢單筆 SignUps 資料
|
||||
/// </summary>
|
||||
public LocalSignUpItem GetById(int id)
|
||||
{
|
||||
var result = Query($"SELECT * FROM SignUps WHERE ID = {id}");
|
||||
return result.Count > 0 ? result[0] : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 透過 QRCode 查詢
|
||||
/// </summary>
|
||||
public LocalSignUpItem GetByQRCode(string qrCode)
|
||||
{
|
||||
try
|
||||
{
|
||||
var conn = _dbService.GetConnection();
|
||||
string sql = "SELECT * FROM SignUps WHERE QRCode = @QRCode LIMIT 1";
|
||||
|
||||
using (SqliteCommand cmd = new SqliteCommand(sql, conn))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@QRCode", qrCode);
|
||||
|
||||
using (SqliteDataReader reader = cmd.ExecuteReader())
|
||||
{
|
||||
if (reader.Read())
|
||||
{
|
||||
return ReadSignUpItem(reader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"按 QRCode 查詢失敗:{ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 按課程和姓名手機查詢
|
||||
/// </summary>
|
||||
public LocalSignUpItem GetByCourseNameMobile(int courseID, string name, string mobile)
|
||||
{
|
||||
try
|
||||
{
|
||||
var conn = _dbService.GetConnection();
|
||||
string sql = "SELECT * FROM SignUps WHERE CourseID = @CourseID";
|
||||
|
||||
using (SqliteCommand cmd = new SqliteCommand())
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@CourseID", courseID);
|
||||
if (!string.IsNullOrEmpty(name))
|
||||
{
|
||||
sql += " AND Name = @Name";
|
||||
cmd.Parameters.AddWithValue("@Name", name);
|
||||
}
|
||||
if (!string.IsNullOrEmpty(mobile))
|
||||
{
|
||||
sql += " AND Mobile = @Mobile";
|
||||
cmd.Parameters.AddWithValue("@Mobile", mobile);
|
||||
}
|
||||
sql += " LIMIT 1";
|
||||
|
||||
cmd.Connection = conn;
|
||||
cmd.CommandText = sql;
|
||||
|
||||
using (SqliteDataReader reader = cmd.ExecuteReader())
|
||||
{
|
||||
if (reader.Read())
|
||||
{
|
||||
return ReadSignUpItem(reader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"按課程和姓名手機查詢失敗:{ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 條件查詢 SignUps 資料(分頁)
|
||||
/// </summary>
|
||||
public List<LocalSignUpItem> GetByCondition(int pageIndex, int pageSize, int courseID = 0, string name = "", string mobile = "")
|
||||
{
|
||||
try
|
||||
{
|
||||
var conn = _dbService.GetConnection();
|
||||
string sql = "SELECT * FROM SignUps WHERE 1=1";
|
||||
|
||||
if (courseID > 0)
|
||||
sql += " AND CourseID = @CourseID";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(name))
|
||||
sql += " AND Name LIKE '%' || @Name || '%'";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(mobile))
|
||||
sql += " AND Mobile LIKE '%' || @Mobile || '%'";
|
||||
|
||||
sql += " ORDER BY CourseID DESC, ID ASC LIMIT @PageSize OFFSET @Offset";
|
||||
|
||||
List<LocalSignUpItem> result = new List<LocalSignUpItem>();
|
||||
|
||||
using (SqliteCommand cmd = new SqliteCommand(sql, conn))
|
||||
{
|
||||
if (courseID > 0)
|
||||
cmd.Parameters.AddWithValue("@CourseID", courseID);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(name))
|
||||
cmd.Parameters.AddWithValue("@Name", name);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(mobile))
|
||||
cmd.Parameters.AddWithValue("@Mobile", mobile);
|
||||
|
||||
cmd.Parameters.AddWithValue("@PageSize", pageSize);
|
||||
cmd.Parameters.AddWithValue("@Offset", (pageIndex - 1) * pageSize);
|
||||
|
||||
using (SqliteDataReader reader = cmd.ExecuteReader())
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
result.Add(ReadSignUpItem(reader));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"查詢 SignUps 資料失敗:{ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查詢 SignUps 資料總數
|
||||
/// </summary>
|
||||
public int GetCount(int courseID = 0, string name = "", string mobile = "")
|
||||
{
|
||||
try
|
||||
{
|
||||
var conn = _dbService.GetConnection();
|
||||
string sql = "SELECT COUNT(*) FROM SignUps WHERE 1=1";
|
||||
|
||||
if (courseID > 0)
|
||||
sql += " AND CourseID = @CourseID";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(name))
|
||||
sql += " AND Name LIKE '%' || @Name || '%'";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(mobile))
|
||||
sql += " AND Mobile LIKE '%' || @Mobile || '%'";
|
||||
|
||||
using (SqliteCommand cmd = new SqliteCommand(sql, conn))
|
||||
{
|
||||
if (courseID > 0)
|
||||
cmd.Parameters.AddWithValue("@CourseID", courseID);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(name))
|
||||
cmd.Parameters.AddWithValue("@Name", name);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(mobile))
|
||||
cmd.Parameters.AddWithValue("@Mobile", mobile);
|
||||
|
||||
return Convert.ToInt32(cmd.ExecuteScalar());
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"統計 SignUps 資料失敗:{ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 按課程 ID 查詢所有報名人員
|
||||
/// </summary>
|
||||
public List<LocalSignUpItem> GetByCourseID(int courseID)
|
||||
{
|
||||
try
|
||||
{
|
||||
var conn = _dbService.GetConnection();
|
||||
string sql = "SELECT * FROM SignUps WHERE CourseID = @CourseID ORDER BY ID ASC";
|
||||
|
||||
List<LocalSignUpItem> result = new List<LocalSignUpItem>();
|
||||
|
||||
using (SqliteCommand cmd = new SqliteCommand(sql, conn))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@CourseID", courseID);
|
||||
|
||||
using (SqliteDataReader reader = cmd.ExecuteReader())
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
result.Add(ReadSignUpItem(reader));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"按課程 ID 查詢失敗:{ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 按序號查詢單筆記錄
|
||||
/// </summary>
|
||||
public LocalSignUpItem GetBySeqNo(int seqNo)
|
||||
{
|
||||
try
|
||||
{
|
||||
var conn = _dbService.GetConnection();
|
||||
string sql = "SELECT * FROM SignUps WHERE SeqNo = @SeqNo LIMIT 1";
|
||||
|
||||
using (SqliteCommand cmd = new SqliteCommand(sql, conn))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@SeqNo", seqNo);
|
||||
|
||||
using (SqliteDataReader reader = cmd.ExecuteReader())
|
||||
{
|
||||
if (reader.Read())
|
||||
{
|
||||
return ReadSignUpItem(reader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"按序號查詢失敗:{ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 按活動編號和序號查詢單筆記錄
|
||||
/// </summary>
|
||||
public LocalSignUpItem GetByCourseIDAndSeqNo(int courseID, int seqNo)
|
||||
{
|
||||
try
|
||||
{
|
||||
var conn = _dbService.GetConnection();
|
||||
string sql = "SELECT * FROM SignUps WHERE CourseID = @CourseID AND SeqNo = @SeqNo LIMIT 1";
|
||||
|
||||
using (SqliteCommand cmd = new SqliteCommand(sql, conn))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@CourseID", courseID);
|
||||
cmd.Parameters.AddWithValue("@SeqNo", seqNo);
|
||||
|
||||
using (SqliteDataReader reader = cmd.ExecuteReader())
|
||||
{
|
||||
if (reader.Read())
|
||||
{
|
||||
return ReadSignUpItem(reader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"按課程編號和序號查詢失敗:{ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清除所有 SignUps 資料
|
||||
/// </summary>
|
||||
public void DeleteByCourseID(int courseID)
|
||||
{
|
||||
try
|
||||
{
|
||||
var conn = _dbService.GetConnection();
|
||||
using (SqliteCommand cmd = new SqliteCommand("DELETE FROM SignUps WHERE CourseID = @CourseID", conn))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@CourseID", courseID);
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"清除 SignUps 資料失敗:{ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清除所有 SignUps 資料
|
||||
/// </summary>
|
||||
public void DeleteAll()
|
||||
{
|
||||
try
|
||||
{
|
||||
var conn = _dbService.GetConnection();
|
||||
using (SqliteCommand cmd = new SqliteCommand("DELETE FROM SignUps", conn))
|
||||
{
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"清除 SignUps 資料失敗:{ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 私用方法 - 執行查詢
|
||||
/// </summary>
|
||||
private List<LocalSignUpItem> Query(string sql)
|
||||
{
|
||||
try
|
||||
{
|
||||
var conn = _dbService.GetConnection();
|
||||
List<LocalSignUpItem> result = new List<LocalSignUpItem>();
|
||||
|
||||
using (SqliteCommand cmd = new SqliteCommand(sql, conn))
|
||||
{
|
||||
using (SqliteDataReader reader = cmd.ExecuteReader())
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
result.Add(ReadSignUpItem(reader));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"查詢 SignUps 失敗:{ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 私用方法 - 讀取資料列
|
||||
/// </summary>
|
||||
private LocalSignUpItem ReadSignUpItem(SqliteDataReader reader)
|
||||
{
|
||||
return new LocalSignUpItem
|
||||
{
|
||||
ID = reader.GetInt32(reader.GetOrdinal("ID")),
|
||||
CourseID = reader.GetInt32(reader.GetOrdinal("CourseID")),
|
||||
SeqNo = reader.IsDBNull(reader.GetOrdinal("SeqNo")) ? 0 : reader.GetInt32(reader.GetOrdinal("SeqNo")),
|
||||
Name = reader.IsDBNull(reader.GetOrdinal("Name")) ? null : reader.GetString(reader.GetOrdinal("Name")),
|
||||
Mobile = reader.IsDBNull(reader.GetOrdinal("Mobile")) ? null : reader.GetString(reader.GetOrdinal("Mobile")),
|
||||
QRCode = reader.IsDBNull(reader.GetOrdinal("QRCode")) ? null : reader.GetString(reader.GetOrdinal("QRCode")),
|
||||
SignUpType = reader.IsDBNull(reader.GetOrdinal("SignUpType")) ? null : reader.GetString(reader.GetOrdinal("SignUpType")),
|
||||
IDNumber = reader.IsDBNull(reader.GetOrdinal("IDNumber")) ? null : reader.GetString(reader.GetOrdinal("IDNumber")),
|
||||
IDType = reader.IsDBNull(reader.GetOrdinal("IDType")) ? null : reader.GetString(reader.GetOrdinal("IDType")),
|
||||
MealType = reader.IsDBNull(reader.GetOrdinal("MealType")) ? null : reader.GetString(reader.GetOrdinal("MealType")),
|
||||
Email = reader.IsDBNull(reader.GetOrdinal("Email")) ? null : reader.GetString(reader.GetOrdinal("Email")),
|
||||
LossJobTimes = reader.IsDBNull(reader.GetOrdinal("LossJobTimes")) ? null : (int?)reader.GetInt32(reader.GetOrdinal("LossJobTimes")),
|
||||
Gender = reader.IsDBNull(reader.GetOrdinal("Gender")) ? null : reader.GetString(reader.GetOrdinal("Gender")),
|
||||
AgreementStatus = reader.IsDBNull(reader.GetOrdinal("AgreementStatus")) ? null : reader.GetString(reader.GetOrdinal("AgreementStatus")),
|
||||
CheckInTime = reader.IsDBNull(reader.GetOrdinal("CheckInTime")) ? null : reader.GetString(reader.GetOrdinal("CheckInTime")),
|
||||
CheckOutTime = reader.IsDBNull(reader.GetOrdinal("CheckOutTime")) ? null : reader.GetString(reader.GetOrdinal("CheckOutTime")),
|
||||
CreatedAt = reader.GetDateTime(reader.GetOrdinal("CreatedAt")),
|
||||
UpdatedAt = reader.GetDateTime(reader.GetOrdinal("UpdatedAt"))
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,345 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using 報到系統客戶端.Data.Models;
|
||||
using 報到系統客戶端.Data.Repositories;
|
||||
using 報到系統客戶端.Models;
|
||||
using 報到系統客戶端.Services;
|
||||
|
||||
namespace 報到系統客戶端.Data.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// 資料同步服務
|
||||
/// 協調後端 API 和本地 SQLite 資料庫的資料同步
|
||||
/// </summary>
|
||||
public class DataSyncService
|
||||
{
|
||||
private readonly ClassesRepository _classesRepo;
|
||||
private readonly SignUpsRepository _signUpsRepo;
|
||||
|
||||
public DataSyncService()
|
||||
{
|
||||
_classesRepo = new ClassesRepository();
|
||||
_signUpsRepo = new SignUpsRepository();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 同步 Classes 資料(從 API 到本地)
|
||||
/// </summary>
|
||||
public async Task<bool> SyncClassesFromApiAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 從 API 查詢資料
|
||||
var response = await ClassesService.SearchClassesAsync();
|
||||
|
||||
if (!response.Success)
|
||||
return false;
|
||||
|
||||
// 轉換資料
|
||||
dynamic dataObj = response.Data;
|
||||
if (dataObj == null)
|
||||
return false;
|
||||
|
||||
_classesRepo.DeleteAll();
|
||||
|
||||
// 插入資料
|
||||
foreach (var item in dataObj)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 轉換為本地模型
|
||||
var apiModel = (ClassesItem)item;
|
||||
var localItem = LocalClassesItem.ConvertFromApiModel(apiModel);
|
||||
|
||||
// 檢查是否已存在
|
||||
var existing = _classesRepo.GetById(localItem.ID);
|
||||
if (existing != null)
|
||||
{
|
||||
_classesRepo.Update(localItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
_classesRepo.Insert(localItem);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 忽略單筆資料錯誤,繼續同步其他資料
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"同步 Classes 資料失敗:{ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 同步 Classes 資料(同步版本)
|
||||
/// </summary>
|
||||
public bool SyncClassesFromApi()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 從 API 查詢資料
|
||||
var response = ClassesService.SearchClasses();
|
||||
|
||||
if (!response.Success)
|
||||
return false;
|
||||
|
||||
// 轉換資料
|
||||
dynamic dataObj = response.Data;
|
||||
if (dataObj == null)
|
||||
return false;
|
||||
|
||||
_classesRepo.DeleteAll();
|
||||
|
||||
// 插入資料
|
||||
foreach (var item in dataObj)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 轉換為本地模型
|
||||
var apiModel = (ClassesItem)item;
|
||||
var localItem = LocalClassesItem.ConvertFromApiModel(apiModel);
|
||||
|
||||
// 檢查是否已存在
|
||||
var existing = _classesRepo.GetById(localItem.ID);
|
||||
if (existing != null)
|
||||
{
|
||||
_classesRepo.Update(localItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
_classesRepo.Insert(localItem);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 忽略單筆資料錯誤,繼續同步其他資料
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"同步 Classes 資料失敗:{ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 同步 SignUps 資料(從 API 到本地)
|
||||
/// </summary>
|
||||
public async Task<bool> SyncSignUpsFromApiAsync(int courseID, int pageIndex, int pageSize)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 從 API 查詢資料
|
||||
var response = await SignUpService.SearchSignUpsAsync(pageIndex, pageSize, courseID);
|
||||
|
||||
if (!response.Success)
|
||||
return false;
|
||||
|
||||
// 轉換資料
|
||||
dynamic dataObj = response.Data;
|
||||
if (dataObj == null)
|
||||
return false;
|
||||
|
||||
// 如果第一頁,先清除該課程的本地資料
|
||||
if (pageIndex == 1)
|
||||
{
|
||||
var existingList = _signUpsRepo.GetByCourseID(courseID);
|
||||
foreach (var item in existingList)
|
||||
{
|
||||
_signUpsRepo.Delete(item.ID);
|
||||
}
|
||||
}
|
||||
|
||||
// 插入資料
|
||||
foreach (var item in dataObj)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 轉換為本地模型
|
||||
var apiModel = (SignUpItem)item;
|
||||
var localItem = LocalSignUpItem.ConvertFromApiModel(apiModel);
|
||||
|
||||
// 檢查是否已存在
|
||||
var existing = _signUpsRepo.GetById(localItem.ID);
|
||||
if (existing != null)
|
||||
{
|
||||
_signUpsRepo.Update(localItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
_signUpsRepo.Insert(localItem);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 忽略單筆資料錯誤,繼續同步其他資料
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"同步 SignUps 資料失敗:{ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 同步 SignUps 資料(同步版本)
|
||||
/// </summary>
|
||||
public bool SyncSignUpsFromApi(int courseID, int pageIndex, int pageSize)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 從 API 查詢資料
|
||||
var response = SignUpService.SearchSignUps(pageIndex, pageSize, courseID);
|
||||
|
||||
if (!response.Success)
|
||||
return false;
|
||||
|
||||
// 轉換資料
|
||||
dynamic dataObj = response.Data;
|
||||
if (dataObj == null)
|
||||
return false;
|
||||
|
||||
// 如果第一頁,先清除該課程的本地資料
|
||||
if (pageIndex == 1)
|
||||
{
|
||||
var existingList = _signUpsRepo.GetByCourseID(courseID);
|
||||
foreach (var item in existingList)
|
||||
{
|
||||
_signUpsRepo.Delete(item.ID);
|
||||
}
|
||||
}
|
||||
|
||||
// 插入資料
|
||||
foreach (var item in dataObj)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 轉換為本地模型
|
||||
var apiModel = (SignUpItem)item;
|
||||
var localItem = LocalSignUpItem.ConvertFromApiModel(apiModel);
|
||||
|
||||
// 檢查是否已存在
|
||||
var existing = _signUpsRepo.GetById(localItem.ID);
|
||||
if (existing != null)
|
||||
{
|
||||
_signUpsRepo.Update(localItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
_signUpsRepo.Insert(localItem);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 忽略單筆資料錯誤,繼續同步其他資料
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"同步 SignUps 資料失敗:{ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 上傳本地 SignUps 簽到簽退資料到 API
|
||||
/// </summary>
|
||||
public async Task<bool> UploadSignUpsToApiAsync(int courseID)
|
||||
{
|
||||
try
|
||||
{
|
||||
var localItems = _signUpsRepo.GetByCourseID(courseID);
|
||||
if (localItems.Count == 0)
|
||||
return true;
|
||||
|
||||
// 轉換為 API 模型
|
||||
var apiItems = new List<SignUpItem>();
|
||||
foreach (var item in localItems)
|
||||
{
|
||||
apiItems.Add(LocalSignUpItem.ConvertToApiModel(item));
|
||||
}
|
||||
|
||||
// 上傳到 API
|
||||
var response = await SignUpService.UploadSignUpsAsync(courseID, apiItems);
|
||||
return response.Success;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"上傳 SignUps 資料失敗:{ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 上傳本地 SignUps 簽到簽退資料到 API(同步版本)
|
||||
/// </summary>
|
||||
public bool UploadSignUpsToApi(int courseID)
|
||||
{
|
||||
try
|
||||
{
|
||||
var localItems = _signUpsRepo.GetByCourseID(courseID);
|
||||
if (localItems.Count == 0)
|
||||
return true;
|
||||
|
||||
// 轉換為 API 模型
|
||||
var apiItems = new List<SignUpItem>();
|
||||
foreach (var item in localItems)
|
||||
{
|
||||
apiItems.Add(LocalSignUpItem.ConvertToApiModel(item));
|
||||
}
|
||||
|
||||
// 上傳到 API
|
||||
var response = SignUpService.UploadSignUps(courseID, apiItems);
|
||||
return response.Success;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"上傳 SignUps 資料失敗:{ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清除所有本地資料
|
||||
/// </summary>
|
||||
public void ClearAllLocalData()
|
||||
{
|
||||
try
|
||||
{
|
||||
DatabaseService.Instance.ClearAllTables();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"清除本地資料失敗:{ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 取得 Classes Repository
|
||||
/// </summary>
|
||||
public ClassesRepository GetClassesRepository()
|
||||
{
|
||||
return _classesRepo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 取得 SignUps Repository
|
||||
/// </summary>
|
||||
public SignUpsRepository GetSignUpsRepository()
|
||||
{
|
||||
return _signUpsRepo;
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
+231
@@ -0,0 +1,231 @@
|
||||
namespace 報到系統客戶端
|
||||
{
|
||||
partial class ExistConfirmForm
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
|
||||
this.label1 = new System.Windows.Forms.Label();
|
||||
this.label2 = new System.Windows.Forms.Label();
|
||||
this.label3 = new System.Windows.Forms.Label();
|
||||
this.lbName = new System.Windows.Forms.Label();
|
||||
this.lbMobile = new System.Windows.Forms.Label();
|
||||
this.label4 = new System.Windows.Forms.Label();
|
||||
this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel();
|
||||
this.button1 = new System.Windows.Forms.Button();
|
||||
this.btnNo = new System.Windows.Forms.Button();
|
||||
this.btnYes = new System.Windows.Forms.Button();
|
||||
this.tableLayoutPanel1.SuspendLayout();
|
||||
this.flowLayoutPanel1.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// tableLayoutPanel1
|
||||
//
|
||||
this.tableLayoutPanel1.ColumnCount = 4;
|
||||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 24F));
|
||||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 142F));
|
||||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
|
||||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 24F));
|
||||
this.tableLayoutPanel1.Controls.Add(this.label1, 1, 1);
|
||||
this.tableLayoutPanel1.Controls.Add(this.label2, 1, 2);
|
||||
this.tableLayoutPanel1.Controls.Add(this.label3, 1, 3);
|
||||
this.tableLayoutPanel1.Controls.Add(this.lbName, 2, 2);
|
||||
this.tableLayoutPanel1.Controls.Add(this.lbMobile, 2, 3);
|
||||
this.tableLayoutPanel1.Controls.Add(this.label4, 1, 4);
|
||||
this.tableLayoutPanel1.Controls.Add(this.flowLayoutPanel1, 1, 5);
|
||||
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0);
|
||||
this.tableLayoutPanel1.Margin = new System.Windows.Forms.Padding(4);
|
||||
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
|
||||
this.tableLayoutPanel1.RowCount = 7;
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 24F));
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 56F));
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 56F));
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 56F));
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 56F));
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 56F));
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 24F));
|
||||
this.tableLayoutPanel1.Size = new System.Drawing.Size(598, 332);
|
||||
this.tableLayoutPanel1.TabIndex = 0;
|
||||
//
|
||||
// label1
|
||||
//
|
||||
this.label1.Anchor = System.Windows.Forms.AnchorStyles.Left;
|
||||
this.label1.AutoSize = true;
|
||||
this.tableLayoutPanel1.SetColumnSpan(this.label1, 3);
|
||||
this.label1.Location = new System.Drawing.Point(28, 32);
|
||||
this.label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
|
||||
this.label1.Name = "label1";
|
||||
this.label1.Size = new System.Drawing.Size(529, 40);
|
||||
this.label1.TabIndex = 0;
|
||||
this.label1.Text = "用姓名或手機號找到預約報名資料:";
|
||||
//
|
||||
// label2
|
||||
//
|
||||
this.label2.Anchor = System.Windows.Forms.AnchorStyles.Right;
|
||||
this.label2.AutoSize = true;
|
||||
this.label2.Location = new System.Drawing.Point(81, 88);
|
||||
this.label2.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
|
||||
this.label2.Name = "label2";
|
||||
this.label2.Size = new System.Drawing.Size(81, 40);
|
||||
this.label2.TabIndex = 1;
|
||||
this.label2.Text = "姓名";
|
||||
//
|
||||
// label3
|
||||
//
|
||||
this.label3.Anchor = System.Windows.Forms.AnchorStyles.Right;
|
||||
this.label3.AutoSize = true;
|
||||
this.label3.Location = new System.Drawing.Point(49, 144);
|
||||
this.label3.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
|
||||
this.label3.Name = "label3";
|
||||
this.label3.Size = new System.Drawing.Size(113, 40);
|
||||
this.label3.TabIndex = 2;
|
||||
this.label3.Text = "手機號";
|
||||
//
|
||||
// lbName
|
||||
//
|
||||
this.lbName.Anchor = System.Windows.Forms.AnchorStyles.Left;
|
||||
this.lbName.AutoSize = true;
|
||||
this.lbName.Font = new System.Drawing.Font("微軟正黑體", 24F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(136)));
|
||||
this.lbName.ForeColor = System.Drawing.Color.Blue;
|
||||
this.lbName.Location = new System.Drawing.Point(170, 88);
|
||||
this.lbName.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
|
||||
this.lbName.Name = "lbName";
|
||||
this.lbName.Size = new System.Drawing.Size(81, 40);
|
||||
this.lbName.TabIndex = 3;
|
||||
this.lbName.Text = "姓名";
|
||||
//
|
||||
// lbMobile
|
||||
//
|
||||
this.lbMobile.Anchor = System.Windows.Forms.AnchorStyles.Left;
|
||||
this.lbMobile.AutoSize = true;
|
||||
this.lbMobile.Font = new System.Drawing.Font("微軟正黑體", 24F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(136)));
|
||||
this.lbMobile.ForeColor = System.Drawing.Color.Blue;
|
||||
this.lbMobile.Location = new System.Drawing.Point(170, 144);
|
||||
this.lbMobile.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
|
||||
this.lbMobile.Name = "lbMobile";
|
||||
this.lbMobile.Size = new System.Drawing.Size(113, 40);
|
||||
this.lbMobile.TabIndex = 4;
|
||||
this.lbMobile.Text = "手機號";
|
||||
//
|
||||
// label4
|
||||
//
|
||||
this.label4.Anchor = System.Windows.Forms.AnchorStyles.Left;
|
||||
this.label4.AutoSize = true;
|
||||
this.tableLayoutPanel1.SetColumnSpan(this.label4, 3);
|
||||
this.label4.Location = new System.Drawing.Point(28, 200);
|
||||
this.label4.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
|
||||
this.label4.Name = "label4";
|
||||
this.label4.Size = new System.Drawing.Size(401, 40);
|
||||
this.label4.TabIndex = 5;
|
||||
this.label4.Text = "是否確認是此位學員資料?";
|
||||
//
|
||||
// flowLayoutPanel1
|
||||
//
|
||||
this.tableLayoutPanel1.SetColumnSpan(this.flowLayoutPanel1, 2);
|
||||
this.flowLayoutPanel1.Controls.Add(this.button1);
|
||||
this.flowLayoutPanel1.Controls.Add(this.btnNo);
|
||||
this.flowLayoutPanel1.Controls.Add(this.btnYes);
|
||||
this.flowLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.flowLayoutPanel1.FlowDirection = System.Windows.Forms.FlowDirection.RightToLeft;
|
||||
this.flowLayoutPanel1.Location = new System.Drawing.Point(24, 248);
|
||||
this.flowLayoutPanel1.Margin = new System.Windows.Forms.Padding(0);
|
||||
this.flowLayoutPanel1.Name = "flowLayoutPanel1";
|
||||
this.flowLayoutPanel1.Size = new System.Drawing.Size(550, 56);
|
||||
this.flowLayoutPanel1.TabIndex = 6;
|
||||
//
|
||||
// button1
|
||||
//
|
||||
this.button1.DialogResult = System.Windows.Forms.DialogResult.Cancel;
|
||||
this.button1.Location = new System.Drawing.Point(273, 4);
|
||||
this.button1.Margin = new System.Windows.Forms.Padding(4);
|
||||
this.button1.Name = "button1";
|
||||
this.button1.Size = new System.Drawing.Size(273, 52);
|
||||
this.button1.TabIndex = 2;
|
||||
this.button1.Text = "C 取消重新輸入";
|
||||
this.button1.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// btnNo
|
||||
//
|
||||
this.btnNo.DialogResult = System.Windows.Forms.DialogResult.No;
|
||||
this.btnNo.Location = new System.Drawing.Point(140, 4);
|
||||
this.btnNo.Margin = new System.Windows.Forms.Padding(4);
|
||||
this.btnNo.Name = "btnNo";
|
||||
this.btnNo.Size = new System.Drawing.Size(125, 52);
|
||||
this.btnNo.TabIndex = 1;
|
||||
this.btnNo.TabStop = false;
|
||||
this.btnNo.Text = "N 否";
|
||||
this.btnNo.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// btnYes
|
||||
//
|
||||
this.btnYes.DialogResult = System.Windows.Forms.DialogResult.Yes;
|
||||
this.btnYes.Location = new System.Drawing.Point(7, 4);
|
||||
this.btnYes.Margin = new System.Windows.Forms.Padding(4);
|
||||
this.btnYes.Name = "btnYes";
|
||||
this.btnYes.Size = new System.Drawing.Size(125, 52);
|
||||
this.btnYes.TabIndex = 0;
|
||||
this.btnYes.TabStop = false;
|
||||
this.btnYes.Text = "Y 是";
|
||||
this.btnYes.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// ExistConfirmForm
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(19F, 40F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(598, 332);
|
||||
this.Controls.Add(this.tableLayoutPanel1);
|
||||
this.Font = new System.Drawing.Font("微軟正黑體", 24F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(136)));
|
||||
this.KeyPreview = true;
|
||||
this.Margin = new System.Windows.Forms.Padding(10, 9, 10, 9);
|
||||
this.Name = "ExistConfirmForm";
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
|
||||
this.Text = "確認";
|
||||
this.Shown += new System.EventHandler(this.ExistConfirmForm_Shown);
|
||||
this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.ExistConfirmForm_KeyDown);
|
||||
this.tableLayoutPanel1.ResumeLayout(false);
|
||||
this.tableLayoutPanel1.PerformLayout();
|
||||
this.flowLayoutPanel1.ResumeLayout(false);
|
||||
this.ResumeLayout(false);
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
|
||||
private System.Windows.Forms.Label label1;
|
||||
private System.Windows.Forms.Label label2;
|
||||
private System.Windows.Forms.Label label3;
|
||||
private System.Windows.Forms.Label lbName;
|
||||
private System.Windows.Forms.Label lbMobile;
|
||||
private System.Windows.Forms.Label label4;
|
||||
private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1;
|
||||
private System.Windows.Forms.Button btnNo;
|
||||
private System.Windows.Forms.Button btnYes;
|
||||
private System.Windows.Forms.Button button1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Data;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace 報到系統客戶端
|
||||
{
|
||||
public partial class ExistConfirmForm : Form
|
||||
{
|
||||
public string 姓名 { get { return lbName.Text; } set { lbName.Text = value; } }
|
||||
public string 手機號 { get { return lbMobile.Text; } set { lbMobile.Text = value; } }
|
||||
|
||||
public ExistConfirmForm()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void ExistConfirmForm_KeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (e.KeyCode == Keys.Y)
|
||||
{
|
||||
DialogResult = DialogResult.Yes;
|
||||
e.SuppressKeyPress = true;
|
||||
return;
|
||||
}
|
||||
if (e.KeyCode == Keys.N)
|
||||
{
|
||||
DialogResult = DialogResult.No;
|
||||
e.SuppressKeyPress = true;
|
||||
return;
|
||||
}
|
||||
if (e.KeyCode == Keys.C)
|
||||
{
|
||||
DialogResult = DialogResult.Cancel;
|
||||
e.SuppressKeyPress = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void ExistConfirmForm_Shown(object sender, EventArgs e)
|
||||
{
|
||||
Services.NotificationSoundService.Ding();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
||||
Generated
+227
@@ -0,0 +1,227 @@
|
||||
namespace 報到系統客戶端
|
||||
{
|
||||
partial class LoginForm
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
|
||||
this.label1 = new System.Windows.Forms.Label();
|
||||
this.label2 = new System.Windows.Forms.Label();
|
||||
this.label3 = new System.Windows.Forms.Label();
|
||||
this.txtUserID = new System.Windows.Forms.TextBox();
|
||||
this.txtPassword = new System.Windows.Forms.TextBox();
|
||||
this.btnLogin = new System.Windows.Forms.Button();
|
||||
this.btnCancel = new System.Windows.Forms.Button();
|
||||
this.cbOffline = new System.Windows.Forms.CheckBox();
|
||||
this.lbApiUrlBase = new System.Windows.Forms.Label();
|
||||
this.lbVersion = new System.Windows.Forms.Label();
|
||||
this.tableLayoutPanel1.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// tableLayoutPanel1
|
||||
//
|
||||
this.tableLayoutPanel1.ColumnCount = 4;
|
||||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 40F));
|
||||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 45F));
|
||||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 55F));
|
||||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 49F));
|
||||
this.tableLayoutPanel1.Controls.Add(this.label1, 1, 1);
|
||||
this.tableLayoutPanel1.Controls.Add(this.label2, 1, 2);
|
||||
this.tableLayoutPanel1.Controls.Add(this.label3, 1, 3);
|
||||
this.tableLayoutPanel1.Controls.Add(this.txtUserID, 2, 2);
|
||||
this.tableLayoutPanel1.Controls.Add(this.txtPassword, 2, 3);
|
||||
this.tableLayoutPanel1.Controls.Add(this.btnLogin, 1, 5);
|
||||
this.tableLayoutPanel1.Controls.Add(this.btnCancel, 2, 5);
|
||||
this.tableLayoutPanel1.Controls.Add(this.cbOffline, 2, 4);
|
||||
this.tableLayoutPanel1.Controls.Add(this.lbApiUrlBase, 1, 6);
|
||||
this.tableLayoutPanel1.Controls.Add(this.lbVersion, 2, 0);
|
||||
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0);
|
||||
this.tableLayoutPanel1.Margin = new System.Windows.Forms.Padding(5, 4, 5, 4);
|
||||
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
|
||||
this.tableLayoutPanel1.RowCount = 7;
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 30F));
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 60F));
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 42F));
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 42F));
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 42F));
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 42F));
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
|
||||
this.tableLayoutPanel1.Size = new System.Drawing.Size(469, 289);
|
||||
this.tableLayoutPanel1.TabIndex = 0;
|
||||
//
|
||||
// label1
|
||||
//
|
||||
this.label1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.label1.AutoSize = true;
|
||||
this.tableLayoutPanel1.SetColumnSpan(this.label1, 2);
|
||||
this.label1.Font = new System.Drawing.Font("微軟正黑體", 21.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
|
||||
this.label1.Location = new System.Drawing.Point(45, 41);
|
||||
this.label1.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0);
|
||||
this.label1.Name = "label1";
|
||||
this.label1.Size = new System.Drawing.Size(370, 37);
|
||||
this.label1.TabIndex = 0;
|
||||
this.label1.Text = "新譽管理課程報到系統";
|
||||
this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
|
||||
//
|
||||
// label2
|
||||
//
|
||||
this.label2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.label2.AutoSize = true;
|
||||
this.label2.Location = new System.Drawing.Point(43, 101);
|
||||
this.label2.Name = "label2";
|
||||
this.label2.Size = new System.Drawing.Size(165, 20);
|
||||
this.label2.TabIndex = 1;
|
||||
this.label2.Text = "登入帳號:";
|
||||
this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
|
||||
//
|
||||
// label3
|
||||
//
|
||||
this.label3.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.label3.AutoSize = true;
|
||||
this.label3.Location = new System.Drawing.Point(43, 143);
|
||||
this.label3.Name = "label3";
|
||||
this.label3.Size = new System.Drawing.Size(165, 20);
|
||||
this.label3.TabIndex = 2;
|
||||
this.label3.Text = "登入密碼:";
|
||||
this.label3.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
|
||||
//
|
||||
// txtUserID
|
||||
//
|
||||
this.txtUserID.Anchor = System.Windows.Forms.AnchorStyles.Left;
|
||||
this.txtUserID.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
|
||||
this.txtUserID.ImeMode = System.Windows.Forms.ImeMode.Close;
|
||||
this.txtUserID.Location = new System.Drawing.Point(214, 96);
|
||||
this.txtUserID.Name = "txtUserID";
|
||||
this.txtUserID.Size = new System.Drawing.Size(150, 29);
|
||||
this.txtUserID.TabIndex = 5;
|
||||
//
|
||||
// txtPassword
|
||||
//
|
||||
this.txtPassword.Anchor = System.Windows.Forms.AnchorStyles.Left;
|
||||
this.txtPassword.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
|
||||
this.txtPassword.ImeMode = System.Windows.Forms.ImeMode.Close;
|
||||
this.txtPassword.Location = new System.Drawing.Point(214, 138);
|
||||
this.txtPassword.Name = "txtPassword";
|
||||
this.txtPassword.PasswordChar = '*';
|
||||
this.txtPassword.Size = new System.Drawing.Size(150, 29);
|
||||
this.txtPassword.TabIndex = 6;
|
||||
//
|
||||
// btnLogin
|
||||
//
|
||||
this.btnLogin.Anchor = System.Windows.Forms.AnchorStyles.Right;
|
||||
this.btnLogin.DialogResult = System.Windows.Forms.DialogResult.OK;
|
||||
this.btnLogin.Location = new System.Drawing.Point(123, 219);
|
||||
this.btnLogin.Name = "btnLogin";
|
||||
this.btnLogin.Size = new System.Drawing.Size(85, 36);
|
||||
this.btnLogin.TabIndex = 3;
|
||||
this.btnLogin.Text = "登入";
|
||||
this.btnLogin.UseVisualStyleBackColor = true;
|
||||
this.btnLogin.Click += new System.EventHandler(this.btnLogin_Click);
|
||||
//
|
||||
// btnCancel
|
||||
//
|
||||
this.btnCancel.Anchor = System.Windows.Forms.AnchorStyles.Left;
|
||||
this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
|
||||
this.btnCancel.Location = new System.Drawing.Point(214, 219);
|
||||
this.btnCancel.Name = "btnCancel";
|
||||
this.btnCancel.Size = new System.Drawing.Size(85, 36);
|
||||
this.btnCancel.TabIndex = 4;
|
||||
this.btnCancel.Text = "取消";
|
||||
this.btnCancel.UseVisualStyleBackColor = true;
|
||||
this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
|
||||
//
|
||||
// cbOffline
|
||||
//
|
||||
this.cbOffline.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.cbOffline.AutoSize = true;
|
||||
this.cbOffline.Location = new System.Drawing.Point(214, 183);
|
||||
this.cbOffline.Name = "cbOffline";
|
||||
this.cbOffline.Size = new System.Drawing.Size(203, 24);
|
||||
this.cbOffline.TabIndex = 7;
|
||||
this.cbOffline.Text = "離線模式";
|
||||
this.cbOffline.UseVisualStyleBackColor = true;
|
||||
this.cbOffline.CheckStateChanged += new System.EventHandler(this.cbOffline_CheckStateChanged);
|
||||
//
|
||||
// lbApiUrlBase
|
||||
//
|
||||
this.lbApiUrlBase.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.lbApiUrlBase.AutoSize = true;
|
||||
this.tableLayoutPanel1.SetColumnSpan(this.lbApiUrlBase, 2);
|
||||
this.lbApiUrlBase.Font = new System.Drawing.Font("微軟正黑體", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(136)));
|
||||
this.lbApiUrlBase.Location = new System.Drawing.Point(43, 265);
|
||||
this.lbApiUrlBase.Name = "lbApiUrlBase";
|
||||
this.lbApiUrlBase.Size = new System.Drawing.Size(374, 17);
|
||||
this.lbApiUrlBase.TabIndex = 1;
|
||||
this.lbApiUrlBase.Text = "(主機網址)";
|
||||
this.lbApiUrlBase.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
|
||||
//
|
||||
// lbVersion
|
||||
//
|
||||
this.lbVersion.Anchor = System.Windows.Forms.AnchorStyles.Right;
|
||||
this.lbVersion.AutoSize = true;
|
||||
this.lbVersion.Font = new System.Drawing.Font("微軟正黑體", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(136)));
|
||||
this.lbVersion.Location = new System.Drawing.Point(336, 6);
|
||||
this.lbVersion.Name = "lbVersion";
|
||||
this.lbVersion.Size = new System.Drawing.Size(81, 17);
|
||||
this.lbVersion.TabIndex = 8;
|
||||
this.lbVersion.Text = "(程式版本號)";
|
||||
//
|
||||
// LoginForm
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
|
||||
this.ClientSize = new System.Drawing.Size(469, 289);
|
||||
this.Controls.Add(this.tableLayoutPanel1);
|
||||
this.Font = new System.Drawing.Font("微軟正黑體", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
|
||||
this.Margin = new System.Windows.Forms.Padding(5, 4, 5, 4);
|
||||
this.Name = "LoginForm";
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
|
||||
this.Text = "登入";
|
||||
this.Activated += new System.EventHandler(this.LoginForm_Activated);
|
||||
this.tableLayoutPanel1.ResumeLayout(false);
|
||||
this.tableLayoutPanel1.PerformLayout();
|
||||
this.ResumeLayout(false);
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
|
||||
private System.Windows.Forms.Label label1;
|
||||
private System.Windows.Forms.Label label2;
|
||||
private System.Windows.Forms.Label label3;
|
||||
private System.Windows.Forms.Button btnLogin;
|
||||
private System.Windows.Forms.Button btnCancel;
|
||||
private System.Windows.Forms.TextBox txtUserID;
|
||||
private System.Windows.Forms.TextBox txtPassword;
|
||||
private System.Windows.Forms.CheckBox cbOffline;
|
||||
private System.Windows.Forms.Label lbApiUrlBase;
|
||||
private System.Windows.Forms.Label lbVersion;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Data;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using 報到系統客戶端.Services;
|
||||
|
||||
namespace 報到系統客戶端
|
||||
{
|
||||
public partial class LoginForm : Form
|
||||
{
|
||||
public LoginForm()
|
||||
{
|
||||
InitializeComponent();
|
||||
lbApiUrlBase.Text = string.Format("主機:{0}", ApiService.Instance.BaseUrl);
|
||||
lbVersion.Text = ClientSession.Version;
|
||||
}
|
||||
|
||||
private void btnCancel_Click(object sender, EventArgs e)
|
||||
{
|
||||
Application.Exit();
|
||||
}
|
||||
|
||||
private async void btnLogin_Click(object sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
btnLogin.Enabled = false;
|
||||
btnCancel.Enabled = false;
|
||||
btnLogin.Text = "登入中";
|
||||
Cursor.Current = Cursors.WaitCursor;
|
||||
Application.DoEvents();
|
||||
|
||||
if (cbOffline.Checked)
|
||||
{
|
||||
var loginResult = ClientSession.LoginOffline(txtUserID.Text);
|
||||
|
||||
if (loginResult.Item1)
|
||||
{
|
||||
DialogResult = DialogResult.OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox.Show(loginResult.Item2, "登入錯誤");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var loginResult = await ClientSession.LoginAsync(txtUserID.Text, txtPassword.Text);
|
||||
|
||||
if (loginResult.Item1)
|
||||
{
|
||||
DialogResult = DialogResult.OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox.Show(loginResult.Item2, "登入錯誤");
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Cursor.Current = Cursors.Default;
|
||||
btnLogin.Enabled = true;
|
||||
btnCancel.Enabled = true;
|
||||
btnLogin.Text = "登入";
|
||||
}
|
||||
}
|
||||
|
||||
private void LoginForm_Activated(object sender, EventArgs e)
|
||||
{
|
||||
txtUserID.Focus();
|
||||
}
|
||||
|
||||
private void cbOffline_CheckStateChanged(object sender, EventArgs e)
|
||||
{
|
||||
txtPassword.Enabled = !cbOffline.Checked;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
||||
Generated
+1746
File diff suppressed because it is too large
Load Diff
+1717
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,174 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<metadata name="statusStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>34, 15</value>
|
||||
</metadata>
|
||||
<metadata name="ColSeqNo.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>True</value>
|
||||
</metadata>
|
||||
<metadata name="ColName.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>True</value>
|
||||
</metadata>
|
||||
<metadata name="ColMobile.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>True</value>
|
||||
</metadata>
|
||||
<metadata name="ColCheckInTime.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>True</value>
|
||||
</metadata>
|
||||
<metadata name="ColCheckOutTime.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>True</value>
|
||||
</metadata>
|
||||
<metadata name="ColIDNumber.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>True</value>
|
||||
</metadata>
|
||||
<metadata name="ColIDType.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>True</value>
|
||||
</metadata>
|
||||
<metadata name="ColMealType.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>True</value>
|
||||
</metadata>
|
||||
<metadata name="ColEmail.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>True</value>
|
||||
</metadata>
|
||||
<metadata name="ColGender.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>True</value>
|
||||
</metadata>
|
||||
<metadata name="ColLossJobTimes.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>True</value>
|
||||
</metadata>
|
||||
<metadata name="AgreementStatus.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>True</value>
|
||||
</metadata>
|
||||
<metadata name="bsSignUp.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>271, 15</value>
|
||||
</metadata>
|
||||
<metadata name="downloadWorker.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>137, 15</value>
|
||||
</metadata>
|
||||
<metadata name="uploadWorker.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>362, 15</value>
|
||||
</metadata>
|
||||
<metadata name="uploadTimer.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>475, 15</value>
|
||||
</metadata>
|
||||
<metadata name="$this.TrayHeight" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>59</value>
|
||||
</metadata>
|
||||
</root>
|
||||
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace 報到系統客戶端.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// 員工資訊項目
|
||||
/// </summary>
|
||||
public class BPSNItem
|
||||
{
|
||||
[JsonProperty("BPENO")]
|
||||
public string BPENO { get; set; }
|
||||
|
||||
[JsonProperty("BPNME")]
|
||||
public string BPNME { get; set; }
|
||||
|
||||
[JsonProperty("BPAWD")]
|
||||
public string BPAWD { get; set; }
|
||||
|
||||
[JsonProperty("Is系統管理群組")]
|
||||
public int Is系統管理群組 { get; set; }
|
||||
|
||||
[JsonProperty("Is客戶管理群組")]
|
||||
public int Is客戶管理群組 { get; set; }
|
||||
|
||||
[JsonProperty("Is現場工作群組")]
|
||||
public int Is現場工作群組 { get; set; }
|
||||
|
||||
[JsonProperty("Is查詢群組")]
|
||||
public int Is查詢群組 { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 員工資訊清單
|
||||
/// </summary>
|
||||
public class BPSNList : List<BPSNItem>
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace 報到系統客戶端.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// API 回應基礎類別
|
||||
/// </summary>
|
||||
public class BaseResponse
|
||||
{
|
||||
[JsonProperty("Success")]
|
||||
public bool Success { get; set; }
|
||||
|
||||
[JsonProperty("Message")]
|
||||
public string Message { get; set; }
|
||||
|
||||
[JsonProperty("Data")]
|
||||
public dynamic Data { get; set; }
|
||||
|
||||
[JsonProperty("TotalCount")]
|
||||
public int TotalCount { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 登入回應資料
|
||||
/// </summary>
|
||||
public class LoginResponse
|
||||
{
|
||||
[JsonProperty("UserName")]
|
||||
public string UserName { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 檢查 Session 回應資料
|
||||
/// </summary>
|
||||
public class CheckSessionResponse
|
||||
{
|
||||
[JsonProperty("UserName")]
|
||||
public string UserName { get; set; }
|
||||
|
||||
[JsonProperty("LoginTime")]
|
||||
public DateTime LoginTime { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace 報到系統客戶端.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// 簽到簽退紀錄項目
|
||||
/// </summary>
|
||||
public class CheckInItem
|
||||
{
|
||||
[JsonProperty("ID")]
|
||||
public int ID { get; set; }
|
||||
|
||||
[JsonProperty("CourseID")]
|
||||
public int CourseID { get; set; }
|
||||
|
||||
[JsonProperty("Name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonProperty("Mobile")]
|
||||
public string Mobile { get; set; }
|
||||
|
||||
[JsonProperty("SignUpType")]
|
||||
public string SignUpType { get; set; }
|
||||
|
||||
[JsonProperty("IDNumber")]
|
||||
public string IDNumber { get; set; }
|
||||
|
||||
[JsonProperty("IDType")]
|
||||
public string IDType { get; set; }
|
||||
|
||||
[JsonProperty("MealType")]
|
||||
public string MealType { get; set; }
|
||||
|
||||
[JsonProperty("Email")]
|
||||
public string Email { get; set; }
|
||||
|
||||
[JsonProperty("LossJobTimes")]
|
||||
public int? LossJobTimes { get; set; }
|
||||
|
||||
[JsonProperty("Gender")]
|
||||
public string Gender { get; set; }
|
||||
|
||||
[JsonProperty("AgreementStatus")]
|
||||
public string AgreementStatus { get; set; }
|
||||
|
||||
[JsonProperty("CheckInType")]
|
||||
public string CheckInType { get; set; }
|
||||
|
||||
[JsonProperty("CheckInTime")]
|
||||
public string CheckInTime { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 簽到簽退紀錄清單
|
||||
/// </summary>
|
||||
public class CheckInList : List<CheckInItem>
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace 報到系統客戶端.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// 活動資訊項目
|
||||
/// </summary>
|
||||
public class ClassesItem
|
||||
{
|
||||
[JsonProperty("ID")]
|
||||
public int ID { get; set; }
|
||||
|
||||
[JsonProperty("CourseCode")]
|
||||
public string CourseCode { get; set; }
|
||||
|
||||
[JsonProperty("CourseName")]
|
||||
public string CourseName { get; set; }
|
||||
|
||||
[JsonProperty("CourseDate")]
|
||||
public DateTime CourseDate { get; set; }
|
||||
|
||||
[JsonProperty("CourseLocation")]
|
||||
public string CourseLocation { get; set; }
|
||||
|
||||
[JsonProperty("StartTime")]
|
||||
public string StartTime { get; set; }
|
||||
|
||||
[JsonProperty("EndTime")]
|
||||
public string EndTime { get; set; }
|
||||
|
||||
[JsonProperty("Status")]
|
||||
public string Status { get; set; }
|
||||
|
||||
[JsonProperty("Description")]
|
||||
public string Description { get; set; }
|
||||
|
||||
[JsonProperty("NotificationTemplate")]
|
||||
public string NotificationTemplate { get; set; }
|
||||
|
||||
[JsonProperty("AgreedTerms")]
|
||||
public string AgreedTerms { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 活動資訊清單
|
||||
/// </summary>
|
||||
public class ClassesList : List<ClassesItem>
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace 報到系統客戶端.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// 報名人員項目
|
||||
/// </summary>
|
||||
|
||||
public class SignUpItem
|
||||
{
|
||||
[JsonProperty("ID")]
|
||||
public int ID { get; set; }
|
||||
|
||||
[JsonProperty("CourseID")]
|
||||
public int CourseID { get; set; }
|
||||
|
||||
[JsonProperty("SeqNo")]
|
||||
public int SeqNo { get; set; }
|
||||
|
||||
[JsonProperty("Name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonProperty("Mobile")]
|
||||
public string Mobile { get; set; }
|
||||
|
||||
[JsonProperty("Email")]
|
||||
public string Email { get; set; }
|
||||
|
||||
[JsonProperty("SignUpType")]
|
||||
public string SignUpType { get; set; }
|
||||
|
||||
[JsonProperty("IDNumber")]
|
||||
public string IDNumber { get; set; }
|
||||
|
||||
[JsonProperty("IDType")]
|
||||
public string IDType { get; set; }
|
||||
|
||||
[JsonProperty("MealType")]
|
||||
public string MealType { get; set; }
|
||||
|
||||
[JsonProperty("QRCode")]
|
||||
public string QRCode { get; set; }
|
||||
|
||||
[JsonProperty("LossJobTimes")]
|
||||
public int? LossJobTimes { get; set; }
|
||||
|
||||
[JsonProperty("Gender")]
|
||||
public string Gender { get; set; }
|
||||
|
||||
[JsonProperty("AgreementStatus")]
|
||||
public string AgreementStatus { get; set; }
|
||||
|
||||
[JsonProperty("CheckInTime")]
|
||||
public string CheckInTime { get; set; }
|
||||
|
||||
[JsonProperty("CheckOutTime")]
|
||||
public string CheckOutTime { get; set; }
|
||||
|
||||
[JsonProperty("Remark")]
|
||||
public string Remark { get; set; }
|
||||
|
||||
[JsonProperty("DBAppNo")]
|
||||
public int DBAppNo { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 報名人員清單
|
||||
/// </summary>
|
||||
public class SignUpList : List<SignUpItem>
|
||||
{
|
||||
}
|
||||
}
|
||||
Generated
+66
@@ -0,0 +1,66 @@
|
||||
namespace 報到系統客戶端
|
||||
{
|
||||
partial class ProcessingForm
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.label1 = new System.Windows.Forms.Label();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// label1
|
||||
//
|
||||
this.label1.AutoSize = true;
|
||||
this.label1.ForeColor = System.Drawing.Color.White;
|
||||
this.label1.Location = new System.Drawing.Point(55, 34);
|
||||
this.label1.Name = "label1";
|
||||
this.label1.Size = new System.Drawing.Size(320, 35);
|
||||
this.label1.TabIndex = 0;
|
||||
this.label1.Text = "上下傳處理中,請稍候.....";
|
||||
//
|
||||
// ProcessingForm
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
|
||||
this.BackColor = System.Drawing.Color.DodgerBlue;
|
||||
this.ClientSize = new System.Drawing.Size(444, 105);
|
||||
this.Controls.Add(this.label1);
|
||||
this.Font = new System.Drawing.Font("微軟正黑體", 20F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(136)));
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
|
||||
this.Margin = new System.Windows.Forms.Padding(8, 9, 8, 9);
|
||||
this.Name = "ProcessingForm";
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
|
||||
this.Text = "處理中";
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.Label label1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Data;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace 報到系統客戶端
|
||||
{
|
||||
public partial class ProcessingForm : Form
|
||||
{
|
||||
public ProcessingForm()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
||||
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace 報到系統客戶端
|
||||
{
|
||||
internal static class Program
|
||||
{
|
||||
/// <summary>
|
||||
/// 應用程式的主要進入點。
|
||||
/// </summary>
|
||||
[STAThread]
|
||||
static void Main()
|
||||
{
|
||||
Application.EnableVisualStyles();
|
||||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
Application.Run(new MainForm());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// 組件的一般資訊是由下列的屬性集控制。
|
||||
// 變更這些屬性的值即可修改組件的相關
|
||||
// 資訊。
|
||||
[assembly: AssemblyTitle("報到系統客戶端")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("Maolaoda Studio")]
|
||||
[assembly: AssemblyProduct("報到系統客戶端")]
|
||||
[assembly: AssemblyCopyright("Copyright © Maolaoda Studio 2026")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// 將 ComVisible 設為 false 可對 COM 元件隱藏
|
||||
// 組件中的類型。若必須從 COM 存取此組件中的類型,
|
||||
// 的類型,請在該類型上將 ComVisible 屬性設定為 true。
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// 下列 GUID 為專案公開 (Expose) 至 COM 時所要使用的 typelib ID
|
||||
[assembly: Guid("8b1277b9-7958-4b49-b8e5-3d32a3a0311c")]
|
||||
|
||||
// 組件的版本資訊由下列四個值所組成:
|
||||
//
|
||||
// 主要版本
|
||||
// 次要版本
|
||||
// 組建編號
|
||||
// 修訂編號
|
||||
//
|
||||
[assembly: AssemblyVersion("26.7.24")]
|
||||
[assembly: AssemblyFileVersion("26.7.24")]
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
This file is automatically generated by Visual Studio .Net. It is
|
||||
used to store generic object data source configuration information.
|
||||
Renaming the file extension or editing the content of this file may
|
||||
cause the file to be unrecognizable by the program.
|
||||
-->
|
||||
<GenericObjectDataSource DisplayName="LocalSignUpItem" Version="1.0" xmlns="urn:schemas-microsoft-com:xml-msdatasource">
|
||||
<TypeInfo>報到系統客戶端.Data.Models.LocalSignUpItem, 報到系統客戶端, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null</TypeInfo>
|
||||
</GenericObjectDataSource>
|
||||
Generated
+63
@@ -0,0 +1,63 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// 這段程式碼是由工具產生的。
|
||||
// 執行階段版本:4.0.30319.42000
|
||||
//
|
||||
// 對這個檔案所做的變更可能會造成錯誤的行為,而且如果重新產生程式碼,
|
||||
// 變更將會遺失。
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace 報到系統客戶端.Properties {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 用於查詢當地語系化字串等的強類型資源類別。
|
||||
/// </summary>
|
||||
// 這個類別是自動產生的,是利用 StronglyTypedResourceBuilder
|
||||
// 類別透過 ResGen 或 Visual Studio 這類工具。
|
||||
// 若要加入或移除成員,請編輯您的 .ResX 檔,然後重新執行 ResGen
|
||||
// (利用 /str 選項),或重建您的 VS 專案。
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Resources {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Resources() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 傳回這個類別使用的快取的 ResourceManager 執行個體。
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("報到系統客戶端.Properties.Resources", typeof(Resources).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 覆寫目前執行緒的 CurrentUICulture 屬性,對象是所有
|
||||
/// 使用這個強類型資源類別的資源查閱。
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
||||
Generated
+35
@@ -0,0 +1,35 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// 這段程式碼是由工具產生的。
|
||||
// 執行階段版本:4.0.30319.42000
|
||||
//
|
||||
// 對這個檔案所做的變更可能會造成錯誤的行為,而且如果重新產生程式碼,
|
||||
// 變更將會遺失。
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace 報到系統客戶端.Properties {
|
||||
|
||||
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.14.0.0")]
|
||||
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
|
||||
|
||||
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
|
||||
|
||||
public static Settings Default {
|
||||
get {
|
||||
return defaultInstance;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.ApplicationScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("http://localhost:58792")]
|
||||
public string ApiBaseUrl {
|
||||
get {
|
||||
return ((string)(this["ApiBaseUrl"]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="報到系統客戶端.Properties" GeneratedClassName="Settings">
|
||||
<Profiles />
|
||||
<Settings>
|
||||
<Setting Name="ApiBaseUrl" Type="System.String" Scope="Application">
|
||||
<Value Profile="(Default)">http://localhost:58792</Value>
|
||||
</Setting>
|
||||
</Settings>
|
||||
</SettingsFile>
|
||||
@@ -0,0 +1,328 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using 報到系統客戶端.Models;
|
||||
|
||||
namespace 報到系統客戶端.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// API 服務基礎類別
|
||||
/// 處理 HTTP 請求、Session cookie、錯誤處理
|
||||
/// </summary>
|
||||
public class ApiService
|
||||
{
|
||||
private static ApiService _instance;
|
||||
private static readonly object _lockObject = new object();
|
||||
|
||||
private string _baseUrl;
|
||||
private CookieContainer _cookieContainer;
|
||||
|
||||
// 用於儲存 cookie 的檔案路徑
|
||||
private string _cookiePath;
|
||||
|
||||
public static JsonSerializerSettings jsonConvertSettings = new JsonSerializerSettings { DateTimeZoneHandling = DateTimeZoneHandling.Local };
|
||||
|
||||
public static object DeserializeObject(string value)
|
||||
{
|
||||
return JsonConvert.DeserializeObject(value, jsonConvertSettings);
|
||||
}
|
||||
public static T DeserializeObject<T>(string value)
|
||||
{
|
||||
return (T)JsonConvert.DeserializeObject(value, typeof(T), jsonConvertSettings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 取得單例實例
|
||||
/// </summary>
|
||||
public static ApiService Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_instance == null)
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
if (_instance == null)
|
||||
{
|
||||
_instance = new ApiService();
|
||||
}
|
||||
}
|
||||
}
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
private ApiService()
|
||||
{
|
||||
// 從設定檔讀取基底 URL
|
||||
_baseUrl = ConfigService.Instance.GetApiBaseUrl();
|
||||
if (string.IsNullOrWhiteSpace(_baseUrl))
|
||||
{
|
||||
_baseUrl = "http://localhost:58792";
|
||||
}
|
||||
|
||||
// 初始化 Cookie 容器
|
||||
_cookieContainer = new CookieContainer();
|
||||
_cookiePath = Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
||||
"報到系統客戶端",
|
||||
"cookies.txt"
|
||||
);
|
||||
|
||||
// 嘗試載入已儲存的 Cookie
|
||||
LoadCookies();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 設定基底 URL
|
||||
/// </summary>
|
||||
public string BaseUrl
|
||||
{
|
||||
set
|
||||
{
|
||||
_baseUrl = value;
|
||||
}
|
||||
get
|
||||
{
|
||||
return _baseUrl;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 執行 POST 請求
|
||||
/// </summary>
|
||||
public async Task<BaseResponse> PostAsync(string servicePath, string methodName, object parameters)
|
||||
{
|
||||
try
|
||||
{
|
||||
string url = $"{_baseUrl}{servicePath}/{methodName}";
|
||||
|
||||
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
|
||||
request.Method = "POST";
|
||||
request.ContentType = "application/json; charset=utf-8";
|
||||
request.CookieContainer = _cookieContainer;
|
||||
request.KeepAlive = true;
|
||||
|
||||
// 序列化參數
|
||||
string jsonData = JsonConvert.SerializeObject(parameters ?? new { });
|
||||
|
||||
// 寫入請求體
|
||||
byte[] data = Encoding.UTF8.GetBytes(jsonData);
|
||||
request.ContentLength = data.Length;
|
||||
|
||||
using (Stream requestStream = request.GetRequestStream())
|
||||
{
|
||||
await requestStream.WriteAsync(data, 0, data.Length);
|
||||
}
|
||||
|
||||
// 取得回應
|
||||
using (HttpWebResponse response = (HttpWebResponse)await request.GetResponseAsync())
|
||||
{
|
||||
// 更新 Cookie(用於 Session 管理)
|
||||
_cookieContainer.Add(response.ResponseUri, response.Cookies);
|
||||
SaveCookies();
|
||||
|
||||
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
|
||||
{
|
||||
string responseJson = await reader.ReadToEndAsync();
|
||||
|
||||
// 解析回應
|
||||
dynamic result = DeserializeObject(responseJson);
|
||||
|
||||
// ASP.NET Web Service 會將結果包在 "d" 屬性中
|
||||
if (result.d != null)
|
||||
{
|
||||
string dataJson = JsonConvert.SerializeObject(result.d);
|
||||
return DeserializeObject<BaseResponse>(dataJson);
|
||||
}
|
||||
else
|
||||
{
|
||||
return DeserializeObject<BaseResponse>(responseJson);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (WebException ex)
|
||||
{
|
||||
return new BaseResponse
|
||||
{
|
||||
Success = false,
|
||||
Message = $"網路錯誤: {ex.Message}"
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new BaseResponse
|
||||
{
|
||||
Success = false,
|
||||
Message = $"錯誤: {ex.Message}"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 同步版本的 POST 請求(用於向後相容)
|
||||
/// </summary>
|
||||
public BaseResponse Post(string servicePath, string methodName, object parameters)
|
||||
{
|
||||
try
|
||||
{
|
||||
string url = $"{_baseUrl}{servicePath}/{methodName}";
|
||||
|
||||
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
|
||||
request.Method = "POST";
|
||||
request.ContentType = "application/json; charset=utf-8";
|
||||
request.CookieContainer = _cookieContainer;
|
||||
request.KeepAlive = true;
|
||||
|
||||
// 序列化參數
|
||||
string jsonData = JsonConvert.SerializeObject(parameters ?? new { });
|
||||
|
||||
// 寫入請求體
|
||||
byte[] data = Encoding.UTF8.GetBytes(jsonData);
|
||||
request.ContentLength = data.Length;
|
||||
|
||||
using (Stream requestStream = request.GetRequestStream())
|
||||
{
|
||||
requestStream.Write(data, 0, data.Length);
|
||||
}
|
||||
|
||||
// 取得回應
|
||||
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
|
||||
{
|
||||
// 更新 Cookie(用於 Session 管理)
|
||||
_cookieContainer.Add(response.ResponseUri, response.Cookies);
|
||||
SaveCookies();
|
||||
|
||||
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
|
||||
{
|
||||
string responseJson = reader.ReadToEnd();
|
||||
|
||||
// 解析回應
|
||||
dynamic result = DeserializeObject(responseJson);
|
||||
|
||||
// ASP.NET Web Service 會將結果包在 "d" 屬性中
|
||||
if (result.d != null)
|
||||
{
|
||||
string dataJson = JsonConvert.SerializeObject(result.d);
|
||||
return DeserializeObject<BaseResponse>(dataJson);
|
||||
}
|
||||
else
|
||||
{
|
||||
return DeserializeObject<BaseResponse>(responseJson);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (WebException ex)
|
||||
{
|
||||
return new BaseResponse
|
||||
{
|
||||
Success = false,
|
||||
Message = $"網路錯誤: {ex.Message}"
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new BaseResponse
|
||||
{
|
||||
Success = false,
|
||||
Message = $"錯誤: {ex.Message}"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清除所有 Session cookie
|
||||
/// </summary>
|
||||
public void ClearSession()
|
||||
{
|
||||
_cookieContainer = new CookieContainer();
|
||||
DeleteCookies();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 儲存 Cookie 到檔案
|
||||
/// </summary>
|
||||
private void SaveCookies()
|
||||
{
|
||||
try
|
||||
{
|
||||
string directory = Path.GetDirectoryName(_cookiePath);
|
||||
if (!Directory.Exists(directory))
|
||||
{
|
||||
Directory.CreateDirectory(directory);
|
||||
}
|
||||
|
||||
using (StreamWriter sw = new StreamWriter(_cookiePath, false, Encoding.UTF8))
|
||||
{
|
||||
foreach (Cookie cookie in _cookieContainer.GetCookies(new Uri(_baseUrl)))
|
||||
{
|
||||
sw.WriteLine($"{cookie.Name}={cookie.Value}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 忽略儲存錯誤
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 從檔案載入 Cookie
|
||||
/// </summary>
|
||||
private void LoadCookies()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (File.Exists(_cookiePath))
|
||||
{
|
||||
using (StreamReader sr = new StreamReader(_cookiePath, Encoding.UTF8))
|
||||
{
|
||||
string line;
|
||||
while ((line = sr.ReadLine()) != null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(line)) continue;
|
||||
|
||||
string[] parts = line.Split(new[] { '=' }, 2);
|
||||
if (parts.Length == 2)
|
||||
{
|
||||
Cookie cookie = new Cookie(parts[0], parts[1])
|
||||
{
|
||||
Path = "/",
|
||||
HttpOnly = true
|
||||
};
|
||||
_cookieContainer.Add(new Uri(_baseUrl), cookie);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 忽略載入錯誤
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 刪除儲存的 Cookie 檔案
|
||||
/// </summary>
|
||||
private void DeleteCookies()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (File.Exists(_cookiePath))
|
||||
{
|
||||
File.Delete(_cookiePath);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 忽略刪除錯誤
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using 報到系統客戶端.Models;
|
||||
|
||||
namespace 報到系統客戶端.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// 員工資料服務客戶端
|
||||
/// 呼叫後端 Services/BPSNService.asmx
|
||||
/// </summary>
|
||||
public class BPSNService
|
||||
{
|
||||
private const string SERVICE_PATH = "/Services/BPSNService.asmx";
|
||||
|
||||
/// <summary>
|
||||
/// 下載員工資料
|
||||
/// </summary>
|
||||
/// <returns>下載結果,包含員工資料清單</returns>
|
||||
public static BaseResponse Download()
|
||||
{
|
||||
return ApiService.Instance.Post(SERVICE_PATH, "Download", null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 下載員工資料(非同步版本)
|
||||
/// </summary>
|
||||
//public static async Task<BaseResponse> DownloadAsync()
|
||||
//{
|
||||
// return await ApiService.Instance.PostAsync(SERVICE_PATH, "Download", null);
|
||||
//}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using 報到系統客戶端.Models;
|
||||
using static System.Windows.Forms.VisualStyles.VisualStyleElement.ToolTip;
|
||||
|
||||
namespace 報到系統客戶端.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// 活動服務客戶端
|
||||
/// 呼叫後端 Services/ClassesService.asmx
|
||||
/// </summary>
|
||||
public class ClassesService
|
||||
{
|
||||
private const string SERVICE_PATH = "/Services/ClassesService.asmx";
|
||||
|
||||
/// <summary>
|
||||
/// 搜尋活動清單
|
||||
/// </summary>
|
||||
/// <param name="pageIndex">頁碼(從 1 開始)</param>
|
||||
/// <param name="pageSize">每頁筆數</param>
|
||||
/// <param name="courseCode">活動代碼</param>
|
||||
/// <param name="courseName">活動名稱</param>
|
||||
/// <param name="courseDate1">活動開始日期</param>
|
||||
/// <param name="courseDate2">活動結束日期</param>
|
||||
/// <param name="courseLocation">活動地點</param>
|
||||
/// <returns>搜尋結果</returns>
|
||||
public static BaseResponse SearchClasses()
|
||||
{
|
||||
var parameters = new
|
||||
{
|
||||
pageIndex = 1,
|
||||
pageSize = Int32.MaxValue,
|
||||
courseCode = string.Empty,
|
||||
courseName = string.Empty,
|
||||
courseDate1 = new DateTime?(),
|
||||
courseDate2 = new DateTime?(),
|
||||
courseLocation = string.Empty,
|
||||
projectID = string.Empty,
|
||||
closed = false
|
||||
};
|
||||
|
||||
return ApiService.Instance.Post(SERVICE_PATH, "SearchClasses", parameters);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 搜尋活動清單(非同步版本)
|
||||
/// </summary>
|
||||
//public static async Task<BaseResponse> SearchClassesAsync()
|
||||
//{
|
||||
// var parameters = new
|
||||
// {
|
||||
// pageIndex = 1,
|
||||
// pageSize = Int32.MaxValue,
|
||||
// courseCode = string.Empty,
|
||||
// courseName = string.Empty,
|
||||
// courseDate1 = new DateTime?(),
|
||||
// courseDate2 = new DateTime?(),
|
||||
// courseLocation = string.Empty,
|
||||
// projectID = string.Empty,
|
||||
// closed = false
|
||||
// };
|
||||
|
||||
// return await ApiService.Instance.PostAsync(SERVICE_PATH, "SearchClasses", parameters);
|
||||
//}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
using System;
|
||||
using System.Configuration;
|
||||
using System.IO;
|
||||
using System.Xml;
|
||||
|
||||
namespace 報到系統客戶端.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// 系統設定管理服務
|
||||
/// 提供讀取和寫入應用程式設定的功能
|
||||
/// 設定檔存儲在用戶的 AppData 資料夾中,以避免 Program Files 權限問題
|
||||
/// </summary>
|
||||
public class ConfigService
|
||||
{
|
||||
private static ConfigService _instance;
|
||||
private static readonly object _lockObject = new object();
|
||||
|
||||
private string _userConfigPath;
|
||||
private string _defaultConfigPath;
|
||||
|
||||
/// <summary>
|
||||
/// 取得 ConfigService 的單一實例
|
||||
/// </summary>
|
||||
public static ConfigService Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_instance == null)
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
if (_instance == null)
|
||||
{
|
||||
_instance = new ConfigService();
|
||||
}
|
||||
}
|
||||
}
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
private ConfigService()
|
||||
{
|
||||
_defaultConfigPath = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
|
||||
|
||||
// 用戶設定檔存儲在 AppData\Roaming\報到系統客戶端 資料夾
|
||||
string appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
|
||||
string configDirectory = Path.Combine(appDataPath, "報到系統客戶端");
|
||||
|
||||
// 確保目錄存在
|
||||
if (!Directory.Exists(configDirectory))
|
||||
{
|
||||
Directory.CreateDirectory(configDirectory);
|
||||
}
|
||||
|
||||
_userConfigPath = Path.Combine(configDirectory, "AppSettings.config");
|
||||
|
||||
// 如果用戶設定檔不存在,從預設設定檔複製
|
||||
if (!File.Exists(_userConfigPath))
|
||||
{
|
||||
InitializeUserConfig();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化用戶設定檔
|
||||
/// 從預設設定檔複製所需的設定項目
|
||||
/// </summary>
|
||||
private void InitializeUserConfig()
|
||||
{
|
||||
try
|
||||
{
|
||||
XmlDocument defaultDoc = new XmlDocument();
|
||||
defaultDoc.Load(_defaultConfigPath);
|
||||
|
||||
XmlDocument userDoc = new XmlDocument();
|
||||
|
||||
// 建立新的設定檔結構
|
||||
XmlDeclaration declaration = userDoc.CreateXmlDeclaration("1.0", "utf-8", null);
|
||||
userDoc.AppendChild(declaration);
|
||||
|
||||
XmlElement configElement = userDoc.CreateElement("configuration");
|
||||
userDoc.AppendChild(configElement);
|
||||
|
||||
// 建立 applicationSettings 節點
|
||||
XmlElement appSettingsNode = userDoc.CreateElement("applicationSettings");
|
||||
configElement.AppendChild(appSettingsNode);
|
||||
|
||||
// 建立報到系統客戶端.Properties.Settings 節點
|
||||
XmlElement settingsNode = userDoc.CreateElement("報到系統客戶端.Properties.Settings");
|
||||
appSettingsNode.AppendChild(settingsNode);
|
||||
|
||||
// 建立 ApiBaseUrl 設定項目
|
||||
XmlElement settingElement = userDoc.CreateElement("setting");
|
||||
settingElement.SetAttribute("name", "ApiBaseUrl");
|
||||
settingElement.SetAttribute("serializeAs", "String");
|
||||
|
||||
XmlElement valueElement = userDoc.CreateElement("value");
|
||||
valueElement.InnerText = "https://thinkyustaff.com/event";
|
||||
settingElement.AppendChild(valueElement);
|
||||
|
||||
settingsNode.AppendChild(settingElement);
|
||||
|
||||
userDoc.Save(_userConfigPath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"初始化用戶設定檔失敗: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 讀取 API 基礎 URL
|
||||
/// </summary>
|
||||
/// <returns>API 基礎 URL</returns>
|
||||
public string GetApiBaseUrl()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (File.Exists(_userConfigPath))
|
||||
{
|
||||
XmlDocument doc = new XmlDocument();
|
||||
doc.Load(_userConfigPath);
|
||||
|
||||
XmlNode valueNode = doc.SelectSingleNode(
|
||||
"/configuration/applicationSettings/報到系統客戶端.Properties.Settings/setting[@name='ApiBaseUrl']/value"
|
||||
);
|
||||
|
||||
if (valueNode != null && !string.IsNullOrWhiteSpace(valueNode.InnerText))
|
||||
{
|
||||
return valueNode.InnerText;
|
||||
}
|
||||
}
|
||||
|
||||
// 回退到預設值或 App.config
|
||||
return Properties.Settings.Default.ApiBaseUrl ?? "https://thinkyustaff.com/event";
|
||||
}
|
||||
catch
|
||||
{
|
||||
return "https://thinkyustaff.com/event";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 設定 API 基礎 URL
|
||||
/// </summary>
|
||||
/// <param name="baseUrl">新的 API 基礎 URL</param>
|
||||
/// <returns>是否設定成功</returns>
|
||||
public bool SetApiBaseUrl(string baseUrl)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(baseUrl))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// 確保用戶設定檔存在
|
||||
if (!File.Exists(_userConfigPath))
|
||||
{
|
||||
InitializeUserConfig();
|
||||
}
|
||||
|
||||
XmlDocument doc = new XmlDocument();
|
||||
doc.Load(_userConfigPath);
|
||||
|
||||
// 找到或建立 applicationSettings 節點
|
||||
XmlNode appSettingsNode = doc.SelectSingleNode("//applicationSettings");
|
||||
if (appSettingsNode == null)
|
||||
{
|
||||
XmlNode configNode = doc.SelectSingleNode("//configuration");
|
||||
if (configNode == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
appSettingsNode = doc.CreateElement("applicationSettings");
|
||||
configNode.AppendChild(appSettingsNode);
|
||||
}
|
||||
|
||||
// 找到或建立 報到系統客戶端.Properties.Settings 節點
|
||||
XmlNode settingsNode = appSettingsNode.SelectSingleNode("報到系統客戶端.Properties.Settings");
|
||||
if (settingsNode == null)
|
||||
{
|
||||
settingsNode = doc.CreateElement("報到系統客戶端.Properties.Settings");
|
||||
appSettingsNode.AppendChild(settingsNode);
|
||||
}
|
||||
|
||||
// 找到 ApiBaseUrl 設定項目
|
||||
XmlNode apiBaseUrlNode = settingsNode.SelectSingleNode("setting[@name='ApiBaseUrl']");
|
||||
if (apiBaseUrlNode == null)
|
||||
{
|
||||
apiBaseUrlNode = doc.CreateElement("setting");
|
||||
apiBaseUrlNode.Attributes.Append(doc.CreateAttribute("name"));
|
||||
apiBaseUrlNode.Attributes["name"].Value = "ApiBaseUrl";
|
||||
apiBaseUrlNode.Attributes.Append(doc.CreateAttribute("serializeAs"));
|
||||
apiBaseUrlNode.Attributes["serializeAs"].Value = "String";
|
||||
settingsNode.AppendChild(apiBaseUrlNode);
|
||||
}
|
||||
|
||||
// 更新值
|
||||
XmlNode valueNode = apiBaseUrlNode.SelectSingleNode("value");
|
||||
if (valueNode != null)
|
||||
{
|
||||
valueNode.InnerText = baseUrl;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果沒有 value 節點,則建立一個
|
||||
valueNode = doc.CreateElement("value");
|
||||
valueNode.InnerText = baseUrl;
|
||||
apiBaseUrlNode.AppendChild(valueNode);
|
||||
}
|
||||
|
||||
// 保存設定檔到用戶的 AppData 資料夾
|
||||
doc.Save(_userConfigPath);
|
||||
|
||||
// 刷新設定以更新記憶體中的值
|
||||
RefreshSettings();
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"設定 ApiBaseUrl 失敗: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 刷新設定值
|
||||
/// </summary>
|
||||
private void RefreshSettings()
|
||||
{
|
||||
try
|
||||
{
|
||||
Properties.Settings.Default.Reload();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 刷新失敗時不執行任何操作
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 取得應用程式設定檔路徑
|
||||
/// 返回用戶設定檔的路徑(位於 AppData 中)
|
||||
/// </summary>
|
||||
/// <returns>設定檔路徑</returns>
|
||||
//public string GetConfigFilePath()
|
||||
//{
|
||||
// return _userConfigPath;
|
||||
//}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using 報到系統客戶端.Models;
|
||||
|
||||
namespace 報到系統客戶端.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// 登入服務客戶端
|
||||
/// 呼叫後端 Services/LoginService.asmx
|
||||
/// </summary>
|
||||
public class LoginService
|
||||
{
|
||||
private const string SERVICE_PATH = "/Services/LoginService.asmx";
|
||||
|
||||
/// <summary>
|
||||
/// 登入
|
||||
/// </summary>
|
||||
/// <param name="username">使用者名稱</param>
|
||||
/// <param name="password">密碼</param>
|
||||
/// <param name="rememberMe">是否記住密碼</param>
|
||||
/// <returns>登入結果</returns>
|
||||
public static BaseResponse Login(string username, string password, bool rememberMe = false)
|
||||
{
|
||||
var parameters = new
|
||||
{
|
||||
username = username,
|
||||
password = password,
|
||||
rememberMe = rememberMe
|
||||
};
|
||||
|
||||
return ApiService.Instance.Post(SERVICE_PATH, "Login", parameters);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 登入(非同步版本)
|
||||
/// </summary>
|
||||
public static async Task<BaseResponse> LoginAsync(string username, string password, bool rememberMe = false)
|
||||
{
|
||||
var parameters = new
|
||||
{
|
||||
username = username,
|
||||
password = password,
|
||||
rememberMe = rememberMe
|
||||
};
|
||||
|
||||
return await ApiService.Instance.PostAsync(SERVICE_PATH, "Login", parameters);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 登出
|
||||
/// </summary>
|
||||
/// <returns>登出結果</returns>
|
||||
public static BaseResponse Logout()
|
||||
{
|
||||
return ApiService.Instance.Post(SERVICE_PATH, "Logout", null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 登出(非同步版本)
|
||||
/// </summary>
|
||||
//public static async Task<BaseResponse> LogoutAsync()
|
||||
//{
|
||||
// return await ApiService.Instance.PostAsync(SERVICE_PATH, "Logout", null);
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// 檢查 Session(當作心跳使用)
|
||||
/// </summary>
|
||||
/// <returns>檢查結果</returns>
|
||||
public static BaseResponse CheckSession()
|
||||
{
|
||||
return ApiService.Instance.Post(SERVICE_PATH, "CheckSession", null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 檢查 Session(非同步版本)
|
||||
/// </summary>
|
||||
//public static async Task<BaseResponse> CheckSessionAsync()
|
||||
//{
|
||||
// return await ApiService.Instance.PostAsync(SERVICE_PATH, "CheckSession", null);
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// 清除 Session
|
||||
/// </summary>
|
||||
public static void ClearSession()
|
||||
{
|
||||
ApiService.Instance.ClearSession();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 測試是否能連線
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static bool Ping()
|
||||
{
|
||||
var pingResult = ApiService.Instance.Post(SERVICE_PATH, "Ping", null);
|
||||
return pingResult != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Media;
|
||||
|
||||
namespace 報到系統客戶端.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// 通知音效服務,密集呼叫時會中斷先前的播放再重新播放。
|
||||
/// </summary>
|
||||
public static class NotificationSoundService
|
||||
{
|
||||
private static readonly string _dingWavePath =
|
||||
Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Sound\\ding.wav");
|
||||
private static readonly string _checkinWavePath =
|
||||
Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Sound\\CheckinOK.wav");
|
||||
private static readonly string _checkoutWavePath =
|
||||
Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Sound\\CheckoutOK.wav");
|
||||
|
||||
private static SoundPlayer _dingPlayer;
|
||||
private static SoundPlayer _checkinPlayer;
|
||||
private static SoundPlayer _checkoutPlayer;
|
||||
|
||||
private static void StopAll()
|
||||
{
|
||||
_dingPlayer?.Stop();
|
||||
_checkinPlayer?.Stop();
|
||||
_checkoutPlayer?.Stop();
|
||||
}
|
||||
|
||||
private static void PlaySound(string path, ref SoundPlayer player)
|
||||
{
|
||||
if (!File.Exists(path))
|
||||
return;
|
||||
|
||||
if (player == null)
|
||||
player = new SoundPlayer(path);
|
||||
|
||||
StopAll();
|
||||
player.Play();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 播放 ding.wav;若有其他音效正在播放,先全部停止再播放。
|
||||
/// </summary>
|
||||
public static void Ding() => PlaySound(_dingWavePath, ref _dingPlayer);
|
||||
|
||||
/// <summary>
|
||||
/// 播放 CheckinOK.wav;若有其他音效正在播放,先全部停止再播放。
|
||||
/// </summary>
|
||||
public static void CheckinOK() => PlaySound(_checkinWavePath, ref _checkinPlayer);
|
||||
|
||||
/// <summary>
|
||||
/// 播放 CheckoutOK.wav;若有其他音效正在播放,先全部停止再播放。
|
||||
/// </summary>
|
||||
public static void CheckoutOK() => PlaySound(_checkoutWavePath, ref _checkoutPlayer);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,298 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using 報到系統客戶端.Models;
|
||||
|
||||
namespace 報到系統客戶端.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// 報名服務客戶端
|
||||
/// 呼叫後端 Services/SignUpService.asmx
|
||||
/// </summary>
|
||||
public class SignUpService
|
||||
{
|
||||
private const string SERVICE_PATH = "/Services/SignUpService.asmx";
|
||||
|
||||
/// <summary>
|
||||
/// 搜尋活動參加人員清單
|
||||
/// </summary>
|
||||
/// <param name="pageIndex">頁碼(從 1 開始)</param>
|
||||
/// <param name="pageSize">每頁筆數</param>
|
||||
/// <param name="courseID">活動編號</param>
|
||||
/// <param name="name">學員姓名</param>
|
||||
/// <param name="mobile">手機號</param>
|
||||
/// <returns>搜尋結果</returns>
|
||||
public static BaseResponse SearchSignUps(
|
||||
int pageIndex,
|
||||
int pageSize,
|
||||
int courseID,
|
||||
string name = "",
|
||||
string mobile = "")
|
||||
{
|
||||
var parameters = new
|
||||
{
|
||||
pageIndex = pageIndex,
|
||||
pageSize = pageSize,
|
||||
courseID = courseID,
|
||||
name = name,
|
||||
mobile = mobile
|
||||
};
|
||||
|
||||
return ApiService.Instance.Post(SERVICE_PATH, "SearchSignUps", parameters);
|
||||
}
|
||||
|
||||
///// <summary>
|
||||
///// 搜尋活動參加人員清單(非同步版本)
|
||||
///// </summary>
|
||||
//public static async Task<BaseResponse> SearchSignUpsAsync(
|
||||
// int pageIndex,
|
||||
// int pageSize,
|
||||
// int courseID,
|
||||
// string name = "",
|
||||
// string mobile = "")
|
||||
//{
|
||||
// var parameters = new
|
||||
// {
|
||||
// pageIndex = pageIndex,
|
||||
// pageSize = pageSize,
|
||||
// courseID = courseID,
|
||||
// name = name,
|
||||
// mobile = mobile
|
||||
// };
|
||||
|
||||
// return await ApiService.Instance.PostAsync(SERVICE_PATH, "SearchSignUps", parameters);
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// 上傳活動參加人員簽到簽退資料
|
||||
/// 由 WinForm 客戶端上傳簽到簽退資料
|
||||
/// </summary>
|
||||
/// <param name="courseID">活動編號</param>
|
||||
/// <param name="items">報名人員列表</param>
|
||||
/// <returns>上傳結果</returns>
|
||||
//public static BaseResponse UploadSignUps(int courseID, List<SignUpItem> items)
|
||||
//{
|
||||
// var parameters = new
|
||||
// {
|
||||
// courseID = courseID,
|
||||
// items = items
|
||||
// };
|
||||
|
||||
// return ApiService.Instance.Post(SERVICE_PATH, "UploadSignUps", parameters);
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// 上傳活動參加人員簽到簽退資料(非同步版本)
|
||||
/// </summary>
|
||||
//public static async Task<BaseResponse> UploadSignUpsAsync(int courseID, List<SignUpItem> items)
|
||||
//{
|
||||
// var parameters = new
|
||||
// {
|
||||
// courseID = courseID,
|
||||
// items = items
|
||||
// };
|
||||
|
||||
// return await ApiService.Instance.PostAsync(SERVICE_PATH, "UploadSignUps", parameters);
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// 掃碼簽到
|
||||
/// </summary>
|
||||
/// <param name="checkType">簽到類型</param>
|
||||
/// <param name="qrCode">QR Code</param>
|
||||
/// <param name="checkInTime">簽到時間</param>
|
||||
/// <returns>簽到結果</returns>
|
||||
//public static BaseResponse CheckIn(string checkType, string qrCode, DateTime checkInTime)
|
||||
//{
|
||||
// var parameters = new
|
||||
// {
|
||||
// checkType = checkType,
|
||||
// qrCode = qrCode,
|
||||
// checkInTime = checkInTime
|
||||
// };
|
||||
|
||||
// return ApiService.Instance.Post(SERVICE_PATH, "CheckIn", parameters);
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// 掃碼簽到(非同步版本)
|
||||
/// </summary>
|
||||
//public static async Task<BaseResponse> CheckInAsync(string checkType, string qrCode, DateTime checkInTime)
|
||||
//{
|
||||
// var parameters = new
|
||||
// {
|
||||
// checkType = checkType,
|
||||
// qrCode = qrCode,
|
||||
// checkInTime = checkInTime
|
||||
// };
|
||||
|
||||
// return await ApiService.Instance.PostAsync(SERVICE_PATH, "CheckIn", parameters);
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// 掃碼簽退
|
||||
/// </summary>
|
||||
/// <param name="checkType">簽退類型</param>
|
||||
/// <param name="qrCode">QR Code</param>
|
||||
/// <param name="checkOutTime">簽退時間</param>
|
||||
/// <returns>簽退結果</returns>
|
||||
//public static BaseResponse CheckOut(string checkType, string qrCode, DateTime checkOutTime)
|
||||
//{
|
||||
// var parameters = new
|
||||
// {
|
||||
// checkType = checkType,
|
||||
// qrCode = qrCode,
|
||||
// checkOutTime = checkOutTime
|
||||
// };
|
||||
|
||||
// return ApiService.Instance.Post(SERVICE_PATH, "CheckOut", parameters);
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// 掃碼簽退(非同步版本)
|
||||
/// </summary>
|
||||
//public static async Task<BaseResponse> CheckOutAsync(string checkType, string qrCode, DateTime checkOutTime)
|
||||
//{
|
||||
// var parameters = new
|
||||
// {
|
||||
// checkType = checkType,
|
||||
// qrCode = qrCode,
|
||||
// checkOutTime = checkOutTime
|
||||
// };
|
||||
|
||||
// return await ApiService.Instance.PostAsync(SERVICE_PATH, "CheckOut", parameters);
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// 現場簽到(未預先登記的參加人員)
|
||||
/// </summary>
|
||||
/// <param name="checkType">簽到類型</param>
|
||||
/// <param name="courseID">活動編號</param>
|
||||
/// <param name="name">姓名</param>
|
||||
/// <param name="mobile">手機號</param>
|
||||
/// <param name="checkInTime">簽到時間</param>
|
||||
/// <returns>簽到結果</returns>
|
||||
//public static BaseResponse OnSiteCheckIn(
|
||||
// string checkType,
|
||||
// int courseID,
|
||||
// string name,
|
||||
// string mobile,
|
||||
// DateTime checkInTime)
|
||||
//{
|
||||
// var parameters = new
|
||||
// {
|
||||
// checkType = checkType,
|
||||
// courseID = courseID,
|
||||
// name = name,
|
||||
// mobile = mobile,
|
||||
// checkInTime = checkInTime
|
||||
// };
|
||||
|
||||
// return ApiService.Instance.Post(SERVICE_PATH, "OnSiteCheckIn", parameters);
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// 現場簽到(非同步版本)
|
||||
/// </summary>
|
||||
//public static async Task<BaseResponse> OnSiteCheckInAsync(
|
||||
// string checkType,
|
||||
// int courseID,
|
||||
// string name,
|
||||
// string mobile,
|
||||
// DateTime checkInTime)
|
||||
//{
|
||||
// var parameters = new
|
||||
// {
|
||||
// checkType = checkType,
|
||||
// courseID = courseID,
|
||||
// name = name,
|
||||
// mobile = mobile,
|
||||
// checkInTime = checkInTime
|
||||
// };
|
||||
|
||||
// return await ApiService.Instance.PostAsync(SERVICE_PATH, "OnSiteCheckIn", parameters);
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// 現場簽退(未預先登記的參加人員)
|
||||
/// </summary>
|
||||
/// <param name="checkType">簽退類型</param>
|
||||
/// <param name="courseID">活動編號</param>
|
||||
/// <param name="name">姓名</param>
|
||||
/// <param name="mobile">手機號</param>
|
||||
/// <param name="checkOutTime">簽退時間</param>
|
||||
/// <returns>簽退結果</returns>
|
||||
//public static BaseResponse OnSiteCheckOut(
|
||||
// string checkType,
|
||||
// int courseID,
|
||||
// string name,
|
||||
// string mobile,
|
||||
// DateTime checkOutTime)
|
||||
//{
|
||||
// var parameters = new
|
||||
// {
|
||||
// checkType = checkType,
|
||||
// courseID = courseID,
|
||||
// name = name,
|
||||
// mobile = mobile,
|
||||
// checkOutTime = checkOutTime
|
||||
// };
|
||||
|
||||
// return ApiService.Instance.Post(SERVICE_PATH, "OnSiteCheckOut", parameters);
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// 現場簽退(非同步版本)
|
||||
/// </summary>
|
||||
//public static async Task<BaseResponse> OnSiteCheckOutAsync(
|
||||
// string checkType,
|
||||
// int courseID,
|
||||
// string name,
|
||||
// string mobile,
|
||||
// DateTime checkOutTime)
|
||||
//{
|
||||
// var parameters = new
|
||||
// {
|
||||
// checkType = checkType,
|
||||
// courseID = courseID,
|
||||
// name = name,
|
||||
// mobile = mobile,
|
||||
// checkOutTime = checkOutTime
|
||||
// };
|
||||
|
||||
// return await ApiService.Instance.PostAsync(SERVICE_PATH, "OnSiteCheckOut", parameters);
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// 批量上傳簽到簽退資料
|
||||
/// 由 WinForm 客戶端上傳簽到簽退資料
|
||||
/// </summary>
|
||||
/// <param name="courseID">活動編號</param>
|
||||
/// <param name="items">簽到簽退資料列表</param>
|
||||
/// <returns>上傳結果</returns>
|
||||
public static BaseResponse UploadCheckIns(int courseID, List<CheckInItem> items)
|
||||
{
|
||||
var parameters = new
|
||||
{
|
||||
courseID = courseID,
|
||||
items = items
|
||||
};
|
||||
|
||||
return ApiService.Instance.Post(SERVICE_PATH, "UploadCheckIns", parameters);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 批量上傳簽到簽退資料(非同步版本)
|
||||
/// </summary>
|
||||
//public static async Task<BaseResponse> UploadCheckInsAsync(int courseID, List<CheckInItem> items)
|
||||
//{
|
||||
// var parameters = new
|
||||
// {
|
||||
// courseID = courseID,
|
||||
// items = items
|
||||
// };
|
||||
|
||||
// return await ApiService.Instance.PostAsync(SERVICE_PATH, "UploadCheckIns", parameters);
|
||||
//}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
namespace 報到系統客戶端.Properties {
|
||||
|
||||
|
||||
// 這個類別可以讓您處理設定類別上的特定事件:
|
||||
// 在設定值變更之前引發 SettingChanging 事件。
|
||||
// 在設定值變更之後引發 PropertyChanged 事件。
|
||||
// 在載入設定值之後引發 SettingsLoaded 事件。
|
||||
// 在儲存設定值之前引發 SettingsSaving 事件。
|
||||
internal sealed partial class Settings {
|
||||
|
||||
public Settings() {
|
||||
// // 若要加入用於儲存與變更設定的事件處理常式,請取消註解下列程式行:
|
||||
//
|
||||
// this.SettingChanging += this.SettingChangingEventHandler;
|
||||
//
|
||||
// this.SettingsSaving += this.SettingsSavingEventHandler;
|
||||
//
|
||||
}
|
||||
|
||||
private void SettingChangingEventHandler(object sender, System.Configuration.SettingChangingEventArgs e) {
|
||||
// 在此加入程式碼以處理 SettingChangingEvent 事件。
|
||||
}
|
||||
|
||||
private void SettingsSavingEventHandler(object sender, System.ComponentModel.CancelEventArgs e) {
|
||||
// 在此加入程式碼以處理 SettingsSaving 事件。
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace 報到系統客戶端
|
||||
{
|
||||
internal class Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// 手機號轉隱碼輸出(前4後3)
|
||||
/// </summary>
|
||||
/// <param name="mobile"></param>
|
||||
/// <returns></returns>
|
||||
public static string HideMobile(string mobile)
|
||||
{
|
||||
if (string.IsNullOrEmpty(mobile))
|
||||
return string.Empty;
|
||||
|
||||
if (mobile.Length >= 7)
|
||||
return mobile.Substring(0, 4) + "***" + mobile.Substring(mobile.Length - 3);
|
||||
|
||||
return "***";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
|
||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
||||
<security>
|
||||
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<!-- UAC 資訊清單選項
|
||||
如果要變更 Windows 使用者帳戶控制層級,請將
|
||||
requestedExecutionLevel 節點以下列其中之一取代。
|
||||
|
||||
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
|
||||
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
|
||||
<requestedExecutionLevel level="highestAvailable" uiAccess="false" />
|
||||
|
||||
指定 requestedExecutionLevel 項目會停用檔案及登錄虛擬化。
|
||||
如果您的應用程式需要針對回溯相容性進行這項虛擬化,請移除這個
|
||||
項目。
|
||||
-->
|
||||
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
|
||||
</requestedPrivileges>
|
||||
</security>
|
||||
</trustInfo>
|
||||
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<!-- 此應用程式已通過測試,並設計要搭配使用的
|
||||
應用程式版本清單。請取消註解適當元素,
|
||||
Windows 就會自動選取最相容的環境。 -->
|
||||
|
||||
<!-- Windows Vista -->
|
||||
<!--<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />-->
|
||||
|
||||
<!-- Windows 7 -->
|
||||
<!--<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />-->
|
||||
|
||||
<!-- Windows 8 -->
|
||||
<!--<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />-->
|
||||
|
||||
<!-- Windows 8.1 -->
|
||||
<!--<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />-->
|
||||
|
||||
<!-- Windows 10 -->
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
|
||||
|
||||
</application>
|
||||
</compatibility>
|
||||
|
||||
<!-- 表示應用程式為 DPI 感知,Windows 不會在 DPI 變高時自動
|
||||
縮放。Windows Presentation Foundation (WPF) 應用程式會自動感知 DPI,因此不需要
|
||||
加入。以 .NET Framework 4.6 為目標的 Windows Form 應用程式若加入這項設定,也
|
||||
應該在其 app.config 中將 'EnableWindowsFormsHighDpiAutoResizing' 設定為 'true'。
|
||||
|
||||
將應用程式設定為感知長路徑。請參閱 https://docs.microsoft.com/windows/win32/fileio/maximum-file-path-limitation-->
|
||||
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<windowsSettings>
|
||||
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
|
||||
<longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>
|
||||
</windowsSettings>
|
||||
</application>
|
||||
|
||||
<!-- 啟用 Windows 通用控制項和對話方塊的佈景主題 (Windows XP 以後版本) -->
|
||||
<!--
|
||||
<dependency>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity
|
||||
type="win32"
|
||||
name="Microsoft.Windows.Common-Controls"
|
||||
version="6.0.0.0"
|
||||
processorArchitecture="*"
|
||||
publicKeyToken="6595b64144ccf1df"
|
||||
language="*"
|
||||
/>
|
||||
</dependentAssembly>
|
||||
</dependency>
|
||||
-->
|
||||
|
||||
</assembly>
|
||||
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Data.Sqlite" version="6.0.36" targetFramework="net481" />
|
||||
<package id="Microsoft.Data.Sqlite.Core" version="6.0.36" targetFramework="net481" />
|
||||
<package id="Newtonsoft.Json" version="13.0.4" targetFramework="net481" />
|
||||
<package id="SourceGear.sqlite3" version="3.50.4.2" targetFramework="net481" />
|
||||
<package id="SQLitePCLRaw.bundle_e_sqlite3" version="3.0.2" targetFramework="net481" />
|
||||
<package id="SQLitePCLRaw.config.e_sqlite3" version="3.0.2" targetFramework="net481" />
|
||||
<package id="SQLitePCLRaw.core" version="3.0.2" targetFramework="net481" />
|
||||
<package id="SQLitePCLRaw.provider.dynamic_cdecl" version="2.1.2" targetFramework="net481" />
|
||||
<package id="SQLitePCLRaw.provider.e_sqlite3" version="3.0.2" targetFramework="net481" />
|
||||
<package id="System.Buffers" version="4.6.1" targetFramework="net481" />
|
||||
<package id="System.Memory" version="4.6.3" targetFramework="net481" />
|
||||
<package id="System.Numerics.Vectors" version="4.6.1" targetFramework="net481" />
|
||||
<package id="System.Runtime.CompilerServices.Unsafe" version="6.1.2" targetFramework="net481" />
|
||||
</packages>
|
||||
@@ -0,0 +1,285 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\報到系統\packages\SourceGear.sqlite3.3.50.4.2\build\net471\SourceGear.sqlite3.props" Condition="Exists('..\報到系統\packages\SourceGear.sqlite3.3.50.4.2\build\net471\SourceGear.sqlite3.props')" />
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{8B1277B9-7958-4B49-B8E5-3D32A3A0311C}</ProjectGuid>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<RootNamespace>報到系統客戶端</RootNamespace>
|
||||
<AssemblyName>報到系統客戶端</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
<Deterministic>true</Deterministic>
|
||||
<NuGetPackageImportStamp>
|
||||
</NuGetPackageImportStamp>
|
||||
<TargetFrameworkProfile />
|
||||
<PublishUrl>publish\</PublishUrl>
|
||||
<Install>true</Install>
|
||||
<InstallFrom>Disk</InstallFrom>
|
||||
<UpdateEnabled>false</UpdateEnabled>
|
||||
<UpdateMode>Foreground</UpdateMode>
|
||||
<UpdateInterval>7</UpdateInterval>
|
||||
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
|
||||
<UpdatePeriodically>false</UpdatePeriodically>
|
||||
<UpdateRequired>false</UpdateRequired>
|
||||
<MapFileExtensions>true</MapFileExtensions>
|
||||
<ApplicationRevision>0</ApplicationRevision>
|
||||
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
|
||||
<IsWebBootstrapper>false</IsWebBootstrapper>
|
||||
<UseApplicationTrust>false</UseApplicationTrust>
|
||||
<BootstrapperEnabled>true</BootstrapperEnabled>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x86\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
||||
<OutputPath>bin\x86\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.Data.Sqlite, Version=6.0.36.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>..\報到系統\packages\Microsoft.Data.Sqlite.Core.6.0.36\lib\netstandard2.0\Microsoft.Data.Sqlite.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\報到系統\packages\Newtonsoft.Json.13.0.4\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SQLitePCLRaw.batteries_v2, Version=3.0.2.2801, Culture=neutral, PublicKeyToken=8226ea5df37bcae9, processorArchitecture=MSIL">
|
||||
<HintPath>..\報到系統\packages\SQLitePCLRaw.config.e_sqlite3.3.0.2\lib\net471\SQLitePCLRaw.batteries_v2.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SQLitePCLRaw.core, Version=3.0.2.2801, Culture=neutral, PublicKeyToken=1488e028ca7ab535, processorArchitecture=MSIL">
|
||||
<HintPath>..\報到系統\packages\SQLitePCLRaw.core.3.0.2\lib\netstandard2.0\SQLitePCLRaw.core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SQLitePCLRaw.provider.dynamic_cdecl, Version=2.1.2.1721, Culture=neutral, PublicKeyToken=b68184102cba0b3b, processorArchitecture=MSIL">
|
||||
<HintPath>..\報到系統\packages\SQLitePCLRaw.provider.dynamic_cdecl.2.1.2\lib\netstandard2.0\SQLitePCLRaw.provider.dynamic_cdecl.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SQLitePCLRaw.provider.e_sqlite3, Version=3.0.2.2801, Culture=neutral, PublicKeyToken=9c301db686d0bd12, processorArchitecture=MSIL">
|
||||
<HintPath>..\報到系統\packages\SQLitePCLRaw.provider.e_sqlite3.3.0.2\lib\net471\SQLitePCLRaw.provider.e_sqlite3.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Buffers, Version=4.0.5.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\報到系統\packages\System.Buffers.4.6.1\lib\net462\System.Buffers.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Configuration.Install" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.EnterpriseServices" />
|
||||
<Reference Include="System.Memory, Version=4.0.5.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\報到系統\packages\System.Memory.4.6.3\lib\net462\System.Memory.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Numerics" />
|
||||
<Reference Include="System.Numerics.Vectors, Version=4.1.6.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\報到系統\packages\System.Numerics.Vectors.4.6.1\lib\net462\System.Numerics.Vectors.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=6.0.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\報到系統\packages\System.Runtime.CompilerServices.Unsafe.6.1.2\lib\net462\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Runtime.Serialization" />
|
||||
<Reference Include="System.ServiceModel" />
|
||||
<Reference Include="System.Transactions" />
|
||||
<Reference Include="System.Web.Services" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Deployment" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="AgreementConfirmForm.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="AgreementConfirmForm.Designer.cs">
|
||||
<DependentUpon>AgreementConfirmForm.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="CheckoutOnlyConfirmForm.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="CheckoutOnlyConfirmForm.Designer.cs">
|
||||
<DependentUpon>CheckoutOnlyConfirmForm.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="ClientSession.cs" />
|
||||
<Compile Include="ExistConfirmForm.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="ExistConfirmForm.Designer.cs">
|
||||
<DependentUpon>ExistConfirmForm.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Data\DatabaseService.cs" />
|
||||
<Compile Include="Data\Models\LocalBPSNItem.cs" />
|
||||
<Compile Include="Data\Models\LocalCheckInItem.cs" />
|
||||
<Compile Include="Data\Models\LocalClassesModel.cs" />
|
||||
<Compile Include="Data\Models\LocalSignUpModel.cs" />
|
||||
<Compile Include="Data\Repositories\BPSNRepository.cs" />
|
||||
<Compile Include="Data\Repositories\CheckInRepository.cs" />
|
||||
<Compile Include="Data\Repositories\ClassesRepository.cs" />
|
||||
<Compile Include="Data\Repositories\SignUpsRepository.cs" />
|
||||
<Compile Include="LoginForm.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="LoginForm.Designer.cs">
|
||||
<DependentUpon>LoginForm.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="MainForm.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="MainForm.Designer.cs">
|
||||
<DependentUpon>MainForm.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Models\BaseResponse.cs" />
|
||||
<Compile Include="Models\BPSNModel.cs" />
|
||||
<Compile Include="Models\CheckInModel.cs" />
|
||||
<Compile Include="Models\ClassesModel.cs" />
|
||||
<Compile Include="Models\SignUpModel.cs" />
|
||||
<Compile Include="ProcessingForm.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="ProcessingForm.Designer.cs">
|
||||
<DependentUpon>ProcessingForm.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Services\ApiService.cs" />
|
||||
<Compile Include="Services\BPSNService.cs" />
|
||||
<Compile Include="Services\ClassesService.cs" />
|
||||
<Compile Include="Services\ConfigService.cs" />
|
||||
<Compile Include="Services\LoginService.cs" />
|
||||
<Compile Include="Services\NotificationSoundService.cs" />
|
||||
<Compile Include="Services\SignUpService.cs" />
|
||||
<Compile Include="Settings.cs" />
|
||||
<Compile Include="Utils.cs" />
|
||||
<Content Include="Sound\CheckinOK.wav">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Sound\CheckoutOK.wav">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Sound\ding.wav">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<EmbeddedResource Include="AgreementConfirmForm.resx">
|
||||
<DependentUpon>AgreementConfirmForm.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="CheckoutOnlyConfirmForm.resx">
|
||||
<DependentUpon>CheckoutOnlyConfirmForm.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="ExistConfirmForm.resx">
|
||||
<DependentUpon>ExistConfirmForm.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="LoginForm.resx">
|
||||
<DependentUpon>LoginForm.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="MainForm.resx">
|
||||
<DependentUpon>MainForm.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="ProcessingForm.resx">
|
||||
<DependentUpon>ProcessingForm.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
<SubType>Designer</SubType>
|
||||
</EmbeddedResource>
|
||||
<Compile Include="Properties\Resources.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
<DesignTime>True</DesignTime>
|
||||
</Compile>
|
||||
<Content Include=".editorconfig" />
|
||||
<None Include="app.manifest" />
|
||||
<None Include="CONFIG_PATH_GUIDE.md" />
|
||||
<None Include="DOC\API服務.md" />
|
||||
<None Include="DOC\Models模型層改造報告.md" />
|
||||
<None Include="DOC\簽到簽退.md" />
|
||||
<None Include="DOC\登入.md" />
|
||||
<None Include="DOC\資料儲存服務.md" />
|
||||
<None Include="DOC\資料存取層改造完成報告.md" />
|
||||
<None Include="packages.config" />
|
||||
<None Include="Properties\DataSources\報到系統客戶端.Data.Models.LocalSignUpItem.datasource" />
|
||||
<None Include="Properties\Settings.settings">
|
||||
<Generator>SettingsSingleFileGenerator</Generator>
|
||||
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||
</None>
|
||||
<Compile Include="Properties\Settings.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Settings.settings</DependentUpon>
|
||||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="ConfigService_Usage_Example.cs.txt" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<WCFMetadata Include="Connected Services\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\報到系統客戶端安裝自訂動作\報到系統客戶端安裝自訂動作.csproj">
|
||||
<Project>{870587F6-4E78-447E-9AFE-D9A44DE908F5}</Project>
|
||||
<Name>報到系統客戶端安裝自訂動作</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<BootstrapperPackage Include=".NETFramework,Version=v4.8">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>Microsoft .NET Framework 4.8 %28x86 和 x64%29</ProductName>
|
||||
<Install>true</Install>
|
||||
</BootstrapperPackage>
|
||||
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>.NET Framework 3.5 SP1</ProductName>
|
||||
<Install>false</Install>
|
||||
</BootstrapperPackage>
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>此專案參考這部電腦上所缺少的 NuGet 套件。請啟用 NuGet 套件還原,以下載該套件。如需詳細資訊,請參閱 http://go.microsoft.com/fwlink/?LinkID=322105。缺少的檔案是 {0}。</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\報到系統\packages\SourceGear.sqlite3.3.50.4.2\build\net471\SourceGear.sqlite3.props')" Text="$([System.String]::Format('$(ErrorText)', '..\報到系統\packages\SourceGear.sqlite3.3.50.4.2\build\net471\SourceGear.sqlite3.props'))" />
|
||||
<Error Condition="!Exists('..\報到系統\packages\SourceGear.sqlite3.3.50.4.2\build\net471\SourceGear.sqlite3.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\報到系統\packages\SourceGear.sqlite3.3.50.4.2\build\net471\SourceGear.sqlite3.targets'))" />
|
||||
</Target>
|
||||
<Import Project="..\報到系統\packages\SourceGear.sqlite3.3.50.4.2\build\net471\SourceGear.sqlite3.targets" Condition="Exists('..\報到系統\packages\SourceGear.sqlite3.3.50.4.2\build\net471\SourceGear.sqlite3.targets')" />
|
||||
</Project>
|
||||
Reference in New Issue
Block a user