# 微信小程序会员绑定系统需求规格说明书
## 文档信息
**项目名称:** 小智AI微信小程序会员绑定系统
**文档版本:** v1.0
**创建日期:** 2025年9月30日
**项目负责人:** 开发团队
**文档类型:** 需求规格说明书
---
## 1. 项目概述
### 1.1 项目背景
小智AI微信小程序需要建立完善的会员体系,通过会员绑定系统实现用户身份验证、会员等级管理和权益分配,提升用户体验和产品价值。
### 1.2 项目目标
1. **无缝对接**:实现微信小程序与会员购买系统的无缝对接
2. **等级管理**:提供VIP和SVIP两档会员绑定功能
3. **权益匹配**:确保会员权益与购买等级准确匹配
4. **用户体验**:提供流畅的会员绑定和管理体验
### 1.3 项目范围
- 微信小程序会员绑定功能开发
- 会员等级管理系统
- 会员权益验证机制
- 相关数据库设计和API接口开发
---
## 2. 功能需求分析
### 2.1 核心功能模块
#### 2.1.1 用户授权登录模块
**功能描述:** 通过微信授权获取用户身份信息
**主要功能:**
- 微信授权登录
- 获取用户openid
- 用户信息存储
- 登录状态管理
**业务流程:**
```
用户点击登录 → 微信授权 → 获取code → 后端换取openid → 用户信息入库 → 登录成功
```
#### 2.1.2 会员身份验证模块
**功能描述:** 验证用户会员身份和等级
**主要功能:**
- 会员身份验证
- 会员等级识别
- 会员状态查询
- 会员信息同步
**验证逻辑:**
```
输入openid → 查询会员表 → 验证会员状态 → 返回会员等级 → 更新绑定状态
```
#### 2.1.3 会员绑定管理模块
**功能描述:** 管理用户与会员身份的绑定关系
**主要功能:**
- 会员绑定操作
- 绑定状态更新
- 绑定历史记录
- 解绑功能
#### 2.1.4 会员等级管理模块
**功能描述:** 管理不同等级会员的权益和特权
**会员等级定义:**
| 等级 | 名称 | 权益描述 | 有效期 |
|------|------|----------|--------|
| **普通用户** | 免费用户 | 基础功能使用 | 永久 |
| **VIP** | 基础会员 | 高级功能 + 优先支持 | 按购买周期 |
| **SVIP** | 高级会员 | 全功能 + 专属服务 | 按购买周期 |
### 2.2 功能优先级
| 优先级 | 功能模块 | 开发周期 |
|--------|----------|----------|
| **P0** | 用户授权登录 | 1周 |
| **P0** | 会员身份验证 | 1周 |
| **P1** | 会员绑定管理 | 1周 |
| **P1** | 会员等级管理 | 1周 |
| **P2** | 会员权益展示 | 0.5周 |
---
## 3. 技术架构设计
### 3.1 系统架构图
```
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ 微信小程序 │ │ 后端API服务 │ │ 数据库存储 │
│ │ │ │ │ │
│ ┌─────────────┐ │ │ ┌─────────────┐ │ │ ┌─────────────┐ │
│ │ 会员绑定页面 │ │◄──►│ │ 会员验证API │ │◄──►│ │ 会员信息表 │ │
│ └─────────────┘ │ │ └─────────────┘ │ │ └─────────────┘ │
│ ┌─────────────┐ │ │ ┌─────────────┐ │ │ ┌─────────────┐ │
│ │ 会员中心页面 │ │◄──►│ │ 权益查询API │ │◄──►│ │ 绑定关系表 │ │
│ └─────────────┘ │ │ └─────────────┘ │ │ └─────────────┘ │
│ ┌─────────────┐ │ │ ┌─────────────┐ │ │ ┌─────────────┐ │
│ │ 权益说明弹窗 │ │◄──►│ │ 绑定管理API │ │◄──►│ │ 用户信息表 │ │
│ └─────────────┘ │ │ └─────────────┘ │ │ └─────────────┘ │
└─────────────────┘ └─────────────────┘ └─────────────────┘
```
### 3.2 技术栈选择
**前端技术栈:**
- 微信小程序原生框架
- WXML + WXSS + JavaScript
- 微信小程序API
**后端技术栈:**
- Java Spring Boot
- MyBatis Plus
- MySQL 数据库
- Redis 缓存
**第三方服务:**
- 微信开放平台API
- 微信支付API
---
## 4. 数据库设计
### 4.1 数据表结构
#### 4.1.1 会员信息表 (member_info)
```sql
CREATE TABLE `member_info` (
`member_id` varchar(64) NOT NULL COMMENT '会员ID,主键',
`openid` varchar(128) NOT NULL COMMENT '微信用户openid',
`member_level` varchar(16) NOT NULL DEFAULT 'FREE' COMMENT '会员等级:FREE-免费,VIP-基础会员,SVIP-高级会员',
`member_status` varchar(16) NOT NULL DEFAULT 'ACTIVE' COMMENT '会员状态:ACTIVE-有效,EXPIRED-过期,SUSPENDED-暂停',
`start_time` datetime DEFAULT NULL COMMENT '会员开始时间',
`end_time` datetime DEFAULT NULL COMMENT '会员结束时间',
`purchase_order_id` varchar(64) DEFAULT NULL COMMENT '购买订单ID',
`auto_renew` tinyint DEFAULT 0 COMMENT '是否自动续费:0-否,1-是',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted` tinyint DEFAULT 0 COMMENT '是否删除:0-未删除,1-已删除',
PRIMARY KEY (`member_id`),
UNIQUE KEY `uk_openid` (`openid`),
KEY `idx_member_level` (`member_level`),
KEY `idx_member_status` (`member_status`),
KEY `idx_end_time` (`end_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='会员信息表';
```
#### 4.1.2 会员绑定记录表 (member_bind_log)
```sql
CREATE TABLE `member_bind_log` (
`log_id` bigint NOT NULL AUTO_INCREMENT COMMENT '日志ID,主键',
`member_id` varchar(64) NOT NULL COMMENT '会员ID',
`openid` varchar(128) NOT NULL COMMENT '微信用户openid',
`bind_type` varchar(16) NOT NULL COMMENT '绑定类型:BIND-绑定,UNBIND-解绑,UPGRADE-升级,DOWNGRADE-降级',
`old_level` varchar(16) DEFAULT NULL COMMENT '原会员等级',
`new_level` varchar(16) NOT NULL COMMENT '新会员等级',
`bind_source` varchar(32) DEFAULT NULL COMMENT '绑定来源:PURCHASE-购买,GIFT-赠送,ADMIN-管理员',
`related_order_id` varchar(64) DEFAULT NULL COMMENT '关联订单ID',
`bind_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '绑定时间',
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`log_id`),
KEY `idx_member_id` (`member_id`),
KEY `idx_openid` (`openid`),
KEY `idx_bind_type` (`bind_type`),
KEY `idx_bind_time` (`bind_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='会员绑定记录表';
```
#### 4.1.3 会员权益配置表 (member_benefit_config)
```sql
CREATE TABLE `member_benefit_config` (
`config_id` int NOT NULL AUTO_INCREMENT COMMENT '配置ID,主键',
`member_level` varchar(16) NOT NULL COMMENT '会员等级',
`benefit_code` varchar(32) NOT NULL COMMENT '权益代码',
`benefit_name` varchar(100) NOT NULL COMMENT '权益名称',
`benefit_desc` varchar(500) DEFAULT NULL COMMENT '权益描述',
`benefit_value` varchar(100) DEFAULT NULL COMMENT '权益值',
`is_enabled` tinyint DEFAULT 1 COMMENT '是否启用:0-禁用,1-启用',
`sort_order` int DEFAULT 0 COMMENT '排序顺序',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`config_id`),
UNIQUE KEY `uk_level_code` (`member_level`, `benefit_code`),
KEY `idx_member_level` (`member_level`),
KEY `idx_benefit_code` (`benefit_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='会员权益配置表';
```
### 4.2 初始化数据
```sql
-- 插入会员权益配置数据
INSERT INTO `member_benefit_config` (`member_level`, `benefit_code`, `benefit_name`, `benefit_desc`, `benefit_value`, `sort_order`) VALUES
('FREE', 'basic_chat', '基础对话', '每日基础AI对话功能', '50', 1),
('FREE', 'basic_voice', '基础语音', '每日语音识别次数', '20', 2),
('VIP', 'advanced_chat', '高级对话', '无限制AI对话功能', 'unlimited', 1),
('VIP', 'advanced_voice', '高级语音', '无限制语音识别', 'unlimited', 2),
('VIP', 'priority_support', '优先支持', '客服优先响应', 'enabled', 3),
('VIP', 'custom_model', '自定义模型', '使用自定义AI模型', 'enabled', 4),
('SVIP', 'premium_chat', '专属对话', '专属AI模型对话', 'unlimited', 1),
('SVIP', 'premium_voice', '专属语音', '高质量语音合成', 'unlimited', 2),
('SVIP', 'exclusive_support', '专属服务', '一对一专属客服', 'enabled', 3),
('SVIP', 'api_access', 'API访问', '开放API接口调用', 'enabled', 4),
('SVIP', 'data_export', '数据导出', '对话数据导出功能', 'enabled', 5);
```
---
## 5. API接口设计
### 5.1 会员验证相关接口
#### 5.1.1 用户登录接口
**接口地址:** `POST /api/member/login`
**请求参数:**
```json
{
"code": "微信授权code",
"userInfo": {
"nickName": "用户昵称",
"avatarUrl": "头像URL"
}
}
```
**响应数据:**
```json
{
"code": 200,
"message": "登录成功",
"data": {
"openid": "用户openid",
"sessionKey": "会话密钥",
"memberInfo": {
"memberId": "会员ID",
"memberLevel": "VIP",
"memberStatus": "ACTIVE",
"endTime": "2025-12-31 23:59:59"
}
}
}
```
#### 5.1.2 会员信息查询接口
**接口地址:** `GET /api/member/info`
**请求参数:**
```
Header: Authorization: Bearer {token}
```
**响应数据:**
```json
{
"code": 200,
"message": "查询成功",
"data": {
"memberId": "会员ID",
"memberLevel": "VIP",
"memberStatus": "ACTIVE",
"startTime": "2025-01-01 00:00:00",
"endTime": "2025-12-31 23:59:59",
"autoRenew": true,
"benefits": [
{
"benefitCode": "advanced_chat",
"benefitName": "高级对话",
"benefitDesc": "无限制AI对话功能",
"benefitValue": "unlimited"
}
]
}
}
```
#### 5.1.3 会员绑定接口
**接口地址:** `POST /api/member/bind`
**请求参数:**
```json
{
"orderId": "购买订单ID",
"memberLevel": "VIP",
"duration": 12,
"durationUnit": "MONTH"
}
```
**响应数据:**
```json
{
"code": 200,
"message": "绑定成功",
"data": {
"memberId": "会员ID",
"memberLevel": "VIP",
"startTime": "2025-01-01 00:00:00",
"endTime": "2025-12-31 23:59:59"
}
}
```
### 5.2 会员权益相关接口
#### 5.2.1 权益列表查询接口
**接口地址:** `GET /api/member/benefits`
**请求参数:**
```
Query: memberLevel=VIP (可选,不传则返回当前用户等级权益)
```
**响应数据:**
```json
{
"code": 200,
"message": "查询成功",
"data": {
"memberLevel": "VIP",
"benefits": [
{
"benefitCode": "advanced_chat",
"benefitName": "高级对话",
"benefitDesc": "无限制AI对话功能",
"benefitValue": "unlimited",
"isEnabled": true
}
]
}
}
```
#### 5.2.2 权益验证接口
**接口地址:** `POST /api/member/verify-benefit`
**请求参数:**
```json
{
"benefitCode": "advanced_chat",
"requestCount": 1
}
```
**响应数据:**
```json
{
"code": 200,
"message": "验证成功",
"data": {
"hasPermission": true,
"remainingCount": "unlimited",
"resetTime": null
}
}
```
---
## 6. 前端页面设计
### 6.1 页面结构
```
pages/
├── member/
│ ├── bind/
│ │ ├── bind.wxml # 会员绑定页面
│ │ ├── bind.wxss # 样式文件
│ │ ├── bind.js # 逻辑文件
│ │ └── bind.json # 配置文件
│ ├── center/
│ │ ├── center.wxml # 会员中心页面
│ │ ├── center.wxss
│ │ ├── center.js
│ │ └── center.json
│ └── benefits/
│ ├── benefits.wxml # 权益说明页面
│ ├── benefits.wxss
│ ├── benefits.js
│ └── benefits.json
└── components/
├── member-card/ # 会员卡片组件
├── benefit-item/ # 权益项组件
└── login-modal/ # 登录弹窗组件
```
### 6.2 关键页面设计
#### 6.2.1 会员绑定页面 (pages/member/bind/bind.wxml)
```xml
{{userInfo.nickName}}
ID: {{userInfo.openid}}
当前状态:
{{memberLevelText}}
到期时间:
{{memberInfo.endTime}}
会员绑定
订单号
会员权益
{{item.benefitName}}
{{item.benefitDesc}}
查看详细权益说明 >
```
#### 6.2.2 会员中心页面 (pages/member/center/center.wxml)
```xml
会员有效期
剩余 {{remainingDays}} 天
我的权益
{{item.benefitName}}
{{item.benefitValue}}
```
### 6.3 组件设计
#### 6.3.1 会员卡片组件 (components/member-card/member-card.wxml)
```xml
{{levelText}}
{{levelDesc}}
会员ID: {{memberId}}
有效期至: {{endTime}}
```
---
## 7. 开发脚本
### 7.1 小程序端脚本
#### 7.1.1 会员绑定页面逻辑 (pages/member/bind/bind.js)
```javascript
// pages/member/bind/bind.js
const app = getApp()
const api = require('../../../utils/api')
Page({
data: {
userInfo: {},
memberInfo: {},
orderId: '',
binding: false,
benefits: [],
memberLevelText: '普通用户'
},
onLoad(options) {
this.initPage()
this.loadMemberInfo()
this.loadBenefits()
},
// 初始化页面
initPage() {
const userInfo = app.globalData.userInfo
if (!userInfo) {
wx.redirectTo({
url: '/pages/login/login'
})
return
}
this.setData({ userInfo })
},
// 加载会员信息
async loadMemberInfo() {
try {
wx.showLoading({ title: '加载中...' })
const res = await api.getMemberInfo()
if (res.code === 200) {
const memberInfo = res.data
const memberLevelText = this.getMemberLevelText(memberInfo.memberLevel)
this.setData({
memberInfo,
memberLevelText
})
}
} catch (error) {
console.error('加载会员信息失败:', error)
wx.showToast({
title: '加载失败',
icon: 'error'
})
} finally {
wx.hideLoading()
}
},
// 加载权益信息
async loadBenefits() {
try {
const res = await api.getMemberBenefits()
if (res.code === 200) {
this.setData({
benefits: res.data.benefits
})
}
} catch (error) {
console.error('加载权益信息失败:', error)
}
},
// 订单号输入
onOrderIdInput(e) {
this.setData({
orderId: e.detail.value.trim()
})
},
// 绑定会员
async onBindMember() {
const { orderId } = this.data
if (!orderId) {
wx.showToast({
title: '请输入订单号',
icon: 'error'
})
return
}
try {
this.setData({ binding: true })
wx.showLoading({ title: '绑定中...' })
const res = await api.bindMember({
orderId: orderId
})
if (res.code === 200) {
wx.showToast({
title: '绑定成功',
icon: 'success'
})
// 刷新会员信息
setTimeout(() => {
this.loadMemberInfo()
}, 1500)
// 清空订单号
this.setData({ orderId: '' })
} else {
wx.showToast({
title: res.message || '绑定失败',
icon: 'error'
})
}
} catch (error) {
console.error('绑定会员失败:', error)
wx.showToast({
title: '绑定失败',
icon: 'error'
})
} finally {
this.setData({ binding: false })
wx.hideLoading()
}
},
// 查看更多权益
onViewMoreBenefits() {
wx.navigateTo({
url: '/pages/member/benefits/benefits'
})
},
// 获取会员等级文本
getMemberLevelText(level) {
const levelMap = {
'FREE': '普通用户',
'VIP': 'VIP会员',
'SVIP': 'SVIP会员'
}
return levelMap[level] || '普通用户'
}
})
```
#### 7.1.2 API工具类 (utils/api.js)
```javascript
// utils/api.js
const config = require('./config')
class ApiService {
constructor() {
this.baseUrl = config.apiBaseUrl
this.token = wx.getStorageSync('token') || ''
}
// 通用请求方法
request(options) {
return new Promise((resolve, reject) => {
wx.request({
url: this.baseUrl + options.url,
method: options.method || 'GET',
data: options.data || {},
header: {
'Content-Type': 'application/json',
'Authorization': this.token ? `Bearer ${this.token}` : '',
...options.header
},
success: (res) => {
if (res.statusCode === 200) {
resolve(res.data)
} else if (res.statusCode === 401) {
// token过期,跳转登录
wx.removeStorageSync('token')
wx.redirectTo({
url: '/pages/login/login'
})
reject(new Error('登录已过期'))
} else {
reject(new Error(`请求失败: ${res.statusCode}`))
}
},
fail: (error) => {
reject(error)
}
})
})
}
// 用户登录
login(data) {
return this.request({
url: '/api/member/login',
method: 'POST',
data
})
}
// 获取会员信息
getMemberInfo() {
return this.request({
url: '/api/member/info'
})
}
// 绑定会员
bindMember(data) {
return this.request({
url: '/api/member/bind',
method: 'POST',
data
})
}
// 获取会员权益
getMemberBenefits(memberLevel) {
const url = memberLevel
? `/api/member/benefits?memberLevel=${memberLevel}`
: '/api/member/benefits'
return this.request({
url
})
}
// 验证权益
verifyBenefit(data) {
return this.request({
url: '/api/member/verify-benefit',
method: 'POST',
data
})
}
// 更新token
setToken(token) {
this.token = token
wx.setStorageSync('token', token)
}
}
module.exports = new ApiService()
```
### 7.2 服务端脚本
#### 7.2.1 会员服务类 (MemberService.java)
```java
package com.xiaozhi.plus.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xiaozhi.plus.entity.MemberInfo;
import com.xiaozhi.plus.entity.MemberBindLog;
import com.xiaozhi.plus.entity.MemberBenefitConfig;
import com.xiaozhi.plus.mapper.MemberInfoMapper;
import com.xiaozhi.plus.mapper.MemberBindLogMapper;
import com.xiaozhi.plus.mapper.MemberBenefitConfigMapper;
import com.xiaozhi.plus.service.MemberService;
import com.xiaozhi.plus.dto.MemberBindRequest;
import com.xiaozhi.plus.dto.MemberInfoResponse;
import com.xiaozhi.plus.utils.IdGenerator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.List;
@Service
public class MemberServiceImpl extends ServiceImpl implements MemberService {
@Autowired
private MemberInfoMapper memberInfoMapper;
@Autowired
private MemberBindLogMapper memberBindLogMapper;
@Autowired
private MemberBenefitConfigMapper memberBenefitConfigMapper;
@Override
public MemberInfoResponse getMemberInfo(String openid) {
MemberInfo memberInfo = memberInfoMapper.selectOne(
new QueryWrapper()
.eq("openid", openid)
.eq("deleted", 0)
);
if (memberInfo == null) {
// 创建免费用户
memberInfo = createFreeMember(openid);
}
// 检查会员是否过期
checkMemberExpiry(memberInfo);
// 获取会员权益
List benefits = getMemberBenefits(memberInfo.getMemberLevel());
return MemberInfoResponse.builder()
.memberId(memberInfo.getMemberId())
.memberLevel(memberInfo.getMemberLevel())
.memberStatus(memberInfo.getMemberStatus())
.startTime(memberInfo.getStartTime())
.endTime(memberInfo.getEndTime())
.autoRenew(memberInfo.getAutoRenew())
.benefits(benefits)
.build();
}
@Override
@Transactional
public boolean bindMember(String openid, MemberBindRequest request) {
// 验证订单有效性
if (!validateOrder(request.getOrderId())) {
throw new RuntimeException("订单验证失败");
}
// 获取或创建会员信息
MemberInfo memberInfo = memberInfoMapper.selectOne(
new QueryWrapper()
.eq("openid", openid)
.eq("deleted", 0)
);
String oldLevel = null;
if (memberInfo == null) {
memberInfo = new MemberInfo();
memberInfo.setMemberId(IdGenerator.generateMemberId());
memberInfo.setOpenid(openid);
memberInfo.setMemberLevel("FREE");
memberInfo.setMemberStatus("ACTIVE");
memberInfo.setCreateTime(LocalDateTime.now());
} else {
oldLevel = memberInfo.getMemberLevel();
}
// 更新会员信息
memberInfo.setMemberLevel(request.getMemberLevel());
memberInfo.setMemberStatus("ACTIVE");
memberInfo.setStartTime(LocalDateTime.now());
memberInfo.setEndTime(calculateEndTime(request.getDuration(), request.getDurationUnit()));
memberInfo.setPurchaseOrderId(request.getOrderId());
memberInfo.setUpdateTime(LocalDateTime.now());
// 保存会员信息
if (oldLevel == null) {
memberInfoMapper.insert(memberInfo);
} else {
memberInfoMapper.updateById(memberInfo);
}
// 记录绑定日志
recordBindLog(memberInfo, oldLevel, "PURCHASE", request.getOrderId());
return true;
}
@Override
public List getMemberBenefits(String memberLevel) {
return memberBenefitConfigMapper.selectList(
new QueryWrapper()
.eq("member_level", memberLevel)
.eq("is_enabled", 1)
.orderByAsc("sort_order")
);
}
@Override
public boolean verifyBenefit(String openid, String benefitCode, Integer requestCount) {
MemberInfo memberInfo = memberInfoMapper.selectOne(
new QueryWrapper()
.eq("openid", openid)
.eq("deleted", 0)
);
if (memberInfo == null) {
return false;
}
// 检查会员状态
if (!"ACTIVE".equals(memberInfo.getMemberStatus())) {
return false;
}
// 检查是否过期
if (memberInfo.getEndTime() != null && memberInfo.getEndTime().isBefore(LocalDateTime.now())) {
return false;
}
// 检查权益配置
MemberBenefitConfig benefitConfig = memberBenefitConfigMapper.selectOne(
new QueryWrapper()
.eq("member_level", memberInfo.getMemberLevel())
.eq("benefit_code", benefitCode)
.eq("is_enabled", 1)
);
return benefitConfig != null;
}
private MemberInfo createFreeMember(String openid) {
MemberInfo memberInfo = new MemberInfo();
memberInfo.setMemberId(IdGenerator.generateMemberId());
memberInfo.setOpenid(openid);
memberInfo.setMemberLevel("FREE");
memberInfo.setMemberStatus("ACTIVE");
memberInfo.setCreateTime(LocalDateTime.now());
memberInfoMapper.insert(memberInfo);
return memberInfo;
}
private void checkMemberExpiry(MemberInfo memberInfo) {
if (memberInfo.getEndTime() != null &&
memberInfo.getEndTime().isBefore(LocalDateTime.now()) &&
!"EXPIRED".equals(memberInfo.getMemberStatus())) {
memberInfo.setMemberStatus("EXPIRED");
memberInfoMapper.updateById(memberInfo);
}
}
private LocalDateTime calculateEndTime(Integer duration, String durationUnit) {
LocalDateTime now = LocalDateTime.now();
switch (durationUnit.toUpperCase()) {
case "DAY":
return now.plusDays(duration);
case "MONTH":
return now.plusMonths(duration);
case "YEAR":
return now.plusYears(duration);
default:
throw new IllegalArgumentException("不支持的时间单位: " + durationUnit);
}
}
private void recordBindLog(MemberInfo memberInfo, String oldLevel, String bindSource, String orderId) {
MemberBindLog bindLog = new MemberBindLog();
bindLog.setMemberId(memberInfo.getMemberId());
bindLog.setOpenid(memberInfo.getOpenid());
bindLog.setBindType(oldLevel == null ? "BIND" : "UPGRADE");
bindLog.setOldLevel(oldLevel);
bindLog.setNewLevel(memberInfo.getMemberLevel());
bindLog.setBindSource(bindSource);
bindLog.setRelatedOrderId(orderId);
bindLog.setBindTime(LocalDateTime.now());
memberBindLogMapper.insert(bindLog);
}
private boolean validateOrder(String orderId) {
// TODO: 实现订单验证逻辑
// 1. 检查订单是否存在
// 2. 检查订单是否已支付
// 3. 检查订单是否已绑定
return true;
}
}
```
#### 7.2.2 会员控制器 (MemberController.java)
```java
package com.xiaozhi.plus.controller;
import com.xiaozhi.plus.service.MemberService;
import com.xiaozhi.plus.dto.MemberBindRequest;
import com.xiaozhi.plus.dto.MemberInfoResponse;
import com.xiaozhi.plus.dto.BenefitVerifyRequest;
import com.xiaozhi.plus.entity.MemberBenefitConfig;
import com.xiaozhi.plus.utils.Result;
import com.xiaozhi.plus.utils.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api/member")
public class MemberController {
@Autowired
private MemberService memberService;
@Autowired
private JwtUtil jwtUtil;
/**
* 获取会员信息
*/
@GetMapping("/info")
public Result getMemberInfo(HttpServletRequest request) {
try {
String openid = getOpenidFromToken(request);
MemberInfoResponse memberInfo = memberService.getMemberInfo(openid);
return Result.success(memberInfo);
} catch (Exception e) {
return Result.error("获取会员信息失败: " + e.getMessage());
}
}
/**
* 绑定会员
*/
@PostMapping("/bind")
public Result bindMember(@RequestBody MemberBindRequest request, HttpServletRequest httpRequest) {
try {
String openid = getOpenidFromToken(httpRequest);
boolean success = memberService.bindMember(openid, request);
if (success) {
return Result.success("绑定成功");
} else {
return Result.error("绑定失败");
}
} catch (Exception e) {
return Result.error("绑定失败: " + e.getMessage());
}
}
/**
* 获取会员权益
*/
@GetMapping("/benefits")
public Result