后台开发

环境
  1. node

  2. npm

  3. yarn(取代npm)

node安装(自带npm):https://www.cnblogs.com/mumusen/p/9760732.html

yarn安装:https://www.cnblogs.com/mumusen/p/9760732.html

技术栈

vue

vue-router

vuex

antd pro :https://pro.loacg.com/docs/getting-started

springboot(kotlin)

登录

https://www.jianshu.com/p/ffe2790578fe

vue.ls

一个vue封装的本地储存的方法

https://www.jianshu.com/p/ab7f67878279

vue.config.js

https://www.jianshu.com/p/b358a91bdf2d

验证码
  1. 前端请求验证码:生成一个unique传递给后端
  2. 后端画图生成验证码,返回给前端:将{unique: versifycode }使用redis缓存
  3. 前端请求验证 :带着unique跟输入的verifyCode
  4. 后端验证:根据unique获取verifyCode,比对
antd pro访问本地springboot服务
  1. 禁用mock

    Main.js中注释掉 import ‘./mock’

  2. 设置query的url ,为springboot的server地址

    env.development:

    1
    2
    3
    NODE_ENV=development
    VUE_APP_PREVIEW=false
    VUE_APP_API_BASE_URL=http://localhost:8082

修改vue.config.js

1
2
3
4
5
6
7
8
9
10
11
devServer: {
// development server port 8888
port: 8000,
proxy: {
'/api': { // 匹配模式,所有api开始的连接都匹配到下面服务器地址
target: VUE_APP_API_BASE_URL, // 这是开发服务器的地址
ws: false,
changeOrigin: true,
}
}
},
  1. 此时因为跨域问题会报错:

    has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.

  2. 后端解决:https://juejin.im/entry/5a0a52ab6fb9a045016777bf

后端登录逻辑

参见开发文档

正式环境配置

后端:

  • mysql(使用lszy小程序的,需要导入admin_api相关的table)oss备份
  • redis(使用lszy小程序的,暂时没有需要处理的)
  • jenkins : 新建task(拉取代码之后,使用maven构建生成jar包,之后在容器中运行该jar包)
  • 后端容器:运行服务端程序(jar)
  • nginx代理(使用lszy小程序的,增加admin_api的代理)

前端:

  • Node:尝试新建node镜像跟容器,完成前端项目构建
    • dockerFile(nodejs,yarn)
    • 启动镜像,设置挂载
    • 启动之后自动执行打包命令
  • nginx代理:使用lszy小程序的,增加admin_web的代理
  • nginx server(前端容器):部署admin_web
  • jenkins:新建task(拉取代码之后使用node容器构建,之后部署到nginx server容器)
jenkins 关联git
数据库8.0连接问题
分页(mybatis)
  1. 定义mapper

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    interface UserMapper {
    fun list(start: Int, pageSize: Int, phone: String?,
    startTime: String?, endTime: String?, nick_name: String?,
    gender: Int = 0, grade: Int = 0, provinceId: Int = 0,
    cityId: Int = 0, countyId: Int = 0, schoolId: Int = 0): List<User>?

    fun count(phone: String?,
    startTime: String?, endTime: String?, nick_name: String?,
    gender: Int = 0, grade: Int = 0, provinceId: Int = 0,
    cityId: Int = 0, countyId: Int = 0, schoolId: Int = 0): Int
    }
  2. 定义xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.lingshiedu.lszyAdmin.mapper.UserMapper">
    <select id="list" resultType="com.lingshiedu.lszyAdmin.model.User">
    select
    ls_user.id,
    ls_user.nick_name,
    ls_user.phone,
    ls_user.province_id,
    ls_user.city_id,
    ls_user.county_id,
    ls_user.school_id,
    ls_user.grade_id,
    ls_user.state,
    ls_schools.name as school_name,
    ls_user.gender,
    ls_user.adv_score,
    ls_user.occurrence_Time
    from ls_user
    INNER JOIN ls_schools ON ls_user.school_id = ls_schools.id
    <where>
    <if test="phone != null">
    AND phone=#{phone}
    </if>
    <if test="nick_name != null">
    AND nick_name LIKE CONCAT('%', #{nick_name}, '%')
    </if>
    <if test="gender != 0">
    AND gender=#{gender}
    </if>
    <if test="grade != 0">
    AND grade_id=#{grade}
    </if>
    <if test="startTime!=null and endTime!=null ">
    occurrence_Time BETWEEN #{startTime} and #{endTime}
    </if>
    <if test="provinceId != 0">
    AND province_id = #{provinceId}
    </if>
    <if test="cityId != 0">
    AND city_id = #{cityId}
    </if>
    <if test="countyId != 0">
    AND county_id = #{countyId}
    </if>
    <if test="schoolId != 0">
    AND school_id = #{schoolId}
    </if>
    </where>
    order by ls_user.occurrence_Time
    LIMIT #{start},
    #{pageSize}
    </select>

    <select id="count" resultType="int">
    select
    COUNT(*)
    from ls_user
    INNER JOIN ls_schools ON ls_user.school_id = ls_schools.id
    <where>
    <if test="phone != null">
    AND phone=#{phone}
    </if>
    <if test="nick_name != null">
    AND nick_name LIKE CONCAT('%', #{nick_name}, '%')
    </if>
    <if test="gender != 0">
    AND gender=#{gender}
    </if>
    <if test="grade != 0">
    AND grade_id=#{grade}
    </if>
    <if test="startTime!=null and endTime!=null ">
    occurrence_Time BETWEEN #{startTime} and #{endTime}
    </if>
    <if test="provinceId != 0">
    AND province_id = #{provinceId}
    </if>
    <if test="cityId != 0">
    AND city_id = #{cityId}
    </if>
    <if test="countyId != 0">
    AND county_id = #{countyId}
    </if>
    <if test="schoolId != 0">
    AND school_id = #{schoolId}
    </if>
    </where>
    </select>
    </mapper>
nginx命令
  • nginx -s reopen #重启Nginx

  • nginx -s reload #重新加载Nginx配置文件,然后以优雅的方式重启Nginx

  • nginx -s stop #强制停止Nginx服务

  • nginx -s quit #优雅地停止Nginx服务(即处理完所有请求后再停止服务)

  • nginx -t #检测配置文件是否有语法错误,然后退出

  • nginx -?,-h #打开帮助信息

  • nginx -v #显示版本信息并退出

  • nginx -V #显示版本和配置选项信息,然后退出

  • nginx -t #检测配置文件是否有语法错误,然后退出

  • nginx -T #检测配置文件是否有语法错误,转储并退出

  • nginx -q #在检测配置文件期间屏蔽非错误信息

  • nginx -p prefix #设置前缀路径(默认是:/usr/share/nginx/)

  • nginx -c filename #设置配置文件(默认是:/etc/nginx/nginx.conf)

  • nginx -g directives #设置配置文件外的全局指令

  • killall nginx #杀死所有nginx进程

docker nginx 挂载nginx.conf文件不同步问题:

https://blog.csdn.net/qq_41980563/article/details/90408460?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task

dockerfile 传参(区分正式环境跟测试环境)
1
2
3
4
5
6
7
8
9
10
11
12
FROM node:13.11.0
RUN npm install -y yarn
WORKDIR /app/lszy_admin_web
COPY package.json ./
COPY yarn.lock ./
RUN yarn install
COPY ./ ./
# 定义arg参数
ARG arg=""
# 定义容器启动之后的参数build_env
ENV build_env=${arg}
CMD yarn ${build_env} && cp -rf dist dist_
1
2
3
4
5
# arg设置为build(正式环境)
docker build --build-arg arg=build . -t node_lszy_admin_web

# arg设置为build(测试环境)
docker build --build-arg arg=build:test . -t node_lszy_admin_web
后台管理员以及权限

antdesign流程:

  1. main.js中引入router模块并设置基础路由

    1
    2
    3
    4
    export default new Router({
    ...
    routes: constantRouterMap
    })
  2. permission.js中添加router拦截器

    1. 如果有token(token使用vue.ls存放在本地)
      1.  如果路由目的地是login,那么重定向到defaultPage
       2.  否则,如果可达的页面为空,那么调用store中的GetAdminUserInfo,成功之后先修改store中的pages信息之后调用store中的GenerateRoutes根据服务端返回的数据更新路由。路由更新完毕之后根据路由跳转
      
    2. 如果没有token,那么跳转登录页面(在免登录名单中的不用登录)

    具体代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    router.beforeEach((to, from, next) => {
    NProgress.start() // start progress bar
    to.meta && (typeof to.meta.title !== 'undefined' && setDocumentTitle(`${to.meta.title} - ${domTitle}`))
    if (Vue.ls.get(ACCESS_TOKEN)) {
    /* has token */
    if (to.path === '/admin/login') {
    next({ path: defaultRoutePath })
    NProgress.done()
    } else {
    if (store.getters.pages.length === 0) {
    store
    .dispatch('GetAdminUserInfo')
    .then(res => {
    const pages = res.data.pages
    store.dispatch('GenerateRoutes', { pages }).then(() => {
    // 根据roles权限生成可访问的路由表
    // 动态添加可访问路由表
    router.addRoutes(store.getters.addRouters)
    // 请求带有 redirect 重定向时,登录自动重定向到该地址
    const redirect = decodeURIComponent(from.query.redirect || to.path)
    if (to.path === redirect) {
    // hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record
    next({ ...to, replace: true })
    } else {
    // 跳转到目的路由
    next({ path: redirect })
    }
    })
    })
    } else {
    next()
    }
    }
    } else {
    if (whiteList.includes(to.name)) {
    // 在免登录白名单,直接进入
    next()
    } else {
    next({ path: '/admin/login', query: { redirect: to.fullPath } })
    NProgress.done() // if current page is login will not trigger afterEach hook, so manually handle it
    }
    }
    })
vue中ref使用(跟子组件通信+emit)

https://www.jianshu.com/p/623c8b009a85

后端日志(log4j2)

https://www.cnblogs.com/LemonFive/p/10737658.html

登陆服务器快捷方式
客户端
  1. 打开~/.ssh,如果没有则创建文件夹

  2. 执行ssh-keygen -t rsa

    Enter file in which to save the key:输入保存私钥的文件名称(id_rsa_host)

    Enter passphrase (empty for no passphrase): (可以不写)

    Enter same passphrase again:(可以不写)

    此时会生成:id_rsa_host文件

  3. ssh-add ~/.ssh/id_rsa_host,通过私钥创建公钥

    此时会生成:id_rsa_host.pub文件

服务端
  1. 打开~/.ssh,如果没有则创建文件夹

  2. 打开authorized_keys,将客户端第3步生成的文件中的公钥保存到此文件中,如果有多个则,换行处理

快捷连接设置

在客户端,~/.ssh下创建config文件

内容如下

Host dev
HostName hostIp
PreferredAuthentications publickey
IdentityFile ~/.ssh/id_rsa_host
User username

完成,测试

ssh dev

回车即可