我的梦想,想做一个很牛逼的产品: 微信小程序《有梦必达》。
有梦必达初衷是一个帮助迷茫的、还没有梦想的、还在寻找梦想的、有梦想却还不知道怎么实现的童鞋更好的寻找或实现梦想的微信小程序。
有梦必达的初始设计功能有:登录、注册、找回密码、梦想列表、梦想详情、推荐课程、收藏、浏览记录、阅读数。
产品不够完善,仍需要完善,不喜勿喷。
备注:如果有同学完全按照此博客开发产品,可能会遇到坑,例如版本问题导致一些未知bug或者安装系统限制等等。在我开发过程中就遇到过很多小问题,不过只要坚持解决问题,这些问题都难不倒我。即使一开始遇到未知bug还是有点慌,但是后面通过各种方法寻找答案,解决完毕时会很自豪。
总体开发流程如下:
1、有一颗完成梦想的决心
2、购买阿里云域名并且备案
3、购买阿里云服务器Cent Os
4、服务器安装Node、Npm、Cnpm、Mysql、Nginx并配置、PM2
5、本地安装Navicat Premium,连接远程服务器的Mysql、创建数据表、安装Xshell连接远程服务器、Xftp查看服务器文件
6、Node+Express创建功能接口
7、Vue+Element创建后台管理系统
8、部署后台管理系统至域名服务器下
9、微信小程序+Vant前端展示
10、发布微信小程序
如果想做一个产品,就应该把他当做一个梦想来看待,因为有了梦想,就会有冲劲,自己会觉得有意义,从而时刻保持冲劲,有热情去完成他,而不是三天打鱼两天晒网,顶多坚持几天就半途而废,到最后,产品没完成,自己也觉得自己能力有问题。所以,即使梦想再难,我们也要完成他,即使会慢一点!
此处较为简单,直接去阿里云官网-购买域名即可 阿里云万网链接地址
备注:购买域名后,需要做域名备案才能在线上访问(咱们要做就做正规的) 阿里云首次备案参考链接地址
亦可参考其他博客方法备案,能实现备案目的就行
五、本地安装Navicat Premium,连接远程服务器的Mysql、创建数据表、安装Xshell连接远程服务器、Xftp查看服务器文件
由于三、四、五步骤篇幅过长, 直接移驾到开源中国预览
nodejs部分:
1、package.json
{ "name": "nodeDream", "version": "1.0.0", "description": "", "main": "app.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "parchments", "license": "MIT", "dependencies": { "body-parser": "^1.19.0", "cors": "^2.8.5", "express": "^4.17.1", "jsonwebtoken": "^8.5.1", "multer": "^1.4.2", "mysql": "^2.17.1", "nodemon": "^2.0.2", "request": "^2.88.2" } } 复制代码 |
2、connect.js
const path = require("path"); const mysql = require("mysql"); const express = require("express"); const app = express();//实例化一个app const router = express.Router(); const cors = require('cors'); const jwt = require('jsonwebtoken'); //用来生成token const request = require('request'); const bodyParser = require('body-parser'); app.use(bodyParser.json());//post请求req.body为空的处理 json请求 app.use(bodyParser.urlencoded({extended: false}));// 表单请求 // 全局配置跨域 app.use(cors()); //本地访问不了已上传的图片,需要开启静态资源路径访问 const pathname = __dirname; //静态文件访问 app.use(express.static(pathname)); // 使用静态文件 这样可以获取静态文件的东西 app.use(express.static('vueDream/dist')) //校验token function verifyToken(req, res, next){ let secretOrPrivateKey = 'jwtDream';//密钥 jwt.verify(req.headers.token, secretOrPrivateKey, function (__err, decode) { //时间失效的时候/ 伪造的token if (__err) { return res.status(401).send({ code: 401, data: null, message: "登录过期,请重新登录" }); } else { next(); } }) } //这里处理全局拦截,一定要写在最上面 app.all('*', (req, res, next) => { //设置响应头 res.header("Access-Control-Allow-Origin", "*"); //*表示允许的域名地址,本地则为'http://localhost' res.header("Access-Control-Allow-Headers", "*"); res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS"); res.header("Content-Type", "application/json;charset=utf-8"); //如果是登录 注册 找回密码 需要放行 无需校验token let isLogin = req.url.indexOf('login'); let isRegister = req.url.indexOf('register'); let isForgotPassword = req.url.indexOf('forgotPassword'); let isCollection = req.url.indexOf('collection'); let isHistory = req.url.indexOf('history'); // let isWechatLogin = req.url.indexOf('wechatLogin'); // let isGetWxAccessToken = req.url.indexOf('getWxAccessToken'); //微信小程序 并且是收藏接口需要校验token let systype = req.headers.systype; if(systype === 'wechat'){ if(isCollection !== -1 || isHistory !== -1){ verifyToken(req, res, next) } else { next(); } } else if(systype === 'pc'){ //pc 登录注册找回密码放行 if(isLogin !== -1 || isRegister !== -1 || isForgotPassword !== -1){ next(); }else{ verifyToken(req, res, next) } } }) //这一步目的是,当访问根路径时,把前端代码读取出来并显示 app.get('/', (req, res) => { //服务器地址vueDream/dist/index.html res.sendFile(path.resolve(__dirname, 'vueDream', 'dist', 'index.html')); }) //配置mysql const option = { host: "www.yousit", // host: "49.985.02.01", user: 'admin', port: '3306', password: "mmmmmmmm.", database: "node", connectTimeout: 5000, //连接超时 //multipleStatements: false //是否允许一个query中包含多条sql语句 } let pool; repool(); function Res ({ code = 200, message = '', data = {} }) { this.code = code; this.message = message; this.data = data; } function resJson (_res, result) { return _res.json(new Res(result)) } //断线重连机制 function repool() { //创建连接池 pool = mysql.createPool({ ...option, waitForConnections: true, //当无连接池可用时,等待(true)还是抛错(false) connectionLimit: 200, //连接数限制 queueLimit: 0 //最大连接等待数(0为不限制) }) pool.on('error', err => { err.code === 'PROTOCOL_CONNECTION_LOST' && setTimeout(repool, 2000) }) app.all('*', (_,__, next) => { pool.getConnection( err => { err && setTimeout(repool, 2000) || next() }) }) } module.exports = { app, pool, router, resJson , jwt , request}复制代码 |
3、app.js
const { app, pool } = require('./connect'); //test app.all('/api', (req, res) => { pool.getConnection((err, conn) => { res.json({ type: 'test success'}) pool.releaseConnection(conn) // 释放连接池,等待别的连接使用 }) }) //引入上传路由 const multerUpload = require('./routes/upload'); const user = require('./routes/user'); const dreamList = require('./routes/dream'); const collectionList = require('./routes/collection'); const historyList = require('./routes/history'); //使用路由 app.use('/upload', multerUpload); app.use('/user', user); app.use('/dream', dreamList); app.use('/collection', collectionList); app.use('/history', historyList); //查看链接成功 app.get('/api/test', function (req, res) { res.json({ message: "连接成功" }) }); //开启监听 app.listen(8888, () => { console.log("服务器端口8888开启中..."); })复制代码 |
express接口部分:
1、其中列表增删改查接口
//梦想接口 const {app, pool, router, resJson, jwt } = require('../connect'); // 查询 /* 按分页显示账号列表的路由 /getData */ app.post("/api/dream/getData", (req, res) => { //后期需要补充校验 console.log("前端传过来的",req.body) // 接收前端参数 let { pageSize, pageNo , name , userId} = req.body; // 默认值 pageSize = pageSize ? pageSize : 5; pageNo = pageNo ? pageNo : 1; name = name ? name : null; // 构造sql语句 (查询所有数据 按照时间排序) let sqlStr = `select * from dream`; // 执行sql语句 pool.getConnection((err, conn) => { conn.query(sqlStr, (err, data) => { if (err) throw err; // 计算数据总条数 let total = data.length; // 分页条件 (跳过多少条) let n = (pageNo - 1) * pageSize; // sqlStr += ` limit ${n}, ${pageSize}`;//表示从pageNo条数据取,取pageSize条数据 此处空格不能去掉不然无响应 // 拼接分页的sql语句 if(name){ sqlStr += ` where name like '%${name}%'`; // 执行sql语句 (查询对应页码的数据) conn.query(sqlStr, (_err, _data) => { if (_err) throw _err; res.send({ code: 1, data: { rows: _data, total: _data.length, pageNo: pageNo, pageSize: pageSize, }, message: '查询成功!' }); }); }else{ sqlStr += ` limit ${n} , ${pageSize}`; conn.query(sqlStr, (_err, data) => { if (_err) throw _err; res.send({ code: 1, data: { rows: data, total: total, pageNo: pageNo, pageSize: pageSize, }, message: '查询成功!' }); }); } }); pool.releaseConnection(conn) // 释放连接池,等待别的连接使用 }) }); //添加接口 app.post('/api/dream/add', (req, res) => { //后期需要补充校验 const data = req.body; const name = req.body.name; const sqlSameName = `select name from dream where name='${name}'`; //先查询数据库 dream 表里是否有前端传来的name值了 如果有返回重复提示 否则插入数据库 pool.getConnection((err, conn) => { conn.query(sqlSameName, data, (_err, _results) => { if(_err){console.log(_err); return false;} //根据查询表结果个数判断,如果1为数据库已经存在此名称,不可插入 0代表数据库不存在此名称,可插入 if(_results.length > 0){ return res.json({ code: 0, message: "不可重复添加!", data: null }) }else{ const sqlStr = 'insert into dream set ?'; conn.query(sqlStr, data, (err, results) => { console.log(data) if (err) throw err; res.json({ code: 1, message: '添加成功', data: results }); }) } }) pool.releaseConnection(conn) // 释放连接池,等待别的连接使用 }) }); //修改 app.post('/api/dream/edit', function (req, res) { //后期需要补充校验 const data = req.body; const id = req.body.id; // let { name, collectionStatus, price, age, experience, education, analysis, introduce, duty, ask, coverImagePath, planImagePathArray, viedoUrl} = req.body; let { name, coverImagePath, content, viedoUrl , recommend} = req.body; let modSql = `update dream set name='${name}', coverImagePath='${coverImagePath}', content='${content}', viedoUrl='${viedoUrl}', recommend='${recommend}' where id ='${id}'`; let nameSql = `select * from dream where name='${name}' and id !='${id}'`; //先查询数据库 dream 表里是否有前端传来的name值了 如果有返回重复提示 否则更新数据库 pool.getConnection((err, conn) => { conn.query(nameSql, data, (err, results) => { console.log(results) if(results.length >= 1){ return res.json({ code: 0, message: "名称已经存在!", data: null }) }else{ conn.query(modSql, data, (err, results) => { res.json({ code: 1, message: '修改成功', data: results }); }) } }) pool.releaseConnection(conn) // 释放连接池,等待别的连接使用 }) }); //查看 app.post('/api/dream/show', function (req, res) { //后期需要补充校验 let data = req.body; //id是商品id let { id , userId} = req.body; let modSql = `select * from dream where id='${id}'`; pool.getConnection((err, conn) => { conn.query(modSql, data, (err, results) => { if (err) { console.log("查询失败原因",err) return res.json({ code: 0, message: "查询失败", affectedRows: err }) } // 1.查询出当前readCount results[0].readCount = results[0].readCount+1; let newReadCount = results[0].readCount; // 2.更新列表的id readCount let sqlDreamCountStr = `update dream set readCount='${newReadCount}' where id ='${id}'`; conn.query(sqlDreamCountStr, data, (_err, _data) => { if (_err) throw _err; console.log('更新列表的id readCount成功'); }); // 返回 res.json({ code: 1, message: '查询成功', data: results }); }) pool.releaseConnection(conn) // 释放连接池,等待别的连接使用 }) }); // 删除 app.post('/api/dream/del', (req, res) => { //后期需要补充校验 console.log(req.body) // let sqlStr = `DELETE FROM dream WHERE id = ${req.body.id}`;//单个删除 let sqlStr = `DELETE FROM dream WHERE id in (${req.body})`; pool.getConnection((err, conn) => { conn.query( sqlStr, (err , results) => { if(err) { console.log(err); }else { res.json({ code: 1, message: '删除成功', data: results }); } }) pool.releaseConnection(conn) // 释放连接池,等待别的连接使用 }) }) module.exports = router;复制代码 |
2、更多功能接口 请前往github预览
以vue-admin-template作为基础后台框架, 查看vue-admin-template地址 ,搭建我的小程序后台管理系统,为实现登录、注册、用户列表增删改查、梦想列表增删改查、梦想详情、收藏、收藏列表增删改查、浏览记录等功能