# 会员招新服务端说明 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(负责打杂)
### 鸣谢全体参与招新的电协人。