first commit

This commit is contained in:
r0n1n7an 2023-11-25 14:02:30 +08:00
commit e8195401d2
8 changed files with 1109 additions and 0 deletions

BIN
APIServer.exe Normal file

Binary file not shown.

489
APIServer.go Normal file
View File

@ -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)
}

17
APIServer.yml Normal file
View File

@ -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

29
Index.html Normal file
View File

@ -0,0 +1,29 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no" />
<title>API Server</title>
<style type="text/css">
ul,li {margin: 0px; padding: 0px; list-style-type: none;}
label {font-family: Consolas; font-size: 16px;}
input {width: 250px; height: 20px; font-family: monospace; font-size: 14px;}
iframe {word-wrap: break-word; position: static; border: 0; top: 0px; left: 0px; min-width: 1000px; min-height: 1000px;}
</style>
</head>
<body leftmargin="10px" >
<h4>机台测试记录查询</h4>
<form name="getuutinfo" action="/getuutinfo/" method="POST" target="result">
<ul>
<li>
<label>USN: </label>
<input type="text" name="USN" id="USN" />
&nbsp;
<input type="submit" name="Query" value="Query" style="width:75px; height:25px;" />
</li>
</ul>
</form>
<br/>
<iframe title="result" name="result" frameborder="no" marginwidth="0" marginheight="0"></iframe>
</body>
</html>

22
cnf/cnf.go Normal file
View File

@ -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"`
}

517
ews/ews.go Normal file
View File

@ -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(`<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>`,
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(`<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>%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("<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>",
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(`<!DOCTYPE html>
<html>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<head>
<style>pre {word-wrap: break-word;}</style>
<style>body {font-family: Calibri; font-size: 14px;}</style>
</head>
<body>
<br/><span style="color: %s;">%s</span><br/>
</body>
</html>`, clr, msg)
return resp
}
func (e *EWS) TemplateHTML(loc, locTime, misc, last, bkup string) string {
if loc == "" {
loc = `<span style="color: red; font-size: 14px; font-family: Consolas;">Unknown</span>`
}
if misc == "" {
misc = `<span style="color: red;">NO Records !</span>`
}
if bkup == "" {
bkup = `<span style="color: red;">NO Records !</span>`
}
resp := fmt.Sprintf(`<!DOCTYPE html>
<html>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Test Records</title>
<style type="text/css">
table {
border: 1px solid rgb(81, 130, 187);
border-collapse: separate;
border-spacing: 0px;
position: static;
}
th {
background-color: rgb(81, 130, 187);
color: #fff;
border: 1px solid rgb(81, 130, 187);
padding: 5px 10px;
font-size: 12px;
font-family: Verdana;
font-weight: bold;
}
tr {
border: 1px solid rgb(81, 130, 187);
}
td {
color: #000;
padding: 5px 10px;
font-size: 12px;
font-family: Consolas;
}
span {
font-size: 14px;
font-family: Consolas;
}
</style>
<body>
<span style="font-weight: bold; font-size: 16px;">Location Info</span>
<table>
<tr>
<th>Location</th>
<th>Location Time</th>
</tr>
<tr>
<td>%s</td>
<td>%s</td>
</tr>
</table>
<br/>
<span style="font-weight: bold; font-size: 16px;">Misc. Info</span>
<br/>
<table>
<tr>
<th>Part NO.</th>
<th>MO</th>
<th>SKU</th>
<th>First Ack</th>
<th>Last Ack</th>
</tr>
%s
</table>
<br/>
<span style="font-weight: bold; font-size: 16px;">测试记录</span>
<br/>
<table>
<tr>
<th>USN</th>
<th>MAC</th>
<th>IP Address</th>
<th>Server</th>
<th>Test Item</th>
<th>Status</th>
<th>Message</th>
<th>Last Change</th>
</tr>
%s
%s
</table>
</body>
</html>`, loc, locTime, misc, bkup, last)
return resp
}

17
go.mod Normal file
View File

@ -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
)

18
go.sum Normal file
View File

@ -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=