// File: xy_sh/go.mod ```go module xy_sh go 1.26 require ( github.com/gofiber/fiber/v2 v2.52.0 github.com/tjfoc/gmsm v1.4.1 ) ``` // File: xy_sh/cmd/server/main.go ```go package main import ( "log" "xy_sh/internal/config" "xy_sh/internal/router" "xy_sh/pkg/crypto" "github.com/gofiber/fiber/v2" ) func main() { cfg := config.LoadConfig() crypto.InitCrypto(cfg.SM3Salt, cfg.SM4Key) app := fiber.New(fiber.Config{ AppName: "XY SH API Server", ReadTimeout: cfg.ReadTimeout, WriteTimeout: cfg.WriteTimeout, }) router.SetupRoutes(app) log.Printf("服务启动,监听端口: %s", cfg.ServerPort) if err := app.Listen(":" + cfg.ServerPort); err != nil { log.Fatalf("服务启动失败: %v", err) } } ``` // File: xy_sh/internal/config/config.go ```go package config import ( "os" "time" ) type Config struct { ServerPort string SM3Salt string SM4Key string ReadTimeout time.Duration WriteTimeout time.Duration } func LoadConfig() *Config { port := getEnv("SERVER_PORT", "8080") sm3Salt := getEnv("SM3_SALT", "default_salt_change_me") sm4Key := getEnv("SM4_KEY", "default_key_16by") return &Config{ ServerPort: port, SM3Salt: sm3Salt, SM4Key: sm4Key, ReadTimeout: 30 * time.Second, WriteTimeout: 30 * time.Second, } } func getEnv(key, defaultValue string) string { if value := os.Getenv(key); value != "" { return value } return defaultValue } ``` // File: xy_sh/pkg/crypto/crypto.go ```go package crypto import ( "bytes" "crypto/cipher" "crypto/hmac" "crypto/rand" "encoding/base64" "encoding/hex" "errors" "fmt" "io" "github.com/tjfoc/gmsm/sm3" "github.com/tjfoc/gmsm/sm4" ) var ( sm3Salt []byte sm4Key []byte ) func InitCrypto(salt, key string) { sm3Salt = []byte(salt) keyBytes := []byte(key) if len(keyBytes) < 16 { padded := make([]byte, 16) copy(padded, keyBytes) sm4Key = padded } else if len(keyBytes) > 16 { sm4Key = keyBytes[:16] } else { sm4Key = keyBytes } } func SM3Hash(data []byte) string { h := sm3.New() h.Write(data) hashBytes := h.Sum(nil) return hex.EncodeToString(hashBytes) } func SM3HashWithSalt(data string) string { h := hmac.New(sm3.New, sm3Salt) h.Write([]byte(data)) return hex.EncodeToString(h.Sum(nil)) } func GenerateSign(timestamp, encryptedData string) string { signStr := timestamp + encryptedData return SM3HashWithSalt(signStr) } func VerifySign(timestamp, encryptedData, sign string) bool { expectedSign := GenerateSign(timestamp, encryptedData) return hmac.Equal([]byte(expectedSign), []byte(sign)) } func SM4CBCEncrypt(plaintext []byte) (string, error) { if len(sm4Key) != 16 { return "", fmt.Errorf("SM4密钥长度必须为16字节") } block, err := sm4.NewCipher(sm4Key) if err != nil { return "", fmt.Errorf("创建SM4 cipher失败: %v", err) } padded := pkcs7Padding(plaintext, block.BlockSize()) iv := make([]byte, block.BlockSize()) if _, err := io.ReadFull(rand.Reader, iv); err != nil { return "", fmt.Errorf("生成IV失败: %v", err) } mode := cipher.NewCBCEncrypter(block, iv) ciphertext := make([]byte, len(padded)) mode.CryptBlocks(ciphertext, padded) result := append(iv, ciphertext...) return base64.StdEncoding.EncodeToString(result), nil } func SM4CBCDecrypt(encryptedData string) ([]byte, error) { if len(sm4Key) != 16 { return nil, fmt.Errorf("SM4密钥长度必须为16字节") } data, err := base64.StdEncoding.DecodeString(encryptedData) if err != nil { return nil, fmt.Errorf("base64解码失败: %v", err) } block, err := sm4.NewCipher(sm4Key) if err != nil { return nil, fmt.Errorf("创建SM4 cipher失败: %v", err) } blockSize := block.BlockSize() if len(data) < blockSize { return nil, errors.New("数据长度不足") } iv := data[:blockSize] ciphertext := data[blockSize:] if len(ciphertext)%blockSize != 0 { return nil, errors.New("密文长度不是块大小的整数倍") } mode := cipher.NewCBCDecrypter(block, iv) plaintext := make([]byte, len(ciphertext)) mode.CryptBlocks(plaintext, ciphertext) plaintext, err = pkcs7UnPadding(plaintext) if err != nil { return nil, fmt.Errorf("去除填充失败: %v", err) } return plaintext, nil } func pkcs7Padding(data []byte, blockSize int) []byte { padding := blockSize - len(data)%blockSize padText := bytes.Repeat([]byte{byte(padding)}, padding) return append(data, padText...) } func pkcs7UnPadding(data []byte) ([]byte, error) { length := len(data) if length == 0 { return nil, errors.New("数据为空") } padding := int(data[length-1]) if padding > length || padding == 0 { return nil, errors.New("无效的填充") } for i := length - padding; i < length; i++ { if data[i] != byte(padding) { return nil, errors.New("无效的填充") } } return data[:length-padding], nil } ``` // File: xy_sh/internal/entities/request.go ```go package entities type EncryptedRequest struct { EncryptedData string `json:"encryptedData"` } type OrderRequest struct { ActCode string `json:"actCode"` GoodsCode string `json:"goodsCode"` ActOrderNum string `json:"actOrderNum"` Account string `json:"account,omitempty"` CallbackUrl string `json:"callbackUrl,omitempty"` } type QueryOrderRequest struct { ActCode string `json:"actCode"` OrderNo string `json:"orderNo"` } type WxRechargeRequest struct { ActCode string `json:"actCode"` OrderNo string `json:"orderNo"` GoodsCode string `json:"goodsCode"` ActOrderNum string `json:"actOrderNum"` AppId string `json:"appId"` OpenId string `json:"openId"` CallbackUrl string `json:"callbackUrl,omitempty"` } type CallbackRequest struct { OrderNo string `json:"orderNo"` ActOrderNum string `json:"actOrderNum"` Status int `json:"status"` Account string `json:"account,omitempty"` CardInfo *CardInfo `json:"cardInfo,omitempty"` CouponId string `json:"couponId,omitempty"` } type CardInfo struct { CouponNo string `json:"couponNo"` CouponCode string `json:"couponCode"` ExpireTime string `json:"expireTime"` } ``` // File: xy_sh/internal/entities/response.go ```go package entities type CommonResponse struct { Code int `json:"code"` Msg string `json:"msg"` Data interface{} `json:"data"` } type OrderResponseData struct { OrderNo string `json:"orderNo"` CouponNo string `json:"couponNo,omitempty"` CouponCode string `json:"couponCode,omitempty"` Status int `json:"status"` ExpireTime string `json:"expireTime,omitempty"` } type QueryOrderResponseData struct { OrderNo string `json:"orderNo"` Status int `json:"status"` Account string `json:"account,omitempty"` CardInfo *CardInfo `json:"cardInfo,omitempty"` CouponId string `json:"couponId,omitempty"` } type WxRechargeResponseData struct { OrderNo string `json:"orderNo"` Status int `json:"status"` CouponId string `json:"couponId,omitempty"` } const ( StatusSending = -1 StatusSuccess = 0 StatusFailed = 1 StatusRedeemed = 2 StatusExpired = 3 ) const ( CodeSuccess = 0 CodeFailed = -1 ) ``` // File: xy_sh/internal/middleware/middleware.go ```go package middleware import ( "encoding/json" "xy_sh/internal/entities" "xy_sh/pkg/crypto" "github.com/gofiber/fiber/v2" ) func SignVerifyMiddleware() fiber.Handler { return func(c *fiber.Ctx) error { timestamp := c.Get("timestamp") sign := c.Get("sign") if timestamp == "" || sign == "" { return c.Status(fiber.StatusUnauthorized).JSON(entities.CommonResponse{ Code: entities.CodeFailed, Msg: "缺少timestamp或sign请求头", Data: nil, }) } body := c.Body() var req entities.EncryptedRequest if err := json.Unmarshal(body, &req); err != nil { return c.Status(fiber.StatusBadRequest).JSON(entities.CommonResponse{ Code: entities.CodeFailed, Msg: "请求体格式错误", Data: nil, }) } if req.EncryptedData == "" { return c.Status(fiber.StatusBadRequest).JSON(entities.CommonResponse{ Code: entities.CodeFailed, Msg: "缺少encryptedData参数", Data: nil, }) } if !crypto.VerifySign(timestamp, req.EncryptedData, sign) { return c.Status(fiber.StatusUnauthorized).JSON(entities.CommonResponse{ Code: entities.CodeFailed, Msg: "签名验证失败", Data: nil, }) } decryptedData, err := crypto.SM4CBCDecrypt(req.EncryptedData) if err != nil { return c.Status(fiber.StatusBadRequest).JSON(entities.CommonResponse{ Code: entities.CodeFailed, Msg: "数据解密失败: " + err.Error(), Data: nil, }) } c.Locals("decryptedData", decryptedData) c.Locals("timestamp", timestamp) return c.Next() } } func EncryptResponseMiddleware() fiber.Handler { return func(c *fiber.Ctx) error { err := c.Next() if err != nil { return err } body := c.Response().Body() if len(body) == 0 { return nil } var resp entities.CommonResponse if err := json.Unmarshal(body, &resp); err != nil { return nil } if resp.Data != nil { dataBytes, err := json.Marshal(resp.Data) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(entities.CommonResponse{ Code: entities.CodeFailed, Msg: "响应数据序列化失败", Data: nil, }) } encryptedData, err := crypto.SM4CBCEncrypt(dataBytes) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(entities.CommonResponse{ Code: entities.CodeFailed, Msg: "响应数据加密失败", Data: nil, }) } resp.Data = encryptedData newBody, err := json.Marshal(resp) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(entities.CommonResponse{ Code: entities.CodeFailed, Msg: "响应序列化失败", Data: nil, }) } c.Response().SetBody(newBody) } return nil } } ``` // File: xy_sh/internal/biz/biz.go ```go package biz import ( "crypto/rand" "encoding/hex" "errors" "io" "strconv" "sync" "time" "xy_sh/internal/entities" ) type OrderStore struct { mu sync.RWMutex orders map[string]*OrderInfo } type OrderInfo struct { OrderNo string ActOrderNum string GoodsCode string ActCode string Account string Status int CouponNo string CouponCode string ExpireTime string CouponId string AppId string OpenId string CreateTime time.Time UpdateTime time.Time } var store *OrderStore func init() { store = &OrderStore{ orders: make(map[string]*OrderInfo), } } func CreateOrder(req *entities.OrderRequest) (*entities.OrderResponseData, error) { store.mu.Lock() defer store.mu.Unlock() orderNo := generateOrderNo() orderInfo := &OrderInfo{ OrderNo: orderNo, ActOrderNum: req.ActOrderNum, GoodsCode: req.GoodsCode, ActCode: req.ActCode, Account: req.Account, Status: entities.StatusSuccess, CreateTime: time.Now(), UpdateTime: time.Now(), } orderInfo.CouponNo = generateCouponNo() orderInfo.CouponCode = orderInfo.CouponNo orderInfo.ExpireTime = time.Now().AddDate(1, 0, 0).Format("2006-01-02 15:04:05") store.orders[orderNo] = orderInfo return &entities.OrderResponseData{ OrderNo: orderNo, CouponNo: orderInfo.CouponNo, CouponCode: orderInfo.CouponCode, Status: orderInfo.Status, ExpireTime: orderInfo.ExpireTime, }, nil } func QueryOrder(req *entities.QueryOrderRequest) (*entities.QueryOrderResponseData, error) { store.mu.RLock() defer store.mu.RUnlock() orderInfo, exists := store.orders[req.OrderNo] if !exists { return nil, errors.New("订单不存在") } resp := &entities.QueryOrderResponseData{ OrderNo: orderInfo.OrderNo, Status: orderInfo.Status, Account: orderInfo.Account, CouponId: orderInfo.CouponId, } if orderInfo.CouponNo != "" { resp.CardInfo = &entities.CardInfo{ CouponNo: orderInfo.CouponNo, CouponCode: orderInfo.CouponCode, ExpireTime: orderInfo.ExpireTime, } } return resp, nil } func WxRecharge(req *entities.WxRechargeRequest) (*entities.WxRechargeResponseData, error) { store.mu.Lock() defer store.mu.Unlock() if orderInfo, exists := store.orders[req.OrderNo]; exists { return &entities.WxRechargeResponseData{ OrderNo: orderInfo.OrderNo, Status: orderInfo.Status, CouponId: orderInfo.CouponId, }, nil } orderNo := req.OrderNo orderInfo := &OrderInfo{ OrderNo: orderNo, ActOrderNum: req.ActOrderNum, GoodsCode: req.GoodsCode, ActCode: req.ActCode, AppId: req.AppId, OpenId: req.OpenId, Status: entities.StatusSuccess, CouponId: generateCouponId(), CreateTime: time.Now(), UpdateTime: time.Now(), } store.orders[orderNo] = orderInfo return &entities.WxRechargeResponseData{ OrderNo: orderNo, Status: orderInfo.Status, CouponId: orderInfo.CouponId, }, nil } func HandleCallback(req *entities.CallbackRequest) error { store.mu.Lock() defer store.mu.Unlock() orderInfo, exists := store.orders[req.OrderNo] if !exists { orderInfo = &OrderInfo{ OrderNo: req.OrderNo, ActOrderNum: req.ActOrderNum, CreateTime: time.Now(), } store.orders[req.OrderNo] = orderInfo } orderInfo.Status = req.Status orderInfo.Account = req.Account orderInfo.CouponId = req.CouponId orderInfo.UpdateTime = time.Now() if req.CardInfo != nil { orderInfo.CouponNo = req.CardInfo.CouponNo orderInfo.CouponCode = req.CardInfo.CouponCode orderInfo.ExpireTime = req.CardInfo.ExpireTime } return nil } func generateOrderNo() string { return "HM" + strconv.FormatInt(time.Now().UnixMilli(), 10) + randomString(16) } func generateCouponNo() string { return randomString(16) } func generateCouponId() string { return randomString(8) } func randomString(n int) string { b := make([]byte, (n+1)/2) if _, err := io.ReadFull(rand.Reader, b); err != nil { panic(err) } return hex.EncodeToString(b)[:n] } ``` // File: xy_sh/internal/service/service.go ```go package service import ( "encoding/json" "xy_sh/internal/biz" "xy_sh/internal/entities" "github.com/gofiber/fiber/v2" ) func CreateOrderHandler(c *fiber.Ctx) error { decryptedData := c.Locals("decryptedData").([]byte) var req entities.OrderRequest if err := json.Unmarshal(decryptedData, &req); err != nil { return c.Status(fiber.StatusBadRequest).JSON(entities.CommonResponse{ Code: entities.CodeFailed, Msg: "业务参数解析失败: " + err.Error(), Data: nil, }) } if req.ActCode == "" || req.GoodsCode == "" || req.ActOrderNum == "" { return c.Status(fiber.StatusBadRequest).JSON(entities.CommonResponse{ Code: entities.CodeFailed, Msg: "缺少必填参数: actCode, goodsCode, actOrderNum", Data: nil, }) } result, err := biz.CreateOrder(&req) if err != nil { return c.Status(fiber.StatusOK).JSON(entities.CommonResponse{ Code: entities.CodeFailed, Msg: err.Error(), Data: nil, }) } return c.Status(fiber.StatusOK).JSON(entities.CommonResponse{ Code: entities.CodeSuccess, Msg: "请求成功", Data: result, }) } func QueryOrderHandler(c *fiber.Ctx) error { decryptedData := c.Locals("decryptedData").([]byte) var req entities.QueryOrderRequest if err := json.Unmarshal(decryptedData, &req); err != nil { return c.Status(fiber.StatusBadRequest).JSON(entities.CommonResponse{ Code: entities.CodeFailed, Msg: "业务参数解析失败: " + err.Error(), Data: nil, }) } if req.ActCode == "" || req.OrderNo == "" { return c.Status(fiber.StatusBadRequest).JSON(entities.CommonResponse{ Code: entities.CodeFailed, Msg: "缺少必填参数: actCode, orderNo", Data: nil, }) } result, err := biz.QueryOrder(&req) if err != nil { return c.Status(fiber.StatusOK).JSON(entities.CommonResponse{ Code: entities.CodeFailed, Msg: err.Error(), Data: nil, }) } return c.Status(fiber.StatusOK).JSON(entities.CommonResponse{ Code: entities.CodeSuccess, Msg: "请求成功", Data: result, }) } func WxRechargeHandler(c *fiber.Ctx) error { decryptedData := c.Locals("decryptedData").([]byte) var req entities.WxRechargeRequest if err := json.Unmarshal(decryptedData, &req); err != nil { return c.Status(fiber.StatusBadRequest).JSON(entities.CommonResponse{ Code: entities.CodeFailed, Msg: "业务参数解析失败: " + err.Error(), Data: nil, }) } if req.ActCode == "" || req.OrderNo == "" || req.GoodsCode == "" || req.ActOrderNum == "" || req.AppId == "" || req.OpenId == "" { return c.Status(fiber.StatusBadRequest).JSON(entities.CommonResponse{ Code: entities.CodeFailed, Msg: "缺少必填参数: actCode, orderNo, goodsCode, actOrderNum, appId, openId", Data: nil, }) } result, err := biz.WxRecharge(&req) if err != nil { return c.Status(fiber.StatusOK).JSON(entities.CommonResponse{ Code: entities.CodeFailed, Msg: err.Error(), Data: nil, }) } return c.Status(fiber.StatusOK).JSON(entities.CommonResponse{ Code: entities.CodeSuccess, Msg: "请求成功", Data: result, }) } func CallbackHandler(c *fiber.Ctx) error { decryptedData := c.Locals("decryptedData").([]byte) var req entities.CallbackRequest if err := json.Unmarshal(decryptedData, &req); err != nil { return c.Status(fiber.StatusBadRequest).SendString("参数解析失败") } if req.OrderNo == "" || req.ActOrderNum == "" { return c.Status(fiber.StatusBadRequest).SendString("缺少必填参数") } if err := biz.HandleCallback(&req); err != nil { return c.Status(fiber.StatusInternalServerError).SendString("处理失败") } return c.Status(fiber.StatusOK).SendString("ok") } func HealthHandler(c *fiber.Ctx) error { return c.Status(fiber.StatusOK).JSON(fiber.Map{ "status": "ok", "message": "服务运行正常", }) } ``` // File: xy_sh/internal/router/router.go ```go package router import ( "xy_sh/internal/middleware" "xy_sh/internal/service" "github.com/gofiber/fiber/v2" ) func SetupRoutes(app *fiber.App) { app.Get("/health", service.HealthHandler) app.Get("/", service.HealthHandler) v1 := app.Group("/api/v1") v1.Post("/order/create", middleware.SignVerifyMiddleware(), middleware.EncryptResponseMiddleware(), service.CreateOrderHandler, ) v1.Post("/order/query", middleware.SignVerifyMiddleware(), middleware.EncryptResponseMiddleware(), service.QueryOrderHandler, ) v1.Post("/wx/recharge", middleware.SignVerifyMiddleware(), middleware.EncryptResponseMiddleware(), service.WxRechargeHandler, ) v1.Post("/callback/notify", middleware.SignVerifyMiddleware(), service.CallbackHandler, ) } ``` // File: xy_sh/internal/test/example_test.go ```go package test import ( "bytes" "encoding/json" "io" "net/http" "net/http/httptest" "strconv" "testing" "time" "xy_sh/internal/entities" "xy_sh/internal/router" "xy_sh/pkg/crypto" "github.com/gofiber/fiber/v2" ) func init() { crypto.InitCrypto("test_salt_12345", "test_key_16bytes") } func setupTestApp() *fiber.App { app := fiber.New() router.SetupRoutes(app) return app } func makeEncryptedRequest(t *testing.T, app *fiber.App, path string, bizData interface{}) (*http.Response, []byte) { t.Helper() bizJSON, err := json.Marshal(bizData) if err != nil { t.Fatalf("业务数据序列化失败: %v", err) } encryptedData, err := crypto.SM4CBCEncrypt(bizJSON) if err != nil { t.Fatalf("加密失败: %v", err) } timestamp := strconv.FormatInt(time.Now().UnixMilli(), 10) sign := crypto.GenerateSign(timestamp, encryptedData) reqBody := entities.EncryptedRequest{ EncryptedData: encryptedData, } reqJSON, _ := json.Marshal(reqBody) req := httptest.NewRequest(http.MethodPost, path, bytes.NewReader(reqJSON)) req.Header.Set("Content-Type", "application/json") req.Header.Set("timestamp", timestamp) req.Header.Set("sign", sign) resp, err := app.Test(req) if err != nil { t.Fatalf("请求失败: %v", err) } body, _ := io.ReadAll(resp.Body) resp.Body.Close() return resp, body } func TestCreateOrder(t *testing.T) { app := setupTestApp() bizData := entities.OrderRequest{ ActCode: "ACT001", GoodsCode: "123456", ActOrderNum: "TEST_ORDER_001", Account: "19912345678", CallbackUrl: "https://example.com/callback", } resp, body := makeEncryptedRequest(t, app, "/api/v1/order/create", bizData) if resp.StatusCode != http.StatusOK { t.Errorf("期望状态码200,实际: %d, 响应: %s", resp.StatusCode, string(body)) } var commonResp entities.CommonResponse if err := json.Unmarshal(body, &commonResp); err != nil { t.Fatalf("响应解析失败: %v, 响应: %s", err, string(body)) } if commonResp.Code != entities.CodeSuccess { t.Errorf("期望code=0,实际: %d, msg: %s", commonResp.Code, commonResp.Msg) } t.Logf("下单接口测试成功,响应: %s", string(body)) } func TestQueryOrder(t *testing.T) { app := setupTestApp() createBizData := entities.OrderRequest{ ActCode: "ACT001", GoodsCode: "123456", ActOrderNum: "TEST_ORDER_002", Account: "19912345678", } _, createBody := makeEncryptedRequest(t, app, "/api/v1/order/create", createBizData) var createResp entities.CommonResponse if err := json.Unmarshal(createBody, &createResp); err != nil { t.Fatalf("创建订单响应解析失败: %v", err) } if createResp.Code != entities.CodeSuccess { t.Fatalf("创建订单失败: %s", createResp.Msg) } encryptedData := createResp.Data.(string) decryptedData, err := crypto.SM4CBCDecrypt(encryptedData) if err != nil { t.Fatalf("解密创建订单响应失败: %v", err) } var orderResp entities.OrderResponseData if err := json.Unmarshal(decryptedData, &orderResp); err != nil { t.Fatalf("解析创建订单业务数据失败: %v", err) } queryBizData := entities.QueryOrderRequest{ ActCode: "ACT001", OrderNo: orderResp.OrderNo, } resp, body := makeEncryptedRequest(t, app, "/api/v1/order/query", queryBizData) if resp.StatusCode != http.StatusOK { t.Errorf("期望状态码200,实际: %d, 响应: %s", resp.StatusCode, string(body)) } var queryResp entities.CommonResponse if err := json.Unmarshal(body, &queryResp); err != nil { t.Fatalf("查询响应解析失败: %v", err) } if queryResp.Code != entities.CodeSuccess { t.Errorf("查询失败,code: %d, msg: %s", queryResp.Code, queryResp.Msg) } t.Logf("查询接口测试成功,响应: %s", string(body)) } func TestWxRecharge(t *testing.T) { app := setupTestApp() bizData := entities.WxRechargeRequest{ ActCode: "FBG6vdYqGE4mGX7EH/woEg==", OrderNo: "WX_TEST_ORDER_001", GoodsCode: "0001", ActOrderNum: "WX_ORDER_001", AppId: "wx1234567890", OpenId: "oABC1234567890", } resp, body := makeEncryptedRequest(t, app, "/api/v1/wx/recharge", bizData) if resp.StatusCode != http.StatusOK { t.Errorf("期望状态码200,实际: %d, 响应: %s", resp.StatusCode, string(body)) } var commonResp entities.CommonResponse if err := json.Unmarshal(body, &commonResp); err != nil { t.Fatalf("响应解析失败: %v", err) } if commonResp.Code != entities.CodeSuccess { t.Errorf("期望code=0,实际: %d, msg: %s", commonResp.Code, commonResp.Msg) } t.Logf("微信立减金充值接口测试成功,响应: %s", string(body)) } func TestCallback(t *testing.T) { app := setupTestApp() bizData := entities.CallbackRequest{ OrderNo: "HM202509291010001", ActOrderNum: "XY2025092910100001", Status: entities.StatusExpired, Account: "19912345678", } resp, body := makeEncryptedRequest(t, app, "/api/v1/callback/notify", bizData) if resp.StatusCode != http.StatusOK { t.Errorf("期望状态码200,实际: %d, 响应: %s", resp.StatusCode, string(body)) } if string(body) != "ok" { t.Errorf("期望响应'ok',实际: %s", string(body)) } t.Logf("回调通知接口测试成功") } func TestInvalidSign(t *testing.T) { app := setupTestApp() bizData := entities.OrderRequest{ ActCode: "ACT001", GoodsCode: "123456", ActOrderNum: "TEST_ORDER_003", } bizJSON, _ := json.Marshal(bizData) encryptedData, _ := crypto.SM4CBCEncrypt(bizJSON) timestamp := strconv.FormatInt(time.Now().UnixMilli(), 10) wrongSign := "wrong_signature_12345" reqBody := entities.EncryptedRequest{EncryptedData: encryptedData} reqJSON, _ := json.Marshal(reqBody) req := httptest.NewRequest(http.MethodPost, "/api/v1/order/create", bytes.NewReader(reqJSON)) req.Header.Set("Content-Type", "application/json") req.Header.Set("timestamp", timestamp) req.Header.Set("sign", wrongSign) resp, _ := app.Test(req) if resp.StatusCode != http.StatusUnauthorized { t.Errorf("期望状态码401(签名验证失败),实际: %d", resp.StatusCode) } t.Logf("签名验证失败测试成功") } func TestHealthCheck(t *testing.T) { app := setupTestApp() req := httptest.NewRequest(http.MethodGet, "/health", nil) resp, body := app.Test(req) if resp.StatusCode != http.StatusOK { t.Errorf("期望状态码200,实际: %d", resp.StatusCode) } t.Logf("健康检查接口测试成功,响应: %s", string(body)) } ```