package controller import ( "errors" "net/http" "strconv" "github.com/gin-gonic/gin" "go.uber.org/zap" "github.com/topfans/backend/gateway/dto" "github.com/topfans/backend/gateway/pkg/response" "github.com/topfans/backend/pkg/logger" "github.com/topfans/backend/services/assetService/service" ) // GetVerificationByHash GET /api/v1/peripheral-info/by-hash/:hash?sign=xxx // // stage 1 唯一验真接口(详见 §5.1.3/§5.1.5):按加密 code(code_hash)验真 // - path 参数:encrypted_code(32 hex) // - query 参数:sign(32 hex,HMAC 签名,第二道防线) // - 验签失败返 50013,前端引导用户重新扫码 func (ctrl *PeripheralController) GetVerificationByHash(c *gin.Context) { codeHash := c.Param("hash") sign := c.Query("sign") if codeHash == "" { response.Error(c, http.StatusBadRequest, "code_hash 必填") return } if sign == "" { response.Error(c, http.StatusBadRequest, "sign 必填") return } result, err := ctrl.svc.GetVerificationByHash(c.Request.Context(), codeHash, sign) if err != nil { ctrl.handleServiceError(c, err) return } response.Success(c, ctrl.toVerificationDTO(result)) } // toVerificationDTO *VerificationResult → *dto.VerificationData 转换 helper func (ctrl *PeripheralController) toVerificationDTO(result *service.VerificationResult) *dto.VerificationData { return &dto.VerificationData{ AssetID: result.AssetID, Code: result.Code, Company: result.Company, Hash: result.Hash, VerifyCount: result.VerifyCount, Brand: result.Brand, Image: result.Image, Verifier: result.Verifier, VerifiedAt: result.VerifiedAt, MaterialType: result.MaterialType, } } // PeripheralController 周边验真 + 加入藏品 controller type PeripheralController struct { svc *service.PeripheralService } // NewPeripheralController 构造 PeripheralController func NewPeripheralController(svc *service.PeripheralService) *PeripheralController { return &PeripheralController{svc: svc} } // GetVerification GET /api/v1/assets/:asset_id/verification // // H5 调用不强制 JWT(spec §4.3);app 内自带 JWT 但本端点不强校验, // 故 handler 不读取 c.Get("user_id")。 func (ctrl *PeripheralController) GetVerification(c *gin.Context) { assetID, err := strconv.ParseInt(c.Param("asset_id"), 10, 64) if err != nil || assetID <= 0 { response.Error(c, http.StatusBadRequest, "asset_id 格式错误") return } result, err := ctrl.svc.GetVerification(c.Request.Context(), assetID) if err != nil { ctrl.handleServiceError(c, err) return } response.Success(c, &dto.VerificationData{ AssetID: result.AssetID, Code: result.Code, Company: result.Company, Hash: result.Hash, VerifyCount: result.VerifyCount, Brand: result.Brand, Image: result.Image, Verifier: result.Verifier, VerifiedAt: result.VerifiedAt, MaterialType: result.MaterialType, }) } // MintFromPeripheralByHash POST /api/v1/peripheral-info/mint-by-hash/:hash?sign=xxx // // stage 1 唯一加入藏品接口(详见 §5.1.5):按加密 code(code_hash)加入藏品 // - path 参数:encrypted_code(32 hex) // - query 参数:sign(32 hex,HMAC 签名) // - 强制 JWT func (ctrl *PeripheralController) MintFromPeripheralByHash(c *gin.Context) { codeHash := c.Param("hash") sign := c.Query("sign") if codeHash == "" { response.Error(c, http.StatusBadRequest, "code_hash 必填") return } if sign == "" { response.Error(c, http.StatusBadRequest, "sign 必填") return } userID, exists := c.Get("user_id") if !exists { response.Error(c, http.StatusUnauthorized, "未登录") return } ownerUID, ok := userID.(int64) if !ok { response.Error(c, http.StatusInternalServerError, "user_id 类型错误") return } result, err := ctrl.svc.MintFromPeripheralByHash(c.Request.Context(), ownerUID, codeHash, sign) if err != nil { ctrl.handleServiceError(c, err) return } response.Success(c, &dto.MintData{ InstanceID: result.InstanceID, AssetID: result.AssetID, MintedAt: result.MintedAt, CoverImage: result.CoverImage, }) } // handleServiceError 统一错误处理 // - BizError → response.ErrorWithCode(code, message) 透传业务码 // - 其他 → 500 服务繁忙,记录 ERROR 日志 func (ctrl *PeripheralController) handleServiceError(c *gin.Context, err error) { var bizErr *service.BizError if errors.As(err, &bizErr) { response.ErrorWithCode(c, bizErr.Code, bizErr.Message) return } logger.Logger.Error("peripheral service error", zap.Error(err)) response.Error(c, http.StatusInternalServerError, "服务繁忙") }