avatar-gateway.js 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. import { post } from '@/core/http/http-client'
  2. import { buildRestPath } from '@/core/config/api-paths'
  3. import { fetchUploadConfig, uploadImageFile } from '@/features/dynamics/publish-gateway'
  4. const ROUTE_PROFILE = buildRestPath('user/profile')
  5. /** 仅允许写入资料字段,避免接口回包覆盖 token / isLogin */
  6. const PROFILE_PATCH_KEYS = [
  7. 'nickname',
  8. 'username',
  9. 'bio',
  10. 'gender',
  11. 'birthday',
  12. 'avatar',
  13. 'mobile',
  14. 'level',
  15. 'score',
  16. 'money',
  17. 'area_code'
  18. ]
  19. function isProfileLike(obj) {
  20. if (!obj || typeof obj !== 'object' || Array.isArray(obj)) return false
  21. if (obj.userinfo && typeof obj.userinfo === 'object') return false
  22. return (
  23. obj.nickname != null ||
  24. obj.avatar != null ||
  25. obj.username != null ||
  26. obj.bio != null
  27. )
  28. }
  29. function unwrapProfilePayload(data, raw) {
  30. const candidates = [
  31. data,
  32. data && data.userinfo,
  33. data && data.data,
  34. raw?.data,
  35. raw?.data?.userinfo,
  36. raw?.res?.data,
  37. raw?.res?.data?.userinfo
  38. ]
  39. for (const c of candidates) {
  40. if (!c || typeof c !== 'object') continue
  41. if (c.userinfo && typeof c.userinfo === 'object') return c.userinfo
  42. if (isProfileLike(c)) return c
  43. if (c.data && typeof c.data === 'object' && isProfileLike(c.data)) {
  44. return c.data
  45. }
  46. }
  47. return {}
  48. }
  49. /**
  50. * 从接口或表单中提取可安全 merge 到 user store 的字段
  51. * @param {Record<string, unknown>} source
  52. */
  53. export function pickProfilePatch(source) {
  54. if (!source || typeof source !== 'object') return {}
  55. let row = source
  56. if (row.userinfo && typeof row.userinfo === 'object') {
  57. row = row.userinfo
  58. }
  59. const out = {}
  60. for (const key of PROFILE_PATCH_KEYS) {
  61. if (row[key] !== undefined && row[key] !== null) {
  62. out[key] = row[key]
  63. }
  64. }
  65. return out
  66. }
  67. /**
  68. * 上传头像并更新用户资料
  69. * @param {string} filePath 本地临时路径
  70. * @returns {Promise<string>} 更新后的 avatar 字段
  71. */
  72. export async function uploadAndUpdateAvatar(filePath) {
  73. const config = await fetchUploadConfig()
  74. const uploadedUrl = await uploadImageFile(filePath, config)
  75. const profile = await updateUserProfile({ avatar: uploadedUrl })
  76. return profile.avatar || uploadedUrl
  77. }
  78. /**
  79. * @param {Record<string, unknown>} fields
  80. */
  81. export async function updateUserProfile(fields) {
  82. const { data, raw } = await post(ROUTE_PROFILE, fields)
  83. const body = unwrapProfilePayload(data, raw)
  84. return pickProfilePatch(body)
  85. }
  86. /**
  87. * @param {{ nickname?: string }} form
  88. */
  89. export function validateProfileForm(form) {
  90. const nickname = String(form.nickname || '').trim()
  91. if (nickname.length < 3 || nickname.length > 32) {
  92. return { ok: false, messageKey: 'profileEdit.nicknameLengthError' }
  93. }
  94. return { ok: true }
  95. }