Basic_Mini 项目初始化

· Basic_Mini

今日进展

  1. 六层架构搭建:页面 → 组件 → 服务 → 接口 → 基础设施 → 模型,与 Basic_Web 五层架构一一对应
  2. JWT 双 Token 自动刷新:Access 过期自动用 Refresh 换新,刷新期间其他请求排队等待
  3. Token 加密存储:AES 加密后存入 wx.storage,防止反编译泄露
  4. 路由守卫:需登录页面自动跳转登录,需权限页面自动检查权限码
  5. 权限组件:auth-guard(登录检查)、permission-guard(权限检查)
  6. 统一错误处理:401 自动刷新 Token、403 提示权限不足、429 提示限流、5xx 提示服务器异常
  7. 基础组件:nav-bar(导航栏)、empty-state(空状态)、loading-state(加载状态)
  8. 页面骨架:登录、注册、忘记密码、首页、个人中心、修改密码

关键代码/伪代码

Token 自动刷新 — 请求排队

// Token 刷新最头疼的问题:
// 同时发了5个请求,Access Token 都过期了
// 如果5个请求都去刷新,会触发5次刷新,后面的可能失败
// 解决方案:只刷新一次,其他请求排队等待

CLASS HttpClient:
    refreshTokenPromise: Promise | null = null

    ASYNC request(url, options):
        TRY:
            RETURN AWAIT this.rawRequest(url, options)
        CATCH (error):
            IF error.code === 401 AND this.hasRefreshToken():
                // 第一个发现过期的请求负责刷新
                IF NOT this.refreshTokenPromise:
                    this.refreshTokenPromise = this.refreshTokens()
                // 其他请求排队等待同一个刷新 Promise
                AWAIT this.refreshTokenPromise
                this.refreshTokenPromise = null
                // 刷新成功,重试原请求
                RETURN AWAIT this.rawRequest(url, options)
            // 刷新也失败了,清空认证数据,跳转登录
            this.clearAuth()
            router.navigateTo('/pages/auth/login/login')
            THROW error

路由守卫 — 自动检查登录和权限

// 路由守卫:跳转前自动检查登录状态和权限
// 不用每个页面都写 if (!isLoggedIn) { ... }

CLASS Router:
    ASYNC navigateTo(url: string):
        // 检查目标页面是否需要登录
        IF requiresAuth(url):
            IF NOT authService.isLoggedIn():
                // 保存目标路径,登录后自动跳回
                storage.set('redirect_url', url)
                RETURN wx.navigateTo({ url: '/pages/auth/login/login' })

        // 检查目标页面是否需要特定权限
        requiredPerm = getRequiredPermission(url)
        IF requiredPerm AND NOT permissionService.hasPermission(requiredPerm):
            wx.showToast({ title: '权限不足', icon: 'none' })
            RETURN

        // 通过所有检查,正常跳转
        wx.navigateTo({ url })

分层架构 — 页面不直接调用 API

// 错误写法:页面直接调 API
// Page({
//   async onLoad() {
//     const res = await client.get('/api/v1/users/me')  // 禁止!
//   }
// })

// 正确写法:页面调 Service,Service 调 API

// services/auth-service.ts
CLASS AuthService:
    ASYNC login(data: LoginRequest): Promise:
        logger.info('auth_login_start', { username: data.username })
        TRY:
            tokenPair = AWAIT auth.login(data)  // API 层
            this.saveTokens(tokenPair)
            profile = AWAIT this.loadProfile()
            logger.info('auth_login_success', { user_id: profile.id })
            RETURN profile
        CATCH (e):
            logger.error('auth_login_failed', { error: String(e) })
            THROW e

// pages/auth/login/login.ts
Page({
    ASYNC onLogin():
        this.setData({ loading: true })
        TRY:
            AWAIT authService.login({ username, password })
            wx.showToast({ title: '登录成功', icon: 'success' })
        CATCH (e):
            wx.showToast({ title: e.message, icon: 'none' })
        FINALLY:
            this.setData({ loading: false })
})

遇到的问题

  • 微信小程序没有 npm 原生支持:需要先 npm install 再"构建 npm",而且构建后的路径和 node_modules 不同。解决方案:package.json 放在 src/ 目录下,构建后 miniprogram_npm 自动可用
  • TypeScript 严格模式下 Page/Component 的类型推断不友好:微信官方没有提供完整的类型定义。解决方案:自己定义 PageOptions 和 ComponentOptions 接口
  • Token 加密存储的密钥管理:密钥不能硬编码在代码里(反编译可读),也不能每次随机生成(换设备就解不开)。解决方案:密钥由设备指纹 + 应用密钥派生

明日计划

  • 完善权限组件的 UI 反馈:无权限时显示空状态而非空白
  • 添加分包页面示例
  • 与 Basic_Web 联调登录流程