网站首页/ 信息中心/ 档案百科/

档案管理系统按模块付费功能从零搭建落地实操全步骤指南

发布时间:2026年09月07日 23:25:05 浏览量:0

一、前置准备工作

1. 环境依赖安装

统一使用Node.js 16.18.0版本,避免高版本兼容性问题,执行以下命令安装所有依赖:

``` npm install express@4.18.2 mysql2@3.6.0 wechatpay-node-v3@2.1.3 jsonwebtoken@9.0.2 cors@2.8.5 ```

2. 基础库表初始化

在MySQL 8.0+版本中执行以下建表语句,所有字段可直接复用无需修改:

```sql -- 模块配置表 CREATE TABLE `module_config` ( `id` int unsigned NOT NULL AUTO_INCREMENT, `module_key` varchar(32) NOT NULL COMMENT '模块唯一标识', `module_name` varchar(64) NOT NULL COMMENT '模块名称', `module_desc` varchar(255) DEFAULT '' COMMENT '模块描述', `price` int unsigned NOT NULL COMMENT '售价,单位为分', `valid_days` int unsigned NOT NULL DEFAULT 365 COMMENT '有效天数', `is_active` tinyint unsigned NOT NULL DEFAULT '1' COMMENT '是否上架 1上架 0下架', `sort` int unsigned NOT NULL DEFAULT '0' COMMENT '排序权重', PRIMARY KEY (`id`), UNIQUE KEY `uk_module_key` (`module_key`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 插入默认测试模块 INSERT INTO `module_config` (`module_key`, `module_name`, `module_desc`, `price`, `valid_days`, `is_active`, `sort`) VALUES ('archive_import', '档案批量导入', '支持Excel批量导入多类档案自动分类', 9900, 365, 1, 1), ('archive_borrow', '档案借阅管理', '支持线上申请、审批、归还全流程追踪', 19900, 365, 1, 2), ('archive_destroy', '档案销毁管理', '支持销毁审批、操作日志留痕', 29900, 365, 1, 3); -- 用户已购模块表 CREATE TABLE `user_module` ( `id` int unsigned NOT NULL AUTO_INCREMENT, `user_id` int unsigned NOT NULL, `module_id` int unsigned NOT NULL, `expire_time` datetime NOT NULL COMMENT '到期时间', `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_user_module` (`user_id`,`module_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 订单表 CREATE TABLE `pay_order` ( `id` int unsigned NOT NULL AUTO_INCREMENT, `order_no` varchar(32) NOT NULL COMMENT '订单号', `user_id` int unsigned NOT NULL, `module_id` int unsigned NOT NULL, `pay_amount` int unsigned NOT NULL COMMENT '支付金额,单位分', `pay_status` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '0待支付 1已支付 2已关闭', `pay_time` datetime DEFAULT NULL, `transaction_id` varchar(64) DEFAULT '' COMMENT '支付平台流水号', `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_order_no` (`order_no`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; ```

二、后端核心逻辑开发

1. 公共配置初始化

新建config.js文件,填入以下可直接复用的配置,仅需替换自身商户相关参数:

```js const fs = require('fs') module.exports = { jwtSecret: '自定义32位随机字符串作为jwt加密密钥', dbConfig: { host: '你的数据库地址', user: '数据库用户名', password: '数据库密码', database: '你的库名' }, payConfig: { appid: '你的小程序/公众号APPID', mchid: '你的微信支付商户号', publicKey: fs.readFileSync('./cert/apiclient_cert.pem'), privateKey: fs.readFileSync('./cert/apiclient_key.pem'), serial_no: '你的微信支付证书序列号', notify_url: 'https://你的域名/api/pay/notify' } } ```

注意:微信支付证书需从商户平台下载,放在项目根目录的cert文件夹下,权限设置为600避免泄露

2. 模块列表查询接口开发

新建routes/module.js文件,实现返回带用户购买状态的模块列表接口:

```js const express = require('express') const router = express.Router() const jwt = require('jsonwebtoken') const { jwtSecret, dbConfig } = require('../config') const mysql = require('mysql2/promise') router.get('/list', async (req, res) => { const token = req.headers.authorization?.split(' ')[1] if (!token) return res.json({code: 401, msg: '请登录'}) try { const decode = jwt.verify(token, jwtSecret) const userId = decode.userId const conn = await mysql.createConnection(dbConfig) // 查询所有上架模块 const [modules] = await conn.execute('SELECT id, module_key, module_name, module_desc, price, valid_days FROM module_config WHERE is_active = 1 ORDER BY sort ASC') // 查询用户已购未过期模块 const [userModules] = await conn.execute('SELECT module_id FROM user_module WHERE user_id = ? AND expire_time > NOW()', [userId]) const userModuleIds = userModules.map(item => item.module_id) // 拼接购买状态 const result = modules.map(item => ({ ...item, has_bought: userModuleIds.includes(item.id) })) await conn.end() res.json({code: 200, data: result}) } catch (e) { res.json({code: 500, msg: '查询失败'}) } }) module.exports = router ```

档案管理系统按模块付费功能从零搭建落地实操全步骤指南

必须在所有模块相关接口添加JWT校验,禁止未登录用户查询模块信息

3. 支付下单与回调接口开发

新建routes/pay.js文件,实现下单与回调逻辑:

```js const express = require('express') const router = express.Router() const jwt = require('jsonwebtoken') const { jwtSecret, dbConfig, payConfig } = require('../config') const mysql = require('mysql2/promise') const WechatPay = require('wechatpay-node-v3') const pay = new WechatPay(payConfig) // 下单接口 router.post('/createOrder', async (req, res) => { const token = req.headers.authorization?.split(' ')[1] const { moduleId } = req.body if (!token || !moduleId) return res.json({code: 400, msg: '参数错误'}) try { const decode = jwt.verify(token, jwtSecret) const userId = decode.userId const conn = await mysql.createConnection(dbConfig) // 校验模块是否上架 const [modules] = await conn.execute('SELECT id, price, module_name FROM module_config WHERE id = ? AND is_active = 1', [moduleId]) if (modules.length === 0) return res.json({code: 400, msg: '模块未上架'}) // 生成订单号:前缀+时间戳+6位随机数 const orderNo = 'ARCHIVE' + Date.now() + Math.random().toString().slice(-6) await conn.execute('INSERT INTO pay_order (order_no, user_id, module_id, pay_amount) VALUES (?, ?, ?, ?)', [orderNo, userId, moduleId, modules[0].price]) // 调用微信支付统一下单 const params = await pay.transactions_jsapi({ description: modules[0].module_name, out_trade_no: orderNo, amount: { total: modules[0].price, currency: 'CNY' }, payer: { openid: decode.openid } }) await conn.end() res.json({code: 200, data: params}) } catch (e) { res.json({code: 500, msg: '下单失败'}) } }) // 支付回调接口 router.post('/notify', async (req, res) => { const signature = req.headers['wechatpay-signature'] const serial = req.headers['wechatpay-serial'] const timestamp = req.headers['wechatpay-timestamp'] const nonce = req.headers['wechatpay-nonce'] // 验签 const verify = pay.verifySignature(nonce, timestamp, req.body, signature, serial) if (!verify) return res.status(400).json({code: 'FAIL', message: '验签失败'}) // 解密回调数据 const result = pay.decipher_gcm(req.body.resource.ciphertext, req.body.resource.associated_data, req.body.resource.nonce, payConfig.key) if (result.trade_state === 'SUCCESS') { const conn = await mysql.createConnection(dbConfig) // 校验订单状态,避免重复处理 const [orders] = await conn.execute('SELECT id, user_id, module_id FROM pay_order WHERE order_no = ? AND pay_status = 0', [result.out_trade_no]) if (orders.length > 0) { // 更新订单状态 await conn.execute('UPDATE pay_order SET pay_status = 1, pay_time = NOW(), transaction_id = ? WHERE order_no = ?', [result.transaction_id, result.out_trade_no]) // 给用户开通模块,有效期1年 await conn.execute('REPLACE INTO user_module (user_id, module_id, expire_time) VALUES (?, ?, DATE_ADD(NOW(), INTERVAL 1 YEAR))', [orders[0].user_id, orders[0].module_id]) } await conn.end() } res.json({code: 'SUCCESS', message: '处理成功'}) }) module.exports = router ```

三、前端权限控制实现

1. 路由拦截配置

以Vue3项目为例,在router/index.js中添加全局前置守卫:

```js router.beforeEach(async (to, from, next) => { const token = localStorage.getItem('token') // 公共页面直接放行 if (to.meta.public) return next() if (!token) return next('/login') // 未缓存已购模块则请求后端 if (!store.state.userModules.length) { const res = await axios.get('/api/module/list') if (res.code === 200) { store.commit('setUserModules', res.data.filter(item => item.has_bought).map(item => item.module_key)) } } // 校验当前路由所需模块权限 if (to.meta.moduleKey && !store.state.userModules.includes(to.meta.moduleKey)) { return next('/pay') } next() }) ```

必须同时在后端所有模块业务接口添加权限校验,禁止仅靠前端拦截绕过付费

2. 模块入口显示控制

自定义权限指令,控制未购买模块的入口隐藏:

```js // main.js中注册全局指令 app.directive('hasModule', { mounted(el, binding) { const userModules = store.state.userModules if (!userModules.includes(binding.value)) { el.remove() } } }) // 页面使用
```

四、上线验证与排查

商标档案管理系统:别再让商标资产“睡大觉”了!
商标档案管理系统:别再让商标资产“睡大觉”了!
你有没有发现,很多公司花大价钱注册了一堆商标,结果呢?这些商标就像被扔进了仓库角落的旧文件,没人记得、没人维护,甚至快过期了都无人知晓。这事儿吧,说白了就是管理上的一笔“糊涂账”。
2026年09月07日 23:25:05
土木工程建筑业企业档案培训:实用干货全揭秘
土木工程建筑业企业档案培训:实用干货全揭秘
哎,各位土木届的搬砖人、包工头、资料员们,终于挖到个能救咱们半条命的宝贝了——今天要唠的就是跟咱们每天离不开的“工地隐形生命线”有关的【土木工程建筑业企业档案培训】!我可不是啥自媒体瞎忽悠,我是真踩过...
2026年09月07日 23:25:05
微信咨询
电话联系
QQ客服
微信咨询一对一服务
服务热线: 028-8744 4417
QQ客服: 2305721818