wechat-auth-api-guide.md 26 KB

微信认证API开发说明手册

概述

本文档为前端开发工程师提供微信认证相关API的详细使用说明,包括路由路径、请求参数、响应格式、错误码说明以及前端示例代码。

重要说明:本系统已升级为符合微信官方规范的授权流程,使用15分钟有效期的微信授权码换取长期有效的openid作为用户唯一标识。

微信授权流程说明

  1. 前端获取授权码:通过微信小程序API获取临时授权码(15分钟有效期)
  2. 后端换取openid:使用授权码调用微信API获取用户openid
  3. 用户身份管理:以openid作为用户唯一标识进行注册、登录等操作

基础信息

  • API基础路径: /api/parse
  • Content-Type: application/json
  • 字符编码: UTF-8
  • 响应格式: JSON

统一响应格式

所有API接口都遵循统一的JSON响应格式:

{
  "reason": "successed|failed",
  "return_code": 200,
  "result": {
    // 具体数据对象
  },
  "error": "错误信息(仅在失败时返回)"
}

HTTP状态码说明

状态码 说明
200 请求成功
201 创建成功
400 请求参数错误
401 认证失败
404 资源不存在
409 资源冲突(如用户已存在)
500 服务器内部错误

1. 用户注册

接口信息

  • 路径: POST /api/parse/wechat-register
  • 功能: 注册新的微信用户
  • 认证: 无需认证

请求参数

参数名 类型 必填 说明 示例
wechat_code string 微信授权码(15分钟有效期) "wx_code_12345abcde"
phone_number string 手机号码 "13800138000"
id_card_number string 身份证号码 "110101199001011234"
platform string 微信平台类型,默认为小程序 "miniprogram"

请求示例

{
  "wechat_code": "wx_code_12345abcde",
  "phone_number": "13800138000",
  "id_card_number": "110101199001011234",
  "platform": "miniprogram"
}

响应示例

成功响应 (201)

{
  "reason": "successed",
  "return_code": 201,
  "result": {
    "id": "1",
    "openid": "wx_openid_abcd1234567890123456",
    "phone_number": "13800138000",
    "id_card_number": "110101199001011234",
    "login_status": false,
    "user_status": "active",
    "created_at": "2024-01-15T10:30:00+08:00"
  }
}

失败响应 (409)

{
  "reason": "failed",
  "return_code": 409,
  "result": null,
  "error": "用户已存在,openid: wx_openid_abcd1234567890123456"
}

前端示例代码

JavaScript (Fetch API)

async function registerWechatUser(wechatCode, phoneNumber = null, idCardNumber = null, platform = 'miniprogram') {
  try {
    const response = await fetch('/api/parse/wechat-register', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        wechat_code: wechatCode,
        phone_number: phoneNumber,
        id_card_number: idCardNumber,
        platform: platform
      })
    });
    
    const data = await response.json();
    
    if (data.return_code === 201) {
      console.log('注册成功:', data.result);
      return { success: true, data: data.result };
    } else {
      console.error('注册失败:', data.error);
      return { success: false, error: data.error };
    }
  } catch (error) {
    console.error('网络错误:', error);
    return { success: false, error: '网络请求失败' };
  }
}

// 使用示例
registerWechatUser('wx_code_12345abcde', '13800138000', '110101199001011234')
  .then(result => {
    if (result.success) {
      // 注册成功处理
      alert('注册成功!');
    } else {
      // 注册失败处理
      alert('注册失败: ' + result.error);
    }
  });

jQuery

function registerWechatUser(wechatCode, phoneNumber, idCardNumber) {
  $.ajax({
    url: '/api/parse/wechat-register',
    type: 'POST',
    contentType: 'application/json',
    data: JSON.stringify({
      wechat_code: wechatCode,
      phone_number: phoneNumber,
      id_card_number: idCardNumber
    }),
    success: function(data) {
      if (data.return_code === 201) {
        console.log('注册成功:', data.result);
        alert('注册成功!');
      } else {
        console.error('注册失败:', data.error);
        alert('注册失败: ' + data.error);
      }
    },
    error: function(xhr, status, error) {
      console.error('请求失败:', error);
      alert('网络请求失败');
    }
  });
}

2. 用户登录

接口信息

  • 路径: POST /api/parse/wechat-login
  • 功能: 微信用户登录,通过授权码获取openid并更新登录状态和时间
  • 认证: 无需认证

请求参数

参数名 类型 必填 说明 示例
wechat_code string 微信授权码(15分钟有效期) "wx_code_12345abcde"
platform string 微信平台类型,默认为小程序 "miniprogram"

请求示例

{
  "wechat_code": "wx_code_12345abcde",
  "platform": "miniprogram"
}

响应示例

成功响应 (200)

{
  "reason": "successed",
  "return_code": 200,
  "result": {
    "id": "1",
    "openid": "wx_openid_abcd1234567890123456",
    "phone_number": "13800138000",
    "id_card_number": "110101199001011234",
    "login_status": true,
    "login_time": "2024-01-15T14:30:00+08:00",
    "user_status": "active"
  }
}

失败响应 (401)

{
  "reason": "failed",
  "return_code": 401,
  "result": null,
  "error": "用户不存在或账户状态异常"
}

前端示例代码

JavaScript (Fetch API)

async function loginWechatUser(wechatCode, platform = 'miniprogram') {
  try {
    const response = await fetch('/api/parse/wechat-login', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        wechat_code: wechatCode,
        platform: platform
      })
    });
    
    const data = await response.json();
    
    if (data.return_code === 200) {
      console.log('登录成功:', data.result);
      // 保存用户信息到本地存储
      localStorage.setItem('wechat_user', JSON.stringify(data.result));
      return { success: true, data: data.result };
    } else {
      console.error('登录失败:', data.error);
      return { success: false, error: data.error };
    }
  } catch (error) {
    console.error('网络错误:', error);
    return { success: false, error: '网络请求失败' };
  }
}

// 使用示例
loginWechatUser('wx_code_12345abcde')
  .then(result => {
    if (result.success) {
      // 登录成功处理
      window.location.href = '/dashboard';
    } else {
      // 登录失败处理
      alert('登录失败: ' + result.error);
    }
  });

Axios

import axios from 'axios';

async function loginWechatUser(wechatCode) {
  try {
    const response = await axios.post('/api/parse/wechat-login', {
      wechat_code: wechatCode
    });
    
    if (response.data.return_code === 200) {
      // 登录成功
      return { success: true, data: response.data.result };
    } else {
      return { success: false, error: response.data.error };
    }
  } catch (error) {
    console.error('登录请求失败:', error);
    return { success: false, error: '网络请求失败' };
  }
}

3. 用户登出

接口信息

  • 路径: POST /api/parse/wechat-logout
  • 功能: 微信用户登出,更新登录状态
  • 认证: 无需认证

请求参数

参数名 类型 必填 说明 示例
openid string 微信用户openid "wx_openid_abcd1234567890123456"

请求示例

{
  "openid": "wx_openid_abcd1234567890123456"
}

响应示例

成功响应 (200)

{
  "reason": "successed",
  "return_code": 200,
  "result": {
    "message": "用户已成功登出"
  }
}

失败响应 (404)

{
  "reason": "failed",
  "return_code": 404,
  "result": null,
  "error": "用户不存在"
}

前端示例代码

JavaScript (Fetch API)

async function logoutWechatUser(openid) {
  try {
    const response = await fetch('/api/parse/wechat-logout', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        openid: openid
      })
    });
    
    const data = await response.json();
    
    if (data.return_code === 200) {
      console.log('登出成功');
      // 清除本地存储的用户信息
      localStorage.removeItem('wechat_user');
      return { success: true };
    } else {
      console.error('登出失败:', data.error);
      return { success: false, error: data.error };
    }
  } catch (error) {
    console.error('网络错误:', error);
    return { success: false, error: '网络请求失败' };
  }
}

// 使用示例
logoutWechatUser('wx_openid_abcd1234567890123456')
  .then(result => {
    if (result.success) {
      // 登出成功处理
      window.location.href = '/login';
    } else {
      // 登出失败处理
      console.error('登出失败:', result.error);
    }
  });

4. 获取用户信息

接口信息

  • 路径: GET /api/parse/wechat-user
  • 功能: 获取指定微信用户的详细信息
  • 认证: 无需认证

请求参数

参数名 类型 必填 传递方式 说明 示例
openid string Query参数 微信用户openid "wx_openid_abcd1234567890123456"

请求示例

GET /api/parse/wechat-user?openid=wx_openid_abcd1234567890123456

响应示例

成功响应 (200)

{
  "reason": "successed",
  "return_code": 200,
  "result": {
    "id": "1",
    "openid": "wx_openid_abcd1234567890123456",
    "phone_number": "13800138000",
    "id_card_number": "110101199001011234",
    "login_status": true,
    "login_time": "2024-01-15T14:30:00+08:00",
    "user_status": "active",
    "created_at": "2024-01-15T10:30:00+08:00",
    "updated_at": "2024-01-15T14:30:00+08:00"
  }
}

失败响应 (404)

{
  "reason": "failed",
  "return_code": 404,
  "result": null,
  "error": "未找到openid为 wx_openid_abcd1234567890123456 的用户"
}

前端示例代码

JavaScript (Fetch API)

async function getWechatUserInfo(openid) {
  try {
    const url = `/api/parse/wechat-user?openid=${encodeURIComponent(openid)}`;
    const response = await fetch(url, {
      method: 'GET',
      headers: {
        'Content-Type': 'application/json',
      }
    });
    
    const data = await response.json();
    
    if (data.return_code === 200) {
      console.log('获取用户信息成功:', data.result);
      return { success: true, data: data.result };
    } else {
      console.error('获取用户信息失败:', data.error);
      return { success: false, error: data.error };
    }
  } catch (error) {
    console.error('网络错误:', error);
    return { success: false, error: '网络请求失败' };
  }
}

// 使用示例
getWechatUserInfo('wx_openid_abcd1234567890123456')
  .then(result => {
    if (result.success) {
      // 显示用户信息
      displayUserInfo(result.data);
    } else {
      // 处理错误
      alert('获取用户信息失败: ' + result.error);
    }
  });

function displayUserInfo(userInfo) {
  document.getElementById('user-id').textContent = userInfo.id;
  document.getElementById('user-phone').textContent = userInfo.phone_number || '未设置';
  document.getElementById('user-status').textContent = userInfo.login_status ? '在线' : '离线';
}

jQuery

function getWechatUserInfo(openid) {
  $.ajax({
    url: '/api/parse/wechat-user',
    type: 'GET',
    data: { openid: openid },
    success: function(data) {
      if (data.return_code === 200) {
        console.log('获取用户信息成功:', data.result);
        displayUserInfo(data.result);
      } else {
        console.error('获取用户信息失败:', data.error);
        alert('获取用户信息失败: ' + data.error);
      }
    },
    error: function(xhr, status, error) {
      console.error('请求失败:', error);
      alert('网络请求失败');
    }
  });
}

5. 更新用户信息

接口信息

  • 路径: PUT /api/parse/wechat-user
  • 功能: 更新微信用户的个人信息
  • 认证: 无需认证

请求参数

参数名 类型 必填 说明 示例
openid string 微信用户openid "wx_openid_abcd1234567890123456"
phone_number string 要更新的手机号码 "13900139000"
id_card_number string 要更新的身份证号码 "110101199001011234"

请求示例

{
  "openid": "wx_openid_abcd1234567890123456",
  "phone_number": "13900139000",
  "id_card_number": "110101199001011234"
}

响应示例

成功响应 (200)

{
  "reason": "successed",
  "return_code": 200,
  "result": {
    "id": "1",
    "openid": "wx_openid_abcd1234567890123456",
    "phone_number": "13900139000",
    "id_card_number": "110101199001011234",
    "login_status": true,
    "login_time": "2024-01-15T14:30:00+08:00",
    "user_status": "active",
    "created_at": "2024-01-15T10:30:00+08:00",
    "updated_at": "2024-01-15T16:45:00+08:00"
  }
}

失败响应 (404)

{
  "reason": "failed",
  "return_code": 404,
  "result": null,
  "error": "未找到openid为 wx_openid_abcd1234567890123456 的用户"
}

前端示例代码

JavaScript (Fetch API)

async function updateWechatUserInfo(openid, updateData) {
  try {
    const requestData = {
      openid: openid,
      ...updateData
    };
    
    const response = await fetch('/api/parse/wechat-user', {
      method: 'PUT',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(requestData)
    });
    
    const data = await response.json();
    
    if (data.return_code === 200) {
      console.log('更新用户信息成功:', data.result);
      return { success: true, data: data.result };
    } else {
      console.error('更新用户信息失败:', data.error);
      return { success: false, error: data.error };
    }
  } catch (error) {
    console.error('网络错误:', error);
    return { success: false, error: '网络请求失败' };
  }
}

// 使用示例
const updateData = {
  phone_number: '13900139000',
  id_card_number: '110101199001011234'
};

updateWechatUserInfo('wx_openid_abcd1234567890123456', updateData)
  .then(result => {
    if (result.success) {
      // 更新成功处理
      alert('信息更新成功!');
      // 更新页面显示的用户信息
      displayUserInfo(result.data);
    } else {
      // 更新失败处理
      alert('更新失败: ' + result.error);
    }
  });

Vue.js 示例

// 在Vue组件中使用
export default {
  data() {
    return {
      userForm: {
        openid: 'wx_openid_abcd1234567890123456',
        phoneNumber: '',
        idCardNumber: ''
      },
      loading: false
    };
  },
  methods: {
    async updateUserInfo() {
      this.loading = true;
      
      try {
        const response = await fetch('/api/parse/wechat-user', {
          method: 'PUT',
          headers: {
            'Content-Type': 'application/json',
          },
          body: JSON.stringify({
            openid: this.userForm.openid,
            phone_number: this.userForm.phoneNumber,
            id_card_number: this.userForm.idCardNumber
          })
        });
        
        const data = await response.json();
        
        if (data.return_code === 200) {
          this.$message.success('信息更新成功!');
          // 更新本地用户数据
          this.updateLocalUserData(data.result);
        } else {
          this.$message.error('更新失败: ' + data.error);
        }
      } catch (error) {
        this.$message.error('网络请求失败');
        console.error('更新用户信息失败:', error);
      } finally {
        this.loading = false;
      }
    },
    
    updateLocalUserData(userData) {
      // 更新本地存储的用户数据
      localStorage.setItem('wechat_user', JSON.stringify(userData));
    }
  }
};

前端集成指南

微信小程序获取授权码

在微信小程序中,需要先获取授权码,然后传递给后端API:

// 微信小程序端代码
wx.login({
  success: function(res) {
    if (res.code) {
      // 将授权码发送给后端
      const wechatCode = res.code;
      
      // 调用注册或登录API
      registerOrLogin(wechatCode);
    } else {
      console.error('获取微信授权码失败');
    }
  },
  fail: function(err) {
    console.error('微信登录失败:', err);
  }
});

// 注册或登录函数
async function registerOrLogin(wechatCode) {
  // 先尝试登录
  const loginResult = await loginWechatUser(wechatCode);
  
  if (loginResult.success) {
    // 登录成功,保存用户信息
    wx.setStorageSync('user_info', loginResult.data);
    wx.showToast({ title: '登录成功', icon: 'success' });
  } else if (loginResult.error && loginResult.error.includes('不存在')) {
    // 用户不存在,尝试注册
    wx.showModal({
      title: '首次使用',
      content: '检测到您是首次使用,是否注册账号?',
      success: async function(modalRes) {
        if (modalRes.confirm) {
          const registerResult = await registerWechatUser(wechatCode);
          if (registerResult.success) {
            wx.setStorageSync('user_info', registerResult.data);
            wx.showToast({ title: '注册成功', icon: 'success' });
          } else {
            wx.showToast({ title: '注册失败', icon: 'error' });
          }
        }
      }
    });
  } else {
    wx.showToast({ title: '登录失败', icon: 'error' });
  }
}

用户状态管理

// 检查用户登录状态
function checkUserLoginStatus() {
  const userInfo = wx.getStorageSync('user_info');
  if (userInfo && userInfo.openid) {
    // 验证用户状态是否仍然有效
    return getWechatUserInfo(userInfo.openid)
      .then(result => {
        if (result.success && result.data.login_status) {
          return { isLoggedIn: true, userInfo: result.data };
        } else {
          // 清除无效的本地数据
          wx.removeStorageSync('user_info');
          return { isLoggedIn: false };
        }
      });
  }
  return Promise.resolve({ isLoggedIn: false });
}

// 用户登出
async function logoutUser() {
  const userInfo = wx.getStorageSync('user_info');
  if (userInfo && userInfo.openid) {
    const result = await logoutWechatUser(userInfo.openid);
    if (result.success) {
      wx.removeStorageSync('user_info');
      wx.showToast({ title: '已退出登录', icon: 'success' });
      // 跳转到登录页面
      wx.redirectTo({ url: '/pages/login/login' });
    }
  }
}

环境配置

确保在不同环境中正确配置API基础路径:

// config.js
const config = {
  development: {
    apiBaseUrl: 'http://localhost:5000/api/parse'
  },
  production: {
    apiBaseUrl: 'https://your-domain.com/api/parse'
  }
};

const currentEnv = 'development'; // 根据实际环境设置
export const API_BASE_URL = config[currentEnv].apiBaseUrl;

// 在API调用中使用
async function registerWechatUser(wechatCode, phoneNumber, idCardNumber, platform = 'miniprogram') {
  const response = await fetch(`${API_BASE_URL}/wechat-register`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      wechat_code: wechatCode,
      phone_number: phoneNumber,
      id_card_number: idCardNumber,
      platform: platform
    })
  });
  return response.json();
}

错误处理最佳实践

1. 统一错误处理函数

function handleApiError(data) {
  const errorMessages = {
    400: '请求参数错误',
    401: '认证失败',
    404: '资源不存在',
    409: '资源冲突',
    500: '服务器内部错误'
  };
  
  const message = data.error || errorMessages[data.return_code] || '未知错误';
  return message;
}

// 使用示例
async function apiCall() {
  try {
    const response = await fetch('/api/parse/wechat-login', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ wechat_code: 'test' })
    });
    
    const data = await response.json();
    
    if (data.return_code === 200) {
      return { success: true, data: data.result };
    } else {
      const errorMessage = handleApiError(data);
      return { success: false, error: errorMessage };
    }
  } catch (error) {
    return { success: false, error: '网络连接失败' };
  }
}

2. 请求重试机制

async function apiCallWithRetry(url, options, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      const response = await fetch(url, options);
      const data = await response.json();
      
      // 如果是服务器错误且还有重试次数,则重试
      if (data.return_code === 500 && i < maxRetries - 1) {
        await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1))); // 递增延迟
        continue;
      }
      
      return data;
    } catch (error) {
      if (i === maxRetries - 1) throw error;
      await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
    }
  }
}

3. 请求拦截器示例 (Axios)

import axios from 'axios';

// 创建axios实例
const apiClient = axios.create({
  baseURL: '/api/parse',
  timeout: 10000,
  headers: {
    'Content-Type': 'application/json'
  }
});

// 响应拦截器
apiClient.interceptors.response.use(
  response => {
    const data = response.data;
    
    if (data.return_code >= 200 && data.return_code < 300) {
      return data;
    } else {
      return Promise.reject(new Error(data.error || '请求失败'));
    }
  },
  error => {
    console.error('API请求失败:', error);
    return Promise.reject(error);
  }
);

// 使用示例
async function loginUser(wechatCode) {
  try {
    const data = await apiClient.post('/wechat-login', {
      wechat_code: wechatCode
    });
    return { success: true, data: data.result };
  } catch (error) {
    return { success: false, error: error.message };
  }
}

常见问题 (FAQ)

Q1: 如何处理网络超时?

A: 建议设置合理的超时时间(如10秒),并提供重试机制:

const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 10000);

fetch('/api/parse/wechat-login', {
  signal: controller.signal,
  // ... 其他选项
}).finally(() => {
  clearTimeout(timeoutId);
});

Q2: 如何存储用户登录状态?

A: 建议使用localStorage或sessionStorage存储用户信息:

// 登录成功后存储
localStorage.setItem('wechat_user', JSON.stringify(userData));

// 检查登录状态
function isUserLoggedIn() {
  const user = localStorage.getItem('wechat_user');
  return user && JSON.parse(user).login_status;
}

// 登出时清除
localStorage.removeItem('wechat_user');

Q3: 如何处理并发请求?

A: 对于同一用户的多个请求,建议使用请求队列或防抖机制:

let pendingRequest = null;

async function loginWithDebounce(wechatCode) {
  if (pendingRequest) {
    return pendingRequest;
  }
  
  pendingRequest = loginWechatUser(wechatCode);
  
  try {
    const result = await pendingRequest;
    return result;
  } finally {
    pendingRequest = null;
  }
}

Q4: 如何实现自动重新登录?

A: 可以在应用启动时检查本地存储的用户信息并验证:

async function autoLogin() {
  const storedUser = localStorage.getItem('wechat_user');
  if (!storedUser) return false;
  
  const userData = JSON.parse(storedUser);
  
  // 验证用户状态是否仍然有效
  const result = await getWechatUserInfo(userData.wechat_code);
  
  if (result.success && result.data.login_status) {
    return true;
  } else {
    // 清除无效的本地数据
    localStorage.removeItem('wechat_user');
    return false;
  }
}

// 应用启动时调用
autoLogin().then(isLoggedIn => {
  if (isLoggedIn) {
    // 跳转到主页面
    window.location.href = '/dashboard';
  } else {
    // 跳转到登录页面
    window.location.href = '/login';
  }
});

版本信息

  • 文档版本: v2.0.0
  • API版本: v2.0.0
  • 最后更新: 2025-01-15
  • 维护者: DataOps团队
  • 更新内容: 升级为基于openid的微信认证系统,符合微信官方授权规范

环境配置要求

后端环境变量

在部署前,请确保设置以下环境变量:

# 微信小程序配置(必需)
WECHAT_MINIPROGRAM_APP_ID=your_miniprogram_app_id_here
WECHAT_MINIPROGRAM_APP_SECRET=your_miniprogram_app_secret_here

# 微信公众号配置(可选)
WECHAT_OFFICIAL_APP_ID=your_official_account_app_id_here  
WECHAT_OFFICIAL_APP_SECRET=your_official_account_app_secret_here

# 数据库配置
DATABASE_URL=postgresql://username:password@localhost:5432/dataops_db

数据库初始化

首次部署时,请执行以下SQL脚本:

# 创建微信用户表
psql -U username -d database_name -f database/create_wechat_users.sql

# 如果从旧版本升级,执行迁移脚本
psql -U username -d database_name -f database/migrate_wechat_code_to_openid.sql

联系支持

如有问题或建议,请联系: