利用go+vue快速开发一个web系统

行云流水
2024-05-31 / 0 评论 / 42 阅读 / 正在检测是否收录...

前言

为什么选择go+vue,博主一直没有接触过java开发。上学时学的东西忘的差不多了。系统功能很简单,但是要部署在客户的服务器上。go的一功能是将代码和静态资源打包成一个二进制执行文件,比较符合我的胃口。心心念一直想入坑go开发。

总体设计

系统很简单,只有两个页面。一个登录页面,登录后到达主页面。主页面包括一个文件上传组件,一个table组件。table组件,记录上传处理记录,下载处理结果。采用vue开发。后端接口采用go语言开发。

前端

后端

采用go语言开发后端接口,接口包含如下四个。同时还有其他一些细节,如将数据库连接,用户密码,程序监听端口等设置成可配置,配置在运行文件同目录的.env文件内
  • 文件下载接口
  • 文件上传接口
  • 任务信息列表
  • 登录接口

开发过程

大部分是chatgpt的功劳,在chatgpt回复的基础上修改。一开始一个main.go到底。后续将不同功能拆成多个文件。

功能代码

main函数

实现所有路由,加载静态资源,监听端口可配置,记录日志,中断程序保存日志
func main() {
    // 日志记录
    initLogger()

    // 捕获 SIGINT 信号(Ctrl+C)
    c := make(chan os.Signal, 1)
    signal.Notify(c, os.Interrupt, syscall.SIGINT)

    go func() {
        <-c
        logger.Println("Received SIGINT. Flushing logs and exiting.")
        closeLogger()
        os.Exit(0)
    }()

    // 路由
    r := mux.NewRouter()

    // 处理文件下载路由
    r.HandleFunc("/api/download", downloadHandler)

    // 处理文件上传路由
    r.HandleFunc("/api/upload", uploadHandler)

    // 新增处理读取任务信息的路由
    r.HandleFunc("/api/readtasks", readTasksHandler)

    // 添加处理登录请求的路由
    r.HandleFunc("/api/login", loginHandler).Methods("POST")

    // 设置静态文件服务
    statikFS, err := fs.New()
    if err != nil {
        log.Fatal(err)
    }
    r.PathPrefix("/").Handler(http.StripPrefix("/", http.FileServer(statikFS)))

     // 加载 .env 文件中的环境变量
        if err := godotenv.Load(); err != nil {
            logger.Fatal("加载.env失败")
            return
        }
    lport := os.Getenv("LISTEN_PORT")
    logger.Printf("Server is listening on port %s...", lport)
    log.Fatal(http.ListenAndServe(":"+lport, r))
}

日志函数

日志同时记录到文件
func initLogger() {
    var err error
    logFile, err = os.OpenFile("smsman.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
    if err != nil {
        log.Fatal(err)
    }

    logger = log.New(io.MultiWriter(os.Stdout, logFile), "LOG: ", log.Ldate|log.Ltime|log.Lshortfile)
}

func closeLogger() {
    if logFile != nil {
        logFile.Close()
    }
}

下载函数

实现处理结果下载功能
func downloadHandler(w http.ResponseWriter, r *http.Request) {
    filename := r.URL.Query().Get("filename") // 获取请求中的文件名参数

    filePath := "./result/" + filename // 构建文件路径

    // 打开文件
    file, err := os.Open(filePath)
    if err != nil {
        http.Error(w, "文件未找到", http.StatusNotFound)
        return
    }
    defer file.Close()

    // 设置响应头
    w.Header().Set("Content-Disposition", "attachment; filename="+filepath.Base(filePath))
    w.Header().Set("Content-Type", "application/octet-stream")
    fileInfo, _ := file.Stat()
    w.Header().Set("Content-Length", strconv.FormatInt(fileInfo.Size(), 10))

    // 传输文件内容到 ResponseWriter
    _, err = io.Copy(w, file)
    if err != nil {
        http.Error(w, "文件传输失败", http.StatusInternalServerError)
        return
    }
}

登录认证函数

实现简单的登录认证
func loginHandler(w http.ResponseWriter, r *http.Request) {
    // 解析请求中的用户名和密码
    r.ParseForm()
    username := r.Form.Get("username")
    passwd := r.Form.Get("password")

    // 加载 .env 文件中的环境变量
    if err := godotenv.Load(); err != nil {
        logger.Fatal("加载.env失败")
        http.Error(w, err.Error(), http.StatusInternalServerError)
    return
    }
    user := os.Getenv("SMSUSER")
    password := os.Getenv("SMSPWD")

    // 验证用户名和密码
    if username == user && passwd == password {
        response := LoginResponse{
            Success: true,
            Username: username,
        }

        jsonResponse, err := json.Marshal(response)
        if err != nil {
            http.Error(w, err.Error(), http.StatusInternalServerError)
            return
        }

        w.Header().Set("Content-Type", "application/json")
        w.WriteHeader(http.StatusOK)
        w.Write(jsonResponse)

    } else {
        response := LoginResponse{
            Success: false,
            Username: username,
        }

        jsonResponse, err := json.Marshal(response)
        if err != nil {
            http.Error(w, err.Error(), http.StatusInternalServerError)
            return
        }

        w.Header().Set("Content-Type", "application/json")
        w.WriteHeader(http.StatusOK)
        w.Write(jsonResponse)
    }
}

前端页面开发

前端页面开发采用vue,这里用到了 viewui, 下载他们的demo,然后改造。

Home.vue

Home.vue中加载了两个组件,一个是文件上传组件Fupload.vue,另一个是Mtable.vue。记录一下加载方法。
<template>
    <div class="layout-base">
        <Layout>
            <Header class="layout-header">短信签名处理后台</Header>
            <Content class="layout-content">
                <Card style="width:100%">
                        <template #title>
                            <p style="text-align: left; margin: 0;">文件上传</p>
                        </template>
                        <Fupload/>
                </Card>
                <Divider>---</Divider>
                <Card style="width:100%;">
                        <template #title>
                            <p style="text-align: left; margin: 0;">上传记录</p>
                        </template>
                         <Mtable/>
                </Card>    
            </Content>
        </Layout>
    </div>
</template>
<script>
    import Fupload from '@/components/Fupload.vue'
    import Mtable from '@/components/Mtable.vue'
    export default {
        name: 'HomeView',
        components: {
          Fupload,
          Mtable
        },
        data () {
            return {
                split: 0.4
            }
        },
    }
</script>

Mtable.vue

Mtable.vue 主要注意每个2s刷新数据操作
      created() {
            this.fetchData();

            // 每2秒刷新数据
            setInterval(() => {
                this.fetchData();
            }, 2000);
        },
        methods: {
            async fetchData() {
                try {
                    const response = await axios.get('/api/readtasks');
                    this.data = response
                        .data; // Assuming the response directly contains the array of task objects
                } catch (error) {
                    console.error('Error fetching data:', error);
                }
            },
            getStatusColor(status) {
                return {
                    'color-green': status === '进行中',
                    'color-red': status === '已完成'
                };
            }
        }

登录检测

默认访问Home.vue,同时检测是否登录。如果没有登录的话,跳转到Login.vue
// router/index.js
router.beforeEach((to, from, next) => {
  if (to.matched.some(record => record.meta.requiresAuth)) {
    // 检查用户是否已登录,这里假设 isAuthenticated 是存储登录状态的变量
    const isAuthenticated = store.state.isAuthenticated;
    if (!isAuthenticated) {
      next('/login'); // 如果未登录,则重定向到登录页面
    } else {
      next();
    }
  } else {
    next(); // 不需要登录认证的页面直接放行
  }
})

// store/index.js
// 存储登录状态
export default createStore({
  state: {
      isAuthenticated: false // 初始登录状态为未认证
  },
  getters: {
  },
  mutations: {
      setAuthentication(state, isAuthenticated) {
          state.isAuthenticated = isAuthenticated;
      }
  },
  actions: {
      login({ commit }) {
          // 模拟登录成功,设置登录状态为 true
          commit('setAuthentication', true);
          
          localStorage.setItem('isAuthenticated', true);
        },
        logout({ commit }) {
          // 登出操作,设置登录状态为 false
          commit('setAuthentication', false);
        }
  },
  modules: {
  }
})
  • Login.vue,登录功能的实现
    import axios from 'axios';
    import { mapActions } from 'vuex';
    import router from '../router'; // 引入 Vue Router
    export default {
        data() {
            return {
                username:'',
                password:''
            }
        },
        methods: {
            ...mapActions(['login']),
            
            async handleSubmit(valid, {
                username,
                password
            }) {
                if (valid) {
                    try {
                        const params = new URLSearchParams();
                        params.append('username', username);
                        params.append('password', password);
                        
                        const config = {
                            headers: {
                                'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
                            }
                        };
                        const response = await axios.post('/api/login', params, config);
                
                        //console.log(response)
                        if (response.data.success) {
                            this.$Modal.success({
                                title: '登录成功',
                                content: '欢迎回来,' + username
                            });
                            this.login();
                            router.push('/'); // 登录成功后跳转到 /home 页面
                        } else {
                            this.$Modal.error({
                                title: '登录失败',
                                content: '用户名或密码错误,请重试'
                            });
                        }
                    } catch (error) {
                        console.error(error);
                        this.$Modal.error({
                            title: '登录失败',
                            content: '出现意外错误,请稍后再试'
                        });
                    }
                }
            }
        }
    }

程序打包

mac安装go-statik, 然后打包
brew install go-statik
statik -src=./dist/

#man - centos
GOOS=linux GOARCH=amd64 go build -o smsman_linux_amd64 main.go

评论 (0)

取消
只有登录/注册用户才可评论