commit e8195401d210f9735db870d293dfb2b11818a941 Author: r0n1n7an Date: Sat Nov 25 14:02:30 2023 +0800 first commit diff --git a/APIServer.exe b/APIServer.exe new file mode 100644 index 0000000..1e3d826 Binary files /dev/null and b/APIServer.exe differ diff --git a/APIServer.go b/APIServer.go new file mode 100644 index 0000000..4e2f2db --- /dev/null +++ b/APIServer.go @@ -0,0 +1,489 @@ +package main + +import ( + "APIServer/cnf" + "APIServer/ews" + "encoding/json" + "errors" + "flag" + "fmt" + "io" + "log" + "net/http" + "os" + "os/exec" + "path/filepath" + "strings" + "text/template" + "time" + + "github.com/chai2010/winsvc" + _ "github.com/go-sql-driver/mysql" + "github.com/natefinch/lumberjack" + uuid "github.com/satori/go.uuid" + yaml "gopkg.in/yaml.v2" +) + +var cfg = cnf.Cfg{} +var svc string = "apisvr" +var vsn bool = false +var ver string = "" +var logger *log.Logger +var flagIns bool = false +var flagRmv bool = false +var flagRun bool = false +var flagTer bool = false +var flagRst bool = false + +func init() { + flag.BoolVar(&flagIns, "i", false, "Install Service") + flag.BoolVar(&flagRmv, "u", false, "Uninstall Service") + flag.BoolVar(&flagRun, "s", false, "Start Service") + flag.BoolVar(&flagTer, "t", false, "Stop Service") + flag.BoolVar(&flagRst, "r", false, "Restart Service") + flag.BoolVar(&vsn, "v", false, "Show Program Version") + flag.Parse() + + logger = log.New(&lumberjack.Logger{ + Filename: strings.TrimSuffix(os.Args[0], ".exe") + ".log", + MaxAge: 30, + MaxSize: 10, + MaxBackups: 100, + LocalTime: true, + }, "", log.Ldate|log.Ltime|log.Lmicroseconds) +} + +func main() { + if vsn { + showVersion() + os.Exit(0) + } + + self := strings.TrimSuffix(os.Args[0], ".exe") + file := self + ".yml" + src, err := os.ReadFile(file) + if err != nil { + log.Printf("[ERR] %s\r\n", err.Error()) + logger.Printf("[ERR] %s\r\n", err.Error()) + os.Exit(1) + } + err = yaml.Unmarshal(src, &cfg) + if err != nil { + log.Printf("[ERR] %s\r\n", err.Error()) + logger.Printf("[ERR] %s\r\n", err.Error()) + os.Exit(1) + } + + cwd, err := winsvc.GetAppPath() + if err != nil { + log.Printf("[ERR] %s\r\n", err.Error()) + logger.Printf("[ERR] %s\r\n", err.Error()) + os.Exit(1) + } + err = os.Chdir(filepath.Dir(cwd)) + if err != nil { + log.Printf("[ERR] %s\r\n", err.Error()) + logger.Printf("[ERR] %s\r\n", err.Error()) + os.Exit(1) + } + + // Install Service + if flagIns { + err := winsvc.InstallService(cwd, svc, "API Server") + if err != nil { + log.Printf("[ERR] Install Service Failed: %s\r\n", err.Error()) + logger.Printf("[ERR] Install Service Failed: %s\r\n", err.Error()) + os.Exit(1) + } + log.Printf("[MSG] Install Service Successfully.\r\n") + logger.Printf("[MSG] Install Service Successfully.\r\n") + return + } + + // Uninstall Service + if flagRmv { + err := winsvc.RemoveService(svc) + if err != nil { + log.Printf("[ERR] Uninstall Service Failed: %s\r\n", err.Error()) + logger.Printf("[ERR] Uninstall Service Failed: %s\r\n", err.Error()) + os.Exit(1) + } + log.Printf("[MSG] Uninstall Service Successfully.\r\n") + logger.Printf("[MSG] Uninstall Service Successfully.\r\n") + return + } + + // Start Service + if flagRun { + err := winsvc.StartService(svc) + if err != nil { + log.Printf("[ERR] Start Service Failed: %s\r\n", err.Error()) + logger.Printf("[ERR] Start Service Failed: %s\r\n", err.Error()) + os.Exit(1) + } + log.Printf("[MSG] Service Started.\r\n") + logger.Printf("[MSG] Service Started.\r\n") + return + } + + // Stop Service + if flagTer { + err := winsvc.StopService(svc) + if err != nil { + log.Printf("[ERR] Stop Service Failed: %s\r\n", err.Error()) + logger.Printf("[ERR] Stop Service Failed: %s\r\n", err.Error()) + os.Exit(1) + } + log.Printf("[MSG] Service Stop.\r\n") + logger.Printf("[MSG] Service Stop.\r\n") + return + } + + // Restart Service + if flagRst { + err := winsvc.StopService(svc) + if err != nil { + log.Printf("[ERR] Stop Service Failed: %s\r\n", err.Error()) + logger.Printf("[ERR] Stop Service Failed: %s\r\n", err.Error()) + os.Exit(1) + } + log.Printf("[MSG] Service Stop.\r\n") + logger.Printf("[MSG] Service Stop.\r\n") + + time.Sleep(time.Second * 1) + err = winsvc.StartService(svc) + if err != nil { + log.Printf("[ERR] Start Service Failed: %s\r\n", err.Error()) + logger.Printf("[ERR] Start Service Failed: %s\r\n", err.Error()) + os.Exit(1) + } + log.Printf("[MSG] Service Started.\r\n") + logger.Printf("[MSG] Service Started.\r\n") + return + } + + if !winsvc.InServiceMode() { + err := winsvc.RunAsService(svc, startSvc, stopSvc, false) + if err != nil { + log.Printf("[ERR] Run As Service Failed: %s\r\n", err.Error()) + logger.Printf("[ERR] Run As Service Failed: %s\r\n", err.Error()) + os.Exit(1) + } + log.Printf("[MSG] Run As Service ...\r\n") + logger.Printf("[MSG] Run As Service ...\r\n") + } +} + +func startSvc() { + log.Printf("[MSG] Starting Service ...\r\n") + logger.Printf("[MSG] Starting Service ...\r\n") + http.HandleFunc("/", handleIndex) + http.HandleFunc("/request/", handleRequest) + http.HandleFunc("/fileinfo/", handleFileInfo) + http.HandleFunc("/getuutinfo/", handleGetUutInfo) + http.HandleFunc("/setuutinfo/", handleSetUutInfo) + logger.Printf("[MSG] Starting HTTP Server On Port: %s\r\n", cfg.Settings.ListenPort) + err := http.ListenAndServe(":"+cfg.Settings.ListenPort, nil) + if err != nil { + logger.Printf("%s\r\n", err.Error()) + os.Exit(1) + } +} + +func stopSvc() { + log.Printf("[MSG] Stopping Service ...\r\n") + logger.Printf("[MSG] Stopping Service ...\r\n") + os.Exit(0) +} + +func handleIndex(w http.ResponseWriter, r *http.Request) { + defer r.Body.Close() + if r.RequestURI == "/favicon.ico" { + return + } + if r.Method != http.MethodGet { + w.WriteHeader(http.StatusMethodNotAllowed) + fmt.Fprintf(w, "MethodNotAllowed: %v\r\n", http.StatusMethodNotAllowed) + return + } + + tpl, err := template.ParseFiles("./Index.html") + if err != nil { + w.WriteHeader(http.StatusMethodNotAllowed) + fmt.Fprintf(w, "ErrMsg: %v\r\n", err) + return + } + err = tpl.Execute(w, nil) + if err != nil { + w.WriteHeader(http.StatusMethodNotAllowed) + fmt.Fprintf(w, "ErrMsg: %v\r\n", err) + return + } +} + +func handleRequest(w http.ResponseWriter, r *http.Request) { + defer r.Body.Close() + if r.RequestURI == "/favicon.ico" { + return + } + if r.Method != http.MethodPost { + w.WriteHeader(http.StatusMethodNotAllowed) + fmt.Fprintf(w, "MethodNotAllowed: %v\r\n", http.StatusMethodNotAllowed) + return + } + + reqID := uuid.NewV4().String() + logger.Printf("RequestID: %s; Addr: %s; URI: %s\r\n", reqID, r.RemoteAddr, r.RequestURI) + + if err := r.ParseForm(); err != nil { + logger.Printf("RequestID: %s; ErrMsg: %s\r\n", reqID, err.Error()) + return + } + + var params []string + if len(r.Form) > 0 { + logger.Printf("RequestID: %s; Form: %#v\r\n", reqID, r.Form) + for k, v := range r.Form { + if cfg.Settings.ParamCheck { + if !chkParams(k) { + continue + } + if !chkParams(v[0]) { + continue + } + } + params = append(params, fmt.Sprintf("%s:%s", k, strings.ReplaceAll(v[0], " ", "_"))) + } + } + + logger.Printf("RequestID: %s; Params: %#v\r\n", reqID, params) + resp := runCommand(cfg.Settings.HookScript, params, reqID) + fmt.Fprintf(w, "%s\r\n", resp) +} + +func handleFileInfo(w http.ResponseWriter, r *http.Request) { + defer r.Body.Close() + if r.RequestURI == "/favicon.ico" { + return + } + + reqID := uuid.NewV4().String() + logger.Printf("RequestID: %s; Addr: %s; URI: %s\r\n", reqID, r.RemoteAddr, r.RequestURI) + + if err := r.ParseForm(); err != nil { + logger.Printf("RequestID: %s; ErrMsg: %s\r\n", reqID, err.Error()) + return + } + + var path string = "" + if len(r.Form) > 0 { + for k, v := range r.Form { + if cfg.Settings.ParamCheck { + if !chkParams(k) { + continue + } + if !chkParams(v[0]) { + continue + } + } + if k == "path" { + path = v[0] + } + } + } + + if path == "" { + fmt.Fprintf(w, "%v", http.StatusBadRequest) + return + } + _, err := os.Stat(path) + if err != nil { + fmt.Fprintf(w, "%v", http.StatusNotFound) + return + } + + type FileInfo struct { + FileName string `json:"FileName"` + FileSize int64 `json:"FileSize"` + LastModified string `json:"LastModified"` + //MD5Hash string `json:"MD5Hash"` + } + type FileList struct { + FileList []FileInfo `json:"FileList"` + } + + var infoList []FileInfo + err = filepath.Walk(path, func(path string, info os.FileInfo, err error) error { + if !info.IsDir() { + var fileInfo FileInfo + fileInfo.FileName = info.Name() + fileInfo.FileSize = info.Size() + fileInfo.LastModified = info.ModTime().Format("2006-01-02 15:04:05") + //fileInfo.MD5Hash = fileMD5(path) + infoList = append(infoList, fileInfo) + } + return nil + }) + + if err != nil { + fmt.Fprintf(w, "%s", err.Error()) + return + } + + fileList := FileList{infoList} + jsonObj, err := json.MarshalIndent(fileList, "", " ") + if err != nil { + fmt.Fprintf(w, "%s", err.Error()) + return + } + fmt.Fprintf(w, "%s", string(jsonObj)) +} + +func handleGetUutInfo(w http.ResponseWriter, r *http.Request) { + defer r.Body.Close() + if r.RequestURI == "/favicon.ico" { + return + } + host := strings.Split(r.Host, ":")[0] + addr := strings.Split(r.RemoteAddr, ":")[0] + uri := r.RequestURI + rst := map[string]string{"RESULT": ""} + + if err := r.ParseForm(); err != nil { + logger.Printf("[ERR] %s; %s; %s\r\n", r.RemoteAddr, r.RequestURI, err.Error()) + rst["RESULT"] = "NG" + rst["ErrMsg"] = err.Error() + w.WriteHeader(http.StatusInternalServerError) + fmt.Fprintf(w, "%v", strings.ReplaceAll(fmt.Sprintf("%#v", rst), "map[string]string", "")) + return + } + + params := make(map[string]string, 0) + if len(r.Form) > 0 { + for k, v := range r.Form { + params[strings.ToUpper(strings.TrimSpace(k))] = strings.TrimSpace(v[0]) + } + } + ews := new(ews.EWS) + rst = ews.GetUutInfo(cfg, logger, host, addr, uri, params) + if rst["RESULT"] != "OK" { + w.WriteHeader(http.StatusInternalServerError) + } else { + w.WriteHeader(http.StatusOK) + } + fmt.Fprintf(w, "%v", rst["ErrMsg"]) +} + +func handleSetUutInfo(w http.ResponseWriter, r *http.Request) { + defer r.Body.Close() + if r.RequestURI == "/favicon.ico" { + return + } + + host := strings.Split(r.Host, ":")[0] + addr := strings.Split(r.RemoteAddr, ":")[0] + uri := r.RequestURI + rst := map[string]string{"RESULT": ""} + + if r.Method != http.MethodPost { + rst["RESULT"] = "NG" + rst["ErrMsg"] = fmt.Sprintf("MethodNotAllowed: %v", http.StatusMethodNotAllowed) + w.WriteHeader(http.StatusMethodNotAllowed) + fmt.Fprintf(w, "%v", strings.ReplaceAll(fmt.Sprintf("%#v", rst), "map[string]string", "")) + return + } + + params, err := parseReqParams(r) + if err != nil { + logger.Printf("[ERR] %s; %s; %#v; %s\r\n", r.RemoteAddr, r.RequestURI, params, rst["ErrMsg"]) + rst["RESULT"] = "NG" + rst["ErrMsg"] = err.Error() + w.WriteHeader(http.StatusBadRequest) + fmt.Fprintf(w, "%v", strings.ReplaceAll(fmt.Sprintf("%#v", rst), "map[string]string", "")) + return + } + + ews := new(ews.EWS) + rst = ews.SetUutInfo(cfg, logger, host, addr, uri, params) + if rst["RESULT"] != "OK" { + w.WriteHeader(http.StatusInternalServerError) + } else { + w.WriteHeader(http.StatusOK) + } + fmt.Fprintf(w, "%v", strings.ReplaceAll(fmt.Sprintf("%#v", rst), "map[string]string", "")) +} + +func parseReqParams(r *http.Request) (map[string]string, error) { + params := make(map[string]string, 0) + if err := r.ParseForm(); err != nil { + logger.Printf("[ERR] %s; %s; %s\r\n", r.RemoteAddr, r.RequestURI, err.Error()) + return params, err + } + if len(r.Form) > 0 { + for k, v := range r.Form { + params[strings.ToUpper(strings.TrimSpace(k))] = strings.TrimSpace(v[0]) + } + } + return params, nil +} + +/* func fileMD5(filePath string) string { + file, err := os.Open(filePath) + if err != nil { + return "" + } + hash := md5.New() + _, err = io.Copy(hash, file) + if err != nil { + return "" + } + return hex.EncodeToString(hash.Sum(nil)) +} */ + +func chkParams(arg string) bool { + if strings.Contains(arg, ";") || + strings.Contains(arg, "|") || + strings.Contains(arg, "&") || + strings.Contains(arg, ">") || + strings.Contains(arg, "<") || + strings.Contains(arg, "(") || + strings.Contains(arg, ")") { + return false + } + return true +} + +func runCommand(cmdStr string, argStr []string, requestID string) string { + cmd := exec.Command(cmdStr, argStr...) + if errors.Is(cmd.Err, exec.ErrDot) { + cmd.Err = nil + } + std, err := cmd.StdoutPipe() + if err != nil { + logger.Printf("RequestID: %s; ErrMsg: %s\r\n", requestID, err.Error()) + return err.Error() + } + defer std.Close() + err = cmd.Start() + if err != nil { + logger.Printf("RequestID: %s; ErrMsg: %s\r\n", requestID, err.Error()) + return err.Error() + } + ret, err := io.ReadAll(std) + if err != nil { + logger.Printf("RequestID: %s; ErrMsg: %s\r\n", requestID, err.Error()) + return err.Error() + } + err = cmd.Wait() + if err != nil { + logger.Printf("RequestID: %s; ErrMsg: %s\r\n", requestID, err.Error()) + return err.Error() + } + //fmt.Printf("\r\n%s\r\n\r\n", string(ret)) + //logger.Printf("RequestID: %s; Result: \r\n%s\r\n\r\n", requestID, string(ret)) + return string(ret) +} + +func showVersion() { + fmt.Printf("%s\r\nVersion: %s\r\n", os.Args[0], ver) +} diff --git a/APIServer.yml b/APIServer.yml new file mode 100644 index 0000000..2a2e0ea --- /dev/null +++ b/APIServer.yml @@ -0,0 +1,17 @@ +Settings: + ListenPort: 9000 + HookScript: APIHOOK + ParamCheck: false + LogRequest: true + LogResponse: false +MySQL: + Server: 10.60.230.109 + Port: 3306 + Database: ewsv3_f716 + User: apisvc + Password: wcqte +Remark: + Columns_UutInfo: [usn,mac,ipaddr,status,message,first_ack,last_ack,last_change,last_transfer] + Columns_LocInfo: [mac,line,col,row,num,last_found] + USER_RO: ewsv3:ewsv3 + USER_RW: apisvc:wcqte diff --git a/Index.html b/Index.html new file mode 100644 index 0000000..6e51c4b --- /dev/null +++ b/Index.html @@ -0,0 +1,29 @@ + + + + + + API Server + + + +

机台测试记录查询

+
+ +
+
+ + + diff --git a/cnf/cnf.go b/cnf/cnf.go new file mode 100644 index 0000000..d71b238 --- /dev/null +++ b/cnf/cnf.go @@ -0,0 +1,22 @@ +package cnf + +type Cfg struct { + Settings Settings `yaml:"Settings"` + MySQL MySQL `yaml:"MySQL"` +} + +type Settings struct { + ListenPort string `yaml:"ListenPort"` + HookScript string `yaml:"HookScript"` + ParamCheck bool `yaml:"ParamCheck"` + LogRequest bool `yaml:"LogRequest"` + LogResponse bool `yaml:"LogResponse"` +} + +type MySQL struct { + Server string `yaml:"Server"` + Port string `yaml:"Port"` + Database string `yaml:"Database"` + User string `yaml:"User"` + Password string `yaml:"Password"` +} diff --git a/ews/ews.go b/ews/ews.go new file mode 100644 index 0000000..b8e3610 --- /dev/null +++ b/ews/ews.go @@ -0,0 +1,517 @@ +package ews + +import ( + "APIServer/cnf" + "database/sql" + "fmt" + "log" + + _ "github.com/go-sql-driver/mysql" +) + +type EWS struct{} + +type UutInfo struct { + USN string + MAC string + IPAddr string + Relay string + Item string + Status string + Message string + PartNO string + MfgMO string + MfgSKU string + MfgLine string + MfgStage string + FirstAck string + LastAck string + LastChg string + LastTrn string +} + +func (e *EWS) SetUutInfo(cfg cnf.Cfg, logger *log.Logger, host string, addr string, uri string, params map[string]string) map[string]string { + if cfg.Settings.LogRequest { + logger.Printf("[MSG] %s; %s; Request: %#v\r\n", addr, uri, params) + } + rst := map[string]string{"RESULT": ""} + + dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s", + cfg.MySQL.User, + cfg.MySQL.Password, + cfg.MySQL.Server, + cfg.MySQL.Port, + cfg.MySQL.Database, + ) + dbo, err := sql.Open("mysql", dsn) + if err != nil { + logger.Printf("[ERR] %s; %s; %#v; %s\r\n", addr, uri, params, err.Error()) + rst["RESULT"] = "NG" + rst["ErrMsg"] = err.Error() + return rst + } + defer dbo.Close() + err = dbo.Ping() + if err != nil { + logger.Printf("[ERR] %s; %s; %#v; %s\r\n", addr, uri, params, err.Error()) + rst["RESULT"] = "NG" + rst["ErrMsg"] = err.Error() + return rst + } + + rec := dbo.QueryRow( + fmt.Sprintf(`SELECT usn,mac,ipaddr,relay,item,status,message,partno,mo,sku,line,stage,first_ack,last_ack,last_change,last_transfer + FROM uutinfo + WHERE usn='%s';`, params["USN"])) + + ui := new(UutInfo) + err = rec.Scan( + &ui.USN, + &ui.MAC, + &ui.IPAddr, + &ui.Relay, + &ui.Item, + &ui.Status, + &ui.Message, + &ui.PartNO, + &ui.MfgMO, + &ui.MfgSKU, + &ui.MfgLine, + &ui.MfgStage, + &ui.FirstAck, + &ui.LastAck, + &ui.LastChg, + &ui.LastTrn, + ) + + //如果在uutinfo表中未查找到此USN的记录, 则新增一条此USN的记录 + if err == sql.ErrNoRows { + dmlInsert := fmt.Sprintf("INSERT INTO uutinfo (usn,mac,ipaddr,relay,item,status,message,partno,mo,sku,line,stage,first_ack,last_ack,last_change) VALUES ('%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s',%s,%s,%s);", + params["USN"], + params["MAC"], + addr, + host, + params["ITEM"], + params["STATUS"], + params["MESSAGE"], + params["PARTNO"], + params["MO"], + params["SKU"], + params["LINE"], + params["STAGE"], + "NOW()", + "NOW()", + "NOW()", + ) + + _, err = dbo.Exec(dmlInsert) + if err != nil { + logger.Printf("[ERR] %s; %s; %#v; %s\r\n", addr, uri, params, err.Error()) + rst["RESULT"] = "NG" + rst["ErrMsg"] = err.Error() + return rst + } + if cfg.Settings.LogResponse { + logger.Printf("[MSG] %s; %s; Response: %#v\r\n", addr, uri, rst) + } + rst["RESULT"] = "OK" + rst["ErrMsg"] = "Inserted 1 new record." + return rst + } + + //如果此USN的记录存在与uutinfo表中, 且此USN的MAC/Item/Status/Message已变化, 则: + //1. 备份此USN对应记录行到uutinfobkup表 + //2. 更新uutinfo表中对应USN记录的MAC/Item/Status/Message/PartNO/MO/SKU/Line/Stage + //3. 更新uutinfo表中此USN记录行的last_ack和last_change栏位为当前时间 + extFlag := false + chgFlag := false + + _, extFlag = params["MAC"] + if extFlag && params["MAC"] != ui.MAC { + chgFlag = true + } + + _, extFlag = params["ITEM"] + if extFlag && params["ITEM"] != ui.Item { + chgFlag = true + } + + _, extFlag = params["STATUS"] + if extFlag && params["STATUS"] != ui.Status { + chgFlag = true + } + + _, extFlag = params["MESSAGE"] + if extFlag && params["MESSAGE"] != ui.Message { + chgFlag = true + } + + if chgFlag { + tx, err := dbo.Begin() + if err != nil { + logger.Printf("[ERR] %s; %s; %#v; %s\r\n", addr, uri, params, err.Error()) + rst["RESULT"] = "NG" + rst["ErrMsg"] = err.Error() + return rst + } + + stmt, err := tx.Prepare("INSERT INTO uutinfobkup (usn,mac,ipaddr,relay,item,status,message,partno,mo,sku,line,stage,first_ack,last_ack,last_change) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);") + if err != nil { + tx.Rollback() + logger.Printf("[ERR] %s; %s; %#v; %s\r\n", addr, uri, params, err.Error()) + rst["RESULT"] = "NG" + rst["ErrMsg"] = err.Error() + return rst + } + defer stmt.Close() + _, err = stmt.Exec( + ui.USN, + ui.MAC, + addr, + host, + ui.Item, + ui.Status, + ui.Message, + ui.PartNO, + ui.MfgMO, + ui.MfgSKU, + ui.MfgLine, + ui.MfgStage, + ui.FirstAck, + ui.LastAck, + ui.LastChg, + ) + if err != nil { + tx.Rollback() + logger.Printf("[ERR] %s; %s; %#v; %s\r\n", addr, uri, params, err.Error()) + rst["RESULT"] = "NG" + rst["ErrMsg"] = err.Error() + return rst + } + + stmt, err = tx.Prepare("UPDATE uutinfo SET mac=?, ipaddr=?, relay=?, item=?, status=?, message=?, partno=?, mo=?, sku=?, line=?, stage=?, last_ack=NOW(), last_change=NOW() WHERE usn=?;") + if err != nil { + tx.Rollback() + logger.Printf("[ERR] %s; %s; %#v; %s\r\n", addr, uri, params, err.Error()) + rst["RESULT"] = "NG" + rst["ErrMsg"] = err.Error() + return rst + } + defer stmt.Close() + _, err = stmt.Exec( + params["MAC"], + addr, + host, + params["ITEM"], + params["STATUS"], + params["MESSAGE"], + params["PARTNO"], + params["MO"], + params["SKU"], + params["LINE"], + params["STAGE"], + params["USN"], + ) + if err != nil { + tx.Rollback() + logger.Printf("[ERR] %s; %s; %#v; %s\r\n", addr, uri, params, err.Error()) + rst["RESULT"] = "NG" + rst["ErrMsg"] = err.Error() + return rst + } + + err = tx.Commit() + if err != nil { + tx.Rollback() + logger.Printf("[ERR] %s; %s; %#v; %s\r\n", addr, uri, params, err.Error()) + rst["RESULT"] = "NG" + rst["ErrMsg"] = err.Error() + return rst + } + if cfg.Settings.LogResponse { + logger.Printf("[MSG] %s; %s; Response: %#v\r\n", addr, uri, rst) + } + rst["RESULT"] = "OK" + rst["ErrMsg"] = "Updated and backed up 1 record." + return rst + } + + //如果此USN的记录存在与uutinfo表中, 且MAC/Item/Status/Message未变化, 则更新此USN记录的ipaddr,relay,stage, 并更新last_ack栏位为当前时间 + dmlUpdate := fmt.Sprintf("UPDATE uutinfo SET ipaddr='%s', relay='%s', stage='%s', last_ack=NOW() WHERE usn='%s'", + addr, host, params["STAGE"], params["USN"]) + _, err = dbo.Exec(dmlUpdate) + if err != nil { + logger.Printf("[ERR] %s; %s; %#v; %s\r\n", addr, uri, params, err.Error()) + rst["RESULT"] = "NG" + rst["ErrMsg"] = err.Error() + return rst + } + if cfg.Settings.LogResponse { + logger.Printf("[MSG] %s; %s; Response: %#v\r\n", addr, uri, rst) + } + rst["RESULT"] = "OK" + rst["ErrMsg"] = "Updated 1 record." + return rst +} + +func (e *EWS) GetUutInfo(cfg cnf.Cfg, logger *log.Logger, host string, addr string, uri string, params map[string]string) map[string]string { + if cfg.Settings.LogRequest { + logger.Printf("[MSG] %s; %s; Request: %#v\r\n", addr, uri, params) + } + + rst := map[string]string{"RESULT": ""} + if params["USN"] == "" { + logger.Printf("[ERR] %s; %s; %#v; %s\r\n", addr, uri, params, "Invalid Parameters !") + rst["RESULT"] = "NG" + rst["ErrMsg"] = e.SimpleMsgHTML("red", "Invalid Parameters !") + return rst + } + + dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s", + cfg.MySQL.User, + cfg.MySQL.Password, + cfg.MySQL.Server, + cfg.MySQL.Port, + cfg.MySQL.Database, + ) + dbo, err := sql.Open("mysql", dsn) + if err != nil { + logger.Printf("[ERR] %s; %s; %#v; %s\r\n", addr, uri, params, err.Error()) + rst["RESULT"] = "NG" + rst["ErrMsg"] = e.SimpleMsgHTML("red", err.Error()) + return rst + } + defer dbo.Close() + err = dbo.Ping() + if err != nil { + logger.Printf("[ERR] %s; %s; %#v; %s\r\n", addr, uri, params, err.Error()) + rst["RESULT"] = "NG" + rst["ErrMsg"] = e.SimpleMsgHTML("red", err.Error()) + return rst + } + + ui := new(UutInfo) + partNO := "" + mfgMO := "" + mfgSKU := "" + fstAck := "" + lstAck := "" + + //Get Last Record + last := "" + row := dbo.QueryRow( + fmt.Sprintf(`SELECT usn,mac,ipaddr,relay,item,status,message,partno,mo,sku,line,stage,first_ack,last_ack,last_change,last_transfer + FROM uutinfo + WHERE usn='%s';`, params["USN"])) + + err = row.Scan( + &ui.USN, + &ui.MAC, + &ui.IPAddr, + &ui.Relay, + &ui.Item, + &ui.Status, + &ui.Message, + &ui.PartNO, + &ui.MfgMO, + &ui.MfgSKU, + &ui.MfgLine, + &ui.MfgStage, + &ui.FirstAck, + &ui.LastAck, + &ui.LastChg, + &ui.LastTrn, + ) + + if err == nil { + last = fmt.Sprintf(`%s%s%s%s%s%s%s%s`, + ui.USN, ui.MAC, ui.IPAddr, ui.Relay, ui.Item, ui.Status, ui.Message, ui.LastChg) + partNO = ui.PartNO + mfgMO = ui.MfgMO + mfgSKU = ui.MfgSKU + fstAck = ui.FirstAck + lstAck = ui.LastAck + } + + //Get Location Information + loc, locTime := "", "" + if last != "" { + qry := dbo.QueryRow(fmt.Sprintf(`SELECT CONCAT_WS('-',line,col,row,num) AS loc, update_time FROM locinfo WHERE mac='%s'`, ui.MAC)) + qry.Scan(&loc, &locTime) + } + + //Get History Records + bkup := "" + rows, err := dbo.Query( + fmt.Sprintf(`SELECT usn,mac,ipaddr,relay,item,status,message,partno,mo,sku,line,stage,first_ack,last_ack,last_change,last_transfer + FROM uutinfobkup + WHERE usn='%s' ORDER BY seqid ASC;`, params["USN"])) + if err == nil { + for rows.Next() { + err := rows.Scan( + &ui.USN, + &ui.MAC, + &ui.IPAddr, + &ui.Relay, + &ui.Item, + &ui.Status, + &ui.Message, + &ui.PartNO, + &ui.MfgMO, + &ui.MfgSKU, + &ui.MfgLine, + &ui.MfgStage, + &ui.FirstAck, + &ui.LastAck, + &ui.LastChg, + &ui.LastTrn, + ) + if err != nil { + break + } + temp := fmt.Sprintf(`%s%s%s%s%s%s%s%s%s`, + ui.USN, ui.MAC, ui.IPAddr, ui.Relay, ui.Item, ui.Status, ui.Message, ui.LastChg, "\r\n") + bkup = bkup + temp + } + } + defer rows.Close() + + //Get Misc. Info + if partNO == "" { + partNO = ui.PartNO + } + if mfgMO == "" { + mfgMO = ui.MfgMO + } + if mfgSKU == "" { + mfgSKU = ui.MfgSKU + } + if fstAck == "" { + fstAck = ui.FirstAck + } + if lstAck == "" { + lstAck = ui.LastAck + } + + misc := "" + misc = fmt.Sprintf("%s%s%s%s%s", + partNO, mfgMO, mfgSKU, fstAck, lstAck) + + rst["RESULT"] = "OK" + rst["ErrMsg"] = e.TemplateHTML(loc, locTime, misc, last, bkup) + if cfg.Settings.LogResponse { + logger.Printf("[MSG] %s; %s; Response: Length=%s\r\n", addr, uri, rst["RESULT"]) + } + return rst +} + +func (e *EWS) SimpleMsgHTML(clr, msg string) string { + resp := fmt.Sprintf(` + + + + + + + +
%s
+ +`, clr, msg) + return resp +} + +func (e *EWS) TemplateHTML(loc, locTime, misc, last, bkup string) string { + if loc == "" { + loc = `Unknown` + } + + if misc == "" { + misc = `NO Records !` + } + + if bkup == "" { + bkup = `NO Records !` + } + + resp := fmt.Sprintf(` + + + Test Records + + + Location Info + + + + + + + + + +
LocationLocation Time
%s%s
+ +
+ Misc. Info +
+ + + + + + + + + %s +
Part NO.MOSKUFirst AckLast Ack
+ +
+ 测试记录 +
+ + + + + + + + + + + + %s + %s +
USNMACIP AddressServerTest ItemStatusMessageLast Change
+ +`, loc, locTime, misc, bkup, last) + return resp +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..2f29c33 --- /dev/null +++ b/go.mod @@ -0,0 +1,17 @@ +module APIServer + +go 1.20 + +require ( + github.com/chai2010/winsvc v0.0.0-20230626053835-2858f12dbb33 + github.com/go-sql-driver/mysql v1.7.1 + github.com/natefinch/lumberjack v2.0.0+incompatible + github.com/satori/go.uuid v1.2.0 + gopkg.in/yaml.v2 v2.4.0 +) + +require ( + github.com/BurntSushi/toml v1.3.2 // indirect + golang.org/x/sys v0.13.0 // indirect + gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..bbafac4 --- /dev/null +++ b/go.sum @@ -0,0 +1,18 @@ +github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= +github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/chai2010/winsvc v0.0.0-20230626053835-2858f12dbb33 h1:xcs7ZpTuGng3AoeBS4aoMX0Ba+btfFE+dQTcqisO1vw= +github.com/chai2010/winsvc v0.0.0-20230626053835-2858f12dbb33/go.mod h1:b9Xy0A0C/binZARjeVfHEr+gHzQUVztL71bTms7PRIM= +github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= +github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM= +github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk= +github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= +gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=