package qiyuesuosdk import ( "encoding/json" "fmt" "net/url" "strconv" "strings" "time" "git.sxidc.com/student-physical-examination/contract_lock_sdk/model/common" user_request "git.sxidc.com/student-physical-examination/contract_lock_sdk/model/user/request" v2auth_request "git.sxidc.com/student-physical-examination/contract_lock_sdk/model/v2auth/request" v2auth_response "git.sxidc.com/student-physical-examination/contract_lock_sdk/model/v2auth/response" ) type userDetailResult struct { ID flexStringID `json:"id"` BizID flexStringID `json:"bizId"` Name string `json:"name"` Mobile string `json:"mobile"` Status string `json:"status"` } func (r userDetailResult) toUserInfo() *UserInfo { info := &UserInfo{ ID: r.ID.String(), BizID: r.BizID.String(), Name: r.Name, Mobile: r.Mobile, Status: r.Status, } if info.ID == "" && info.BizID == "" && info.Name == "" { return nil } return info } func (p UserLocateParams) toUserQuery() (url.Values, error) { q := url.Values{} switch { case p.UserID != "": q.Set("id", p.UserID) case p.Mobile != "": q.Set("mobile", p.Mobile) case p.CardNo != "": q.Set("cardNo", p.CardNo) case p.OpenUserID != "": q.Set("bizId", p.OpenUserID) case p.AccountNo != "": q.Set("accountNo", p.AccountNo) default: return nil, ErrInvalidParams } return q, nil } func (p UserLocateParams) toUserInfoRequest() (*common.UserInfoRequest, error) { req := &common.UserInfoRequest{} switch { case p.UserID != "": id, err := strconv.ParseInt(p.UserID, 10, 64) if err != nil { return nil, ErrInvalidParams } req.UserId = &id case p.Mobile != "": req.Mobile = p.Mobile case p.CardNo != "": req.CardNo = p.CardNo case p.OpenUserID != "": req.OpenUserId = p.OpenUserID case p.AccountNo != "": req.AccountNo = p.AccountNo default: return nil, ErrInvalidParams } return req, nil } func isUserNotFound(err error) bool { if err == nil { return false } msg := err.Error() return strings.Contains(msg, "用户不存在") || strings.Contains(msg, "未查询到用户") || (strings.Contains(msg, "找不到") && strings.Contains(msg, "的用户")) } // FindUser 按手机号、证件号、三方用户 id 或登录账号查询契约锁用户;不存在时返回 nil。 func (c *Client) FindUser(p UserLocateParams) (*UserInfo, error) { q, err := p.toUserQuery() if err != nil { return nil, err } var resp struct { apiResponse Result userDetailResult `json:"result"` } if err = c.getQuery("/user", q, &resp); err != nil { return nil, err } if err = resp.err(); err != nil { if isUserNotFound(err) { return nil, nil } return nil, err } return resp.Result.toUserInfo(), nil } // FindUserByAccountNo 按 accountNo(通常等于 openUserId)查询用户。 func (c *Client) FindUserByAccountNo(accountNo string) (*UserInfo, error) { return c.FindUser(UserLocateParams{AccountNo: accountNo}) } // CreateInternalUser 在契约锁创建内部用户。 func (c *Client) CreateInternalUser(p CreateUserParams) error { req := user_request.UserV2CreateRequest{ Name: p.Name, OpenUserId: p.OpenUserID, AccountNo: p.AccountNo, Password: p.Password, Mobile: p.Mobile, } if req.AccountNo == "" { req.AccountNo = p.OpenUserID } var resp apiResponse if err := c.postJSON("/user/v2/create", req, &resp); err != nil { return err } return resp.err() } // CreateInternalUserSkipMobileConflict 创建用户;手机号冲突时去掉手机号重试(旧系统行为)。 func (c *Client) CreateInternalUserSkipMobileConflict(p CreateUserParams) error { err := c.CreateInternalUser(p) if err == nil { return nil } if p.Mobile != "" && strings.Contains(err.Error(), "手机号已被其他用户绑定") { p.Mobile = "" return c.CreateInternalUser(p) } return err } // UserSignSilentURL 获取个人签名静默授权页;authEnd 为空则默认 10 个月。 // applyCompany 传入当前体检机构(openCompanyId 通常等于 tenant_id),授权记录按机构维度拆分。 func (c *Client) UserSignSilentURL(openUserID string, applyCompany ApplyCompanyParams, authEnd time.Time, completeToPage string) (string, error) { if authEnd.IsZero() { authEnd = time.Now().Add(DefaultPersonalAuthDuration) } timeEditable := false req := v2auth_request.V2AuthPersonalsignsilentUrlRequest{ AuthUser: &common.SilentUserRequest{OpenUserId: openUserID}, AuthEndDate: authEnd.Format("2006-01-02"), AuthTimeModifiable: &timeEditable, AuthorizedMode: []string{"FACEAUTH", "PINAUTH"}, CompleteToPage: completeToPage, } if applyCompany.CompanyID != "" || applyCompany.OpenCompanyID != "" || applyCompany.Name != "" || applyCompany.RegisterNo != "" { pac := &common.PersonalApplyCompany{ Name: applyCompany.Name, RegisterNo: applyCompany.RegisterNo, OpenCompanyId: applyCompany.OpenCompanyID, } if applyCompany.CompanyID != "" { if id, err := strconv.ParseInt(applyCompany.CompanyID, 10, 64); err == nil { pac.Id = &id } } req.ApplyCompany = pac } var resp struct { apiResponse Result v2auth_response.V2AuthPersonalsignsilentUrlResponse `json:"result"` } if err := c.postJSON("/v2/auth/personalsignsilent/url", req, &resp); err != nil { return "", err } if err := resp.err(); err != nil { return "", err } return resp.Result.Url, nil } // QueryPersonalSignAuthRecords 查询个人静默授权记录。 func (c *Client) QueryPersonalSignAuthRecords(openUserID, status string) ([]AuthRecord, error) { return c.QueryPersonalSignAuthRecordsFor(UserLocateParams{OpenUserID: openUserID}, status) } func (c *Client) QueryPersonalSignAuthRecordsFor(user UserLocateParams, status string) ([]AuthRecord, error) { authUser, err := user.toUserInfoRequest() if err != nil { return nil, err } req := v2auth_request.V2AuthSignsilentRecordRequest{ AuthUser: authUser, Status: status, } var resp struct { apiResponse Result []v2auth_response.V2AuthSignsilentRecordResponse `json:"result"` } if err := c.postJSON("/v2/auth/signSilent/record", req, &resp); err != nil { return nil, err } if err := resp.err(); err != nil { if IsPersonalSignAuthRecordNotFound(err) || isUserNotFound(err) { return []AuthRecord{}, nil } return nil, err } out := make([]AuthRecord, 0, len(resp.Result)) for _, item := range resp.Result { out = append(out, authRecordFromResponse(item)) } return out, nil } func authRecordFromResponse(item v2auth_response.V2AuthSignsilentRecordResponse) AuthRecord { rec := AuthRecord{ Status: item.Status, EndTime: item.AuthEndDate, AuthScope: item.AuthScope, } if item.ApplyCompany != nil { rec.ApplyCompanyID = item.ApplyCompany.Id rec.ApplyCompanyOpenCompanyID = item.ApplyCompany.OpenCompanyId rec.ApplyCompanyName = item.ApplyCompany.Name rec.ApplyCompanyRegisterNo = item.ApplyCompany.RegisterNo } return rec } // PersonalSignAuthRecordForCompany 查询 openUserId 对指定法人单位的静默授权记录。 func (c *Client) PersonalSignAuthRecordForCompany(openUserID string, company ApplyCompanyParams, status string) (*AuthRecord, bool, error) { records, err := c.QueryPersonalSignAuthRecords(openUserID, status) if err != nil { return nil, false, err } if rec, ok := findAuthRecordForCompany(records, company, status); ok { return rec, true, nil } if status != "" { all, err := c.QueryPersonalSignAuthRecords(openUserID, "") if err != nil { return nil, false, err } if rec, ok := findAuthRecordForCompany(all, company, status); ok { return rec, true, nil } } return nil, false, nil } func findAuthRecordForCompany(records []AuthRecord, company ApplyCompanyParams, wantStatus string) (*AuthRecord, bool) { for i := range records { if wantStatus != "" && records[i].Status != wantStatus { continue } if authRecordMatchesCompany(&records[i], company) { return &records[i], true } } return nil, false } func authRecordMatchesCompany(rec *AuthRecord, company ApplyCompanyParams) bool { if company.CompanyID != "" && rec.ApplyCompanyID != "" && company.CompanyID == rec.ApplyCompanyID { return true } if company.OpenCompanyID != "" && rec.ApplyCompanyOpenCompanyID != "" && company.OpenCompanyID == rec.ApplyCompanyOpenCompanyID { return true } if company.RegisterNo != "" && rec.ApplyCompanyRegisterNo != "" && company.RegisterNo == rec.ApplyCompanyRegisterNo { return true } return false } // IsUserSignAuthorized 是否存在任意有效个人静默授权。 func (c *Client) IsUserSignAuthorized(openUserID string) (bool, error) { records, err := c.QueryPersonalSignAuthRecords(openUserID, "EFFECT") if err != nil { return false, err } return len(records) > 0, nil } // IsUserSignAuthorizedForCompany 是否已对指定法人单位完成有效个人静默授权。 func (c *Client) IsUserSignAuthorizedForCompany(openUserID string, company ApplyCompanyParams) (bool, error) { _, found, err := c.PersonalSignAuthRecordForCompany(openUserID, company, "EFFECT") return found, err } // structFormFields 将 struct 转为 multipart 表单字段(与旧系统 HttpMultipart 行为一致)。 func structFormFields(v any, arrayConnector string) (map[string]string, error) { b, err := json.Marshal(v) if err != nil { return nil, err } var raw map[string]any if err = json.Unmarshal(b, &raw); err != nil { return nil, err } out := make(map[string]string, len(raw)) for k, val := range raw { switch t := val.(type) { case nil: continue case string: out[k] = t case bool: out[k] = strconv.FormatBool(t) case float64: out[k] = strconv.FormatInt(int64(t), 10) case []any: parts := make([]string, 0, len(t)) for _, item := range t { parts = append(parts, fmt.Sprint(item)) } out[k] = strings.Join(parts, arrayConnector) default: out[k] = fmt.Sprint(val) } } return out, nil }