# 會員招新服務端說明 by xiaohui & taiqin
## 0.0 服務端源碼說明
### 0.1 src目錄結構說明
#### .
#### ├── dianxie 代碼目錄
#### │ ├── appconf 軟件配置的代碼
#### │ ├── associator_pkg 會員數據庫的相關函數
#### │ ├── conf 存放軟件的配置文件
#### │ ├── database 存放數據庫
#### │ ├── httpscertificate 存放https加密需要用到的證書
#### │ ├── log 存放軟件的日志
#### │ ├── myhttp 存放http相關的代碼
#### │ ├── mylog 軟件生成日志的相關代碼
#### │ ├── sent_udp_heartbeak 存放軟件發送心跳包的相關代碼
#### │ ├── server server的主函數(入口文件)
#### │ └── soft_version 存放聲明軟件版本的代碼
#### ├── github.com 從github.com下載的相關庫
#### ├── goconf 存放軟件處理配置文件的相關代碼
#### ├── golang.org 從golang.org下載的庫
#### .
### 0.2 源碼分析
#### 0.2.0 服務端主函數(入口函數)
##### 地址:/src/dianxie/server/main.go
##### 函數:main()服務端的主函數
```
func main() {
mylog.StartLog()//開始記錄日志
*************
向日志打印啟動信息
*************
appconf.Read_conf()//讀取配置文件
*************
打印信息
*************
//數據庫初始化
associator_pkg.Init_sqllite_databse()
W.Add(1)//添加一個信號量。
//Http服務初始化
go myhttp.Http_server_init()
//Udp心跳廣播服務初始化
go sent_udp_heartbeak.Boardcast_udp_heartbeak_to_all_netcard()
//for {
//}
W.Wait()//等待信號量,這里主要用于阻塞main函數,防止程序關閉,和while(1);作用類似,但是while(1);消耗cpu。
associator_pkg.Db.Close()
}
```
##### .
#### 0.2.1 軟件配置的函數
#### 地址:/src/dianxie/appconf/appconf.go
##### ServerConfig結構體定義了配置文件的結構
```
type ServerConfig struct {
SqlDb string `goconf:"database:SqlDb"` //數據庫路徑
SqlAssociatorTable string `goconf:"database:SqlAssociatorTable"` //數據庫的會員表的名字
SqlAssociatorNumberTable string `goconf:"database:SqlAssociatorNumberTable"` //數據庫的會員號表的名字
SqlAdministratorTable string `goconf:"database:SqlAdministratorTable"` //數據庫的管理員表的名字
HttpAddr string `goconf:"http:HttpAddrAndPort"` //http的地址和端口
HttpsEnable string `goconf:"http:HttpsEnable"` //使能https
HttpsCertFile string `goconf:"http:HttpsCertFile"` //https的Certfile路徑
HttpsKeyFile string `goconf:"http:HttpsKeyFile"` //https的keyfile的路徑
HttpPort int `goconf:"http:HttpOnlyPort"` //心跳包里包含的http的端口,必須跟上面定義的一樣
HttpReadTimeout time.Duration `goconf:"http:HttpReadTimeout"` //http讀超時
HttpWriteTimeout time.Duration `goconf:"http:HttpWriteTimeout"` //http寫超時
HttpMaxHeaderBytes int `goconf:"http:HttpMaxHeaderBytes"` //http讀取最大字節
UdpServerPort int `goconf:"udp:UdpServerPort"` //udp服務端端口
UdpClientPort int `goconf:"udp:UdpClientPort"` //udp客戶端端口
UdpHeartbeakTime time.Duration `goconf:"udp:UdpHeartbeakTime"` //udp心跳包刷新時間
}
```
##### 解析配置文件函數
```
var Tf *ServerConfig //定義一個全局的配置結構體
func Read_conf() { //讀取并解析配置文件函數
conf := goconf.New()
Tf = &ServerConfig{}
if err := conf.Parse("../conf/app.conf"); err != nil {
************
省略部分代碼
************
if err := conf.Unmarshal(Tf); err != nil {
************
省略部分代碼
************
**************
對讀取到的配置信息進行合法性過濾和修正
**************
}
```
#### 0.2.2 數據庫處理函數
#### 地址:src/dianxie/associator_pkg
##### init_database.go 數據庫初始化
```
type Associator_s struct {//定義一個會員數據的結構體
***********************
省略會員數據的結構體
***********************
}
var Db *sql.DB //聲明一個全局的數據庫連接句柄,用于初始化數據庫后操作數據庫不需要再次初始化數據庫。
var Sqlite_operation_lock *sync.Mutex//定義一個sqlite3的全局鎖,用于強制數據庫操作串行執行,防止出現沖突。這里以后可以優化成并行操作。加快效率。
func Init_sqllite_databse() {
var err error
Db, err = sql.Open("sqlite3", appconf.Tf.SqlDb)//打開數據庫文件
**************
省略部分代碼
**************
_, err = Db.Query("SELECT * FROM " + appconf.Tf.SqlAssociatorTable)//檢查數據庫是否合法
**************
省略部分代碼
**************
fmt.Println("sqlite3數據庫初始化成功!!(addr:" + appconf.Tf.SqlDb + ")")
Sqlite_operation_lock = new(sync.Mutex)//初始化數據庫串行鎖
}
func create_associator_table() {
var create_associator_table_sql_text string = `
************
創建會員表的sql語句
***********
var create_number_table_sql_text string = `
************
創建緩存會員號表的sql語句
***********
var create_administrator_table_sql_text string = `
************
創建管理員表的sql語句
***********
var init_number_table_sql_text string = `
************
創建緩存會員號表表的sql語句
***********
************
省略部分代碼
***********
_, err = Db.Exec(create_associator_table_sql_text) //這里使用Exec函數,因為這里是執行,經測試Query函數執行失敗
************
初始化失敗就報錯并關閉數據庫連接
***********
_, err = Db.Exec(create_number_table_sql_text) //這里使用Exec函數,因為這里是執行,經測試Query函數執行失敗
************
初始化失敗就報錯并關閉數據庫連接
***********
_, err = Db.Exec(create_administrator_table_sql_text) //這里使用Exec函數,因為這里是執行,經測試Query函數執行失敗
************
初始化失敗就報錯并關閉數據庫連接
***********
_, err = Db.Exec(init_number_table_sql_text) //這里使用Exec函數,因為這里是執行,經測試Query函數執行失敗
************
初始化失敗就報錯并關閉數據庫連接
***********
fmt.Println("嘗試修復sqlite3數據庫成功!!")
}
```
##### cancel_associator_number.go 取消會員號的操作
```
func Cancel_associator_number(associator Associator_s) (string, error)//取消會員號的入口函數(注意入口函數都是全局的),判斷是否要執行取消會員號操作
func database_cancel_associator_number(associator Associator_s, associator_number string) (string, error)//執行取消會員號操作
```
##### change_associator_card_id.go 修改會員卡號
```
func Change_associator_card_id(associator Associator_s) error//修改會員卡號的入口函數(注意入口函數都是全局的),判斷是否要執行修改會員卡號操作
func check_this_card_id_is_exsite(associator Associator_s)//檢查會員卡是否已存在
func database_change_associator_card_id(associator Associator_s)//執行修改會員卡號操作
```
##### change_associator_receipt_status.go 修改會員收據打印狀態
```
func Change_associator_receipt_status(associator Associator_s) error//修改會員收據打印狀態的入口函數(注意入口函數都是全局的),判斷是否要執行修改會員收據打印狀態操作
func database_change_associator_receipt_status(associator Associator_s) error//修改會員收據打印狀態的執行函數
```
##### check_associator_exist_card_number.go 檢查會員是否存在會員卡號
```
func Check_associator_exist_card_id(associator Associator_s) (bool, error)執行檢查會員是否存在會員卡號操作
```
##### check_associator_receipt_adminstrator.go 查詢打印這個收據管理員信息
```
Check_associator_receipt_adminstrator(associator Associator_s) (string, error)//查詢打印這個收據管理員信息
```
##### check_associator_receipt_status.go 檢查會員收據打印狀態,用于會返回會員的收據打印狀態
```
Check_associator_receipt_status(associator Associator_s) (string, error)//檢查會員收據打印狀態,用于會返回會員的收據打印狀態
```
##### delete_associator.go 刪除會員
```
func Delete_associator(associator Associator_s) (string, error)//刪除會員的入口函數(注意入口函數都是全局的),判斷是否要執行刪除會員操作
func delete_associator_when_it_exist(associator Associator_s) (string, error)//執行刪除會員操作
```
##### get_associator_list.go 獲取會員列表
```
func Get_associator_list_by_receipt_status(receipt_Print_Status string) ([]interface{}, error//根據會員收據打印收據狀態獲取會員列表
```
##### get_associator_number.go 申請會員號
```
func Get_associator_number(associator Associator_s) (string, error)//申請會員號的入口函數(注意入口函數都是全局的),判斷如何進行會員號申請
func database_get_associator_number(associator Associator_s) (string, error)//當這個會員還沒有會員號,這個函數會被Get_associator_number這個函數所調用。進行會員號申請操作。
func database_get_new_associator_number(st *sql.Tx) (string, error) //這個函數會被database_get_associator_number函數調用,進行從會員號緩存表里獲取一個合法可用的會員號。
func database_get_new_associator_number_when_had_canceled(st *sql.Tx, stmt *sql.Stmt, query *sql.Rows) (string, error)//當緩存會員號表里有冗余的會員號這個函數會被database_get_new_associator_number函數調用并返回一個可用的會員號。
func database_get_new_associator_number_when_hadnt_canceled(st *sql.Tx, stmt *sql.Stmt, query *sql.Rows) (string, error)//當緩存會員號表里沒有冗余的會員號這個函數會被database_get_new_associator_number函數調用并返回一個可用的會員號。
```
##### login_administrator.go 檢查收據管理員是否合法,用于管理員登錄
```
func Login_administrator(name string, passwd string) (bool, error) //檢查收據管理員是否合法,合法的話第一個返回值會是true,否則false。第二個返回值是報錯信息
```
##### register_associator.go 會員注冊
```
func Register_associator(associator Associator_s) error//會員注冊的入口函數(注意入口函數都是全局的),判斷是否進行會員注冊。
func database_register_associator(associator Associator_s) error //執行會員注冊
```
#### 0.2.3 http處理函數
#### 地址:src/dianxie/myhttp
##### myhttp.go http初始化函數
```
func Http_server_init()//初始化http,并且把MyHandle結構體綁定為請求回調接口。
```
##### analysis.go http路徑解析函數
```
func (*MyHandle) ServeHTTP(w http.ResponseWriter, r *http.Request)//當http數據請求來臨時會被調用,w是你寫數據的句柄,r是讀請求數據的句柄。
```
##### cancel_associator_number_func.go 處理取消會員號請求的函數
```
func httpfun_cancel_associator_number(w http.ResponseWriter, r *http.Request)
```
##### change_associator_card_id_func.go 處理修改會員卡號請求的函數
```
func httpfun_change_associator_card_id(w http.ResponseWriter, r *http.Request)
```
##### change_associator_receipt_status_func.go 處理修改會員收據打印狀態請求的函數
```
func httpfun_change_associator_receipt_status(w http.ResponseWriter, r *http.Request)
```
##### check_associator_exist_card_number_func.go 處理查詢會員卡號請求的函數
```
func httpfun_check_associator_exist_card_number(w http.ResponseWriter, r *http.Request)
```
##### check_associator_receipt_adminstartor_func.go 處理查詢處理此收據的管理員信息請求的函數
```
func httpfun_check_associator_receipt_adminstrator(w http.ResponseWriter, r *http.Request)
```
##### check_associator_receipt_status_func.go 處理查詢此會員收據的打印狀態請求的函數
```
func httpfun_check_associator_receipt_status(w http.ResponseWriter, r *http.Request)
```
##### delete_associator_func.go 處理刪除會員請求的函數
```
func httpfun_delete_associator(w http.ResponseWriter, r *http.Request)
```
##### get_associator_list_func.go 處理獲取會員列表請求的函數
```
func httpfun_get_associator_list(w http.ResponseWriter, r *http.Request)
```
##### get_associator_number_func.go 處理申請會員號請求的函數
```
func httpfun_get_associator_number(w http.ResponseWriter, r *http.Request)
```
##### login_administrator_func.go 處理管理員登錄請求的函數
```
func httpfun_login_administrator(w http.ResponseWriter, r *http.Request)
```
##### login_administrator_func.go 處理管理員登錄請求的函數
```
func httpfun_login_administrator(w http.ResponseWriter, r *http.Request)
```
##### register_associator_func.go 處理會員注冊請求的函數
```
func httpfun_register_associator(w http.ResponseWriter, r *http.Request)
```
#### 0.2.4 軟件日志處理函數
#### 地址:src/dianxie/mylog/mylog.go
##### 初始化日志處理
```
func StartLog()
```
##### 打印錯誤日志信息
```
func ErrorLog(text string)
```
#### 0.2.5 軟件的心跳包封包和發送函數
#### 地址:src/dianxie/sent_udp_heartbeak/sent_udp_boardcast.go
##### 初始化心跳包發送定時器
```
func Boardcast_udp_heartbeak_to_all_netcard()
```
##### 向本機的所有網卡發送心跳包,此函數會被定時器回調(支持ipv4和ipv6)
```
func sent_udp_heartbeak_to_all_netcard()
```
##### 向指定ip的網卡發送心跳包
```
func sent_udp_heartbeak(ip string)
```
##### 向指定ip的網卡發送心跳包
```
func sent_udp_heartbeak(ip string)
```
##### 向指定ip的網卡發送數據包,此函數會被sent_udp_heartbeak調用。
```
func sent_udp_boardcast_page(sent_addr net.UDPAddr, page []byte)
```
#### 0.2.0 服務端軟件版本
##### 地址:/src/dianxie/soft_version/soft_version.go
```
var Soft_version string = "1.1.0"
```
#### .
## 1.0 服務端使用udp廣播的方式來發送心跳包,心跳包里包含了服務端的ip,mac,版本等信息。封裝心跳包的代碼位于服務端源碼的(src/dianxie/sent_udp_heartbeak/sent_udp_boardcast.go的func sent_udp_heartbeak(ip string)函數,參數ip是要發送心跳包的網卡的ip)
## 1.1 心跳包使用json結構封裝。舉例:
``` func sent_udp_heartbeak(ip string) {
var heartbeak_json_text []byte
heartbeak_table := make(map[string]interface{})
heartbeak_table["Soft_version"] = soft_version.Soft_version
heartbeak_table["Ip"] = ip
heartbeak_table["HttpSEnable"] = appconf.Tf.HttpsEnable
heartbeak_table["HttpServerPort"] = appconf.Tf.HttpPort
heartbeak_json := make(map[string]interface{})
heartbeak_json["Udp_Heartbeak"] = heartbeak_table
heartbeak_json_text, _ = json.Marshal(heartbeak_json)
var sent_addr net.UDPAddr
sent_addr.IP = net.ParseIP(ip)
sent_addr.Port = appconf.Tf.UdpServerPort
sent_udp_boardcast_page(sent_addr, heartbeak_json_text)
//fmt.Println(ip + " " + string(heartbeak_json_text))
}
```
### 心跳包例子:
```
{"Udp_Heartbeak":{"HttpSEnable":"true","HttpServerPort":8686,"Ip":"192.168.1.182","Soft_version":"1.1.0"}}
```
### Udp_Heartbeak說明后面的是心跳包的內容
### HttpSEnable 如果這個屬性的值為true代表服務端開啟了https,客戶端必須使用https來傳輸數據。false反之。
### HttpServerPort 這個屬性是服務端http/https服務所使用的端口號。
### Ip 這個屬性是服務端的ip。
### Soft_version 這個屬性是服務端的版本號。
## 2.0 服務端使用http或者https的方式與客戶端進行數據交換。(嚴重推薦使用https)
### 2.1 服務端的http代碼位于(src/dianxie/myhttp文件夾)
#### myhttp文件夾的文件名稱說明:
#### 文件名包含_func的都是存放實現這一功能的函數的go文件
####
### 2.2 http的api說明。
#### 會員注冊
##### 地址:/PC_APP_API/Register_Associator
##### 請求方式:POST
##### 參數名稱:data
##### 數據格式:json
##### json例子:
```
{"Register_Associator":{"Name":"會員名字","Class":"班級","Sex":"man或者girl","Phone_Number":"手機號碼","QQ_Number":"QQ號碼","Wechat_Number":"微信號碼",,"Register_Time":"注冊時間","Register_MechineTime":"注冊時間戳","Register_Mac":"注冊的機器的mac地址"}}
```
#### 根據收據打印狀態獲取會員列表
##### 地址:/PC_APP_API/Get_Associator_List
##### 請求方式:POST
##### 參數名稱:data
##### 數據格式:json
##### json例子:
```
{"Get_Associator_List":{"Receipt_Print_Status":"no_proceed"}}
```
#### //備注
#### Receipt_Print_Status = "no_proceed" => 獲取未進行中列表
#### Receipt_Print_Status = "proceed" => 獲取進行中列表
#### 申請會員號
##### 地址:/PC_APP_API/Get_Associator_Number
##### 請求方式:POST
##### 參數名稱:data
##### 數據格式:json
##### json例子:
```
{"Get_Associator_Number":{"Name":"姓名","Class":"班級","Sex":"性別","Phone_Number":"手機號碼"}}
```
#### 修改會員的收據打印狀態
##### 地址:/PC_APP_API/Change_Associator_Receipt_Status
##### 請求方式:POST
##### 參數名稱:data
##### 數據格式:json
##### json例子:
```
{"Change_Associator_Receipt_Status":{"Name":"姓名","Class":"班級","Sex":"性別","Phone_Number":"手機號碼","Receipt_Print_Status":"no_proceed","Print_Time":"打印現行時間","Print_MechineTime":"打印現行時間戳","Print_Mac":"本地mac地址","Print_Manager":"管理員名字"}}
```
#### 修改會員的會員卡號
##### 地址:/PC_APP_API/Change_Associator_Card_Id
##### 請求方式:POST
##### 參數名稱:data
##### 數據格式:json
##### json例子:
```
{"Chang_Associator_Card_Number":{"Name":"姓名","Class":"班級","Sex":"性別","Phone_Number":"手機號碼","Card_Number":"會員卡號"}}
```
#### 取消會員已申請的會員號
##### 地址:/PC_APP_API/Cancel_Associator_Number
##### 請求方式:POST
##### 參數名稱:data
##### 數據格式:json
##### json例子:
```
{"Cancel_Associator_Number":{"Name":"姓名","Class":"班級","Sex":"性別","Phone_Number":"手機號碼","Number":"會員號碼"}}
```
#### 查詢此會員是否存在會員卡
##### 地址:/PC_APP_API/Check_Associator_Exist_Card_Number
##### 請求方式:POST
##### 參數名稱:data
##### 數據格式:json
```
{"Check_Associator_Exist_Card_Number":{"Name":"姓名","Class":"班級","Sex":"性別","Phone_Number":"手機號碼"}}
```
#### 管理員登錄
##### 地址:/PC_APP_API/Login_Administrator
##### 請求方式:POST
##### 參數名稱:data
##### 數據格式:json
##### json例子:
```
{"Login_Administrator":{"Name":"管理員賬號","Passwd":"管理員密碼"}}
```
#### 查詢此會員當前收據打印狀態
##### 地址:/PC_APP_API/Check_Associator_Receipt_Status
##### 請求方式:POST
##### 參數名稱:data
##### 數據格式:json
##### json例子:
```
{"Check_Associator_Receipt_Status":{"Name":"姓名","Class":"班級","Sex":"性別","Phone_Number":"手機號碼"}}
```
#### 查詢此會員當前收據打印的管理員
##### 地址:/PC_APP_API/Check_Associator_Receipt_Adminstartor
##### 請求方式:POST
##### 參數名稱:data
##### 數據格式:json
##### json例子:
```
{"Check_Associator_Receipt_Adminstartor":{"Name":"姓名","Class":"班級","Sex":"性別","Phone_Number":"手機號碼"}}
```
#### 刪除會員
##### 地址:/PC_APP_API/Delete_Associator
##### 請求方式:POST
##### 參數名稱:data
##### 數據格式:json
##### json例子:
```
{"Delete_Associator":{"Name":"姓名","Class":"班級","Sex":"性別","Phone_Number":"手機號碼"}}
```
### 參與人員
#### jiamei mingming taiqin zibo zhaoyong jiaji xiaohui(負責打雜)
### 鳴謝全體參與招新的電協人。