From 8655dca7b4aa1ab2325d55fb7d8c3cdcd5de854b Mon Sep 17 00:00:00 2001 From: Alex-larget <33240357+Alex-larget@users.noreply.github.com> Date: Fri, 27 Feb 2026 14:22:58 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3=EF=BC=8C?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=E8=BE=93=E5=85=A5=E6=A1=86=E6=A0=B7=E5=BC=8F?= =?UTF-8?q?=E6=9C=80=E4=BD=B3=E5=AE=9E=E8=B7=B5=EF=BC=8C=E5=BC=BA=E8=B0=83?= =?UTF-8?q?=E5=9C=A8=E5=B0=8F=E7=A8=8B=E5=BA=8F=E5=92=8C=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E7=AB=AF=E5=BC=80=E5=8F=91=E4=B8=AD=E4=BD=BF=E7=94=A8=E5=AE=B9?= =?UTF-8?q?=E5=99=A8=E5=8C=85=E8=A3=B9=E8=BE=93=E5=85=A5=E6=A1=86=E4=BB=A5?= =?UTF-8?q?=E9=81=BF=E5=85=8D=E5=B8=83=E5=B1=80=E9=97=AE=E9=A2=98=E3=80=82?= =?UTF-8?q?=E8=B0=83=E6=95=B4=E7=BB=8F=E9=AA=8C=E5=BA=93=EF=BC=8C=E8=AE=B0?= =?UTF-8?q?=E5=BD=95=E7=9B=B8=E5=85=B3=E6=9C=80=E4=BD=B3=E5=AE=9E=E8=B7=B5?= =?UTF-8?q?=E5=92=8C=E5=BC=80=E5=8F=91=E8=BF=9B=E5=BA=A6=EF=BC=8C=E7=A1=AE?= =?UTF-8?q?=E4=BF=9D=E5=9B=A2=E9=98=9F=E5=85=B1=E4=BA=AB=E7=BB=8F=E9=AA=8C?= =?UTF-8?q?=E7=9A=84=E4=B8=80=E8=87=B4=E6=80=A7=E5=92=8C=E5=8F=AF=E8=BF=BD?= =?UTF-8?q?=E6=BA=AF=E6=80=A7=E3=80=82=E4=BC=98=E5=8C=96=E5=B0=8F=E7=A8=8B?= =?UTF-8?q?=E5=BA=8F=E9=A1=B5=E9=9D=A2=EF=BC=8C=E5=A2=9E=E5=8A=A0=E5=88=86?= =?UTF-8?q?=E4=BA=AB=E5=8A=9F=E8=83=BD=EF=BC=8C=E6=8F=90=E5=8D=87=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E4=BD=93=E9=AA=8C=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .cursor/skills/SKILL-小程序开发.md | 10 +- .cursor/skills/SKILL-管理端开发.md | 6 +- .cursor/经验库/团队/2026-02-27.md | 5 + .cursor/经验库/小程序/2026-02-27.md | 19 +++ .cursor/经验库/小程序/项目索引.md | 1 + .cursor/经验库/经验清单.md | 4 +- miniprogram/pages/about/about.js | 6 + miniprogram/pages/addresses/addresses.js | 6 + miniprogram/pages/addresses/edit.js | 6 + miniprogram/pages/agreement/agreement.js | 6 + miniprogram/pages/chapters/chapters.js | 6 + miniprogram/pages/index/index.js | 20 ++- miniprogram/pages/index/index.wxml | 15 ++- miniprogram/pages/index/index.wxss | 39 ++++++ miniprogram/pages/match/match.js | 6 + .../pages/member-detail/member-detail.js | 24 ++-- miniprogram/pages/my/my.js | 6 + miniprogram/pages/privacy/privacy.js | 6 + miniprogram/pages/purchases/purchases.js | 6 + miniprogram/pages/read/read.js | 6 +- miniprogram/pages/referral/referral.js | 2 + miniprogram/pages/referral/referral.wxml | 2 +- miniprogram/pages/search/search.js | 6 + miniprogram/pages/settings/settings.js | 6 + miniprogram/pages/vip/vip.js | 64 ++++++++-- miniprogram/pages/vip/vip.wxml | 58 ++++++--- miniprogram/pages/vip/vip.wxss | 28 +++-- .../withdraw-records/withdraw-records.js | 6 + soul-api/internal/handler/miniprogram.go | 25 ++-- soul-api/internal/handler/vip.go | 119 +++++++----------- soul-api/scripts/add-vip-profile-fields.sql | 20 +++ .../avatars/1772165051417228100_qpc606.png | Bin 0 -> 67231 bytes 临时需求池/VIP页保存资料-开发团队分析.md | 112 +++++++++++++++++ 开发文档/8、部署/VIP功能-数据库迁移说明.md | 5 + 34 files changed, 514 insertions(+), 142 deletions(-) create mode 100644 .cursor/经验库/小程序/2026-02-27.md create mode 100644 soul-api/scripts/add-vip-profile-fields.sql create mode 100644 soul-api/uploads/avatars/1772165051417228100_qpc606.png create mode 100644 临时需求池/VIP页保存资料-开发团队分析.md diff --git a/.cursor/skills/SKILL-小程序开发.md b/.cursor/skills/SKILL-小程序开发.md index a6202c83..ff027dba 100644 --- a/.cursor/skills/SKILL-小程序开发.md +++ b/.cursor/skills/SKILL-小程序开发.md @@ -58,10 +58,18 @@ description: Soul 创业派对小程序开发规范。在 miniprogram/ 下编辑 --- -## 6. 何时使用本 Skill +## 6. 表单与输入框 + +- **输入框 padding**:设置 padding、背景、边框时,用 `` 包裹 ``;padding/背景/边框写在 view 上,input 设置 `width: 100%`、`font-size`、`color`、`background: transparent`。可避免光标截断、布局异常。 +- **示例**:``,`.form-input { padding: 16rpx 24rpx; ... }`,`.form-input input { width: 100%; font-size: 28rpx; ... }` + +--- + +## 7. 何时使用本 Skill - 在 **miniprogram/** 下新增或修改页面、组件、utils 时。 - 在小程序内新增或修改任何网络请求路径时(必须保持 `/api/miniprogram/...`)。 - 做阅读、支付、推荐、提现等与 soul-api 对接的功能时。 +- 做表单、输入框样式时(遵循 §6 包裹 input 的写法)。 遵循本 Skill 可保证小程序只与 soul-api 的 miniprogram 路由组对接,避免与管理端或 next-project 接口混用。 diff --git a/.cursor/skills/SKILL-管理端开发.md b/.cursor/skills/SKILL-管理端开发.md index 56c1a76d..dbee184e 100644 --- a/.cursor/skills/SKILL-管理端开发.md +++ b/.cursor/skills/SKILL-管理端开发.md @@ -61,7 +61,11 @@ description: Soul 创业派对管理端开发规范。在 soul-admin/ 下编辑 **检查清单**:分页、搜索防抖、刷新、loading、空状态、错误条、仅 admin/db 路径。详见 `开发文档/列表标准与角色分工.md`。 -### 4.1 表单弹窗与「可选择+可手动填写」(吸收 SetVipModal 经验) +### 4.1 输入框 padding(前端通用) + +设置 input 的 padding、背景、边框时,用 div 包裹 input;padding 写在容器上,input 仅做文字样式。可避免光标截断、布局异常。React:`
`。 + +### 4.2 表单弹窗与「可选择+可手动填写」(吸收 SetVipModal 经验) 当表单需支持「从预设选项选择」或「手动填写自定义值」时: diff --git a/.cursor/经验库/团队/2026-02-27.md b/.cursor/经验库/团队/2026-02-27.md index 33823626..f552fafb 100644 --- a/.cursor/经验库/团队/2026-02-27.md +++ b/.cursor/经验库/团队/2026-02-27.md @@ -3,3 +3,8 @@ > 本日跨角色共享的架构决策、业务规则。格式:类型 | 摘要 | 关联 Skill --- + +## 输入框样式(前端通用) + +- **最佳实践 | 输入框 padding**:设置 input 的 padding/背景/边框时,用 div 或 view 包裹 input;padding 写在容器上,input 仅做文字样式,可避免光标截断、布局异常。 +- **适用**:小程序、管理端(React input 同理) diff --git a/.cursor/经验库/小程序/2026-02-27.md b/.cursor/经验库/小程序/2026-02-27.md new file mode 100644 index 00000000..eae48339 --- /dev/null +++ b/.cursor/经验库/小程序/2026-02-27.md @@ -0,0 +1,19 @@ +# 小程序开发工程师 经验记录 - 2026-02-27 + +--- + +## 输入框 padding 最佳实践 + +- **场景**:设置 input 的 padding、背景、边框等样式时,直接作用于 input 可能导致光标被截断、布局异常。 +- **方案**:用 `` 包裹 ``,padding/背景/边框写在 view 上,input 仅设置 width、font-size、color 等文字相关样式。 +- **示例**: + ```xml + + + + ``` + ```css + .form-input { padding: 16rpx 24rpx; background: rgba(255,255,255,0.06); border: 1rpx solid rgba(255,255,255,0.1); border-radius: 12rpx; } + .form-input input { width: 100%; font-size: 28rpx; color: #fff; background: transparent; } + ``` +- **升级**:已写入 SKILL-小程序开发 §6 表单与输入框 diff --git a/.cursor/经验库/小程序/项目索引.md b/.cursor/经验库/小程序/项目索引.md index 8f45c455..52c0dada 100644 --- a/.cursor/经验库/小程序/项目索引.md +++ b/.cursor/经验库/小程序/项目索引.md @@ -17,6 +17,7 @@ | 2026-02-26 | 项目索引初始化,.cursor 规则优化完成 | 已完成 | | 2026-02-27 | 开发进度汇报:永平落地已完成(海报 scene、我的收益、推广中心、VIP 相关);找伙伴、提现、阅读、分销核心功能已上线 | 已完成 | | 2026-02-27 | 开发进度同步会议:进度已同步至开发文档,待办资料完善弹窗、≥3 章弹窗 | 已完成 | +| 2026-02-27 | 吸收经验:输入框 padding 用 view 包裹,已升级 SKILL-小程序开发 §6 | 已完成 | > **格式说明**:每次开发后在此追加一行,日期格式 YYYY-MM-DD,状态用:已完成 / 进行中 / 待续 / 搁置 diff --git a/.cursor/经验库/经验清单.md b/.cursor/经验库/经验清单.md index a2c23799..bb4f8b77 100644 --- a/.cursor/经验库/经验清单.md +++ b/.cursor/经验库/经验清单.md @@ -21,7 +21,7 @@ | 日期 | 角色 | 类型 | 升级 Skill | 摘要 | |------|------|------|------------|------| -| (待补充) | | | | | +| 2026-02-27 | 小程序、团队 | 最佳实践 | SKILL-小程序开发 §6、SKILL-管理端开发 §4.1 | 输入框 padding 用 view/div 包裹 | --- @@ -32,4 +32,4 @@ --- -**最后更新**:2026-02-26 +**最后更新**:2026-02-27 diff --git a/miniprogram/pages/about/about.js b/miniprogram/pages/about/about.js index b337a620..ff164288 100644 --- a/miniprogram/pages/about/about.js +++ b/miniprogram/pages/about/about.js @@ -40,6 +40,7 @@ Page({ }, onLoad() { + wx.showShareMenu({ withShareTimeline: true }) this.setData({ statusBarHeight: app.globalData.statusBarHeight }) @@ -85,5 +86,10 @@ Page({ title: 'Soul创业派对 - 关于', path: ref ? `/pages/about/about?ref=${ref}` : '/pages/about/about' } + }, + + onShareTimeline() { + const ref = app.getMyReferralCode() + return { title: 'Soul创业派对 - 关于', query: ref ? `ref=${ref}` : '' } } }) diff --git a/miniprogram/pages/addresses/addresses.js b/miniprogram/pages/addresses/addresses.js index c3dfda61..1d4a50e1 100644 --- a/miniprogram/pages/addresses/addresses.js +++ b/miniprogram/pages/addresses/addresses.js @@ -14,6 +14,7 @@ Page({ }, onLoad() { + wx.showShareMenu({ withShareTimeline: true }) this.setData({ statusBarHeight: app.globalData.statusBarHeight || 44 }) @@ -127,5 +128,10 @@ Page({ title: 'Soul创业派对 - 地址管理', path: ref ? `/pages/addresses/addresses?ref=${ref}` : '/pages/addresses/addresses' } + }, + + onShareTimeline() { + const ref = app.getMyReferralCode() + return { title: 'Soul创业派对 - 地址管理', query: ref ? `ref=${ref}` : '' } } }) diff --git a/miniprogram/pages/addresses/edit.js b/miniprogram/pages/addresses/edit.js index 4f45893c..efacd000 100644 --- a/miniprogram/pages/addresses/edit.js +++ b/miniprogram/pages/addresses/edit.js @@ -27,6 +27,7 @@ Page({ }, onLoad(options) { + wx.showShareMenu({ withShareTimeline: true }) this.setData({ statusBarHeight: app.globalData.statusBarHeight || 44 }) @@ -205,5 +206,10 @@ Page({ title: 'Soul创业派对 - 编辑地址', path: ref ? `/pages/addresses/edit?ref=${ref}` : '/pages/addresses/edit' } + }, + + onShareTimeline() { + const ref = app.getMyReferralCode() + return { title: 'Soul创业派对 - 编辑地址', query: ref ? `ref=${ref}` : '' } } }) diff --git a/miniprogram/pages/agreement/agreement.js b/miniprogram/pages/agreement/agreement.js index cff31e3b..ea413a87 100644 --- a/miniprogram/pages/agreement/agreement.js +++ b/miniprogram/pages/agreement/agreement.js @@ -10,6 +10,7 @@ Page({ }, onLoad() { + wx.showShareMenu({ withShareTimeline: true }) this.setData({ statusBarHeight: app.globalData.statusBarHeight || 44 }) @@ -25,5 +26,10 @@ Page({ title: 'Soul创业派对 - 用户协议', path: ref ? `/pages/agreement/agreement?ref=${ref}` : '/pages/agreement/agreement' } + }, + + onShareTimeline() { + const ref = app.getMyReferralCode() + return { title: 'Soul创业派对 - 用户协议', query: ref ? `ref=${ref}` : '' } } }) diff --git a/miniprogram/pages/chapters/chapters.js b/miniprogram/pages/chapters/chapters.js index ff1c4787..4425b0ed 100644 --- a/miniprogram/pages/chapters/chapters.js +++ b/miniprogram/pages/chapters/chapters.js @@ -206,6 +206,7 @@ Page({ }, onLoad() { + wx.showShareMenu({ withShareTimeline: true }) this.setData({ statusBarHeight: app.globalData.statusBarHeight, navBarHeight: app.globalData.navBarHeight @@ -302,5 +303,10 @@ Page({ title: 'Soul创业派对 - 目录', path: ref ? `/pages/chapters/chapters?ref=${ref}` : '/pages/chapters/chapters' } + }, + + onShareTimeline() { + const ref = app.getMyReferralCode() + return { title: 'Soul创业派对 - 真实商业故事', query: ref ? `ref=${ref}` : '' } } }) diff --git a/miniprogram/pages/index/index.js b/miniprogram/pages/index/index.js index ad4d0145..937c3254 100644 --- a/miniprogram/pages/index/index.js +++ b/miniprogram/pages/index/index.js @@ -45,6 +45,7 @@ Page({ // 超级个体(VIP会员) superMembers: [], + superMembersLoading: true, // 最新新增章节 latestChapters: [], @@ -71,7 +72,7 @@ Page({ app.handleReferralCode({ query: options }) } - // 初始化数据 + wx.showShareMenu({ withShareTimeline: true }) this.initData() }, @@ -119,6 +120,7 @@ Page({ }, async loadSuperMembers() { + this.setData({ superMembersLoading: true }) try { // 优先加载 VIP 会员(购买 1980 fullbook/vip 订单的用户) let members = [] @@ -128,8 +130,8 @@ Page({ // 不再过滤无头像用户,无头像时用首字母展示 members = (Array.isArray(res.data) ? res.data : []).slice(0, 4).map(u => ({ id: u.id, - name: u.vip_name || u.nickname || '会员', - avatar: u.vip_avatar || u.avatar || '', + name: u.vipName || u.vip_name || u.nickname || '会员', + avatar: u.vipAvatar || u.vip_avatar || u.avatar || '', isVip: true })) if (members.length > 0) { @@ -153,8 +155,11 @@ Page({ } } catch (e) {} } - this.setData({ superMembers: members }) - } catch (e) { console.log('[Index] 加载超级个体失败:', e) } + this.setData({ superMembers: members, superMembersLoading: false }) + } catch (e) { + console.log('[Index] 加载超级个体失败:', e) + this.setData({ superMembersLoading: false }) + } }, // 从服务端获取精选推荐(加权算法:阅读量50% + 时效30% + 付款率20%)和最新更新 @@ -336,5 +341,10 @@ Page({ title: 'Soul创业派对 - 真实商业故事', path: ref ? `/pages/index/index?ref=${ref}` : '/pages/index/index' } + }, + + onShareTimeline() { + const ref = app.getMyReferralCode() + return { title: 'Soul创业派对 - 真实商业故事', query: ref ? `ref=${ref}` : '' } } }) diff --git a/miniprogram/pages/index/index.wxml b/miniprogram/pages/index/index.wxml index b04cc314..e0536412 100644 --- a/miniprogram/pages/index/index.wxml +++ b/miniprogram/pages/index/index.wxml @@ -90,7 +90,17 @@
- + + + + + + + + + + + - + + 成为会员,展示你的项目 加入创业派对 → diff --git a/miniprogram/pages/index/index.wxss b/miniprogram/pages/index/index.wxss index a26506b1..861d0990 100644 --- a/miniprogram/pages/index/index.wxss +++ b/miniprogram/pages/index/index.wxss @@ -547,6 +547,45 @@ } /* ===== 超级个体(横向滚动) ===== */ +/* 加载骨架动画 */ +.super-loading { + width: 100%; + margin: 0 -32rpx; + padding: 0 32rpx; +} +.super-loading-inner { + display: flex; + gap: 32rpx; + padding-bottom: 16rpx; +} +.super-loading-item { + display: flex; + flex-direction: column; + align-items: center; + gap: 16rpx; + min-width: 140rpx; +} +.super-loading-avatar { + width: 112rpx; + height: 112rpx; + border-radius: 50%; + background: linear-gradient(90deg, #2c2c2e 25%, #3a3a3c 50%, #2c2c2e 75%); + background-size: 200% 100%; + animation: super-shimmer 1.2s ease-in-out infinite; +} +.super-loading-name { + width: 80rpx; + height: 24rpx; + border-radius: 8rpx; + background: linear-gradient(90deg, #2c2c2e 25%, #3a3a3c 50%, #2c2c2e 75%); + background-size: 200% 100%; + animation: super-shimmer 1.2s ease-in-out infinite 0.2s; +} +@keyframes super-shimmer { + 0% { background-position: 100% 0; } + 100% { background-position: -100% 0; } +} + .super-scroll { white-space: nowrap; width: 100%; diff --git a/miniprogram/pages/match/match.js b/miniprogram/pages/match/match.js index eff8b841..63fde49a 100644 --- a/miniprogram/pages/match/match.js +++ b/miniprogram/pages/match/match.js @@ -72,6 +72,7 @@ Page({ }, onLoad() { + wx.showShareMenu({ withShareTimeline: true }) this.setData({ statusBarHeight: app.globalData.statusBarHeight || 44 }) @@ -667,5 +668,10 @@ Page({ title: 'Soul创业派对 - 找伙伴', path: ref ? `/pages/match/match?ref=${ref}` : '/pages/match/match' } + }, + + onShareTimeline() { + const ref = app.getMyReferralCode() + return { title: 'Soul创业派对 - 找伙伴', query: ref ? `ref=${ref}` : '' } } }) diff --git a/miniprogram/pages/member-detail/member-detail.js b/miniprogram/pages/member-detail/member-detail.js index 8eeb6396..7af4e8e7 100644 --- a/miniprogram/pages/member-detail/member-detail.js +++ b/miniprogram/pages/member-detail/member-detail.js @@ -11,6 +11,7 @@ Page({ data: { statusBarHeight: 44, member: null, loading: true }, onLoad(options) { + wx.showShareMenu({ withShareTimeline: true }) this.setData({ statusBarHeight: app.globalData.statusBarHeight }) if (options.id) this.loadMember(options.id) }, @@ -30,10 +31,10 @@ Page({ const u = Array.isArray(dbRes.data) ? dbRes.data[0] : dbRes.data if (u) { this.setData({ member: this.enrichAndFormat({ - id: u.id, name: u.vip_name || u.nickname || '创业者', - avatar: u.vip_avatar || u.avatar || '', isVip: u.is_vip === 1, - contactRaw: u.vip_contact || u.phone || '', project: u.vip_project || '', - bio: u.vip_bio || '', mbti: '', region: '', skills: '', + id: u.id, name: u.vipName || u.vip_name || u.nickname || '创业者', + avatar: u.vipAvatar || u.vip_avatar || u.avatar || '', isVip: u.is_vip === 1, + contactRaw: u.vipContact || u.vip_contact || u.phone || '', project: u.vipProject || u.vip_project || '', + bio: u.vipBio || u.vip_bio || '', mbti: '', region: '', skills: '', }), loading: false }) return } @@ -48,19 +49,19 @@ Page({ const merged = { id: raw.id, - name: raw.name || raw.vip_name || raw.nickname || '创业者', - avatar: raw.avatar || raw.vip_avatar || '', + name: raw.name || raw.vipName || raw.vip_name || raw.nickname || '创业者', + avatar: raw.avatar || raw.vipAvatar || raw.vip_avatar || '', isVip: raw.isVip || raw.is_vip === 1, mbti: raw.mbti || mock.mbti, region: raw.region || mock.region, skills: raw.skills || mock.skills, - contactRaw: raw.contactRaw || raw.vip_contact || mock.contactRaw, + contactRaw: raw.contactRaw || raw.vipContact || raw.vip_contact || mock.contactRaw, bestMonth: raw.bestMonth || mock.bestMonth, achievement: raw.achievement || mock.achievement, turningPoint: raw.turningPoint || mock.turningPoint, canHelp: raw.canHelp || mock.canHelp, needHelp: raw.needHelp || mock.needHelp, - project: raw.project || raw.vip_project || mock.project + project: raw.project || raw.vipProject || raw.vip_project || mock.project } const contact = merged.contactRaw || '' @@ -96,5 +97,12 @@ Page({ title: 'Soul创业派对 - 创业者详情', path: id && ref ? `/pages/member-detail/member-detail?id=${id}&ref=${ref}` : id ? `/pages/member-detail/member-detail?id=${id}` : ref ? `/pages/member-detail/member-detail?ref=${ref}` : '/pages/member-detail/member-detail' } + }, + + onShareTimeline() { + const ref = app.getMyReferralCode() + const id = this.data.member?.id + const q = id ? (ref ? `id=${id}&ref=${ref}` : `id=${id}`) : (ref ? `ref=${ref}` : '') + return { title: 'Soul创业派对 - 创业者详情', query: q } } }) diff --git a/miniprogram/pages/my/my.js b/miniprogram/pages/my/my.js index c857e4d0..2a01da3d 100644 --- a/miniprogram/pages/my/my.js +++ b/miniprogram/pages/my/my.js @@ -60,6 +60,7 @@ Page({ }, onLoad() { + wx.showShareMenu({ withShareTimeline: true }) this.setData({ statusBarHeight: app.globalData.statusBarHeight, navBarHeight: app.globalData.navBarHeight @@ -719,5 +720,10 @@ Page({ title: 'Soul创业派对 - 我的', path: ref ? `/pages/my/my?ref=${ref}` : '/pages/my/my' } + }, + + onShareTimeline() { + const ref = app.getMyReferralCode() + return { title: 'Soul创业派对 - 我的', query: ref ? `ref=${ref}` : '' } } }) diff --git a/miniprogram/pages/privacy/privacy.js b/miniprogram/pages/privacy/privacy.js index 0c95c06e..a12def28 100644 --- a/miniprogram/pages/privacy/privacy.js +++ b/miniprogram/pages/privacy/privacy.js @@ -10,6 +10,7 @@ Page({ }, onLoad() { + wx.showShareMenu({ withShareTimeline: true }) this.setData({ statusBarHeight: app.globalData.statusBarHeight || 44 }) @@ -25,5 +26,10 @@ Page({ title: 'Soul创业派对 - 隐私政策', path: ref ? `/pages/privacy/privacy?ref=${ref}` : '/pages/privacy/privacy' } + }, + + onShareTimeline() { + const ref = app.getMyReferralCode() + return { title: 'Soul创业派对 - 隐私政策', query: ref ? `ref=${ref}` : '' } } }) diff --git a/miniprogram/pages/purchases/purchases.js b/miniprogram/pages/purchases/purchases.js index a332a03c..86293d66 100644 --- a/miniprogram/pages/purchases/purchases.js +++ b/miniprogram/pages/purchases/purchases.js @@ -11,6 +11,7 @@ Page({ }, onLoad() { + wx.showShareMenu({ withShareTimeline: true }) this.setData({ statusBarHeight: app.globalData.statusBarHeight }) this.loadOrders() }, @@ -71,5 +72,10 @@ Page({ title: 'Soul创业派对 - 购买记录', path: ref ? `/pages/purchases/purchases?ref=${ref}` : '/pages/purchases/purchases' } + }, + + onShareTimeline() { + const ref = app.getMyReferralCode() + return { title: 'Soul创业派对 - 购买记录', query: ref ? `ref=${ref}` : '' } } }) diff --git a/miniprogram/pages/read/read.js b/miniprogram/pages/read/read.js index 9b0aa041..7d6981b8 100644 --- a/miniprogram/pages/read/read.js +++ b/miniprogram/pages/read/read.js @@ -74,6 +74,7 @@ Page({ }, async onLoad(options) { + wx.showShareMenu({ withShareTimeline: true }) // 支持 scene(扫码)、mid、id、ref const sceneStr = (options && options.scene) || '' const parsed = parseScene(sceneStr) @@ -505,10 +506,7 @@ Page({ const { section, sectionId, sectionMid } = this.data const ref = app.getMyReferralCode() const q = sectionMid ? `mid=${sectionMid}` : `id=${sectionId}` - return { - title: `${section?.title || 'Soul创业派对'} - 来自派对房的真实故事`, - query: ref ? `${q}&ref=${ref}` : q - } + return { title: section?.title ? `📚 ${section.title}` : 'Soul创业派对 - 真实商业故事', query: ref ? `${q}&ref=${ref}` : q } }, // 显示登录弹窗(每次打开协议未勾选,符合审核要求) diff --git a/miniprogram/pages/referral/referral.js b/miniprogram/pages/referral/referral.js index 9985baff..9685e2e7 100644 --- a/miniprogram/pages/referral/referral.js +++ b/miniprogram/pages/referral/referral.js @@ -69,6 +69,8 @@ Page({ onLoad() { this.setData({ statusBarHeight: app.globalData.statusBarHeight }) this.initData() + // 启用分享到朋友圈(需同时有 onShareAppMessage 和 onShareTimeline;menus 在 Android 支持,iOS 为 Beta) + wx.showShareMenu({ menus: ['shareAppMessage', 'shareTimeline'] }) }, onShow() { diff --git a/miniprogram/pages/referral/referral.wxml b/miniprogram/pages/referral/referral.wxml index 38c587aa..b8536499 100644 --- a/miniprogram/pages/referral/referral.wxml +++ b/miniprogram/pages/referral/referral.wxml @@ -191,7 +191,7 @@ - + diff --git a/miniprogram/pages/search/search.js b/miniprogram/pages/search/search.js index 0eeb383c..485b7fcc 100644 --- a/miniprogram/pages/search/search.js +++ b/miniprogram/pages/search/search.js @@ -25,6 +25,7 @@ Page({ }, onLoad() { + wx.showShareMenu({ withShareTimeline: true }) this.setData({ statusBarHeight: app.globalData.statusBarHeight || 44 }) @@ -113,5 +114,10 @@ Page({ title: 'Soul创业派对 - 搜索', path: ref ? `/pages/search/search?ref=${ref}` : '/pages/search/search' } + }, + + onShareTimeline() { + const ref = app.getMyReferralCode() + return { title: 'Soul创业派对 - 搜索', query: ref ? `ref=${ref}` : '' } } }) diff --git a/miniprogram/pages/settings/settings.js b/miniprogram/pages/settings/settings.js index c46ab852..3f2cb8b1 100644 --- a/miniprogram/pages/settings/settings.js +++ b/miniprogram/pages/settings/settings.js @@ -27,6 +27,7 @@ Page({ }, onLoad() { + wx.showShareMenu({ withShareTimeline: true }) this.setData({ statusBarHeight: app.globalData.statusBarHeight, isLoggedIn: app.globalData.isLoggedIn, @@ -501,5 +502,10 @@ Page({ title: 'Soul创业派对 - 设置', path: ref ? `/pages/settings/settings?ref=${ref}` : '/pages/settings/settings' } + }, + + onShareTimeline() { + const ref = app.getMyReferralCode() + return { title: 'Soul创业派对 - 设置', query: ref ? `ref=${ref}` : '' } } }) diff --git a/miniprogram/pages/vip/vip.js b/miniprogram/pages/vip/vip.js index f28cf200..22ccd79d 100644 --- a/miniprogram/pages/vip/vip.js +++ b/miniprogram/pages/vip/vip.js @@ -20,11 +20,12 @@ Page({ { title: '链接资源', desc: '进群聊天、链接资源的权利' }, { title: '专属VIP标识', desc: '头像金色VIP光圈' } ], - profile: { name: '', project: '', contact: '', bio: '' }, + profile: { vipName: '', vipProject: '', vipContact: '', vipAvatar: '', vipBio: '' }, purchasing: false }, onLoad() { + wx.showShareMenu({ withShareTimeline: true }) this.setData({ statusBarHeight: app.globalData.statusBarHeight }) this.loadVipInfo() }, @@ -55,10 +56,54 @@ Page({ async loadProfile(userId) { try { const res = await app.request(`/api/miniprogram/vip/profile?userId=${userId}`) - if (res?.success) this.setData({ profile: res.data }) + if (res?.success) { + const p = res.data + // 头像若为相对路径则补全 + if (p.vipAvatar && !p.vipAvatar.startsWith('http')) { + p.vipAvatar = app.globalData.baseUrl.replace(/\/$/, '') + (p.vipAvatar.startsWith('/') ? p.vipAvatar : '/' + p.vipAvatar) + } + this.setData({ profile: p }) + } } catch (e) { console.log('[VIP] 资料加载失败', e) } }, + async onChooseVipAvatar() { + wx.chooseMedia({ + count: 1, + mediaType: ['image'], + sourceType: ['album', 'camera'], + success: async (res) => { + const tempPath = res.tempFiles[0].tempFilePath + wx.showLoading({ title: '上传中...', mask: true }) + try { + const uploadRes = await new Promise((resolve, reject) => { + wx.uploadFile({ + url: app.globalData.baseUrl + '/api/miniprogram/upload', + filePath: tempPath, + name: 'file', + formData: { folder: 'avatars' }, + success: (r) => { + try { + const d = JSON.parse(r.data) + d.success ? resolve(d) : reject(new Error(d.error || '上传失败')) + } catch { reject(new Error('解析失败')) } + }, + fail: reject + }) + }) + const path = uploadRes.url || uploadRes.data?.url || '' + const avatarUrl = path.startsWith('http') ? path : (app.globalData.baseUrl.replace(/\/$/, '') + (path.startsWith('/') ? path : '/' + path)) + this.setData({ 'profile.vipAvatar': avatarUrl }) + wx.hideLoading() + wx.showToast({ title: '头像已更新', icon: 'success' }) + } catch (e) { + wx.hideLoading() + wx.showToast({ title: e.message || '上传失败', icon: 'none' }) + } + } + }) + }, + async handlePurchase() { let userId = app.globalData.userInfo?.id let openId = app.globalData.openId || app.globalData.userInfo?.open_id @@ -110,10 +155,10 @@ Page({ } finally { this.setData({ purchasing: false }) } }, - onNameInput(e) { this.setData({ 'profile.name': e.detail.value }) }, - onProjectInput(e) { this.setData({ 'profile.project': e.detail.value }) }, - onContactInput(e) { this.setData({ 'profile.contact': e.detail.value }) }, - onBioInput(e) { this.setData({ 'profile.bio': e.detail.value }) }, + onVipNameInput(e) { this.setData({ 'profile.vipName': e.detail.value }) }, + onVipProjectInput(e) { this.setData({ 'profile.vipProject': e.detail.value }) }, + onVipContactInput(e) { this.setData({ 'profile.vipContact': e.detail.value }) }, + onVipBioInput(e) { this.setData({ 'profile.vipBio': e.detail.value }) }, async saveProfile() { const userId = app.globalData.userInfo?.id @@ -121,7 +166,7 @@ Page({ const p = this.data.profile try { const res = await app.request('/api/miniprogram/vip/profile', { - method: 'POST', data: { userId, name: p.name, project: p.project, contact: p.contact, bio: p.bio } + method: 'POST', data: { userId, vipName: p.vipName, vipProject: p.vipProject, vipContact: p.vipContact, vipAvatar: p.vipAvatar, vipBio: p.vipBio } }) if (res?.success) wx.showToast({ title: '资料已保存', icon: 'success' }) else wx.showToast({ title: res?.error || '保存失败', icon: 'none' }) @@ -136,5 +181,10 @@ Page({ title: 'Soul创业派对 - VIP会员', path: ref ? `/pages/vip/vip?ref=${ref}` : '/pages/vip/vip' } + }, + + onShareTimeline() { + const ref = app.getMyReferralCode() + return { title: 'Soul创业派对 - VIP会员', query: ref ? `ref=${ref}` : '' } } }) diff --git a/miniprogram/pages/vip/vip.wxml b/miniprogram/pages/vip/vip.wxml index c7ebfb09..b7fde33a 100644 --- a/miniprogram/pages/vip/vip.wxml +++ b/miniprogram/pages/vip/vip.wxml @@ -1,44 +1,50 @@ - + - + + + 卡若创业派对 - 卡若创业派对 - 加入卡若的创业派对会员 + + 加入卡若的 + 创业派对 + 会员 + 有效期至 {{expireDateStr}}(剩余{{daysRemaining}}天) 专属会员尊享权益 - 内容权益 - + + + {{item.title}} {{item.desc}} - 社交权益 - + + + {{item.title}} {{item.desc}} - @@ -51,28 +57,46 @@ 加入卡若创业派对,获取创业资讯与优质人脉资源 - 会员资料(展示在创业老板排行) + + 头像 + + + + + + 上传头像 + + + 姓名 - + + + 项目名称 - + + + 联系方式 - + + + 一句话简介 - + + + + + + - - - + \ No newline at end of file diff --git a/miniprogram/pages/vip/vip.wxss b/miniprogram/pages/vip/vip.wxss index 01071efe..be547857 100644 --- a/miniprogram/pages/vip/vip.wxss +++ b/miniprogram/pages/vip/vip.wxss @@ -12,15 +12,16 @@ .gold { color: #FFD700; } .vip-hero-sub { display: block; font-size: 24rpx; color: rgba(255,255,255,0.5); margin-top: 12rpx; } -.rights-card { margin: 24rpx; } -.rights-item { display: flex; align-items: flex-start; gap: 20rpx; padding: 24rpx; margin-bottom: 16rpx; background: rgba(255,255,255,0.04); border: 1rpx solid rgba(255,255,255,0.06); border-radius: 16rpx; } +.rights-card { margin: 24rpx; padding: 0 8rpx; } +.rights-item { display: flex; align-items: flex-start; padding: 24rpx; margin-bottom: 16rpx; background: rgba(255,255,255,0.04); border: 1rpx solid rgba(255,255,255,0.06); border-radius: 16rpx; } +.rights-item .rights-check-wrap { margin-right: 20rpx; } .rights-check-wrap { width: 44rpx; height: 44rpx; border-radius: 50%; background: rgba(0,206,209,0.15); display: flex; align-items: center; justify-content: center; flex-shrink: 0; margin-top: 4rpx; } .rights-check { color: #00CED1; font-size: 24rpx; font-weight: bold; } .rights-info { display: flex; flex-direction: column; } .rights-title { font-size: 30rpx; font-weight: 600; color: rgba(255,255,255,0.95); } .rights-desc { font-size: 24rpx; color: rgba(255,255,255,0.45); margin-top: 6rpx; } -.rights-section-title { display: block; font-size: 26rpx; color: #00CED1; font-weight: 600; margin-bottom: 16rpx; padding-bottom: 12rpx; border-bottom: 1rpx solid rgba(0,206,209,0.15); } +.rights-section-title { display: block; font-size: 26rpx; color: #00CED1; font-weight: 600; margin-bottom: 16rpx; margin-left: 16rpx; padding-bottom: 12rpx; border-bottom: 1rpx solid rgba(0,206,209,0.15); } .buy-area { margin: 24rpx; padding: 32rpx; text-align: center; background: rgba(255,255,255,0.03); border-radius: 20rpx; } .price-row { display: flex; align-items: baseline; justify-content: center; gap: 12rpx; margin-bottom: 24rpx; } @@ -32,12 +33,23 @@ .buy-btn[disabled] { opacity: 0.5; } .buy-sub { display: block; font-size: 22rpx; color: rgba(255,255,255,0.4); margin-top: 16rpx; } -.profile-card { margin: 24rpx; padding: 28rpx; background: #1c1c1e; border-radius: 20rpx; } +.profile-card { margin: 24rpx; padding: 32rpx; background: rgba(255,255,255,0.04); border: 1rpx solid rgba(255,255,255,0.08); border-radius: 20rpx; } .profile-title { font-size: 30rpx; font-weight: 600; color: rgba(255,255,255,0.9); display: block; margin-bottom: 24rpx; } -.form-group { margin-bottom: 20rpx; } -.form-label { font-size: 24rpx; color: rgba(255,255,255,0.5); display: block; margin-bottom: 8rpx; } -.form-input { background: rgba(255,255,255,0.06); border: 1rpx solid rgba(255,255,255,0.1); border-radius: 12rpx; padding: 16rpx 20rpx; font-size: 28rpx; color: #fff; } -.save-btn { margin-top: 24rpx; width: 100%; height: 80rpx; line-height: 80rpx; background: #00CED1; color: #000; font-size: 30rpx; font-weight: 600; border-radius: 40rpx; border: none; } +.avatar-row { display: flex; align-items: center; margin-bottom: 24rpx; } +.avatar-label { font-size: 26rpx; color: rgba(255,255,255,0.6); width: 140rpx; flex-shrink: 0; } +.avatar-slot { width: 128rpx; height: 128rpx; border-radius: 50%; background: rgba(255,255,255,0.06); border: 2rpx dashed rgba(255,255,255,0.2); overflow: hidden; display: flex; align-items: center; justify-content: center; } +.avatar-slot .avatar-img { width: 100%; height: 100%; } +.avatar-placeholder { display: flex; flex-direction: column; align-items: center; justify-content: center; color: rgba(255,255,255,0.4); } +.avatar-add { font-size: 48rpx; line-height: 1; } +.avatar-hint { font-size: 20rpx; margin-top: 8rpx; } +.form-group { margin-bottom: 24rpx; } +.form-label { font-size: 26rpx; color: rgba(255,255,255,0.6); display: block; margin-bottom: 10rpx; } +.form-input { box-sizing: border-box; width: 100%; min-height: 76rpx; background: rgba(255,255,255,0.06); border: 1rpx solid rgba(255,255,255,0.1); border-radius: 12rpx; padding: 16rpx 24rpx; } +.form-input input { width: 100%; font-size: 28rpx; color: #fff; line-height: 1.5; background: transparent; } +.form-placeholder { color: rgba(255,255,255,0.35); } +.save-btn-wrap { margin-top: 32rpx; width: 100%; display: flex; justify-content: center; } +.save-btn { display: block; margin: 0; padding: 0; width: 100%; height: 88rpx; line-height: 88rpx; text-align: center; background: linear-gradient(135deg, #00CED1, #20B2AA); color: #000; font-size: 32rpx; font-weight: 600; border-radius: 44rpx; border: none; box-sizing: border-box; } +.save-btn::after { border: none; margin: 0; padding: 0; } .author-section { margin: 32rpx 24rpx; padding: 24rpx; border-top: 1rpx solid rgba(255,255,255,0.08); } .author-row { display: flex; justify-content: space-between; padding: 8rpx 0; } diff --git a/miniprogram/pages/withdraw-records/withdraw-records.js b/miniprogram/pages/withdraw-records/withdraw-records.js index 9757efac..22000ecd 100644 --- a/miniprogram/pages/withdraw-records/withdraw-records.js +++ b/miniprogram/pages/withdraw-records/withdraw-records.js @@ -11,6 +11,7 @@ Page({ }, onLoad() { + wx.showShareMenu({ withShareTimeline: true }) this.setData({ statusBarHeight: app.globalData.statusBarHeight || 44 }) this.loadRecords() }, @@ -127,5 +128,10 @@ Page({ title: 'Soul创业派对 - 提现记录', path: ref ? `/pages/withdraw-records/withdraw-records?ref=${ref}` : '/pages/withdraw-records/withdraw-records' } + }, + + onShareTimeline() { + const ref = app.getMyReferralCode() + return { title: 'Soul创业派对 - 提现记录', query: ref ? `ref=${ref}` : '' } } }) diff --git a/soul-api/internal/handler/miniprogram.go b/soul-api/internal/handler/miniprogram.go index 862e32b2..12feef1a 100644 --- a/soul-api/internal/handler/miniprogram.go +++ b/soul-api/internal/handler/miniprogram.go @@ -757,22 +757,17 @@ func MiniprogramUsers(c *gin.Context) { var cnt int64 db.Model(&model.Order{}).Where("user_id = ? AND status = ? AND (product_type = ? OR product_type = ?)", id, "paid", "fullbook", "vip").Count(&cnt) - nick := getStringValue(user.Nickname) - avatar := getStringValue(user.Avatar) - contact := getStringValue(user.Phone) - if contact == "" { - contact = getStringValue(user.WechatID) - } + // 用户信息(nickname/avatar)与会员资料(vip*)区分,字段与 model 一致(camelCase) item := gin.H{ - "id": user.ID, - "nickname": nick, - "avatar": avatar, - "vip_name": nick, - "vip_avatar": avatar, - "vip_contact": contact, - "vip_project": "", - "vip_bio": "", - "is_vip": cnt > 0, + "id": user.ID, + "nickname": getStringValue(user.Nickname), + "avatar": getStringValue(user.Avatar), + "vipName": getStringValue(user.VipName), + "vipAvatar": getStringValue(user.VipAvatar), + "vipContact": getStringValue(user.VipContact), + "vipProject": getStringValue(user.VipProject), + "vipBio": getStringValue(user.VipBio), + "is_vip": cnt > 0, } c.JSON(http.StatusOK, gin.H{"success": true, "data": item}) return diff --git a/soul-api/internal/handler/vip.go b/soul-api/internal/handler/vip.go index b5a4d64a..112a31aa 100644 --- a/soul-api/internal/handler/vip.go +++ b/soul-api/internal/handler/vip.go @@ -80,7 +80,7 @@ func VipStatus(c *gin.Context) { "isVip": false, "daysRemaining": 0, "expireDate": "", - "profile": gin.H{"name": "", "project": "", "contact": "", "avatar": "", "bio": ""}, + "profile": gin.H{"vipName": "", "vipProject": "", "vipContact": "", "vipAvatar": "", "vipBio": ""}, "price": float64(defaultVipPrice), "rights": defaultVipRights, }, @@ -117,36 +117,23 @@ func VipStatus(c *gin.Context) { }) } +// buildVipProfile 仅从 vip_* 字段构建会员资料,不混入用户信息(nickname/avatar/phone/wechat_id) +// 返回字段与 users 表 vip_* 对应,统一 vipName/vipProject/vipContact/vipAvatar/vipBio func buildVipProfile(u *model.User) gin.H { - name, project, contact, avatar, bio := "", "", "", "", "" - if u.VipName != nil && *u.VipName != "" { - name = *u.VipName + return gin.H{ + "vipName": getStr(u.VipName), + "vipProject": getStr(u.VipProject), + "vipContact": getStr(u.VipContact), + "vipAvatar": getStr(u.VipAvatar), + "vipBio": getStr(u.VipBio), } - if name == "" && u.Nickname != nil { - name = *u.Nickname +} + +func getStr(s *string) string { + if s == nil { + return "" } - if u.VipProject != nil { - project = *u.VipProject - } - if u.VipContact != nil { - contact = *u.VipContact - } - if contact == "" && u.Phone != nil { - contact = *u.Phone - } - if contact == "" && u.WechatID != nil { - contact = *u.WechatID - } - if u.VipAvatar != nil && *u.VipAvatar != "" { - avatar = *u.VipAvatar - } - if avatar == "" && u.Avatar != nil { - avatar = *u.Avatar - } - if u.VipBio != nil { - bio = *u.VipBio - } - return gin.H{"name": name, "project": project, "contact": contact, "avatar": avatar, "bio": bio} + return *s } // VipProfileGet GET /api/miniprogram/vip/profile 小程序-获取 VIP 资料 @@ -159,7 +146,7 @@ func VipProfileGet(c *gin.Context) { db := database.DB() var user model.User if err := db.Where("id = ?", userID).First(&user).Error; err != nil { - c.JSON(http.StatusOK, gin.H{"success": true, "data": gin.H{"name": "", "project": "", "contact": "", "avatar": "", "bio": ""}}) + c.JSON(http.StatusOK, gin.H{"success": true, "data": gin.H{"vipName": "", "vipProject": "", "vipContact": "", "vipAvatar": "", "vipBio": ""}}) return } c.JSON(http.StatusOK, gin.H{ @@ -169,15 +156,15 @@ func VipProfileGet(c *gin.Context) { } // VipProfilePost POST /api/miniprogram/vip/profile 小程序-更新 VIP 资料 -// 仅 VIP 会员可更新,更新 vip_name/vip_avatar/vip_project/vip_contact/vip_bio +// 请求/响应字段与 users 表 vip_* 一致:vipName/vipProject/vipContact/vipAvatar/vipBio func VipProfilePost(c *gin.Context) { var req struct { - UserID string `json:"userId" binding:"required"` - Name string `json:"name"` - Project string `json:"project"` - Contact string `json:"contact"` - Avatar string `json:"avatar"` - Bio string `json:"bio"` + UserID string `json:"userId" binding:"required"` + VipName string `json:"vipName"` + VipProject string `json:"vipProject"` + VipContact string `json:"vipContact"` + VipAvatar string `json:"vipAvatar"` + VipBio string `json:"vipBio"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusOK, gin.H{"success": false, "error": "请求体无效"}) @@ -196,20 +183,20 @@ func VipProfilePost(c *gin.Context) { } updates := map[string]interface{}{} - if req.Name != "" { - updates["vip_name"] = req.Name + if req.VipName != "" { + updates["vip_name"] = req.VipName } - if req.Project != "" { - updates["vip_project"] = req.Project + if req.VipProject != "" { + updates["vip_project"] = req.VipProject } - if req.Contact != "" { - updates["vip_contact"] = req.Contact + if req.VipContact != "" { + updates["vip_contact"] = req.VipContact } - if req.Avatar != "" { - updates["vip_avatar"] = req.Avatar + if req.VipAvatar != "" { + updates["vip_avatar"] = req.VipAvatar } - if req.Bio != "" { - updates["vip_bio"] = req.Bio + if req.VipBio != "" { + updates["vip_bio"] = req.VipBio } if len(updates) == 0 { c.JSON(http.StatusOK, gin.H{"success": false, "error": "无更新内容"}) @@ -284,24 +271,20 @@ func VipMembers(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"success": true, "data": list, "total": len(list)}) } +// formatVipMember 仅从 vip_* 字段构建会员展示数据,不混入用户信息 +// 用于创业老板排行等场景;未填会员资料时 name 显示「创业者」占位 func formatVipMember(u *model.User, isVip bool) gin.H { name := "" - if u.VipName != nil && *u.VipName != "" { + if u.VipName != nil { name = *u.VipName } - if name == "" && u.Nickname != nil { - name = *u.Nickname - } if name == "" { name = "创业者" } avatar := "" - if u.VipAvatar != nil && *u.VipAvatar != "" { + if u.VipAvatar != nil { avatar = *u.VipAvatar } - if avatar == "" && u.Avatar != nil { - avatar = *u.Avatar - } project := "" if u.VipProject != nil { project = *u.VipProject @@ -314,28 +297,22 @@ func formatVipMember(u *model.User, isVip bool) gin.H { if u.VipContact != nil { contact = *u.VipContact } - if contact == "" && u.Phone != nil { - contact = *u.Phone - } - if contact == "" && u.WechatID != nil { - contact = *u.WechatID - } vipRole := "" if u.VipRole != nil { vipRole = *u.VipRole } return gin.H{ - "id": u.ID, - "name": name, - "nickname": name, - "avatar": avatar, - "vip_name": name, - "vip_role": vipRole, - "vip_avatar": avatar, - "vip_project": project, - "vip_contact": contact, - "vip_bio": bio, - "is_vip": isVip, + "id": u.ID, + "name": name, + "nickname": name, + "avatar": avatar, + "vipName": name, + "vipRole": vipRole, + "vipAvatar": avatar, + "vipProject": project, + "vipContact": contact, + "vipBio": bio, + "is_vip": isVip, } } diff --git a/soul-api/scripts/add-vip-profile-fields.sql b/soul-api/scripts/add-vip-profile-fields.sql new file mode 100644 index 00000000..335a460b --- /dev/null +++ b/soul-api/scripts/add-vip-profile-fields.sql @@ -0,0 +1,20 @@ +-- ============================================================ +-- 会员资料字段 - users 表 +-- 用途:VIP 页保存资料、创业老板排行展示(与用户信息 phone/wechat_id 分离) +-- 执行前请先备份数据库! +-- ============================================================ + +-- 1. 检查:查看 users 表是否已有这些列(可选执行) +-- SHOW COLUMNS FROM users LIKE 'vip_name'; +-- SHOW COLUMNS FROM users LIKE 'vip_avatar'; +-- SHOW COLUMNS FROM users LIKE 'vip_project'; +-- SHOW COLUMNS FROM users LIKE 'vip_contact'; +-- SHOW COLUMNS FROM users LIKE 'vip_bio'; + +-- 2. 新增会员资料字段(若列已存在会报 Duplicate column name,可忽略该条) +-- -------------------------------------------------------- +ALTER TABLE users ADD COLUMN vip_name VARCHAR(100) NULL COMMENT '会员姓名(创业老板排行)'; +ALTER TABLE users ADD COLUMN vip_avatar VARCHAR(500) NULL COMMENT '会员头像'; +ALTER TABLE users ADD COLUMN vip_project VARCHAR(200) NULL COMMENT '项目名称'; +ALTER TABLE users ADD COLUMN vip_contact VARCHAR(100) NULL COMMENT '会员联系方式(展示用,与 phone/wechat_id 分离)'; +ALTER TABLE users ADD COLUMN vip_bio TEXT NULL COMMENT '一句话简介'; diff --git a/soul-api/uploads/avatars/1772165051417228100_qpc606.png b/soul-api/uploads/avatars/1772165051417228100_qpc606.png new file mode 100644 index 0000000000000000000000000000000000000000..fabb67db465e732f9d8b0ed394d20cf58f47fb39 GIT binary patch literal 67231 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 mK93R?Yfz!*{`~jj`2PTczl_Z0G8Y~I0000 针对 `miniprogram/pages/vip` 的「保存资料」逻辑,与《规则说明》、提现、找伙伴等业务规则的对齐分析。 + +--- + +## 一、当前实现梳理 + +### 1.1 VIP 页资料字段 + +| 字段 | 存库字段 | 说明 | +|------|----------|------| +| 姓名 | vip_name | 展示在创业老板排行 | +| 项目名称 | vip_project | | +| 联系方式 | vip_contact | placeholder: 微信号或手机号 | +| 一句话简介 | vip_bio | | + +保存时调用:`POST /api/miniprogram/vip/profile`,仅更新 `vip_*` 字段。 + +### 1.2 规则说明中的资料要求 + +| 场景 | 规则要求 | +|------|----------| +| 提现前 | 必须填写**手机号、微信号**,有单独弹窗,填写完后**自动同步保存到资料页** | +| 找伙伴前 | 需要引导完善个人资料 | +| 资源对接 | 需填写**联系方式** + **我能帮到大家什么** + **我需要什么帮助** | +| 导师顾问/团队招募 | 需填写**联系方式** | + +### 1.3 相关资料存储位置 + +| 入口 | 接口 | 更新字段 | 用途 | +|------|------|----------|------| +| 设置页 | POST /api/miniprogram/user/profile | phone, wechatId | 提现校验、资料页展示 | +| 提现弹窗 | (待实现)填写手机号、微信号 | phone, wechat_id | 提现到账 | +| VIP 页 | POST /api/miniprogram/vip/profile | vip_name, vip_project, vip_contact, vip_bio | 创业老板排行、VIP 展示 | +| 找伙伴-资源对接 | match 页表单 | 需核对后端存储 | 我能帮、需要什么、联系方式 | + +--- + +## 二、对齐问题分析 + +### 2.1 会员资料与用户信息已区分(设计正确) + +**users 表字段划分**: +- **用户信息**(phone、wechat_id、nickname、avatar):用于登录、提现、找伙伴前置校验 +- **会员资料**(vip_name、vip_project、vip_contact、vip_avatar、vip_bio):仅用于创业老板排行展示 + +**结论**:✅ **已区分**。VIP 页保存的「联系方式」只写入 `vip_contact`,不混入 `phone`/`wechat_id`。提现、找伙伴所需手机号/微信号需在设置页或提现弹窗单独填写并更新用户信息。 + +### 2.2 规则要求「手机号、微信号」分开 + +**现状**:VIP 页、设置页均为单一「联系方式」或混合输入。 + +**规则说明**:明确要求「手机号、微信号」两项。 + +**建议**: +- 短期:保持 VIP 页单一「联系方式」框,但保存时后端需将 contact 解析并同步到 phone/wechat_id(如:11 位数字→phone,否则→wechat_id) +- 中期:产品确认是否在资料页/VIP 页拆分为「手机号」「微信号」两个输入框,与规则完全一致 + +### 2.3 找伙伴「我能帮」「需要什么」与 VIP 资料分离 + +**现状**: +- VIP 页:姓名、项目、联系方式、一句话简介 → 用于创业老板排行 +- 找伙伴-资源对接:在 match 页填写「我能帮到你什么」「我需要什么帮助」→ 与 VIP 资料分离 + +**规则**:资源对接需填写「联系方式 + 我能帮到大家什么 + 我需要什么帮助」。 + +**结论**:✅ **用途不同,分离合理**。VIP 资料侧重排行展示,找伙伴侧重匹配信息。但需确保「联系方式」统一:若在 VIP 页填了 contact,应能作为找伙伴/提现的前置条件。 + +### 2.4 填写后「自动同步保存到资料页」 + +**规则**:提现弹窗填写手机号、微信号后,应自动同步保存到资料页(用户信息)。 + +**现状**: +- VIP 页保存 → 只更新 vip_*(会员资料),**不**写入 user/profile +- 设置页保存 → 更新 user/profile(phone、wechatId 等用户信息) +- 提现弹窗(待实现)→ 应写入 phone、wechat_id,并同步到用户资料 + +**结论**:✅ **职责正确**。会员资料与用户信息分离;提现所需手机号/微信号通过设置页或提现弹窗更新用户信息即可。 + +--- + +## 三、开发团队决议(已确认) + +### 3.1 会员资料与用户信息分离 + +- **VipProfilePost**:仅更新 `vip_name`、`vip_project`、`vip_contact`、`vip_avatar`、`vip_bio` +- **buildVipProfile**:仅读取 vip_* 字段,不再 fallback 到 nickname/phone/wechat_id/avatar +- 提现、找伙伴所需手机号/微信号:通过设置页或提现弹窗 → `POST /api/miniprogram/user/profile` 更新 + +### 3.2 产品确认项(可选) + +| 项目 | 说明 | +|------|------| +| 提现弹窗填写后同步到资料页的具体交互 | 是否直接更新 users.phone/wechat_id + 刷新设置页展示 | + +--- + +## 四、总结表 + +| 维度 | 状态 | 说明 | +|------|------|------| +| 会员资料与用户信息分离 | ✅ 已确认 | vip_* 仅用于排行;phone/wechat_id 用于提现、找伙伴 | +| buildVipProfile 不再混入用户信息 | ✅ 已实现 | 仅读 vip_* 字段 | +| 规则「手机号、微信号」分开 | ⚠️ 待产品确认 | 设置页/提现弹窗可拆分 | +| 找伙伴资料与 VIP 资料分离 | ✅ 合理 | 用途不同 | +| 提现弹窗填写后同步资料页 | 待实现 | 见运营与变更待办 | +| VIP 页样式 | ✅ 已修复 | 表单、按钮、权益卡片 | + +--- + +*分析日期:2026-02-27 | 参与角色:产品、后端、管理端、小程序* diff --git a/开发文档/8、部署/VIP功能-数据库迁移说明.md b/开发文档/8、部署/VIP功能-数据库迁移说明.md index c27ba77f..66b09558 100644 --- a/开发文档/8、部署/VIP功能-数据库迁移说明.md +++ b/开发文档/8、部署/VIP功能-数据库迁移说明.md @@ -10,6 +10,7 @@ |------|------| | `soul-api/scripts/add-vip-activated-at.sql` | 新增 `users.vip_activated_at`(成为 VIP 时间,排序用) | | `soul-api/scripts/add-vip-roles-and-fields.sql` | 新建 `vip_roles` 表;新增 `users.vip_sort`、`users.vip_role` | +| `soul-api/scripts/add-vip-profile-fields.sql` | 新增 `users.vip_name`、`vip_avatar`、`vip_project`、`vip_contact`、`vip_bio`(会员资料,与用户信息分离) | --- @@ -21,6 +22,9 @@ mysql -u user -p database < soul-api/scripts/add-vip-activated-at.sql # 2. vip_roles 表 + users 新字段 mysql -u user -p database < soul-api/scripts/add-vip-roles-and-fields.sql + +# 3. 会员资料字段(若尚未执行;列已存在会报 Duplicate column,可忽略) +mysql -u user -p database < soul-api/scripts/add-vip-profile-fields.sql ``` 若 `vip_sort`、`vip_role` 已存在,对应 `ALTER` 会报错,可忽略或单独执行未执行过的语句。 @@ -35,6 +39,7 @@ mysql -u user -p database < soul-api/scripts/add-vip-roles-and-fields.sql | `vip_sort` | 手动排序,数字越小越靠前;NULL 时按 vip_activated_at | | `vip_role` | 角色:从 vip_roles 选或手动填写 | | `vip_roles` | 预设角色表(创始人、投资人、产品经理等),管理端可 CRUD | +| `vip_name`、`vip_avatar`、`vip_project`、`vip_contact`、`vip_bio` | 会员资料(创业老板排行),与用户信息 phone/wechat_id 分离 | ---