智慧党建系统源码及部署教程

image

 

智慧党建系统全解析:从桌面系统到「网页版 + 小程序」双端(项目综述)

📅 2026-08-17
🏷️ uni-app / Vue3 / Express / SQLite / 小程序后端迁移
⏱️ 阅读约 25 分钟

一、项目背景:从内网桌面系统说起

单位之前有一个基于 Vue 3 + Node.js + SQLite 的智慧党建管理系统,运行在内网服务器上,用于党员管理、三会一课、主题党日、党员发展全周期追踪等工作。系统功能完善,但有两个痛点:

  1. 使用场景受限:只能在内网电脑上访问,领导外出调研、组织生活现场等移动场景用不了;
  2. 数据录入不便:党员信息、会议记录等都需要回到办公室电脑上操作。

于是有了「移动化 / 小程序化」的需求:把桌面系统搬到随时随地可用,同时复用已有的业务数据(SQLite 数据库里有 700+ 条真实数据)。

二、技术演进:桌面 → 云开发 → 自建服务器

这个项目经历了 三轮架构迭代,每一步都踩了不少坑,也沉淀了不少经验:

2.1 第一轮:内网桌面系统(Vue3 + Express + SQLite)

最初的网页版跑在内网,功能完善但只能在办公室用。

2.2 第二轮:微信小程序 + 云开发(CloudBase)

为了让系统移动化,用 uni-app + 微信云开发 把桌面系统搬进了微信小程序。云函数 + 云数据库 + 云存储,免运维确实爽。但随着用户量增长,云开发的限制越来越明显:

  1. 免费额度有限:数据库读写次数、云函数调用次数都有上限,超量就要付费;
  2. 数据不互通:网页版跑在自有服务器(Node.js + SQLite),小程序数据在微信云,两套数据无法打通;
  3. 云函数冷启动慢:首次调用有 1-3 秒延迟,体验不佳;
  4. 平台锁定:业务逻辑绑定在微信云上,无法迁移到其他平台。

2.3 第三轮:弃用云开发,小程序直连自建服务器

项目里已经有一套完整的 Express + SQLite + JWT 网页版后端,于是做了一个关键决策:弃用云开发,小程序直连自建服务器后端,与网页版共用同一套后端、同一个数据库。

✅ 迁移后效果:小程序与网页版数据实时同步、共享用户体系(账号密码 + 微信双通道登录)、免费额度不再受限。

三、功能模块(12 个页面 / 8 大业务场景)

系统完整复刻了桌面系统的核心功能,共 12 个页面,覆盖 8 大业务场景:

模块 功能
登录注册 账号密码登录、微信一键登录、开放注册(每人独立数据空间)
工作台 6 项数据统计 + 功能导航 + 最近会议/活动
党员管理 党员列表、按组织筛选、搜索、增删改查、党员详情
组织架构 党总支 → 党支部树形展示、党员数统计
三会一课 会议列表、详情、新增
主题党日 活动卡片展示、新增
党员发展 25 步标准流程时间线 + 逾期/到期自动提醒
理论学习 中心组学习记录、新增
工作档案 文件分类浏览、搜索、分页
待办事项 优先级标签、完成勾选、增删

四、数据迁移:SQLite 主键转字符串 _id

桌面系统的数据在 SQLite 数据库里,需要迁移到云端(云数据库或自建服务器)。这里有一个关键的坑:外键关联

⚠️ SQLite 数字主键 → 字符串 _id 的转换规则:

  • 主键 id → 转成字符串作为 _id(保证外键可关联)
  • 外键 org_id / mentor_id → 同步转成字符串
  • 数组字段(参会党员 attendees 等)→ 元素转字符串
// 迁移工具核心逻辑
// 1. 通过服务器 API 登录获取 token
// 2. 分页拉取全量数据(members、archives 等)
// 3. 树形结构扁平化(orgs 的 children 展开)
// 4. 剔除联表派生字段(org_name / attendee_count 等)
// 5. 生成导入所需的 data/*.json

// 导入时(doc(_id).set() 精确控制 _id)
function toCloudDoc(collection, item) {
  const { id, ...rest } = item;
  const doc = { ...rest };
  if (id !== undefined) doc._id = String(id);  // 主键转字符串
  if (collection !== 'users') doc.owner_id = 'admin'; // 数据归属
  // 外键/数组字段转字符串...
  return doc;
}

迁移后的数据规模(示例):

集合 记录数
members(党员) 46
orgs(组织) 4
meetings(三会一课) 3
activities(主题党日) 8
archives(工作档案) 614
study / todos / development / users 6 / 7 / 1 / 1

五、多用户数据隔离设计

系统采用开放注册 + 每人独立数据空间的模式:任何人可注册账号,注册后拥有自己的一套数据(党员、会议、档案等),互不可见。

实现原理:所有业务集合增加 owner_id 字段,后端在查询和写入时强制按当前用户过滤:

// 统一鉴权与数据隔离
function buildQuery(collection, params) {
  const where = {};
  if (params.username) {
    where.owner_id = params.username;   // 只查自己的数据
  }
  return where;
}

// 写入时强制带上 owner_id
const data = { ..., owner_id: currentUser.username };
await db.collection('members').add({ data });

// 更新/删除时校验归属(防止越权操作)
const res = await db.collection('members')
  .where({ _id: event.id, owner_id: currentUser.username })
  .update({ data });
✅ 数据隔离效果:每个账号保留自己的一套数据,互不可见;创建/编辑/删除都校验归属,防止越权。

六、党员发展 25 步时间线提醒

这是最有业务价值的模块。依据《中国共产党发展党员工作细则》,发展党员分为 5 个阶段 25 个步骤,每个关键节点都有时限要求。系统自动计算每个人的进度并提醒:

阶段 步骤数 关键时限
一、申请入党 2 党组织收到申请后 1 个月内谈话
二、积极分子确定和培养教育 4 申请满 6 个月可确定积极分子;培养考察 至少 1 年
三、发展对象的确定和考察 5 政治审查、集中培训(≥3 天/24 学时)
四、预备党员的接收 7 党委审批 3 个月内(可延至 6 个月)
五、预备党员教育考察和转正 7 预备期 1 年、转正审批 3 个月内
// 时间轴计算核心(timeline.js)
// 25 个节点按阶段定义,每个节点有基准日期与偏移天数
const FLOW = [
  { key: 'apply', name: '递交入党申请书', stage: 1, offset: 0 },
  { key: 'talk',  name: '党组织派人谈话', stage: 1, offset: 30,  desc: '收到申请后 1 个月内' },
  { key: 'activist', name: '推荐和确定入党积极分子', stage: 2, offset: 180 },
  { key: 'inspect',  name: '培养教育考察', stage: 2, offset: 365, desc: '至少 1 年' },
  // ... 共 25 个节点
];

// 每个节点计算:目标日期、剩余天数、状态
// status: done(已完成) / soon(30天内到期) / overdue(已逾期) / pending(待完成)
// 关键设计:前置节点未完成则不计算后续提醒(防误报)

页面效果:

  • 按 5 个阶段分组展示,每组显示完成进度(如 4/5)
  • 🔴 已逾期节点标红、🟠 即将到期(30 天内)标橙、🟢 已完成打勾
  • 顶部汇总「X 项逾期 / Y 项即将到期」,并提示下一步节点
  • 点击未完成节点可一键标记完成(自动回填日期、推进阶段)

七、整体架构:一后端双前端

技术栈 通信方式
微信小程序 uni-app 3.x + Vue 3 + Vite POST /api/mp(action 分发)
网页版 原生 HTML + JS REST:/api/data /api/login 等
后端(共用) Express + better-sqlite3 + JWT
数据库(共用) SQLite(zhdj-web.db) users + user_data 两张表

架构图非常简洁——没有微服务,没有消息队列,就是一台服务器 + 一个数据库 + 两套前端:

┌─────────────┐      ┌──────────────┐      ┌──────────────┐
│  微信小程序   │ ───▶ │              │      │              │
│ (uni-app)    │      │  Express 后端 │ ───▶ │  SQLite 数据库│
├─────────────┤      │  /api/mp     │      │  zhdj-web.db │
│  网页版      │ ───▶ │  /api/data   │      │              │
│ (原生 HTML)  │      │  /api/login  │      │              │
└─────────────┘      └──────────────┘      └──────────────┘

核心思想:小程序端把所有业务调用收敛到一个统一入口(POST /api/mp + action 参数),后端按 action 分发到对应处理器,这样小程序端不需要知道 REST 资源路径,只需传「我要干什么」。这与微信云函数时代 wx.cloud.callFunction 的调用方式保持了一致性,迁移成本极低。

源码目录结构:

智慧党建小程序/
├── src/                       # 小程序源码(uni-app 工程)
│   ├── App.vue                # 应用入口
│   ├── main.js                # 框架初始化
│   ├── manifest.json          # 小程序配置(填入自己的 AppID)
│   ├── pages.json             # 12 个页面 + 4 个 tabBar
│   ├── utils/
│   │   └── request.js         # ★ 核心请求层(129 行)
│   ├── pages/
│   │   ├── login/             # 登录/注册/微信绑定
│   │   ├── index/             # 工作台(数据概览 + 功能导航)
│   │   ├── members/           # 党员列表 + 详情
│   │   ├── orgs/              # 组织架构树
│   │   ├── meetings/          # 三会一课列表 + 详情
│   │   ├── activities/        # 主题党日
│   │   ├── development/       # 党员发展(25 步时间线)
│   │   ├── study/             # 理论学习中心组
│   │   ├── archives/          # 工作档案
│   │   └── todos/             # 待办事项(乐观更新勾选)
│   └── cloudfunctions/        # 已废弃的云函数(保留作迁移参考)
├── web/                       # 网页版前端(单文件 HTML)
├── web-server/                # 后端(与双端共用)
│   ├── server.js              # Express 主程序
│   ├── timeline.js            # 党员发展 25 步计算
│   └── seed.json              # 脱敏示例数据
└── README.md
⚠️ 注意:cloudfunctions/ 目录是迁移前的云函数代码,已经不再被小程序调用,保留仅作为逻辑参考(后端 /api/mp 的 action 处理器就是从 api/index.js 平移过来的)。

八、核心:request.js 请求层

这是小程序最关键的一个文件——所有请求的收发、鉴权、错误处理都在这里。核心设计:

// src/utils/request.js(精简版)
const API_BASE = 'https://YOUR_DOMAIN/api';

function callCloud(action, data = {}, funcName) {
  return new Promise((resolve, reject) => {
    const token = uni.getStorageSync('token');
    const payload = Object.assign({ action }, data);

    const handleResult = (r) => {
      if (r && r.code === 0) {
        resolve(r.data);                    // 成功:直接返回 data
      } else {
        const msg = (r && r.error) || '请求失败';
        if (r && r.code === 401) {
          // token 过期:清登录态,跳回登录页
          uni.removeStorageSync('token');
          uni.removeStorageSync('user');
          uni.setStorageSync('skip_auto_login', true);
          uni.showToast({ title: '登录已过期,请重新登录', icon: 'none' });
          setTimeout(() => uni.reLaunch({ url: '/pages/login/login' }), 800);
        } else {
          uni.showToast({ title: msg, icon: 'none' });  // 其他错误:toast 展示后端 error
        }
        reject(new Error(msg));
      }
    };

    // 登录/注册走 REST,业务统一走 /api/mp
    if (funcName === 'login') {
      if (action === 'register') {
        request(API_BASE + '/register', 'POST', data, ...);
      } else if (action === 'wechatLogin') {
        // 微信登录:先 wx.login 拿 code,再换 openid
        uni.login({
          provider: 'weixin',
          success: (loginRes) => {
            request(API_BASE + '/wx-login', 'POST', { code: loginRes.code }, ...);
          }
        });
      } else {
        request(API_BASE + '/login', 'POST', data, ...);
      }
      return;
    }

    // 业务接口统一走 /api/mp,带上 JWT
    request(API_BASE + '/mp', 'POST', payload, {
      'content-type': 'application/json',
      'Authorization': token ? 'Bearer ' + token : ''
    });
  });
}

// 对外暴露的所有业务方法(24 个)
export default {
  auth: (action, data) => callCloud(action, data, 'login'),
  wechatLogin: () => callCloud('wechatLogin', {}, 'login'),
  getWorkbench: () => callCloud('workbench'),
  getMembers: (params) => callCloud('members:list', { params }),
  createMember: (data) => callCloud('members:create', { data }),
  updateMember: (id, data) => callCloud('members:update', { id, data }),
  deleteMember: (id) => callCloud('members:delete', { id }),
  // ... 共 24 个 action 方法
};

设计亮点:

  • 统一错误处理:401 自动清登录态跳回登录页;其他错误直接 toast 后端返回的 error 文案(比如演示账号的 403 提示就是前端自动弹出的);
  • 兼容双登录模式funcName === 'login' 时走 REST 登录接口,业务时走 /api/mp,一个函数封装全部;
  • 无 ES2020 新语法:为了兼容微信小程序旧版基础库,全程只用 ES6 语法(没有 ???.);
  • token 存储在 uni storage:跨页面共享,30 天有效期。

九、后端 /api/mp 统一入口(24 个 action)

后端 web-server/server.js 新增了一个统一入口,模仿云函数的 wx.cloud.callFunction 语义:

// 后端统一入口:POST /api/mp { action, ...业务参数 }
app.post('/api/mp', (req, res) => {
  const body = req.body || {};
  const action = body.action;

  // 鉴权:Authorization Bearer 或 body._token(兼容旧调用)
  let token = (req.headers.authorization || '').replace('Bearer ', '');
  if (!token && body._token) token = String(body._token);
  if (!token) return res.json({ code: 401, error: '未登录或登录已过期' });

  let user;
  try { user = jwt.verify(token, JWT_SECRET); }
  catch (e) { return res.json({ code: 401, error: '未登录或登录已过期' }); }

  // 只读演示账号拦截写操作(增/改/删/标记)
  if (READONLY_DEMO_USERS.includes(user.username) && WRITE_ACTION_RE.test(action)) {
    return res.json({ code: 403, error: '演示账号仅可查看,请注册登录后使用' });
  }

  const handler = mpHandlers[action];
  if (!handler) return res.json({ code: 400, error: '未知操作: ' + action });

  try {
    const data = handler(user.username, body);
    if (data && data.error) return res.json({ code: 400, error: data.error });
    res.json({ code: 0, data });
  } catch (e) {
    res.json({ code: 500, error: '服务器错误: ' + e.message });
  }
});

处理器用对象映射组织,共 24 个 action:

const mpHandlers = {
  workbench(username) { /* 工作台统计 */ },
  'members:list'(username, event) { /* 分页+搜索+筛选 */ },
  'members:detail'(username, event) { },
  'members:create'(username, event) { },
  'members:update'(username, event) { },
  'members:delete'(username, event) { },
  'members:meetings'(username, event) { /* 某党员的会议记录 */ },
  'orgs:list'(username, event) { },
  'meetings:list' / 'detail' / 'create' / 'update' / 'delete',
  'activities:list' / 'create' / 'delete',
  'development:list' / 'create' / 'update' / 'mark',
  'study:list' / 'create',
  'archives:list' / 'categories' / 'fileurl',
  'todos:list' / 'create' / 'update' / 'delete'
};
✅ 这个设计最大的好处:小程序端 0 改动即可切换后端。把 API_BASE 从云函数改成自建服务器,其余代码全部保留。

十、微信一键登录与账号绑定

这是迁移后新增的核心能力。完整链路如下:

10.1 登录流程

用户打开小程序
  └─▶ onLoad 自动调 wx.login() 拿临时 code
  └─▶ POST /api/wx-login { code }
  └─▶ 服务器用 code 向微信官方换 openid
  └─▶ SELECT users WHERE openid = ?
       ├─ 已绑定 ──▶ 签发 JWT(30天),免密进入
       └─ 未绑定 ──▶ 返回 { needBind: true, openid }
                     └─▶ 前端存 pending_openid,提示用户用账号密码登录/注册
                           └─▶ 登录/注册请求自动带上 openid
                                 └─▶ 服务器写入 users.openid,完成绑定

10.2 关键代码

前端(login.vue):

handleWechatLogin(silent = false) {
  if (silent) {
    const token = uni.getStorageSync('token');
    if (token) { uni.switchTab({ url: '/pages/index/index' }); return; }
  }
  request.wechatLogin().then((data) => {
    if (data && data.token) {
      // 已绑定:直接登录
      uni.setStorageSync('token', data.token);
      uni.setStorageSync('user', data.user);
      uni.switchTab({ url: '/pages/index/index' });
    } else if (data && data.needBind && data.openid) {
      // 未绑定:暂存 openid,提示绑定
      uni.setStorageSync('pending_openid', data.openid);
      uni.showToast({ title: '该微信未绑定账号,请用已有账号登录绑定', icon: 'none' });
    }
  });
}

// 登录/注册时自动带上暂存的 openid 完成绑定
const pendingOpenid = uni.getStorageSync('pending_openid');
if (pendingOpenid) {
  payload.openid = pendingOpenid;
  uni.removeStorageSync('pending_openid');
}

后端(/api/wx-login):

app.post('/api/wx-login', async (req, res) => {
  const { code } = req.body || {};
  const appid = process.env.WX_APPID;
  const secret = process.env.WX_SECRET;
  if (!appid || !secret) return res.json({ code: 400, error: '微信登录未配置' });

  const url = 'https://api.weixin.qq.com/sns/jscode2session?appid=' + appid
    + '&secret=' + secret + '&js_code=' + encodeURIComponent(code)
    + '&grant_type=authorization_code';
  const r = await fetch(url).then((x) => x.json());
  const openid = String(r.openid || '');

  const user = db.prepare('SELECT * FROM users WHERE openid = ?').get(openid);
  if (!user) {
    return res.json({ code: 0, data: { needBind: true, openid } });
  }
  const token = jwt.sign({ username: user.username }, JWT_SECRET, { expiresIn: '30d' });
  res.json({ code: 0, data: { token, user: {...} } });
});
⚠️ 关键坑:微信 AppSecret 属于敏感信息,不能写死在前端或代码里。通过 pm2 的 ecosystem.config.js 注入环境变量 WX_APPID/WX_SECRET,且 pm2 改环境变量必须 delete + start(restart 不生效)。

十一、数据模型与 ID 映射

数据库只有两张表:

users     (username, password, display_name, openid, created_at, source)
user_data (username PRIMARY KEY, data JSON)

// data 是一个完整 JSON,每个用户独立空间:
{
  owner: 'moran',
  orgs: [],         // 组织架构
  members: [],      // 党员
  meetings: [],     // 三会一课
  activities: [],   // 主题党日
  study: [],        // 学习台账
  todos: [],        // 待办
  archives: [],     // 档案
  development: []   // 发展对象
}

ID 映射是迁移中最容易踩的坑:网页版用数字 id,云端数据是字符串 _id。后端做了兼容处理:

// 返回时附加 _id = String(id),匹配时兼容数字/字符串
function withId(rec) {
  return Object.assign({}, rec, { _id: String(rec.id) });
}
function idMatch(a, b) {
  return String(a) === String(b);
}

十二、演示账号设计(只读 + 不绑微信)

为了让用户先体验再注册,系统内置了演示账号 moran,并做了三重保护:

保护 实现
只读(禁止增改删) READONLY_DEMO_USERS = ['moran'],写操作一律 403
演示数据脱敏 组织为”机关党总支/第一/第二党支部”,党员为占位”党员01~10″,地点统一”党员活动室”,无任何真实信息
不绑定微信 login/wx-login 均拦截演示账号的 openid 绑定
// 只读演示账号
const READONLY_DEMO_USERS = ['moran'];
const READONLY_MSG = '演示账号仅可查看,请注册登录后使用';
const WRITE_ACTION_RE = /:(create|update|delete|mark)$/;

// /api/mp 入口拦截
if (READONLY_DEMO_USERS.includes(user.username) && WRITE_ACTION_RE.test(action)) {
  return res.json({ code: 403, error: READONLY_MSG });
}

十三、小程序部署教程(手把手)

下面是把小程序部署到微信的完整流程,跟着做就能跑起来。

第 1 步:准备工作

需要的东西 说明 费用
微信小程序账号 微信公众平台 注册,个人身份证即可 注册免费,认证 30 元/年(可选)
微信开发者工具 官方下载,选稳定版 免费
源码压缩包 文末提供下载
💡 关于费用:开发阶段云开发完全免费;体验版(不发布正式版)也免费,体验成员上限 15 人,内部使用足够。

第 2 步:导入项目到开发者工具

下载源码解压后,打开开发者工具 → 点「导入」→ 选择源码目录 → 填入你的 AppID

项目名称:智慧党建
目录:<源码解压路径>/src
AppID:你的小程序 AppID
后端服务:微信云开发(或自建服务器)
⚠️ 常见坑:如果项目路径含中文(如「文件/脚本」),macOS 开发者工具可能报编译错误。建议把源码解压到纯英文路径,如 /Users/you/projects/zhdj

第 3 步:部署后端(云开发 或 自建服务器)

方案 A:微信云开发

开发者工具顶部工具栏 → 点「云开发」按钮 → 开通并创建环境。然后找到 cloudfunctions 目录,逐个右键部署 3 个云函数(login / api / initDB),选择「上传并部署:云端安装依赖」。重点:把 initDB 云函数超时时间改为 60 秒(默认 3 秒导入大数据会失败)。

方案 B:自建服务器(推荐)

按第十四节部署网页版后端后,把 src/utils/request.js 里的 API_BASE 改成你的服务器域名即可,无需部署任何云函数。

第 4 步:初始化数据库

云开发方案,在云开发控制台 → 云函数 → 选中 initDB → 「云端测试」,依次执行:

{"action":"collections"}       # 创建 8 个集合
{"action":"init"}              # 导入基础数据
{"action":"import:archives","batch":1}  # 614 条档案分 7 批导入
{"action":"import:archives","batch":2}
...
{"action":"count:archives"}    # 预期 {"count":614}

第 5 步:注册账号并登录

系统没有默认账号(安全设计)。点「没有账号?立即注册」→ 填写用户名、密码(至少 6 位)→ 注册成功后自动登录。每个账号独立数据空间。

第 6 步:发布体验版给同事

  1. 开发者工具点顶部「上传」→ 填版本号(如 1.0.0)→ 上传成功;
  2. 登录微信公众平台 → 「管理」→「版本管理」→ 找到刚上传的版本 → 点「选为体验版」;
  3. 「管理」→「成员管理」→「体验成员」→ 添加对方微信号,对方扫码即可使用(上限 15 人)。

十四、网页版部署:一键脚本 + 服务器

之前写过小程序版的部署教程,需要微信开发者工具、云开发环境,门槛不低。这次做了网页版——一个 HTML 文件 + 一个 Node.js 服务,浏览器打开就能用,不依赖微信、不需要云开发付费。

14.1 一键安装脚本(本地测试 / 单机使用)

跨平台一键脚本(Linux/macOS/Windows 全覆盖),一条命令搞定从零到能用:

Linux / macOS:

bash install.sh

Windows:

powershell -ExecutionPolicy Bypass -File install.ps1

脚本自动做的事:

  • 检测 Node.js 18+,没装就自动下载安装(支持 x64 / ARM64)
  • npm install 安装后端依赖(Express + better-sqlite3 + JWT)
  • 自动生成本地版前端(API 地址自动配置,解决跨域)
  • 后台启动后端服务,端口被占用自动切换
  • 自动打开浏览器进入登录页
注意:如果一键脚本因网络问题自动安装 Node.js 失败,请手动到 nodejs.org 下载安装 Node.js 18+ LTS 版本,然后重新运行脚本即可。

14.2 服务器正式部署(多人使用)

步骤 命令 说明
1. 上传文件 scp web-server/* 用户@服务器:/opt/zhdj-web-server/ 后端文件传到服务器
2. 安装依赖 cd /opt/zhdj-web-server && npm install 需要 build-essential + python3
3. PM2 守护 pm2 start server.js --name zhdj-web-server 关掉 SSH 也不停
4. Nginx 代理 location /api/ proxy_pass http://127.0.0.1:3100 前端页面 + API 反向代理
5. HTTPS sudo certbot --nginx -d 你的域名 Let’s Encrypt 免费证书

部署后访问 https://你的域名/api/ping,返回 {"code":0,"data":"pong"} 即成功。敏感信息(JWT_SECRET / WX_APPID / WX_SECRET)通过环境变量注入,不要硬编码。

十五、网页版 vs 小程序版

对比项 网页版 小程序版
部署成本 一台服务器即可,零付费 需微信云开发(正式版付费)
访问方式 浏览器/微信打开链接 微信小程序内打开
微信登录 账号密码注册登录 支持微信一键免密登录
功能 完整功能(8 大模块) 完整功能(8 大模块)
数据隔离 每人独立空间,跨设备同步 每人独立空间(owner_id)
适用场景 不想搞小程序、快速试用 正式党建台账管理

两套系统共享同一套业务逻辑和数据格式,选择哪个取决于你的需求。小程序版适合需要微信生态(微信登录、小程序分享)的场景;网页版适合不想折腾微信开发者工具、只想快速跑起来的场景。

十六、踩坑记录与注意事项

原因 解决方案
小程序报「未登录」 云函数时代的 token 校验逻辑与 REST 不同 统一 JWT:Authorization Bearer 或 body._token 双通道兼容
数据库缺 openid 列 旧库结构没有该字段 启动时 ALTER TABLE users ADD COLUMN openid 自动补列
pm2 环境变量不生效 pm2 restart 不会加载新的 env 必须 pm2 delete + pm2 start 重新启动
ES2020 语法报错 微信小程序旧版基础库不支持 ??/?. 前端产物只用 ES6 语法
id 数字/字符串不匹配 网页版数字 id vs 云端字符串 _id withId() + idMatch() 兼容
注册绑定微信失败 register 接口没接收 openid 补充 openid 参数 + 防重复绑定校验
演示账号被改数据 无只读限制 READONLY_DEMO_USERS + WRITE_ACTION_RE 拦截
macOS 工具中文路径 bug 编译器对中文路径支持有缺陷 项目放到纯英文路径
614 条档案导入超时 云函数默认超时 3 秒 超时调到 60 秒 + 分批导入
退出后被微信免密”拉回去” 登录页 onLoad 自动静默登录 退出时写入 skip_auto_login 标记
better-sqlite3 安装报错 缺编译工具 Ubuntu: build-essential;CentOS: gcc-c++ make;macOS: xcode-select

十七、总结

这个项目从内网 SQLite 桌面系统起步,经过「网页版 → 云开发小程序 → 自建服务器双端」三轮迭代。核心收获:

  • 复用后端:小程序与网页版共用 Express 后端 + SQLite,一套部署两份前端;
  • 复用调用语义:/api/mp 的 action 分发模拟云函数 callFunction,小程序端迁移近乎零成本;
  • 复用数据:同一张 user_data 表,双端数据实时同步;
  • 复用用户体系:账号密码 + 微信登录双通道,共享 JWT。

最终效果:完全免费(自建服务器 + 无云开发额度限制)、双端数据打通微信一键登录演示账号零门槛体验

十八、源码下载(评论后可见)

本文涉及的所有源码已经整理为开源包,包含完整小程序源码、网页版前端、后端服务、部署脚本,可直接下载使用:

  • ✅ 完整脱敏(不含任何真实用户数据、密码、密钥)
  • ✅ 含 README 详细部署文档
  • ✅ MIT 开源协议,可自由使用、修改、分发
  • ✅ 含小程序源码(uni-app)、网页版前端(HTML)、后端服务(Node.js + Express)

💡 为了防止机器爬虫批量下载,源码包采用「评论后可见」机制。
请在下方评论区留下任意一句话(如「感谢分享」「已下载」),刷新页面后即可看到下载链接。

部署遇到问题?欢迎在评论区留言,作者会及时回复。
如果觉得有用,也欢迎 请作者喝杯咖啡 ☕ 支持开源。

© 版权声明
THE END
喜欢就支持一下吧
点赞5 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容