hn-readcard-go/server.go
2026-08-06 11:23:33 +08:00

236 lines
8.1 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
import (
"encoding/json"
"fmt"
"net/http"
"runtime/debug"
"strings"
"sync/atomic"
"time"
)
const jiaxingReaderType = "ZJ_SX_MT_TSW"
type apiServer struct {
cfg Config
reader cardOperations
logger *appLogger
request atomic.Uint64
}
type apiResponse struct {
Code int `json:"code"`
Message string `json:"message"`
Data any `json:"data"`
}
func newHandler(cfg Config, reader cardOperations, logger *appLogger) http.Handler {
s := &apiServer{cfg: cfg, reader: reader, logger: logger}
mux := http.NewServeMux()
mux.HandleFunc("GET /health", s.health)
mux.HandleFunc("POST /api/card/read-nopin", s.readNoPIN)
mux.HandleFunc("POST /api/card/read", s.readWithPIN)
mux.HandleFunc("POST /api/hmac-sm3", s.hmacSM3)
mux.HandleFunc("/", s.notFound)
return s.middleware(mux)
}
func (s *apiServer) middleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
origin := s.cfg.CORSOrigin
if origin == "*" {
w.Header().Set("Access-Control-Allow-Origin", "*")
} else if r.Header.Get("Origin") == origin {
w.Header().Set("Access-Control-Allow-Origin", origin)
w.Header().Add("Vary", "Origin")
}
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
if r.Method == http.MethodOptions {
w.WriteHeader(http.StatusNoContent)
return
}
requestID := fmt.Sprintf("%d-%06d", time.Now().Unix(), s.request.Add(1))
w.Header().Set("X-Request-ID", requestID)
wrapped := &statusWriter{ResponseWriter: w, status: http.StatusOK}
started := time.Now()
defer func() {
if recovered := recover(); recovered != nil {
s.logger.Error("HTTP 处理发生 panic", "event", "http_panic", "request_id", requestID, "method", r.Method, "path", r.URL.Path, "panic", fmt.Sprint(recovered), "stack", string(debug.Stack()))
if !wrapped.wroteHeader {
writeJSON(wrapped, http.StatusInternalServerError, apiResponse{Code: 500, Message: "服务内部异常", Data: nil})
}
}
if wrapped.status >= 400 {
s.logger.Warn("HTTP 请求失败", "event", "http_failure", "request_id", requestID, "method", r.Method, "path", r.URL.Path, "status", wrapped.status, "duration_ms", time.Since(started).Milliseconds(), "remote", r.RemoteAddr)
}
}()
next.ServeHTTP(wrapped, r)
})
}
type statusWriter struct {
http.ResponseWriter
status int
wroteHeader bool
}
func (w *statusWriter) WriteHeader(status int) {
if w.wroteHeader {
return
}
w.status = status
w.wroteHeader = true
w.ResponseWriter.WriteHeader(status)
}
func (w *statusWriter) Write(p []byte) (int, error) {
if !w.wroteHeader {
w.WriteHeader(http.StatusOK)
}
return w.ResponseWriter.Write(p)
}
func (s *apiServer) health(w http.ResponseWriter, _ *http.Request) {
status := s.reader.Status()
data := map[string]any{
"service": "card-read-service",
"loaded": status.Loaded,
"dllDir": status.DLLDir,
"arch": status.Arch,
"platform": status.Platform,
"error": nil,
"jiaxing_reader_type": jiaxingReaderType,
"dll_init_ok": status.Loaded,
}
if status.Error != "" {
data["error"] = status.Error
}
writeJSON(w, http.StatusOK, apiResponse{Code: 0, Message: "ok", Data: data})
}
func (s *apiServer) readNoPIN(w http.ResponseWriter, r *http.Request) {
var body struct {
ReaderType string `json:"reader_type"`
}
if !decodeBody(w, r, &body) {
return
}
readerType := strings.TrimSpace(body.ReaderType)
if readerType == "" {
writeJSON(w, http.StatusBadRequest, apiResponse{Code: 400, Message: "请传入 reader_type地市读卡器标识如 ZJ_HZ_310000", Data: nil})
return
}
result, err := s.reader.ReadCard(readerType, true)
if err != nil {
s.logger.Error("免 PIN 读卡异常", "event", "dll_call_error", "method", "ZJ_ReadCardInfo_NoPin", "reader_type", readerType, "error", err)
writeJSON(w, http.StatusInternalServerError, apiResponse{Code: 500, Message: err.Error(), Data: nil})
return
}
if !result.Success {
message := resultMessage(result, "读卡失败")
s.logger.Error("免 PIN 读卡失败", "event", "card_read_failure", "method", "ZJ_ReadCardInfo_NoPin", "reader_type", readerType, "dll_code", result.Code, "message", message)
writeJSON(w, http.StatusInternalServerError, apiResponse{Code: 500, Message: message, Data: result})
return
}
writeJSON(w, http.StatusOK, apiResponse{Code: 0, Message: "ok", Data: successfulData(result)})
}
func (s *apiServer) readWithPIN(w http.ResponseWriter, r *http.Request) {
var body struct {
ReaderType any `json:"reader_type"`
}
if !decodeBody(w, r, &body) {
return
}
if body.ReaderType != nil && strings.TrimSpace(fmt.Sprint(body.ReaderType)) != jiaxingReaderType {
writeJSON(w, http.StatusBadRequest, apiResponse{Code: 400, Message: "带 PIN 读卡仅支持嘉兴reader_type 必须为 " + jiaxingReaderType, Data: nil})
return
}
result, err := s.reader.ReadCard(jiaxingReaderType, false)
if err != nil {
s.logger.Error("带 PIN 读卡异常", "event", "dll_call_error", "method", "ZJ_ReadCardInfo", "reader_type", jiaxingReaderType, "error", err)
writeJSON(w, http.StatusInternalServerError, apiResponse{Code: 500, Message: err.Error(), Data: nil})
return
}
if !result.Success {
message := resultMessage(result, "读卡失败")
s.logger.Error("带 PIN 读卡失败", "event", "card_read_failure", "method", "ZJ_ReadCardInfo", "reader_type", jiaxingReaderType, "dll_code", result.Code, "message", message)
writeJSON(w, http.StatusInternalServerError, apiResponse{Code: 500, Message: message, Data: result})
return
}
writeJSON(w, http.StatusOK, apiResponse{Code: 0, Message: "ok", Data: successfulData(result)})
}
func (s *apiServer) hmacSM3(w http.ResponseWriter, r *http.Request) {
var body map[string]any
if !decodeBody(w, r, &body) {
return
}
key, keyOK := body["key"]
secret, secretOK := body["secret"]
requestBody, bodyOK := body["request_body"]
if !keyOK || key == nil || !secretOK || secret == nil || !bodyOK || requestBody == nil {
writeJSON(w, http.StatusBadRequest, apiResponse{Code: 400, Message: "缺少 key / secret / request_body", Data: nil})
return
}
timestamp := "0"
if value, ok := body["unix_timestamp"]; ok && value != nil {
timestamp = fmt.Sprint(value)
}
result, err := s.reader.HMACSM3(fmt.Sprint(key), fmt.Sprint(secret), timestamp, fmt.Sprint(requestBody))
if err != nil {
s.logger.Error("HMAC-SM3 调用异常", "event", "dll_call_error", "method", "ZJ_Hmac_SM3", "error", err)
writeJSON(w, http.StatusInternalServerError, apiResponse{Code: 500, Message: err.Error(), Data: nil})
return
}
if !result.Success {
message := resultMessage(result, "签名失败")
s.logger.Error("HMAC-SM3 失败", "event", "hmac_failure", "method", "ZJ_Hmac_SM3", "dll_code", result.Code, "message", message)
writeJSON(w, http.StatusInternalServerError, apiResponse{Code: 500, Message: message, Data: result})
return
}
writeJSON(w, http.StatusOK, apiResponse{Code: 0, Message: "ok", Data: successfulData(result)})
}
func (s *apiServer) notFound(w http.ResponseWriter, _ *http.Request) {
writeJSON(w, http.StatusNotFound, apiResponse{Code: 404, Message: "Not Found", Data: nil})
}
func decodeBody(w http.ResponseWriter, r *http.Request, target any) bool {
r.Body = http.MaxBytesReader(w, r.Body, 1<<20)
decoder := json.NewDecoder(r.Body)
if err := decoder.Decode(target); err != nil {
writeJSON(w, http.StatusBadRequest, apiResponse{Code: 400, Message: "请求 JSON 格式错误: " + err.Error(), Data: nil})
return false
}
return true
}
func resultMessage(result DLLResult, fallback string) string {
if object, ok := result.Data.(map[string]any); ok {
if message, ok := object["Message"].(string); ok && strings.TrimSpace(message) != "" {
return message
}
}
if strings.TrimSpace(result.Raw) != "" {
return result.Raw
}
return fallback
}
func successfulData(result DLLResult) any {
if result.Data != nil {
return result.Data
}
return result
}
func writeJSON(w http.ResponseWriter, status int, response apiResponse) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(status)
_ = json.NewEncoder(w).Encode(response)
}