user.go 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. package qiyuesuosdk
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "net/url"
  6. "strconv"
  7. "strings"
  8. "time"
  9. "git.sxidc.com/student-physical-examination/contract_lock_sdk/model/common"
  10. user_request "git.sxidc.com/student-physical-examination/contract_lock_sdk/model/user/request"
  11. v2auth_request "git.sxidc.com/student-physical-examination/contract_lock_sdk/model/v2auth/request"
  12. v2auth_response "git.sxidc.com/student-physical-examination/contract_lock_sdk/model/v2auth/response"
  13. )
  14. type userDetailResult struct {
  15. ID flexStringID `json:"id"`
  16. BizID flexStringID `json:"bizId"`
  17. Name string `json:"name"`
  18. Mobile string `json:"mobile"`
  19. Status string `json:"status"`
  20. }
  21. func (r userDetailResult) toUserInfo() *UserInfo {
  22. info := &UserInfo{
  23. ID: r.ID.String(),
  24. BizID: r.BizID.String(),
  25. Name: r.Name,
  26. Mobile: r.Mobile,
  27. Status: r.Status,
  28. }
  29. if info.ID == "" && info.BizID == "" && info.Name == "" {
  30. return nil
  31. }
  32. return info
  33. }
  34. func (p UserLocateParams) toUserQuery() (url.Values, error) {
  35. q := url.Values{}
  36. switch {
  37. case p.UserID != "":
  38. q.Set("id", p.UserID)
  39. case p.Mobile != "":
  40. q.Set("mobile", p.Mobile)
  41. case p.CardNo != "":
  42. q.Set("cardNo", p.CardNo)
  43. case p.OpenUserID != "":
  44. q.Set("bizId", p.OpenUserID)
  45. case p.AccountNo != "":
  46. q.Set("accountNo", p.AccountNo)
  47. default:
  48. return nil, ErrInvalidParams
  49. }
  50. return q, nil
  51. }
  52. func (p UserLocateParams) toUserInfoRequest() (*common.UserInfoRequest, error) {
  53. req := &common.UserInfoRequest{}
  54. switch {
  55. case p.UserID != "":
  56. id, err := strconv.ParseInt(p.UserID, 10, 64)
  57. if err != nil {
  58. return nil, ErrInvalidParams
  59. }
  60. req.UserId = &id
  61. case p.Mobile != "":
  62. req.Mobile = p.Mobile
  63. case p.CardNo != "":
  64. req.CardNo = p.CardNo
  65. case p.OpenUserID != "":
  66. req.OpenUserId = p.OpenUserID
  67. case p.AccountNo != "":
  68. req.AccountNo = p.AccountNo
  69. default:
  70. return nil, ErrInvalidParams
  71. }
  72. return req, nil
  73. }
  74. func isUserNotFound(err error) bool {
  75. if err == nil {
  76. return false
  77. }
  78. msg := err.Error()
  79. return strings.Contains(msg, "用户不存在") || strings.Contains(msg, "未查询到用户")
  80. }
  81. // FindUser 按手机号、证件号、三方用户 id 或登录账号查询契约锁用户;不存在时返回 nil。
  82. func (c *Client) FindUser(p UserLocateParams) (*UserInfo, error) {
  83. q, err := p.toUserQuery()
  84. if err != nil {
  85. return nil, err
  86. }
  87. var resp struct {
  88. apiResponse
  89. Result userDetailResult `json:"result"`
  90. }
  91. if err = c.getQuery("/user", q, &resp); err != nil {
  92. return nil, err
  93. }
  94. if err = resp.err(); err != nil {
  95. if isUserNotFound(err) {
  96. return nil, nil
  97. }
  98. return nil, err
  99. }
  100. return resp.Result.toUserInfo(), nil
  101. }
  102. // FindUserByAccountNo 按 accountNo(通常等于 openUserId)查询用户。
  103. func (c *Client) FindUserByAccountNo(accountNo string) (*UserInfo, error) {
  104. return c.FindUser(UserLocateParams{AccountNo: accountNo})
  105. }
  106. // CreateInternalUser 在契约锁创建内部用户。
  107. func (c *Client) CreateInternalUser(p CreateUserParams) error {
  108. req := user_request.UserV2CreateRequest{
  109. Name: p.Name,
  110. OpenUserId: p.OpenUserID,
  111. AccountNo: p.AccountNo,
  112. Password: p.Password,
  113. Mobile: p.Mobile,
  114. }
  115. if req.AccountNo == "" {
  116. req.AccountNo = p.OpenUserID
  117. }
  118. var resp apiResponse
  119. if err := c.postJSON("/user/v2/create", req, &resp); err != nil {
  120. return err
  121. }
  122. return resp.err()
  123. }
  124. // CreateInternalUserSkipMobileConflict 创建用户;手机号冲突时去掉手机号重试(旧系统行为)。
  125. func (c *Client) CreateInternalUserSkipMobileConflict(p CreateUserParams) error {
  126. err := c.CreateInternalUser(p)
  127. if err == nil {
  128. return nil
  129. }
  130. if p.Mobile != "" && strings.Contains(err.Error(), "手机号已被其他用户绑定") {
  131. p.Mobile = ""
  132. return c.CreateInternalUser(p)
  133. }
  134. return err
  135. }
  136. // UserSignSilentURL 获取个人签名静默授权页;authEnd 为空则默认 10 个月。
  137. // applyCompany 传入当前体检机构(openCompanyId 通常等于 tenant_id),授权记录按机构维度拆分。
  138. func (c *Client) UserSignSilentURL(openUserID string, applyCompany ApplyCompanyParams, authEnd time.Time, completeToPage string) (string, error) {
  139. if authEnd.IsZero() {
  140. authEnd = time.Now().Add(DefaultPersonalAuthDuration)
  141. }
  142. timeEditable := false
  143. req := v2auth_request.V2AuthPersonalsignsilentUrlRequest{
  144. AuthUser: &common.SilentUserRequest{OpenUserId: openUserID},
  145. AuthEndDate: authEnd.Format("2006-01-02"),
  146. AuthTimeModifiable: &timeEditable,
  147. AuthorizedMode: []string{"FACEAUTH", "PINAUTH"},
  148. CompleteToPage: completeToPage,
  149. }
  150. if applyCompany.CompanyID != "" || applyCompany.OpenCompanyID != "" || applyCompany.Name != "" || applyCompany.RegisterNo != "" {
  151. pac := &common.PersonalApplyCompany{
  152. Name: applyCompany.Name,
  153. RegisterNo: applyCompany.RegisterNo,
  154. OpenCompanyId: applyCompany.OpenCompanyID,
  155. }
  156. if applyCompany.CompanyID != "" {
  157. if id, err := strconv.ParseInt(applyCompany.CompanyID, 10, 64); err == nil {
  158. pac.Id = &id
  159. }
  160. }
  161. req.ApplyCompany = pac
  162. }
  163. var resp struct {
  164. apiResponse
  165. Result v2auth_response.V2AuthPersonalsignsilentUrlResponse `json:"result"`
  166. }
  167. if err := c.postJSON("/v2/auth/personalsignsilent/url", req, &resp); err != nil {
  168. return "", err
  169. }
  170. if err := resp.err(); err != nil {
  171. return "", err
  172. }
  173. return resp.Result.Url, nil
  174. }
  175. // QueryPersonalSignAuthRecords 查询个人静默授权记录。
  176. func (c *Client) QueryPersonalSignAuthRecords(openUserID, status string) ([]AuthRecord, error) {
  177. return c.QueryPersonalSignAuthRecordsFor(UserLocateParams{OpenUserID: openUserID}, status)
  178. }
  179. func (c *Client) QueryPersonalSignAuthRecordsFor(user UserLocateParams, status string) ([]AuthRecord, error) {
  180. authUser, err := user.toUserInfoRequest()
  181. if err != nil {
  182. return nil, err
  183. }
  184. req := v2auth_request.V2AuthSignsilentRecordRequest{
  185. AuthUser: authUser,
  186. Status: status,
  187. }
  188. var resp struct {
  189. apiResponse
  190. Result []v2auth_response.V2AuthSignsilentRecordResponse `json:"result"`
  191. }
  192. if err := c.postJSON("/v2/auth/signSilent/record", req, &resp); err != nil {
  193. return nil, err
  194. }
  195. if err := resp.err(); err != nil {
  196. if IsPersonalSignAuthRecordNotFound(err) {
  197. return []AuthRecord{}, nil
  198. }
  199. return nil, err
  200. }
  201. out := make([]AuthRecord, 0, len(resp.Result))
  202. for _, item := range resp.Result {
  203. out = append(out, authRecordFromResponse(item))
  204. }
  205. return out, nil
  206. }
  207. func authRecordFromResponse(item v2auth_response.V2AuthSignsilentRecordResponse) AuthRecord {
  208. rec := AuthRecord{
  209. Status: item.Status,
  210. EndTime: item.AuthEndDate,
  211. AuthScope: item.AuthScope,
  212. }
  213. if item.ApplyCompany != nil {
  214. rec.ApplyCompanyID = item.ApplyCompany.Id
  215. rec.ApplyCompanyOpenCompanyID = item.ApplyCompany.OpenCompanyId
  216. rec.ApplyCompanyName = item.ApplyCompany.Name
  217. rec.ApplyCompanyRegisterNo = item.ApplyCompany.RegisterNo
  218. }
  219. return rec
  220. }
  221. // PersonalSignAuthRecordForCompany 查询 openUserId 对指定法人单位的静默授权记录。
  222. func (c *Client) PersonalSignAuthRecordForCompany(openUserID string, company ApplyCompanyParams, status string) (*AuthRecord, bool, error) {
  223. records, err := c.QueryPersonalSignAuthRecords(openUserID, status)
  224. if err != nil {
  225. return nil, false, err
  226. }
  227. if rec, ok := findAuthRecordForCompany(records, company, status); ok {
  228. return rec, true, nil
  229. }
  230. if status != "" {
  231. all, err := c.QueryPersonalSignAuthRecords(openUserID, "")
  232. if err != nil {
  233. return nil, false, err
  234. }
  235. if rec, ok := findAuthRecordForCompany(all, company, status); ok {
  236. return rec, true, nil
  237. }
  238. }
  239. return nil, false, nil
  240. }
  241. func findAuthRecordForCompany(records []AuthRecord, company ApplyCompanyParams, wantStatus string) (*AuthRecord, bool) {
  242. for i := range records {
  243. if wantStatus != "" && records[i].Status != wantStatus {
  244. continue
  245. }
  246. if authRecordMatchesCompany(&records[i], company) {
  247. return &records[i], true
  248. }
  249. }
  250. return nil, false
  251. }
  252. func authRecordMatchesCompany(rec *AuthRecord, company ApplyCompanyParams) bool {
  253. if company.CompanyID != "" && rec.ApplyCompanyID != "" && company.CompanyID == rec.ApplyCompanyID {
  254. return true
  255. }
  256. if company.OpenCompanyID != "" && rec.ApplyCompanyOpenCompanyID != "" && company.OpenCompanyID == rec.ApplyCompanyOpenCompanyID {
  257. return true
  258. }
  259. if company.RegisterNo != "" && rec.ApplyCompanyRegisterNo != "" && company.RegisterNo == rec.ApplyCompanyRegisterNo {
  260. return true
  261. }
  262. return false
  263. }
  264. // IsUserSignAuthorized 是否存在任意有效个人静默授权。
  265. func (c *Client) IsUserSignAuthorized(openUserID string) (bool, error) {
  266. records, err := c.QueryPersonalSignAuthRecords(openUserID, "EFFECT")
  267. if err != nil {
  268. return false, err
  269. }
  270. return len(records) > 0, nil
  271. }
  272. // IsUserSignAuthorizedForCompany 是否已对指定法人单位完成有效个人静默授权。
  273. func (c *Client) IsUserSignAuthorizedForCompany(openUserID string, company ApplyCompanyParams) (bool, error) {
  274. _, found, err := c.PersonalSignAuthRecordForCompany(openUserID, company, "EFFECT")
  275. return found, err
  276. }
  277. // structFormFields 将 struct 转为 multipart 表单字段(与旧系统 HttpMultipart 行为一致)。
  278. func structFormFields(v any, arrayConnector string) (map[string]string, error) {
  279. b, err := json.Marshal(v)
  280. if err != nil {
  281. return nil, err
  282. }
  283. var raw map[string]any
  284. if err = json.Unmarshal(b, &raw); err != nil {
  285. return nil, err
  286. }
  287. out := make(map[string]string, len(raw))
  288. for k, val := range raw {
  289. switch t := val.(type) {
  290. case nil:
  291. continue
  292. case string:
  293. out[k] = t
  294. case bool:
  295. out[k] = strconv.FormatBool(t)
  296. case float64:
  297. out[k] = strconv.FormatInt(int64(t), 10)
  298. case []any:
  299. parts := make([]string, 0, len(t))
  300. for _, item := range t {
  301. parts = append(parts, fmt.Sprint(item))
  302. }
  303. out[k] = strings.Join(parts, arrayConnector)
  304. default:
  305. out[k] = fmt.Sprint(val)
  306. }
  307. }
  308. return out, nil
  309. }