zoukankan      html  css  js  c++  java
  • 用golang写了个统计各单位报送的信息数量的微服务

    代码很乱,bug很多,将就着看吧。参考了很多网上代码,只能说声感谢了。

    //cjl.ZongHeInfo.1.0
    //目的:对各部门报上来的信息数量进行排名
    //思路:预计一年信息量不超过100M,全部存入全局变量GlobalInfoDoc中,以方便排序,统计
    //在协程中每5分钟将GlobalInfoDoc用json编码后存入文件中。因此,退出程序前应先手动保存(一定程度上可考虑用signal),避免5分钟内的数据丢失
    //重要:生成的json备份文件不能用notepad编辑,要保存为UTF-8 NO BOM
    
    package main
    
    import (
        "io/ioutil"
        "math"
        "net/url"
        "strconv"
    
        //"encoding/base64"
        "encoding/json"
        "fmt"
        "log"
        "sort"
        "strings"
    
        "sync"
    
        "net/http"
        "os"
        "os/exec"
        "time"
    )
    
    var (
        DEPA              = []string{"XX部", "XXX部", "X1部", "X2部", "XX公司", "XX公司", "XX厂", "XXX厂", "XX中心"}
        GlobalInfoDoc     TInfoDoc                  //保存了所有报上来的信息,其实就相当于一个文本文件
        GlobalConf        = make(map[string]string) //配置文件
        GlobalManuscripts = ""                      //约稿要求,直接放投稿页面的placeholder中了
    
    )
    
    type TInfoDoc struct {
        Infos        []TInfo //属性名一定要大写,血的教训
        sync.RWMutex         //http是多线程的,加上锁
    }
    
    type TInfo struct {
        Time       int64  `json:"name,omitempty"`       //`时间`
        Title      string `json:"title,omitempty"`      //`标题`
        Content    string `json:"Content,omitempty"`    //`内容`
        Department string `json:"Department,omitempty"` //`单位`
        Who        string `json:"Who,omitempty"`        //`报送人`
        Tel        string `json:"Tel,omitempty"`        //`电话`
        Ip         string `json:"IP,omitempty"`         //IP
    }
    
    //原计划利用cookie保存,后来认为用不上,把相关代码注释掉了
    type TUser struct {
        Name string
        Tel  string
    }
    
    //用于对所报的信息按单位名称排序
    type TInfoRanks []TInfoRank
    
    type TInfoRank struct {
        Key   string
        Value int
    }
    
    func main() {
        //打开文件,若文件不存在则生成
        year, _, _ := time.Now().Date()
        fname := "info" + strconv.Itoa(year)
        f, _ := os.OpenFile(fname, os.O_RDWR|os.O_CREATE, 0755)
        //将文件内容反序列化到全局变量infoDoc
        out, _ := ioutil.ReadAll(f)
        //fmt.Println(string(out))
        json.Unmarshal(out, &GlobalInfoDoc)
        //这里不能用defer f.Close(),因为main函数不会结束
        f.Close()
        //fmt.Println(infoDoc)
        http.HandleFunc("/Add", Add)
        http.HandleFunc("/Add2", Add2)
        http.HandleFunc("/List", List)
        http.HandleFunc("/save", save)
        http.HandleFunc("/conf", conf)
        http.HandleFunc("/yg", Manuscripts)
        http.HandleFunc("/", Index)
        exec.Command("cmd", "/c", "C:\Progra~2\Google\Chrome\Application\chrome.exe", "http://localhost:8000/Add").Run()
        go savefile()
        fmt.Println("因5分钟才保存一次文件,所以退出程序前请访问/save以防止最近5分钟提交的信息丢失")
        fmt.Println("请访问/conf更新配置文件的allowip")
        http.ListenAndServe(":8000", nil)
    
    }
    
    func checkErr(err error) {
        if err != nil {
            log.Println(err)
        }
    }
    
    func Index(w http.ResponseWriter, r *http.Request) {
        s := `
        <!DOCTYPE html>
    <html>
    <head> 
    <meta charset="utf-8"> 
    <title></title> 
    <style type="text/css">
    a:link,a:visited{
     text-decoration:none;  /*超链接无下划线*/
     color:red;
    }
    a:hover{
     text-decoration:underline;  /*鼠标放上去有下划线*/
    }
    </style>
    </head>
    <body>
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="http://192.168.0.239:8000/Add" target="_blank">信息报送</a>
    </body>
    </html>
        `
        fmt.Fprintf(w, s)
    }
    
    func Add(w http.ResponseWriter, r *http.Request) {
    
        //模板
        tpl := `<!DOCTYPE html>
    <html>
    <head> 
    <meta charset="utf-8"> 
    <title></title> 
    <style>
    input{
        500px;
    }
    textarea{
        500px;
    }
    .labelspan{
        display:inline-block;
        80px;
    }
    .input2{
        410px;
    }
    
    </style>
    
    </head>
    <body onload="javascript:init()">
    
    <form action="Add2" method="post" onsubmit="return check()">
    <fieldset>
    <legend><a href="yg">_</a>信息汇报<a href="List">_</a></legend>
    标题: <input id="Title" name="Title" type="text" size="30" required="required"><br/>
    内容: <textarea id="Content"   name="Content"   rows="15" placeholder="{{Manuscripts}}" required="required"></textarea><br/>
    正文限制(100-1000字):<span id="txtNum">0</span><br/>
    部门(单位): <select id="Department" name="Department" required="required">
    <option value="">请选择</option>
    {{OPTIONS}}
    </select><br>
    <span class="labelspan">报 送 人:</span> <input id="Who"  name="Who" class="input2"  value="" type="text" size="30" required="required"><br/>
    <span class="labelspan">联系电话:</span> <input id="Tel" name="Tel"  class="input2"  value="" type="text" size="30" required="required"><br/>
    <input type="submit" value="提交">
    </fieldset>
    </form>
    {{SORTLIST}}
    <script>
    var txt = document.getElementById("Content");
     var txtNum = document.getElementById("txtNum");
     txt.addEventListener("keyup", function(){
     txtNum.textContent = txt.value.length;
     })
    </script>
    <script>
         function check(){
            var title = document.getElementById("Title").value;
            var Content = document.getElementById("Content").value;    
            var Department = document.getElementById("Department").selectedIndex;    
            var Who = document.getElementById("Who").value;
            var Tel = document.getElementById("Tel").value;
             if(title ==  null || title == '' ||title.length <6){
                  alert("请完善标题!");
                  return false;
             }else if(Content.length <100||Content.length >1000){
                 alert("正文限制100至1000字!");
                  return false;
            }else if(Department == 0){
                 alert("请选择部门!");
                  return false;
            }else if(Who.length <2){
                 alert("请填写正确的姓名!");
                  return false;
            }else if(Tel.length <7){
                 alert("请填写联系电话!");
                  return false;
            }            
             return true;
          }
    </script>
        <script>
        function init(){    
        self.moveTo(0, 0);
        self.resizeTo(screen.width, screen.height);}
        </script>
    </body>
    </html>`
    
        //读cookie  注意:直接读来自用户的数据不安全
        /*
            var User TUser
            u, err := r.Cookie("u")
            if err == nil {
                uu, _ := base64.StdEncoding.DecodeString(u.Value)
                json.Unmarshal(uu, &User)
            }
            //fmt.Println(User)
            tpl = strings.Replace(tpl, `{{WHO}}`, string(User.Name), -1)
            tpl = strings.Replace(tpl, `{{TEL}}`, string(User.Tel), -1)
        */
        //选项
        var options strings.Builder
        for _, v := range DEPA {
            fmt.Fprintln(&options, `<option value="`, v, `">`, v, `</option>`)
        }
        //fmt.Println(options.String())
    
        //统计
        year, month, _ := time.Now().Date()
        //统计各部门稿件数量的map
        //本月
        thisMonthFirstDay := time.Date(year, month, 1, 0, 0, 0, 0, time.Local) //本月第一天
        nextMonthFirstDay := thisMonthFirstDay.AddDate(0, 1, 0)                //下月第一天
        _ThisMonth := periodInfos(thisMonthFirstDay, nextMonthFirstDay)
    
        //上月
        lastMonthFirstDay := thisMonthFirstDay.AddDate(0, -1, 0) //上月第一天
        _LastMonth := periodInfos(lastMonthFirstDay, thisMonthFirstDay)
    
        //本年
        thisYearFirstDay := time.Date(year, 1, 1, 0, 0, 0, 0, time.Local) //本年第一天
        nextYearLastDay := thisYearFirstDay.AddDate(1, 0, 0)              //明年第一天
        _ThisYear := periodInfos(thisYearFirstDay, nextYearLastDay)
    
        //将map转切片,用sort.Slice排序后输出
        var sortlist strings.Builder
        s_ThisMonth := sortByValue(_ThisMonth)
        s_LastMonth := sortByValue(_LastMonth)
        s_thisYear := sortByValue(_ThisYear)
        //本月ol
        fmt.Fprintln(&sortlist, `<table><tr><th>本月排序</th><th>上月排序</th><th>本年排序</th><tr><td>`)
        fmt.Fprintln(&sortlist, `<ol>`)
        for _, v := range s_ThisMonth {
            fmt.Fprintln(&sortlist, `<li>`, v.Key, v.Value, `篇`, `</li>`)
        }
        //上月ol
        fmt.Fprintln(&sortlist, `</ol></td><td><ol>`)
        for _, v := range s_LastMonth {
            fmt.Fprintln(&sortlist, `<li>`, v.Key, v.Value, `篇`, `</li>`)
        }
        //本年ol
        fmt.Fprintln(&sortlist, `</ol></td><td><ol>`)
        for _, v := range s_thisYear {
            fmt.Fprintln(&sortlist, `<li>`, v.Key, v.Value, `篇`, `</li>`)
        }
        fmt.Fprintln(&sortlist, `</ol></td></tr></table>`)
    
        //替换模板
        tpl = strings.Replace(tpl, `{{OPTIONS}}`, options.String(), -1)
        tpl = strings.Replace(tpl, `{{SORTLIST}}`, sortlist.String(), -1)
        tpl = strings.Replace(tpl, `{{Manuscripts}}`, GlobalManuscripts, -1)
    
        //模板输出
        fmt.Fprintln(w, tpl)
    
    }
    
    //对报送的稿件数量排序
    func sortByValue(m map[string]int) TInfoRanks {
    
        pl := make(TInfoRanks, len(m))
        i := 0
        for k, v := range m {
            pl[i] = TInfoRank{k, v}
            i++
        }
        sort.Slice(pl, func(i, j int) bool {
            flag := false
            if pl[i].Value > pl[j].Value {
                flag = true
            } else if pl[i].Value == pl[j].Value {
                if pl[i].Key < pl[j].Key {
                    flag = true
                }
            }
            return flag
        })
        return pl
    }
    
    func Add2(w http.ResponseWriter, r *http.Request) {
        //r.ParseForm()
        if r.Method == "POST" {
            info := TInfo{}
            info.Time = time.Now().Unix()
            info.Title = safeFilter(r.PostFormValue("Title"))
            info.Content = safeFilter(r.PostFormValue("Content"))
            info.Department = safeFilter(r.PostFormValue("Department"))
            info.Who = safeFilter(r.PostFormValue("Who"))
            info.Tel = safeFilter(r.PostFormValue("Tel"))
            ip := r.RemoteAddr
            info.Ip = ip[0:strings.LastIndex(ip, ":")]
            //在全局InfoDoc的Info切片后追加info
            GlobalInfoDoc.Lock()
            GlobalInfoDoc.Infos = append(GlobalInfoDoc.Infos, info)
            GlobalInfoDoc.Unlock()
    
            //设置cookie
            //base64
            /*
                u := TUser{}
                u.Name = info.Who
                u.Tel = info.Tel
                us, _ := json.Marshal(u)
                uu := base64.StdEncoding.EncodeToString(us)
                cookieu := http.Cookie{Name: "u", Value: uu, Path: "/", MaxAge: 86400 * 180}
                http.SetCookie(w, &cookieu)
            */
            //重定向,避免用户重复提交
            http.Redirect(w, r, "Add", http.StatusFound)
            return
        }
        //不允许GET访问
        fmt.Fprintln(w, "what are you 弄啥哩!")
    }
    
    func List(w http.ResponseWriter, r *http.Request) {
        ip := r.RemoteAddr
        ip = ip[0:strings.LastIndex(ip, ":")]
        allowip := GlobalConf["allowip"]
        if !strings.Contains(allowip, ip) {
            fmt.Fprintln(w, "not allow")
            return
        }
        tpl := `
    <html><head>
            <meta charset="UTF-8">
            <title></title>
             <style type="text/css">  
            body{background-color:#f0f0f0;}
                table{
        1000px;
        table-layout:fixed;/* 只有定义了表格的布局算法为fixed,下面td的定义才能起作用。 */
    }
    td{
        100%;
        min-300px;
        word-break:keep-all;/* 不换行 */
        white-space:nowrap;/* 不换行 */
        overflow:hidden;/* 内容超出宽度时隐藏超出部分的内容 */
        text-overflow:ellipsis;/* 当对象内文本溢出时显示省略标记(...) ;需与overflow:hidden;一起使用*/
    }
    ul{
        border:1px solid #000;
    }
    li{
        word-break:break-all;
    }
      .ctx{background-color:#f0f0f9;}
            </style>
        </head>   
        <body>  
         {{TB}}    
        {{PAGENAV}}
    </body></html>`
    
        //获取用户输入url参数?p=1中的页码1
        uri, _ := url.Parse(r.RequestURI)
        urlParam := uri.RawQuery
        uri_m, _ := url.ParseQuery(urlParam)
        curpage := 1   //设当前页为1
        p_uri_int := 1 //用户提供的页码,默认为1
        uri_m_p, ok := uri_m["p"]
        if ok {
            p_uri_int, _ = strconv.Atoi(uri_m_p[0])
        }
        perpage := 1000 //每页1000条
        numall := len(GlobalInfoDoc.Infos)
        if numall <= 0 {
            fmt.Fprintln(w, "无数据")
            return
        } //总信息数
        maxpage := int(math.Ceil(float64(numall) / float64(perpage))) //末页
    
        if p_uri_int <= 0 {
            curpage = 1
        } else if p_uri_int > maxpage {
            curpage = maxpage
        } else {
            curpage = p_uri_int
        }
        pagenav := `<a href=List>首页</a> <a href=List?p=` + strconv.Itoa(curpage-1) + `>上一页</a> <a href=List?p=` + strconv.Itoa(curpage+1) + `>下一页</a> <a href=List?p=` + strconv.Itoa(maxpage) + `>尾页</a>`
    
        //以ul>li方式输出表格
        var sb strings.Builder
        //逆序输出1000条,最新的稿件显示在最上面
        start1000 := numall - (curpage-1)*1000
        end1000 := numall - curpage*1000 + 1 //如:从1001到2000,而不是1000到2000,所以加1
        if end1000 < 1 {
            end1000 = 1 //逆序输出,最后一条也就是第一条
        }
        for i := start1000; i >= end1000; i-- {
            info := GlobalInfoDoc.Infos[i-1] //第一条对应的索引为0,所以减1
            time1 := time.Unix(int64(1553254972), 0).Format("2006-01-02 15:04:05")
            fmt.Fprintln(&sb, `<ul>`)
            fmt.Fprintln(&sb, `<li>`, `ID:`, i, `</li>`)
            fmt.Fprintln(&sb, `<li>`, time1, `</li>`)
            fmt.Fprintln(&sb, `<li>`, info.Title, `</li>`)
            fmt.Fprintln(&sb, `<li class="ctx">`, info.Content, `</li>`)
            fmt.Fprintln(&sb, `<li>`, info.Department, `</li>`)
            fmt.Fprintln(&sb, `<li>`, info.Who, `</li>`)
            fmt.Fprintln(&sb, `<li>`, info.Tel, `</li>`)
            fmt.Fprintln(&sb, `<li>`, info.Ip, `</li>`)
            fmt.Fprintln(&sb, `</ul>`)
        }
    
        //替换模板
        tpl = strings.Replace(tpl, `{{TB}}`, sb.String(), -1)
        tpl = strings.Replace(tpl, `{{PAGENAV}}`, pagenav, -1)
        fmt.Fprintln(w, tpl)
    }
    
    func save(w http.ResponseWriter, r *http.Request) {
        savefile()
    }
    
    //保存GlobalInfoDoc,每5分钟保存一次
    func savefile() {
        t1 := time.Tick(300 * time.Second)
        for {
            select {
            case <-t1:
                //fmt.Println("t1定时器")
                year, _, _ := time.Now().Date()
                fname := "info" + strconv.Itoa(year)
                //文件不存在则创建
                _, _ = os.OpenFile(fname, os.O_RDWR|os.O_CREATE, 0755)
                out, _ := json.Marshal(GlobalInfoDoc)
                ioutil.WriteFile(fname, out, os.ModeExclusive)
            }
        }
    
    }
    
    //安全过滤字符串
    func safeFilter(s string) string {
        s = strings.Replace(s, `=`, `=`, -1)
        s = strings.Replace(s, `'`, `’`, -1)
        s = strings.Replace(s, `"`, `”`, -1)
        s = strings.Replace(s, `<`, `〈`, -1)
        s = strings.Replace(s, `>`, `〉`, -1)
        s = strings.Replace(s, string(byte(10)), `<br>`, -1)
        return s
    }
    
    //统计一段时间内的稿件数量排名
    func periodInfos(t1, t2 time.Time) map[string]int {
        m := make(map[string]int, len(DEPA))
        for _, v := range DEPA {
            m[v] = 0
        }
        //更新投稿数量
        for _, info := range GlobalInfoDoc.Infos {
            dp := strings.TrimSpace(info.Department) //这里将字符串转[]byte后发现前后有空格,asii为32
            if _, ok := m[dp]; ok {
                if info.Time >= t1.Unix() && info.Time < t2.Unix() {
                    m[dp]++
                }
            }
    
        }
        return m
    }
    
    //读配置文件
    func conf(w http.ResponseWriter, r *http.Request) {
        //配置文件中每行的格式类似:a=1
        //将a=1解析到map中,k为等号左边的a,v为等号右边的1
        var m = make(map[string]string, 0)
        fname := "conf.txt"
        //文件不存在则创建
        f, _ := os.OpenFile(fname, os.O_RDWR|os.O_CREATE, 0755)
        defer f.Close()
        if si, _ := f.Stat(); si.Size() == 0 {
            f.WriteString("allowip=[::1],192.168.3.4,192.168.2.4")
        }
        b, _ := ioutil.ReadFile(fname)
        c := strings.Split(string(b), "
    ")
        for _, v := range c {
    
            if v != "" {
                d := strings.Split(v, "=")
                m[d[0]] = d[1]
            }
        }
        var l *sync.Mutex
        l = new(sync.Mutex)
        l.Lock()
        defer l.Unlock()
        GlobalConf = m
    }
    
    //设置约稿内容
    func Manuscripts(w http.ResponseWriter, r *http.Request) {
        ip := r.RemoteAddr
        ip = ip[0:strings.LastIndex(ip, ":")]
        allowip := GlobalConf["allowip"]
        if !strings.Contains(allowip, ip) {
            fmt.Fprintln(w, "not allow")
            return
        }
        //输入录入页面
        if r.Method == "GET" {
            tpl := `
        <html>
        <head>
        <meta charset="utf-8"> 
        <title></title></head>
        <body>
        <form action="" method="POST">
        <textarea id="yg" name="yg" rows=15 cols=50 value="" >{{Manuscripts}}</textarea>
        <input type="submit" value="提交">
        </form>
        </body>
        </html>
        `
            //替换模板
            tpl = strings.Replace(tpl, `{{Manuscripts}}`, GlobalManuscripts, -1)
            fmt.Fprintln(w, tpl)
            return
        } else if r.Method == "POST" {
            s := r.PostFormValue("yg")
            var l *sync.Mutex
            l = new(sync.Mutex)
            l.Lock()
            defer l.Unlock()
            GlobalManuscripts = s
            //同时将约稿内容保存为yg.txt  如果用ioutil.WriteFile,则os.ModeAppend是无效的
            //outil.WriteFile("yg.txt", []byte(s), os.ModeAppend)
            fl, err := os.OpenFile("yg.txt", os.O_APPEND|os.O_CREATE, 0644)
            if err != nil {
                return
            }
            defer fl.Close()
            fl.Write([]byte(s))
            //重定向,避免用户重复提交
            http.Redirect(w, r, "Add", http.StatusFound)
            return
        } else {
            fmt.Fprintln(w, "what are you 弄啥哩!")
        }
    
    }
  • 相关阅读:
    2020/1/27 代码审计学习-宽字节注入与二次注入
    2020/1/27代码审计学习之SQL注入漏洞
    2020/1/27代码审计学习之审计涉及的超全局变量
    2019总结与最近
    鸽一天
    [极客大挑战 2019]Knife
    [LuoguP1438]无聊的数列(差分+线段树/树状数组)
    [BJWC2018]最长上升子序列
    笙上月
    笔下梅
  • 原文地址:https://www.cnblogs.com/pu369/p/10620731.html
Copyright © 2011-2022 走看看