静默授权功能解耦方案.md 7.73 KB

静默授权功能解耦方案

当前功能分析

核心功能

静默授权功能主要包含以下核心能力:

  1. 静默授权 - silentAuth() 函数,自动获取微信授权并保存会话信息
  2. 授权状态检查 - needAuth() 函数,检查是否需要重新授权
  3. 页面路径管理 - 保存和恢复用户访问路径
  4. 授权重定向 - 授权完成后的页面跳转逻辑
  5. 分享页面处理 - 处理从分享链接进入的授权逻辑

当前耦合点分析

1. 项目特定依赖

  • 路由存储: 依赖 @/stores/router (Pinia store)
  • 网络请求: 依赖 @/utils/request (axios封装)
  • 业务API: 依赖 @/api/family (家庭相关接口)
  • 页面路径: 硬编码了项目特定的页面路径
    • /pages/Dashboard/index (首页)
    • /pages/Welcome/index (欢迎页)
    • /pages/auth/index (授权页)

2. 业务逻辑耦合

  • 家庭状态检查: checkUserHasFamily() 函数与业务强耦合
  • 跳转逻辑: 根据家庭状态决定跳转目标的业务逻辑
  • 测试环境配置: 硬编码的测试openid列表

3. 框架依赖

  • Taro框架: 使用Taro的API进行页面跳转和存储操作
  • 微信小程序: 依赖微信小程序的wx API

解耦方案

方案一:配置化解耦(推荐)

创建一个通用的授权管理器,通过配置参数来适配不同项目。

1. 核心授权管理器

// auth-manager.js
export class AuthManager {
  constructor(config) {
    this.config = {
      // 授权接口配置
      authUrl: '/srv/?a=openid',

      // 页面路径配置
      authPage: '/pages/auth/index',
      defaultPage: '/pages/index/index',

      // 存储配置
      sessionKey: 'sessionid',
      routeKey: 'saved_route',

      // 测试环境配置
      testOpenIds: [],

      // 自定义钩子函数
      onAuthSuccess: null,
      onAuthError: null,
      checkUserStatus: null, // 用户状态检查函数
      getRedirectPath: null, // 获取重定向路径函数

      // 网络请求实例
      httpClient: null,

      ...config
    }
  }

  // 静默授权
  async silentAuth(onSuccess, onError) {
    // 实现逻辑...
  }

  // 检查授权状态
  needAuth() {
    // 实现逻辑...
  }

  // 页面跳转管理
  async returnToOriginalPage(defaultPath) {
    // 实现逻辑...
  }
}

2. 项目适配配置

// 老来赛项目配置
import { getMyFamiliesAPI } from '@/api/family'
import request from '@/utils/request'

const authConfig = {
  authPage: '/pages/auth/index',
  defaultPage: '/pages/Dashboard/index',
  httpClient: request,
  testOpenIds: ['h-008', 'h-009', 'h-010'],

  // 用户状态检查
  async checkUserStatus() {
    try {
      const { code, data } = await getMyFamiliesAPI()
      return code && data && data.length > 0
    } catch (error) {
      return false
    }
  },

  // 获取重定向路径
  async getRedirectPath(savedPath, defaultPath) {
    const hasFamily = await this.checkUserStatus()
    if (!hasFamily) {
      return '/pages/Welcome/index'
    }
    return savedPath || defaultPath
  }
}

export const authManager = new AuthManager(authConfig)

方案二:插件化解耦

将授权功能拆分为多个独立的插件模块。

1. 核心授权插件

// plugins/auth-core.js
export class AuthCore {
  constructor(storage, http) {
    this.storage = storage
    this.http = http
  }

  async silentAuth(config) {
    // 核心授权逻辑
  }

  needAuth(sessionKey = 'sessionid') {
    // 授权状态检查
  }
}

2. 路由管理插件

// plugins/route-manager.js
export class RouteManager {
  constructor(storage, navigator) {
    this.storage = storage
    this.navigator = navigator
  }

  saveCurrentPath() {
    // 保存当前路径
  }

  async returnToPath(pathResolver) {
    // 返回指定路径
  }
}

3. 业务适配插件

// plugins/business-adapter.js
export class BusinessAdapter {
  constructor(apiClient) {
    this.apiClient = apiClient
  }

  async checkUserStatus() {
    // 业务相关的用户状态检查
  }

  resolveRedirectPath(userStatus, savedPath, defaultPath) {
    // 根据业务逻辑解析重定向路径
  }
}

方案三:抽象接口解耦

定义标准接口,让不同项目实现自己的适配器。

1. 定义抽象接口

// interfaces/auth-interfaces.js
export class IStorageAdapter {
  get(key) { throw new Error('Not implemented') }
  set(key, value) { throw new Error('Not implemented') }
  remove(key) { throw new Error('Not implemented') }
}

export class IHttpAdapter {
  post(url, data) { throw new Error('Not implemented') }
}

export class INavigatorAdapter {
  navigateTo(url) { throw new Error('Not implemented') }
  redirectTo(url) { throw new Error('Not implemented') }
  reLaunch(url) { throw new Error('Not implemented') }
}

export class IBusinessAdapter {
  async checkUserStatus() { throw new Error('Not implemented') }
  resolveRedirectPath(savedPath, defaultPath) { throw new Error('Not implemented') }
}

2. Taro适配器实现

// adapters/taro-adapters.js
import Taro from '@tarojs/taro'

export class TaroStorageAdapter extends IStorageAdapter {
  get(key) {
    return wx.getStorageSync(key)
  }

  set(key, value) {
    wx.setStorageSync(key, value)
  }

  remove(key) {
    wx.removeStorageSync(key)
  }
}

export class TaroNavigatorAdapter extends INavigatorAdapter {
  navigateTo(url) {
    return Taro.navigateTo({ url })
  }

  redirectTo(url) {
    return Taro.redirectTo({ url })
  }

  reLaunch(url) {
    return Taro.reLaunch({ url })
  }
}

推荐实施步骤

第一步:提取核心功能

  1. silentAuthneedAuth 函数提取为独立模块
  2. 移除硬编码的页面路径和业务逻辑
  3. 通过参数传递配置信息

第二步:创建适配层

  1. 创建存储适配器(支持不同的存储方案)
  2. 创建网络请求适配器(支持不同的HTTP客户端)
  3. 创建导航适配器(支持不同的路由方案)

第三步:业务逻辑分离

  1. 将用户状态检查逻辑抽象为可配置的函数
  2. 将重定向逻辑抽象为策略模式
  3. 提供默认实现和自定义扩展点

第四步:打包发布

  1. 创建npm包,支持不同框架的适配器
  2. 提供详细的文档和示例
  3. 支持TypeScript类型定义

使用示例

在新项目中使用

// 安装
npm install @your-org/universal-auth

// 配置
import { AuthManager } from '@your-org/universal-auth'
import { TaroAdapters } from '@your-org/universal-auth/adapters/taro'

const authManager = new AuthManager({
  ...TaroAdapters,
  authUrl: '/api/auth',
  authPage: '/pages/login/index',
  defaultPage: '/pages/home/index',

  async checkUserStatus() {
    // 项目特定的用户状态检查逻辑
    const userInfo = await getUserInfo()
    return userInfo.isActive
  },

  async getRedirectPath(savedPath, defaultPath) {
    // 项目特定的重定向逻辑
    const userStatus = await this.checkUserStatus()
    return userStatus ? (savedPath || defaultPath) : '/pages/onboarding/index'
  }
})

// 使用
await authManager.silentAuth()

总结

推荐使用方案一:配置化解耦,因为它:

  1. 实施成本最低 - 只需要重构现有代码,不需要重新设计架构
  2. 兼容性最好 - 保持现有功能不变,只是增加了配置能力
  3. 维护成本最低 - 一套代码支持多个项目,统一维护和升级
  4. 学习成本最低 - API保持基本不变,只需要了解配置选项

通过这种方式,你可以将静默授权功能打包成一个通用的npm包,在其他项目中只需要提供相应的配置参数即可快速集成。