package service import ( "context" "encoding/json" "strconv" "go-backend-template/internal/client" "go-backend-template/internal/contract" ) type DukcapilService interface { FaceMatch(ctx context.Context, req *contract.FaceMatchRequest) (*contract.FaceMatchResponse, error) } type DukcapilServiceImpl struct { client *client.DukcapilClient } func NewDukcapilService(c *client.DukcapilClient) *DukcapilServiceImpl { return &DukcapilServiceImpl{client: c} } func (s *DukcapilServiceImpl) FaceMatch(ctx context.Context, req *contract.FaceMatchRequest) (*contract.FaceMatchResponse, error) { upstream, err := s.client.FaceMatch(ctx, req) if err != nil { return nil, err } matches := parseFaceMatches(upstream.Response) return &contract.FaceMatchResponse{ TID: upstream.TID, ErrorCode: upstream.ErrorCode, Error: upstream.Error, RequestType: upstream.RequestType, Threshold: upstream.FaceThreshold, MaxResults: upstream.MaxResults, Matches: matches, Raw: upstream, }, nil } // parseFaceMatches decodes the nested string field // `{"face":{"FACE_T5":{"":, ...}}}` into a slice of results. func parseFaceMatches(raw string) []contract.FaceMatchResult { if raw == "" { return nil } var envelope struct { Face map[string]map[string]json.Number `json:"face"` } if err := json.Unmarshal([]byte(raw), &envelope); err != nil { return nil } results := make([]contract.FaceMatchResult, 0) for _, group := range envelope.Face { for nik, scoreNum := range group { score, err := strconv.ParseFloat(scoreNum.String(), 64) if err != nil { continue } results = append(results, contract.FaceMatchResult{NIK: nik, Score: score}) } } return results } // Compile-time assertion. var _ DukcapilService = (*DukcapilServiceImpl)(nil)