作者: 孟繁永

  • 去医院挂专家号还是普通号?

    首先看是什么病?如果用默沙东诊疗手册都可以自己判断个差不多的,想必就不用去挂专家号了,去医院主要是为了用检查手段,再让医生确认下。

    其实很多医院的专家号和普通号是一波医生在出诊,给医生轮流出专家号是为了提高诊费收入,让医生轮流出普通号是为了照顾普通患者,其实接诊普通号的大夫并不差,改天他/她就去出专家号了。

    如果不是特别特殊的病,完全不必去挂专家号,除非你确信只有这个专家才能看,这样的情况并不多,反而是很多有钱有闲的人,一个不要紧的病,把圈子里的专家看一遍,还要去河南去看某个神医,这才是真正的医疗资源浪费。

  • 明基M209扫描仪驱动和软件安装

    明基M209扫描仪驱动和软件安装

    网上找一下M209的驱动安装包,貌似官网已经搜不到了,这是老产品,我是从太平洋下载的,压缩包里面是个iso文件,另外安装了一个virtual clone drive来解压这个iso。

    驱动安装以后,系统菜单中的路径有可能是错误的,可以直接去c盘下面找一下安装目录,确认下路径,并把这个路径更新到快捷菜单中。

    驱动包里是实际上有一个扫描软件PanelV2.0,只是这个官方的软件并不好用,连幅面都无法修改,默认是A3,跟机器一样。

    所以,另外装了一个NAPS2,也是grok推荐给我的。安装的时候提示需要.net4.6.2,如有问题可以看上一篇博客。

  • windows7安装.net framework 4.6.2 时间戳签名或证书无法验证

    在微软官网下载了4.6.2的离线安装包,安装时报错,这是因为win7上没有sha2验证,通过安装系统更新的方式有点麻烦了,于是问了下grok,它给出一个办法,绕过验证,于是,在点开离线安装包以后,c盘下就有了一个临时文件夹,把它复制一份,存到别的位置,在里面找到netfx_Full_x64.msi(64 位系统)或 netfx_Full_x86.msi(32 位系统),双击运行就行了。

  • 剑桥词典 +Plus 平台上的一款在线单词游戏

    剑桥词典 +Plus 平台上的一款在线单词游戏

    1. 功能概述

    Word Scramble 是剑桥词典 +Plus 平台上的一款在线单词游戏,玩家需要在限定时间内根据给定的单词定义,将一组打乱的字母重新排列组成正确的单词。该游戏旨在帮助英语学习者提升词汇和拼写能力,同时通过游戏化的方式增加趣味性。以下是主要功能:

    • 核心玩法:显示单词定义和一组打乱的字母,玩家需在规定时间内拼出正确单词。
    • 计时机制:玩家需要在限定时间内完成单词拼写,增加挑战性。
    • 交互反馈:提供即时反馈(正确/错误),并可能记录玩家的历史成绩。
    • 多语言支持:支持多种语言界面(如中文、英语、西班牙语等),便于全球用户使用。
    • 用户账户功能:允许用户注册/登录以保存游戏进度、创建词汇表或参与社区活动。
    • 其他功能:提供词汇表创建、分享功能,以及与语法和图片测验的链接。

    2. 技术实现分析

    基于网页游戏的特性,Word Scramble 可能采用了以下技术栈和实现方式:

    (1) 前端技术

    • HTML/CSS/JavaScript:
      • HTML:用于构建游戏的基本结构,如显示定义、字母块、输入区域和计时器。
      • CSS:负责界面样式,包括响应式设计(适配桌面、平板和手机)。游戏可能使用现代 CSS(如 Flexbox 或 Grid)来布局字母块和交互元素,并通过动画(如 CSS 动画或 transitions)增强用户体验。
      • JavaScript:核心交互逻辑通过 JavaScript 实现。例如:
        • 动态生成打乱字母:通过算法随机打乱单词的字母序列。
        • 计时器逻辑:使用 setInterval 或 requestAnimationFrame 实现倒计时功能。
        • 用户输入处理:监听用户拖动字母或键盘输入,实时验证拼写的正确性。
        • 反馈动画:通过 JavaScript 操作 DOM 或使用动画库(如 GSAP)实现正确/错误反馈的动态效果。
      • 前端框架:可能使用 React、Vue.js 或 Angular 等框架来管理复杂的状态(如游戏进度、用户输入、计时器状态等),提高开发效率和代码可维护性。
    • 响应式设计:
      • 游戏支持多种设备(桌面、平板、手机),可能使用媒体查询(Media Queries)或 Bootstrap 等框架确保界面适配不同屏幕尺寸。
      • 触摸事件(如拖放字母)可能通过 touchstart、touchmove 等 API 实现移动端交互。
    • 音效与动画:
      • 游戏包含动画和音效(如点击字母时的声音或完成单词时的提示音)。音效可能通过 HTML5 的 <audio> 标签或 Web Audio API 实现。
      • 动画可能通过 CSS 或 JavaScript 库(如 Lottie 或 GSAP)实现,增强沉浸感。

    (2) 后端技术

    • 内容管理系统 (CMS):
      • 根据参考信息,剑桥词典团队与 Alternative View Studios 合作开发了游戏和内容管理系统(CMS)。CMS 用于管理每日更新的单词定义和打乱的字母组合。
      • 后端可能使用 Python(Django/Flask)、Node.js 或 PHP 等技术生成动态内容,并通过 API 提供给前端。
      • 数据库(如 MySQL、PostgreSQL 或 MongoDB)存储单词库、定义、用户数据(如分数、词汇表)等intregration of the word list functionality with the game itself is uncertain, as the provided references do not explicitly detail this integration. However, it is reasonable to infer that the word lists created or shared via the Cambridge Dictionary +Plus platform could be used as a source for the words and definitions in the Word Scramble game. The game likely pulls from a curated database of words and their definitions, which could be linked to the platform’s word list feature, allowing for dynamic content updates and personalized user experiences.

    (3) 数据管理与词库

    • 单词与定义数据库:
      • 游戏依赖剑桥词典的词汇数据库,包含单词、定义和翻译。数据库可能按难度(如初级、中级、高级)分类,确保游戏适合不同英语水平用户。
      • 每日更新的定义可能通过后端 CMS 自动抽取或人工 curation,确保内容新鲜且多样化。
      • 打乱字母的算法可能在后端或前端实现。一种可能的算法是:javascriptfunction shuffleWord(word) { let letters = word.split(''); for (let i = letters.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [letters[i], letters[j]] = [letters[j], letters[i]]; // 交换字母 } return letters.join(''); }该算法随机打乱单词的字母序列,确保每次游戏的字母顺序不同。
    • 用户数据:
      • 用户登录后,游戏可能通过 API 存储玩家的历史成绩、进度或自定义词汇表到数据库。用户数据可能通过 JWT(JSON Web Token)或 session-based 认证机制管理。

    (4) API 交互

    • 前端通过 RESTful API 或 GraphQL 与后端通信,获取每日单词、定义、验证用户输入的正确性等。
    • 示例 API 端点:
      • GET /api/word-scramble/daily-word:获取每日单词和定义。
      • POST /api/word-scramble/submit:提交用户拼写的单词并验证。
      • GET /api/user/word-lists:获取用户创建的词汇表。
    • API 可能使用 HTTPS 确保数据传输安全,并通过 rate-limiting 或 throttling 防止滥用。

    (5) 多语言支持

    • 游戏支持多种语言界面(如中文、英语、西班牙语等)。这可能通过以下方式实现:
      • 国际化 (i18n):使用类似 i18next 或 react-i18next 的库管理多语言文本。
      • 动态语言切换:通过检测用户浏览器语言或提供语言选择器,动态加载对应语言的 UI 文本。
      • 后端 API 可能返回特定语言的定义和提示,确保游戏内容与用户语言一致。

    (6) 用户账户与社区功能

    • 用户可注册/登录以访问 Cambridge Dictionary +Plus 的功能(如创建词汇表、分享内容)。
    • 登录可能通过 OAuth、SAML 或自定义认证机制实现,用户数据存储在安全的数据库中。
    • 词汇表功能允许用户创建、下载和分享单词列表,可能通过以下流程:
      • 用户通过 UI 创建词汇表,数据通过 API 提交到后端。
      • 后端将词汇表存储到数据库,并生成可分享的链接。
      • 社区词汇表可能通过审核机制(如人工或自动)确保内容质量。

    (7) 性能优化

    • 前端优化:
      • 使用代码分割(code splitting)或懒加载(lazy loading)减少初始加载时间。
      • 缓存静态资源(如 CSS、JS、图片)到 CDN(如 Cloudflare、AWS CloudFront)以加速加载。
    • 后端优化:
      • 使用数据库索引优化单词查询性能。
      • 缓存常用单词和定义(如使用 Redis 或 Memcached)以减少数据库负载。
    • SEO 和可访问性:
      • 游戏页面可能包含元标签(如 <meta name=”description”>)以优化搜索引擎排名。
      • 遵循 WCAG(Web Content Accessibility Guidelines)标准,确保游戏对残障人士友好(如支持键盘导航、屏幕阅读器)。

    3. 用户体验设计

    • 游戏流程:
      1. 用户进入游戏页面,系统加载每日单词和定义。
      2. 显示打乱的字母块(可能支持拖放或点击选择),并启动计时器。
      3. 用户提交答案后,系统验证并显示结果(正确/错误)。
      4. 提供“重玩”或“下一题”选项,鼓励用户继续挑战。
    • 反馈机制:
      • 正确答案可能触发正面反馈(如绿色勾选、庆祝动画)。
      • 错误答案可能显示提示或正确答案,帮助用户学习。
    • 游戏化元素:
      • 计时器增加紧迫感,激励玩家快速思考。
      • 每日更新鼓励用户反复访问,类似“每日挑战”模式。
      • 可能的积分或排行榜系统(虽然参考信息未明确提及),增加竞争性。

    4. 可能的挑战与解决方案

    • 挑战 1:跨设备兼容性:
      • 问题:不同设备的屏幕尺寸和输入方式(如触摸 vs 鼠标)可能影响体验。
      • 解决方案:使用响应式设计和测试多设备兼容性,确保触摸和键盘输入流畅。
    • 挑战 2:单词难度控制:
      • 问题:如何确保单词难度适合不同英语水平用户?
      • 解决方案:从数据库中按难度分级抽取单词,或允许用户选择难度级别。
    • 挑战 3:性能与延迟:
      • 问题:高并发用户访问可能导致 API 或页面加载缓慢。
      • 解决方案:使用 CDN、数据库优化和负载均衡(如 AWS ELB)确保性能。

    5. 总结

    Word Scramble 是一款结合教育与娱乐的在线游戏,通过现代 Web 技术(HTML/CSS/JavaScript、可能的框架如 React)、后端 CMS 和数据库、以及 API 驱动的内容更新实现。其多语言支持、响应式设计和用户账户功能增强了全球用户的使用体验。游戏通过每日更新的单词和定义、计时挑战以及交互反馈,成功吸引英语学习者反复参与,同时通过词汇表功能与剑桥词典 +Plus 平台深度整合。

    参考资料:

    • 剑桥词典 +Plus 平台信息
    • 游戏设计与开发信息
  • 基于 Vue3 的 monorepo 架构设计一个数字阅读前端项目

    基于 Vue3 的 monorepo 架构设计一个数字阅读前端项目

    基于 Vue3 的 monorepo 架构设计一个数字阅读前端项目,需要清晰的模块划分和合理的目录结构,以支持图书搜索、用户登录、用户中心、数字图书模板以及特色应用场景(如汉字笔画练习、中英文字词听写、普通话语音测评等)。以下是一个详细的项目结构设计,采用 monorepo 方式,使用 apps、packages 和 shared-components 文件夹来组织子模块,同时考虑 Vue3 的组件化开发和复用性。


    项目设计目标

    1. 模块化:通过 monorepo 架构,将不同功能模块(如搜索、用户中心、特色场景)拆分为独立子模块,便于维护和扩展。
    2. 复用性:将通用组件、工具函数和类型定义放在 shared-components 和 packages 中,供多个 apps 复用。
    3. 可扩展性:支持多种数字图书模板和特色应用场景,确保新功能可以轻松接入。
    4. 开发效率:使用 pnpm(推荐用于 monorepo)管理依赖,结合 Vite 作为构建工具,提升开发体验。

    技术栈

    • 框架:Vue3(组合式 API)
    • 构建工具:Vite
    • 包管理:pnpm(支持 monorepo 的 workspace 功能)
    • 状态管理:Pinia
    • 路由:Vue Router
    • UI 组件库:可选(如 Element Plus 或 Naive UI),或基于 shared-components 自定义
    • 类型检查:TypeScript
    • 代码规范:ESLint + Prettier
    • 测试:Vitest + Vue Test Utils
    • 部署:支持静态部署(如 Vercel)或 SSR(如 Nuxt 3,可选)

    项目结构

    以下是项目的目录结构,分为 apps(应用入口)、packages(工具库和业务逻辑)、shared-components(通用组件)三大模块,并包含配置文件和文档。

    digital-reading-project/
    ├── apps/
    │   ├── book-search/                    # 图书搜索应用
    │   │   ├── src/
    │   │   │   ├── components/           # 搜索相关组件
    │   │   │   ├── views/                # 页面视图
    │   │   │   ├── router/               # 路由配置
    │   │   │   ├── store/                # Pinia 状态管理
    │   │   │   ├── assets/               # 静态资源
    │   │   │   ├── App.vue               # 应用根组件
    │   │   │   └── main.ts               # 入口文件
    │   │   ├── vite.config.ts            # Vite 配置文件
    │   │   ├── package.json              # 应用依赖
    │   │   └── tsconfig.json             # TypeScript 配置
    │   ├── user-center/                    # 用户中心应用(登录、个人资料等)
    │   │   ├── src/
    │   │   │   ├── components/           # 用户中心相关组件
    │   │   │   ├── views/                # 页面视图(如个人资料、设置)
    │   │   │   ├── router/
    │   │   │   ├── store/
    │   │   │   ├── assets/
    │   │   │   ├── App.vue
    │   │   │   └── main.ts
    │   │   ├── vite.config.ts
    │   │   ├── package.json
    │   │   └── tsconfig.json
    │   ├── digital-books/                  # 数字图书模板应用
    │   │   ├── src/
    │   │   │   ├── templates/            # 多种图书模板
    │   │   │   │   ├── novel/          # 小说阅读模板
    │   │   │   │   ├── textbook/       # 教材阅读模板
    │   │   │   │   └── comic/          # 漫画阅读模板
    │   │   │   ├── components/
    │   │   │   ├── views/
    │   │   │   ├── router/
    │   │   │   ├── store/
    │   │   │   ├── assets/
    │   │   │   ├── App.vue
    │   │   │   └── main.ts
    │   │   ├── vite.config.ts
    │   │   ├── package.json
    │   │   └── tsconfig.json
    │   ├── special-scenes/                 # 特色应用场景
    │   │   ├── src/
    │   │   │   ├── scenes/
    │   │   │   │   ├── hanzi-practice/ # 汉字笔画练习
    │   │   │   │   ├── dictation/      # 中英文字词听写
    │   │   │   │   └── mandarin-test/  # 普通话语音测评
    │   │   │   ├── components/
    
    
    │   │   │   ├── views/
    │   │   │   ├── router/
    │   │   │   ├── store/
    │   │   │   ├── assets/
    │   │   │   ├── App.vue
    │   │   │   └── main.ts
    │   │   ├── vite.config.ts
    │   │   ├── package.json
    │   │   └── tsconfig.json
    ├── packages/
    │   ├── api-client/                     # API 请求封装
    │   │   ├── src/
    │   │   │   ├── index.ts              # 导出 API 方法
    │   │   │   ├── book.ts               # 图书相关 API
    │   │   │   ├── user.ts               # 用户相关 API
    │   │   │   └── special.ts            # 特色场景 API
    │   │   ├── package.json
    │   │   └── tsconfig.json
    │   ├── utils/                          # 通用工具函数
    │   │   ├── src/
    │   │   │   ├── index.ts              # 导出工具函数
    │   │   │   ├── format.ts             # 格式化工具(如时间、文本)
    │   │   │   ├── storage.ts            # 本地存储封装
    │   │   │   └── audio.ts              # 音频处理工具(如语音测评)
    │   │   ├── package.json
    │   │   └── tsconfig.json
    │   ├── types/                          # TypeScript 类型定义
    │   │   ├── src/
    │   │   │   ├── index.ts              # 导出类型
    │   │   │   ├── book.ts               # 图书相关类型
    │   │   │   ├── user.ts               # 用户相关类型
    │   │   │   └── scene.ts              # 特色场景类型
    │   │   ├── package.json
    │   │   └── tsconfig.json
    ├── shared-components/                  # 通用组件
    │   ├── src/
    │   │   ├── index.ts                  # 导出所有组件
    │   │   ├── Button.vue                # 通用按钮
    │   │   ├── Input.vue                 # 通用输入框
    │   │   ├── Modal.vue                 # 通用模态框
    │   │   ├── Reader.vue                # 通用阅读器组件
    │   │   ├── AudioPlayer.vue           # 音频播放器
    │   │   └── StrokeCanvas.vue          # 汉字笔画画布
    │   ├── package.json
    │   ├── vite.config.ts
    │   └── tsconfig.json
    ├── scripts/                            # 构建和部署脚本
    │   ├── build-all.ts                  # 构建所有应用的脚本
    │   └── deploy.ts                     # 部署脚本
    ├── docs/                               # 项目文档
    │   ├── README.md                     # 项目说明
    │   └── architecture.md               # 架构说明
    ├── .eslintrc.js                       # ESLint 配置
    ├── .prettierrc                        # Prettier 配置
    ├── pnpm-workspace.yaml                # pnpm monorepo 配置文件
    ├── tsconfig.json                      # 全局 TypeScript 配置
    ├── vite.config.ts                     # 全局 Vite 配置(可选)
    └── package.json                       # 根目录依赖

    目录结构说明

    1. apps 目录

    apps 包含独立的应用入口,每个应用是一个完整的 Vue3 项目,使用 Vite 构建,共享 packages 和 shared-components 的内容。

    • book-search:
      • 功能:图书搜索、分类浏览、搜索历史。
      • 组件:搜索框、结果列表、筛选器。
      • 路由:/search(搜索页)、/category(分类页)。
      • 状态管理:Pinia 存储搜索关键词和结果。
      • API 调用:通过 packages/api-client 获取图书数据。
    • user-center:
      • 功能:用户登录、注册、个人资料管理、阅读记录。
      • 组件:登录表单、用户头像、设置面板。
      • 路由:/login、/register、/profile。
      • 状态管理:Pinia 存储用户信息和认证状态。
      • API 调用:通过 packages/api-client 处理用户相关请求。
    • digital-books:
      • 功能:支持多种数字图书模板(如小说、教材、漫画)。
      • 子目录 templates:
        • novel:小说阅读模板,支持翻页、字体调整、夜间模式。
        • textbook:教材模板,支持目录导航、笔记功能。
        • comic:漫画模板,支持图片缩放、左右翻页。
      • 组件:阅读器(基于 shared-components/Reader.vue)、目录、书签。
      • 路由:/book/:id(阅读页面)、/book/:id/toc(目录页)。
      • 状态管理:Pinia 存储阅读进度和设置。
    • special-scenes:
      • 功能:特色应用场景,如汉字笔画练习、中英文字词听写、普通话语音测评。
      • 子目录 scenes:
        • hanzi-practice:基于 Canvas 的汉字笔画练习,使用 shared-components/StrokeCanvas.vue。
        • dictation:中英文字词听写,支持音频播放(基于 shared-components/AudioPlayer.vue)和输入验证。
        • mandarin-test:普通话语音测评,使用 Web Speech API 或第三方语音服务,结合 packages/utils/audio.ts。
      • 路由:/scene/hanzi、/scene/dictation、/scene/mandarin。
      • 状态管理:Pinia 存储练习进度和评分。

    2. packages 目录

    packages 包含可复用的工具库和业务逻辑模块,供多个 apps 使用。

    • api-client:
      • 封装 Axios 或 Fetch 请求,提供图书、用户、特色场景的 API 方法。
      • 示例:book.ts 包含 searchBooks、getBookDetail 方法;user.ts 包含 login、getProfile 方法。
      • 使用 TypeScript 定义请求和响应类型,引用 packages/types。
    • utils:
      • 通用工具函数,如时间格式化、本地存储封装、音频处理。
      • 示例:audio.ts 提供音频播放和录制功能,支持语音测评场景。
    • types:
      • 定义 TypeScript 类型和接口,如图书、用户、场景的数据结构。
      • 示例:book.ts 定义 Book 接口,包含 id、title、author 等字段。

    3. shared-components 目录

    shared-components 包含通用 Vue 组件,支持跨应用复用。

    • Button.vue:通用按钮,支持不同样式和禁用状态。
    • Input.vue:通用输入框,支持搜索、表单等场景。
    • Modal.vue:通用模态框,用于登录、提示等。
    • Reader.vue:通用阅读器组件,支持小说、教材、漫画的渲染。
    • AudioPlayer.vue:音频播放器,用于听写和语音测评。
    • StrokeCanvas.vue:基于 Canvas 的汉字笔画练习组件。

    4. 根目录配置

    • pnpm-workspace.yaml:yamlpackages: - 'apps/*' - 'packages/*' - 'shared-components'定义 monorepo 的 workspace,允许 apps 和 packages 互相引用。
    • tsconfig.json: 配置路径别名(如 @shared 指向 shared-components)和 TypeScript 选项。
    • vite.config.ts: 全局 Vite 配置,定义共享的插件和优化选项,子应用可继承或覆盖。
    • scripts:
      • build-all.ts:使用 pnpm -r build 构建所有应用。
      • deploy.ts:自动化部署脚本(可选)。
    • docs:
      • README.md:项目概述、安装和运行说明。
      • architecture.md:详细描述 monorepo 架构和模块职责。

    实现细节

    1. Monorepo 管理

    • 使用 pnpm 管理依赖,确保 apps 和 packages 的依赖隔离。
    • 在 package.json 中配置脚本:json{ "scripts": { "dev:search": "pnpm --filter book-search dev", "dev:user": "pnpm --filter user-center dev", "build": "pnpm -r build", "test": "pnpm -r test" } }

    2. 组件复用

    • shared-components 中的组件通过 index.ts 导出,供 apps 按需引入:ts// shared-components/src/index.ts export { default as Button } from './Button.vue'; export { default as Reader } from './Reader.vue';
    • 在 apps 中通过路径别名引入:tsimport { Button } from '@shared';

    3. API 封装

    • packages/api-client 使用 Axios 封装请求:ts// packages/api-client/src/book.ts import axios from 'axios'; import type { Book } from '@types'; export async function searchBooks(query: string): Promise<Book[]> { const response = await axios.get('/api/books', { params: { query } }); return response.data; }

    4. 特色场景实现

    • 汉字笔画练习:
      • 使用 StrokeCanvas.vue 实现 Canvas 画板,记录用户笔画并与标准笔顺比较。
      • 数据通过 packages/api-client/special.ts 获取汉字笔顺信息。
    • 中英文字词听写:
      • 使用 AudioPlayer.vue 播放单词音频,用户输入答案后通过 packages/api-client 校验。
    • 普通话语音测评:
      • 使用 Web Speech API 录制用户语音,结合 packages/utils/audio.ts 进行预处理。
      • 调用第三方语音测评 API(如科大讯飞)进行评分。

    5. 路由和状态管理

    • 每个 app 使用 Vue Router 管理页面跳转,Pinia 管理状态。
    • 示例(book-search 的路由):ts// apps/book-search/src/router/index.ts import { createRouter, createWebHistory } from 'vue-router'; import SearchView from '../views/SearchView.vue'; const router = createRouter({ history: createWebHistory(), routes: [ { path: '/search', component: SearchView }, { path: '/category', component: () => import('../views/CategoryView.vue') }, ], }); export default router;

    6. 测试

    • 使用 Vitest 和 Vue Test Utils 编写单元测试,覆盖组件和工具函数。
    • 示例测试(shared-components/Button.vue):tsimport { mount } from '@vue/test-utils'; import Button from '@shared/Button.vue'; describe('Button', () => { it('renders correctly', () => { const wrapper = mount(Button, { props: { label: 'Click Me' } }); expect(wrapper.text()).toContain('Click Me'); }); });

    开发流程

    1. 初始化项目:
      • 初始化 monorepo:pnpm init 和 pnpm-workspace.yaml。
      • 为每个 app 和 package 创建 package.json 和 vite.config.ts。
    2. 开发通用组件:
      • 在 shared-components 中开发通用组件,先实现基础功能(如 Button、Input)。
      • 逐步添加复杂组件(如 Reader、StrokeCanvas)。
    3. 开发工具库:
      • 在 packages 中实现 API 客户端、工具函数和类型定义。
      • 使用 TypeScript 确保类型安全。
    4. 开发应用:
      • 从 book-search 开始,逐步实现其他 apps。
      • 复用 shared-components 和 packages 的内容。
    5. 测试和部署:
      • 编写单元测试,确保组件和工具函数的稳定性。
      • 使用 scripts/build-all.ts 构建所有应用,部署到 Vercel 或其他平台。

    可扩展性考虑

    • 新增图书模板:在 apps/digital-books/templates 中添加新模板目录(如 magazine),复用 Reader.vue。
    • 新增特色场景:在 apps/special-scenes/scenes 中添加新场景目录,复用现有组件和工具。
    • 多语言支持:在 packages/utils 中添加 i18n 工具,使用 Vue I18n 实现多语言切换。
    • 性能优化:使用 Vite 的按需加载和 Tree Shaking,减少打包体积。

    总结

    这个 monorepo 架构将数字阅读项目的功能模块化,清晰划分了 apps(应用入口)、packages(工具库)和 shared-components(通用组件)。通过 pnpm 和 Vite 管理依赖和构建,结合 Vue3 和 TypeScript 实现高效开发。每个子模块(图书搜索、用户中心、数字图书、特色场景)独立维护,同时复用通用代码,确保项目可扩展和易维护。

  • 基于Markdown的电子书方案:MDEpub

    一个基于 Markdown 的电子书方案,类似 EPUB 标准并原生支持 Mermaid 和 Markmap 等可视化和交互功能,解决 Markdown 的轻量特性与电子书格式的结构化需求之间的平衡,同时确保对 Mermaid(流程图、时序图等)和 Markmap(思维导图)等工具的原生支持。以下是一个详细的设计方案,涵盖架构、实现方法、可视化支持和交互功能。

    1. 设计目标

    • 基于 Markdown:以 Markdown 作为主要内容创作格式,保持简洁和易用。
    • 类似 EPUB 的结构:支持电子书所需的元数据、目录、样式和多文件组织,类似 EPUB 的 OPF 和 NCX 文件。
    • 原生支持 Mermaid 和 Markmap:直接解析和渲染 Mermaid 和 Markmap 代码块为可视化内容(例如 SVG 或交互式 HTML)。
    • 交互性:支持在电子书中嵌入交互式图表(如可缩放的流程图或动态思维导图)。
    • 跨平台兼容:生成的电子书可在多种阅读器上运行,同时支持 Web 浏览器查看。
    • 可扩展性:允许扩展其他可视化工具(如 PlantUML、D3.js 等)。

    2. 方案架构

    基于 Markdown 的电子书格式(暂称为 MDEpub,Markdown-based ePub)可以分为以下核心组件:

    2.1 文件结构

    类似 EPUB,MDEpub 使用文件夹结构组织内容:

    mdepub-book/
    ├── meta.yaml           # 元数据(标题、作者、语言等)
    ├── toc.md              # 目录(类似 EPUB 的 NCX)
    ├── content/            # 内容文件夹
    │   ├── chapter1.md     # 章节 1(Markdown 文件)
    │   ├── chapter2.md     # 章节 2
    │   └── assets/         # 静态资源(图片、CSS、JS 等)
    ├── styles.css          # 全局样式
    └── config.json         # 构建配置(例如 Mermaid/Markmap 渲染设置)
    • meta.yaml:定义电子书元数据,类似 EPUB 的 content.opf。yamltitle: 示例电子书 author: 作者姓名 language: zh-CN publisher: 出版社 date: 2025-05-28
    • toc.md:定义目录结构,支持嵌套章节。markdown# 目录 - [第一章](content/chapter1.md) - [第二章](content/chapter2.md) - [子章节](content/chapter2.md#section1)
    • content/:存放 Markdown 章节,包含 Mermaid 和 Markmap 代码块。
    • assets/:存储图片、字体、CSS、JS(如 Mermaid 和 Markmap 的运行时库)。
    • styles.css:自定义样式,控制电子书外观。
    • config.json:配置可视化工具的参数(例如 Mermaid 主题、Markmap 样式)。

    2.2 Markdown 扩展

    为支持 Mermaid 和 Markmap,扩展 Markdown 语法,允许在代码块中指定渲染参数。例如:

    markdown

    # 第一章
    
    ## 流程图
    ```mermaid {theme: "dark", format: "svg"}
    graph TD
        A[开始] --> B{判断}
        B -->|是| C[执行]
        B -->|否| D[结束]

    思维导图

    markmap {height: “300px”, zoom: true}

    # 思维导图
    ## 分支1
    ### 子节点1
    ## 分支2
    - 使用 `{}` 指定渲染参数(如主题、高度、交互性)。
    - 支持标准 Markdown 语法,同时兼容 Mermaid 和 Markmap 的特定代码块。
    
    #### **2.3 输出格式**
    MDEpub 的最终输出是一个压缩包(类似 EPUB 的 ZIP 格式),包含:
    - **HTML 文件**:每个 Markdown 章节转换为独立的 HTML 文件,嵌入 Mermaid 和 Markmap 的渲染结果(SVG 或交互式 JS)。
    - **元数据文件**:基于 `meta.yaml` 生成的 XML 或 JSON。
    - **目录文件**:基于 `toc.md` 生成的导航文件(类似 NCX 或 HTML TOC)。
    - **资源文件**:CSS、JS、图片等,包含 Mermaid 和 Markmap 的运行时库。
    - **容器文件**:定义文件结构的元文件,类似 EPUB 的 `mimetype` 和 `META-INF/container.xml`。
    
    ---
    
    ### 3. **实现方法**
    以下是实现 MDEpub 的核心步骤和技术选型:
    
    #### **3.1 解析 Markdown**
    - **工具**:使用 [marked](https://marked.js.org/) 或 [markdown-it](https://github.com/markdown-it/markdown-it) 解析 Markdown 文件,转换为 HTML。
    - **扩展插件**:
      - **Mermaid**:集成 `@mermaid-js/mermaid` 解析代码块,渲染为 SVG 或交互式 HTML。
      - **Markmap**:集成 `markmap-lib` 和 `markmap-view`,将 Markmap 代码块渲染为 SVG 或交互式思维导图。
    - **自定义代码块**:扩展 Markdown 解析器,识别 `mermaid` 和 `markmap` 代码块,并调用对应渲染器。
    
    #### **3.2 渲染可视化内容**
    - **Mermaid**:
      - 使用 Mermaid CLI 或 `@mermaid-js/mermaid` 将代码块渲染为 SVG(静态)或嵌入 JS 代码(交互式)。
      - 示例(Node.js 环境):
        ```javascript
        import mermaid from 'mermaid';
        mermaid.initialize({ theme: 'dark' });
        const svg = await mermaid.render('graph', 'graph TD A-->B');
        ```
      - 将 SVG 嵌入 HTML,或包含 Mermaid JS 库以支持交互(例如点击放大)。
    - **Markmap**:
      - 使用 `markmap-lib` 解析 Markmap 代码,生成 SVG 或 HTML+JS。
      - 示例:
        ```javascript
        import { Markmap } from 'markmap-view';
        const mm = Markmap.create(svgElement, { height: 300, zoom: true });
        mm.setData(markmapData);
        ```
      - 支持交互功能(如缩放、展开/折叠节点)。
    - **嵌入方式**:
      - **静态**:将 SVG 直接嵌入 HTML,适合离线阅读器。
      - **动态**:嵌入 Mermaid/Markmap 的 JS 运行时,生成交互式图表,适合 Web 浏览器或支持 JS 的阅读器。
    
    #### **3.3 生成电子书**
    - **构建工具**:开发一个 CLI 工具(例如 `mdepub-cli`),基于 Node.js,执行以下步骤:
      1. 读取 `meta.yaml` 和 `toc.md`,生成元数据和导航。
      2. 解析 `content/` 下的 Markdown 文件,转换为 HTML。
      3. 处理 Mermaid 和 Markmap 代码块,渲染为 SVG 或交互式 HTML。
      4. 合并 CSS 和 JS 资源,生成最终文件结构。
      5. 打包为 ZIP 文件,添加 `mimetype`(例如 `application/mdepub+zip`)。
    - **示例命令**:
      ```bash
      mdepub build mdepub-book/ -o book.mdepub

    3.4 阅读器支持

    • 静态模式:生成 SVG 的 MDEpub 文件可被大多数 EPUB 阅读器(如 Calibre、iBooks)支持。
    • 交互模式:包含 JS 的 MDEpub 需要支持 HTML5 和 JavaScript 的阅读器(如 Web 浏览器、Readium、或自定义阅读器)。
    • 自定义阅读器:开发一个基于 Web 的阅读器(使用 Electron 或 PWA),加载 MDEpub 文件,运行 Mermaid 和 Markmap 的 JS 代码,实现交互。

    4. 支持 Mermaid 和 Markmap 的关键技术

    4.1 Mermaid

    • 渲染:将 Mermaid 代码渲染为 SVG(静态)或 HTML+JS(交互)。
    • 交互性:
      • 支持点击节点高亮、缩放等功能。
      • 需要在 MDEpub 中嵌入 mermaid.min.js(约 1.5MB,压缩后更小)。
    • 样式:通过 config.json 或代码块参数自定义主题(如 dark、forest)。
    • 兼容性:SVG 输出兼容大多数阅读器;交互模式需 JS 支持。

    4.2 Markmap

    • 渲染:将 Markmap 代码渲染为 SVG 或交互式 HTML(使用 D3.js 和 markmap-view)。
    • 交互性:
      • 支持节点展开/折叠、缩放、拖拽。
      • 需要嵌入 markmap-view 和 d3 的 JS 库(约 200KB 压缩后)。
    • 样式:支持自定义颜色、字体、节点样式。
    • 兼容性:类似 Mermaid,SVG 适合静态阅读,交互模式需 JS 支持。

    4.3 其他可视化工具

    • 扩展性:通过插件机制支持其他工具,如 PlantUML、Vega-Lite 等。
    • 实现:为每种工具开发独立的 Markdown 代码块解析器和渲染器,输出 SVG 或 JS。

    5. 优势与挑战

    优势

    • 简洁性:Markdown 作为输入格式,易于编写和维护。
    • 可视化支持:原生支持 Mermaid 和 Markmap,无需外部转换。
    • 交互性:通过 JS 提供动态图表,增强阅读体验。
    • 灵活性:可扩展支持其他可视化工具。
    • 跨平台:兼容现有 EPUB 阅读器(静态模式)和 Web 浏览器(交互模式)。

    挑战

    • 阅读器兼容性:许多 EPUB 阅读器不支持 JavaScript,交互模式可能受限。
    • 文件大小:嵌入 Mermaid 和 Markmap 的 JS 库会增加文件体积。
    • 性能:复杂图表可能导致渲染时间增加,尤其在低端设备上。
    • 标准推广:MDEpub 作为新格式,需要开发专用阅读器或推广兼容性。

    6. 实现工具与工作流

    6.1 开发工具

    • Node.js:核心开发环境,用于解析 Markdown 和渲染图表。
    • markdown-it:Markdown 解析器,支持自定义插件。
    • Mermaid:用于流程图等渲染。
    • Markmap:用于思维导图渲染。
    • archiver:Node.js 库,用于打包 MDEpub 文件为 ZIP。
    • esbuild:压缩和打包 JS/CSS 资源。

    6.2 工作流

    1. 编写:在 mdepub-book/ 中编写 Markdown 文件,包含 Mermaid 和 Markmap 代码块。
    2. 配置:设置 meta.yaml、toc.md 和 config.json。
    3. 构建:运行 mdepub build 命令,生成 MDEpub 文件。
    4. 预览:使用 Web 浏览器或自定义阅读器预览。
    5. 发布:分发 .mdepub 文件,或转换为标准 EPUB(静态模式)。

    6.3 示例命令

    bash

    # 初始化项目
    mdepub init my-book
    # 构建 MDEpub
    mdepub build my-book/ -o my-book.mdepub
    # 预览(启动本地 Web 服务器)
    mdepub preview my-book/
    # 转换为标准 EPUB(静态)
    mdepub export my-book/ -o my-book.epub --static

    7. 与 EPUB 的对比

    特性EPUBMDEpub
    输入格式XHTML, XML, CSSMarkdown
    Mermaid 支持需外部转换原生支持(SVG/JS)
    Markmap 支持需外部转换原生支持(SVG/JS)
    交互性有限(需特定阅读器)强(支持 JS 的交互式图表)
    兼容性广泛(标准阅读器)需专用阅读器或浏览器
    文件大小较小(静态内容)可能较大(含 JS 库)
    开发复杂性高(XML 结构复杂)低(Markdown 简单)

    8. 未来扩展

    • 插件系统:支持更多可视化工具(如 PlantUML、Chart.js)。
    • 云服务:提供在线编辑器和渲染服务,类似 GitBook。
    • 阅读器开发:开发跨平台的 MDEpub 阅读器(基于 Electron 或 Web)。
    • 标准推广:提交 MDEpub 作为开源标准,邀请社区贡献。

    9. 快速原型实现

    以下是一个简单的 Node.js 脚本,展示如何将包含 Mermaid 和 Markmap 的 Markdown 转换为 MDEpub 核心组件:

    javascript

    const fs = require('fs');
    const markdownIt = require('markdown-it')();
    const mermaid = require('mermaid');
    const { Markmap } = require('markmap-view');
    const archiver = require('archiver');
    
    // 初始化 Mermaid
    mermaid.initialize({ startOnLoad: false });
    
    // 自定义 Markdown 插件
    markdownIt.use((md) => {
      md.renderer.rules.fence_custom = {
        mermaid: async (tokens, idx) => {
          const code = tokens[idx].content;
          const { svg } = await mermaid.render('graph', code);
          return `<div class="mermaid">${svg}</div>`;
        },
        markmap: (tokens, idx) => {
          const code = tokens[idx].content;
          return `<div class="markmap" data-markmap="${encodeURIComponent(code)}"></div>`;
        }
      };
    });
    
    // 解析 Markdown 文件
    const mdContent = fs.readFileSync('content/chapter1.md', 'utf-8');
    const htmlContent = markdownIt.render(mdContent);
    
    // 生成 HTML 文件
    fs.writeFileSync('output/chapter1.html', `
    <!DOCTYPE html>
    <html>
    <head>
      <link rel="stylesheet" href="styles.css">
      <script src="mermaid.min.js"></script>
      <script src="d3.min.js"></script>
      <script src="markmap-view.min.js"></script>
    </head>
    <body>
      ${htmlContent}
      <script>
        // 初始化 Markmap
        document.querySelectorAll('.markmap').forEach(el => {
          const data = decodeURIComponent(el.dataset.markmap);
          const mm = Markmap.create(el, { height: 300 });
          mm.setData(data);
        });
      </script>
    </body>
    </html>
    `);
    
    // 打包为 MDEpub
    const output = fs.createWriteStream('book.mdepub');
    const archive = archiver('zip');
    archive.pipe(output);
    archive.directory('output/', false);
    archive.finalize();

    10. 总结

    通过设计一个基于 Markdown 的电子书格式(MDEpub),可以实现类似 EPUB 的功能,同时原生支持 Mermaid 和 Markmap 的可视化和交互功能。核心是扩展 Markdown 解析器,集成 Mermaid 和 Markmap 渲染器,生成包含 SVG 或交互式 HTML 的电子书文件。MDEpub 结合 Markdown 的简洁性和可视化工具的强大功能,适合技术文档、教程和交互式电子书。下一步是开发 CLI 工具和专用阅读器,优化兼容性和性能。

  • mysql服务器优化过程小记

    mysql服务器优化过程小记

    有一个查询很慢,27s,查询了一下mysql的状态:

    mysql> SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool%';
    
    +-------------------------------------------+--------------------------------------------------+
    | Variable_name                             | Value                                            |
    +-------------------------------------------+--------------------------------------------------+
    | Innodb_buffer_pool_dump_status            | Dumping of buffer pool not started               |
    | Innodb_buffer_pool_load_status            | Buffer pool(s) load completed at 241109 21:58:30 |
    | Innodb_buffer_pool_resize_status          |                                                  |
    | Innodb_buffer_pool_resize_status_code     | 0                                                |
    | Innodb_buffer_pool_resize_status_progress | 0                                                |
    | Innodb_buffer_pool_pages_data             | 7168                                             |
    | Innodb_buffer_pool_bytes_data             | 117440512                                        |
    | Innodb_buffer_pool_pages_dirty            | 5                                                |
    | Innodb_buffer_pool_bytes_dirty            | 81920                                            |
    | Innodb_buffer_pool_pages_flushed          | 152905174                                        |
    | Innodb_buffer_pool_pages_free             | 1007                                             |
    | Innodb_buffer_pool_pages_misc             | 17                                               |
    | Innodb_buffer_pool_pages_total            | 8192                                             |
    | Innodb_buffer_pool_read_ahead_rnd         | 0                                                |
    | Innodb_buffer_pool_read_ahead             | 34590772215                                      |
    | Innodb_buffer_pool_read_ahead_evicted     | 46737241                                         |
    | Innodb_buffer_pool_read_requests          | 718733851921                                     |
    | Innodb_buffer_pool_reads                  | 1798889354                                       |
    | Innodb_buffer_pool_wait_free              | 6345356                                          |
    | Innodb_buffer_pool_write_requests         | 506818935                                        |
    +-------------------------------------------+--------------------------------------------------+
    20 rows in set (0.26 sec)

    问了下Grok,它说我的默认配置太低了,数据库服务器是16G内存,数据库占用的很少,大部分是被一个java程序占用了,查了一下原来是当时安装了并未使用的apache-dolphinscheduler,这个东西很占资源,于是干掉。再改以下配置:

    innodb_buffer_pool_size = 8G
    innodb_buffer_pool_instances = 4
    innodb_buffer_pool_dump_at_shutdown = 1
    innodb_buffer_pool_load_at_startup = 1

    重启mysql以后:

    
    mysql> SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool%';
    +-------------------------------------------+--------------------------------------------------+
    | Variable_name                             | Value                                            |
    +-------------------------------------------+--------------------------------------------------+
    | Innodb_buffer_pool_dump_status            | Dumping of buffer pool not started               |
    | Innodb_buffer_pool_load_status            | Buffer pool(s) load completed at 250528 22:42:35 |
    | Innodb_buffer_pool_resize_status          |                                                  |
    | Innodb_buffer_pool_resize_status_code     | 0                                                |
    | Innodb_buffer_pool_resize_status_progress | 0                                                |
    | Innodb_buffer_pool_pages_data             | 231482                                           |
    | Innodb_buffer_pool_bytes_data             | 3792601088                                       |
    | Innodb_buffer_pool_pages_dirty            | 0                                                |
    | Innodb_buffer_pool_bytes_dirty            | 0                                                |
    | Innodb_buffer_pool_pages_flushed          | 1455                                             |
    | Innodb_buffer_pool_pages_free             | 292740                                           |
    | Innodb_buffer_pool_pages_misc             | 66                                               |
    | Innodb_buffer_pool_pages_total            | 524288                                           |
    | Innodb_buffer_pool_read_ahead_rnd         | 0                                                |
    | Innodb_buffer_pool_read_ahead             | 226612                                           |
    | Innodb_buffer_pool_read_ahead_evicted     | 0                                                |
    | Innodb_buffer_pool_read_requests          | 4875046                                          |
    | Innodb_buffer_pool_reads                  | 4713                                             |
    | Innodb_buffer_pool_wait_free              | 0                                                |
    | Innodb_buffer_pool_write_requests         | 4724                                             |
    +-------------------------------------------+--------------------------------------------------+
    20 rows in set (0.00 sec)
    

    不过查询依然很慢,分析了一下请求来自laravel的一个多态关联表,4000W+行数据,两个关键字段是able_type和able_id,于是给这两个字段做了一个联合索引,idx_able,索引创建花了8分钟,索引创建过程中可能有锁,避开访问高峰时操作,完成以后查询降到了300ms以内。

    总结:

    laravel的多态表可以给able_type和able_id建联合索引。

  • WordPress总是挂掉的处理过程

    WordPress总是挂掉的处理过程

    升级服务器的话代价有点高,暂且不考虑,于是:

    1. 切换到官方25
    2. 删除多余的主题
    3. 停止插件自动更新

    接下来观察下是否能解决问题。

  • 既然短信签名审核那么严格,为什么不让运营商统一同一个短信验证码模板呢?

    短信最主要的一个使用场景就是短信验证码了,运营商提供一个由工信部指定的短信验证码模板,一个4位,一个6位,岂不是很简单一件事情,为什么要把精力和时间浪费在签名审核上呢?

  • 老mac设置php版本

    2015的老mac,brew安装的php版本只能是7了,不能再升级,自行安装了php8.2,在.zshrc中通过设置path没有问题,但是vscode却总是提示composer所检查的版本不够。

    直到在vscode中找到

    Laravel: PHP Environment(此处设置为local),可忽略

    Atisan:PHP:Location

    设置为:/usr/local/opt/php@8.2/bin/php

    以此来解决vscode默认调用/usr/bin/php的问题