├── go.mod ├── main.go ├── app ├── model │ ├── basemodel │ │ └── basemodel.go │ ├── analyse │ │ └── analyse.go │ ├── datamodel │ │ ├── unicomnotice.go │ │ ├── simflow.go │ │ └── simunicom.go │ └── unicommodel │ │ └── unicommodel.go ├── api │ ├── collect │ │ └── getdata.go │ ├── operate │ │ └── operate.go │ ├── notice │ │ └── notice.go │ └── analyse │ │ └── analyse.go └── service │ ├── operate │ ├── chang19to20.go │ └── autoplan.go │ ├── collect │ ├── common.go │ └── getdata.go │ └── analyse │ ├── monthsimflow.go │ └── flow.go ├── boot ├── boot.go └── router.go ├── library ├── response │ └── response.go └── utils │ └── utils.go ├── bin ├── config │ └── config.toml └── curl.sh ├── config └── config.example.toml ├── LICENSE.txt ├── README.md ├── go.sum └── deploy └── db.sql /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/xinjiayu/SimServerUnicom 2 | 3 | go 1.12 4 | 5 | require ( 6 | github.com/gogf/gf v1.9.10 7 | github.com/qianlnk/pgbar v0.0.0-20190929032005-46c23acad4ed 8 | github.com/qianlnk/to v0.0.0-20180426070425-a52c7fda1751 // indirect 9 | ) 10 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/gogf/gf/frame/g" 5 | "github.com/gogf/gf/os/glog" 6 | _ "github.com/xinjiayu/SimServerUnicom/boot" 7 | 8 | ) 9 | 10 | func main() { 11 | glog.Info("SimServerUnicom Version:", "V1.0.1.201911291930") 12 | g.Server().Run() 13 | } 14 | 15 | -------------------------------------------------------------------------------- /app/model/basemodel/basemodel.go: -------------------------------------------------------------------------------- 1 | package basemodel 2 | 3 | 4 | type BaseModel struct { 5 | Createtime int `json:"createtime"` 6 | Updatetime int `json:"updatetime"` 7 | } 8 | // 定义model interface 9 | type IModel interface { 10 | // 获取表名 11 | TableName() string 12 | // 获取主键值 13 | PkVal() int 14 | } 15 | -------------------------------------------------------------------------------- /boot/boot.go: -------------------------------------------------------------------------------- 1 | package boot 2 | 3 | import ( 4 | "github.com/gogf/gf/frame/g" 5 | "github.com/gogf/gf/os/glog" 6 | ) 7 | 8 | // 用于应用初始化。 9 | func init() { 10 | c := g.Config() 11 | s := g.Server() 12 | routerInit() 13 | 14 | // log配置 15 | logPath := c.GetString("system.logPath") 16 | glog.SetPath(logPath) 17 | glog.SetStdoutPrint(true) 18 | 19 | // Web Server配置 20 | s.SetLogPath(logPath) 21 | s.SetErrorLogEnabled(true) 22 | s.SetAccessLogEnabled(true) 23 | 24 | port := c.GetInt("system.port") 25 | s.SetPort(port) 26 | } 27 | -------------------------------------------------------------------------------- /library/response/response.go: -------------------------------------------------------------------------------- 1 | package response 2 | 3 | import ( 4 | "github.com/gogf/gf/frame/g" 5 | "github.com/gogf/gf/net/ghttp" 6 | ) 7 | 8 | // 标准返回结果数据结构封装。 9 | // 返回固定数据结构的JSON: 10 | // err: 错误码(0:成功, 1:失败, >1:错误码); 11 | // msg: 请求结果信息; 12 | // data: 请求结果,根据不同接口返回结果的数据结构不同; 13 | func Json(r *ghttp.Request, err int, msg string, data ...interface{}) { 14 | responseData := interface{}(nil) 15 | if len(data) > 0 { 16 | responseData = data[0] 17 | } 18 | r.Response.WriteJson(g.Map{ 19 | "err": err, 20 | "msg": msg, 21 | "data": responseData, 22 | }) 23 | r.Exit() 24 | } 25 | -------------------------------------------------------------------------------- /bin/config/config.toml: -------------------------------------------------------------------------------- 1 | # 服务系统设置 2 | [system] 3 | logPath = "./log" 4 | port = "8189" 5 | 6 | # 数据库配置 7 | [database] 8 | [[database.default]] 9 | host = "127.0.0.1" 10 | port = "3306" 11 | user = "root" 12 | pass = "root" 13 | name = "simdb" 14 | type = "mysql" 15 | max-idle = "10" 16 | max-open = "10" 17 | max-lifetime = "30" 18 | debug = "false" 19 | 20 | # 使用的数据表名配置 21 | [datatable] 22 | flow_table = "sim_flow" 23 | info_table = "sim_unicom" 24 | notice_table = "unicom_notice" 25 | 26 | # 联通物联网平台配置 27 | [unicom] 28 | api_url = "https://api.10646.cn/rws/api/v1/" 29 | api_key = "xxxxxxxxxxxxxxxx" 30 | api_user = "xxxxxxxxxxxxxxx" 31 | -------------------------------------------------------------------------------- /config/config.example.toml: -------------------------------------------------------------------------------- 1 | # 服务系统设置 2 | [system] 3 | logPath = "./log" 4 | port = "8189" 5 | 6 | # 数据库配置 7 | [database] 8 | [[database.default]] 9 | host = "127.0.0.1" 10 | port = "3306" 11 | user = "root" 12 | pass = "root" 13 | name = "simdb" 14 | type = "mysql" 15 | max-idle = "10" 16 | max-open = "10" 17 | max-lifetime = "30" 18 | debug = "false" 19 | 20 | # 使用的数据表名配置 21 | [datatable] 22 | flow_table = "sim_flow" 23 | info_table = "sim_unicom" 24 | notice_table = "unicom_notice" 25 | 26 | # 联通物联网平台配置 27 | [unicom] 28 | api_url = "https://api.10646.cn/rws/api/v1/" 29 | api_key = "xxxxxxxxxxxxxxxx" 30 | api_user = "xxxxxxxxxxxxxxx" 31 | -------------------------------------------------------------------------------- /boot/router.go: -------------------------------------------------------------------------------- 1 | package boot 2 | 3 | import ( 4 | "github.com/gogf/gf/frame/g" 5 | "github.com/gogf/gf/net/ghttp" 6 | "github.com/xinjiayu/SimServerUnicom/app/api/analyse" 7 | "github.com/xinjiayu/SimServerUnicom/app/api/collect" 8 | "github.com/xinjiayu/SimServerUnicom/app/api/notice" 9 | "github.com/xinjiayu/SimServerUnicom/app/api/operate" 10 | ) 11 | 12 | func MiddlewareCORS(r *ghttp.Request) { 13 | r.Response.CORSDefault() 14 | r.Middleware.Next() 15 | } 16 | // 统一路由注册 17 | func routerInit() { 18 | s := g.Server() 19 | s.SetNameToUriType(ghttp.URI_TYPE_ALLLOWER) 20 | s.BindMiddlewareDefault(MiddlewareCORS) 21 | 22 | s.BindObject("/unicom", new(collect.Controller)) 23 | s.BindObject("/unicom/op",new(operate.Controller)) 24 | s.BindObject("/unicom/analyse",new(analyse.Controller)) 25 | s.BindObject("/unicom/notice",new(notice.Controller)) 26 | 27 | } -------------------------------------------------------------------------------- /app/api/collect/getdata.go: -------------------------------------------------------------------------------- 1 | package collect 2 | 3 | import ( 4 | "github.com/gogf/gf/net/ghttp" 5 | "github.com/gogf/gf/os/glog" 6 | "github.com/gogf/gf/util/gconv" 7 | collectService "github.com/xinjiayu/SimServerUnicom/app/service/collect" 8 | "github.com/xinjiayu/SimServerUnicom/library/response" 9 | ) 10 | 11 | 12 | // Controller API管理对象 13 | type Controller struct{} 14 | 15 | // CtdUsages 获取指定sim卡的流量,返回指定设备的周期累计用量信息。 16 | func (c *Controller) CtdUsages(r *ghttp.Request) { 17 | iccid := r.GetString("iccid") 18 | dataModel :=collectService.GetUsages(iccid) 19 | response.Json(r, 0, "", dataModel) 20 | 21 | } 22 | 23 | // CardList 通过联通平台的devices接口获取设备卡列表 24 | func (c *Controller) CardList(r *ghttp.Request) { 25 | 26 | glog.Info("\n开始获取SIM卡列表数据...") 27 | simList := collectService.GetCardList() 28 | number := len(simList.Devices) 29 | numstr := gconv.String(number) 30 | glog.Infof("SIM卡共%d张卡", number) 31 | response.Json(r, 0, "SIM卡流量数据条数:"+numstr, simList.Devices) 32 | 33 | } 34 | 35 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 xinjy@qq.com https://github.com/xinjiayu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /app/service/operate/chang19to20.go: -------------------------------------------------------------------------------- 1 | package operate 2 | 3 | import ( 4 | "github.com/gogf/gf/util/gconv" 5 | ) 6 | 7 | func luhnSum(inVal string) int { 8 | tmpStr := reverseString(inVal) 9 | evenSum := 0 10 | isOdd := false 11 | oddSum := 0 12 | 13 | for i := 0; i < len(tmpStr); i++ { 14 | digit := gconv.Int(tmpStr[i : i+1]) 15 | if isOdd { 16 | oddSum = oddSum + digit 17 | isOdd = false 18 | 19 | } else { 20 | //把奇数位数字取出,然后乘以2 21 | digit = digit * 2 22 | //把所得数字相加,如果是两位数结果,拆开相加得到A,比如14拆成1+4; 23 | if digit > 9 { 24 | digit1 := gconv.Int(gconv.String(digit)[0:1]) 25 | digit2 := gconv.Int(gconv.String(digit)[1:2]) 26 | digit = digit1 + digit2 27 | } 28 | evenSum = evenSum + digit 29 | isOdd = true 30 | } 31 | 32 | } 33 | 34 | return oddSum + evenSum 35 | } 36 | 37 | func LuhnNext(inVal string) int { 38 | rst := luhnSum(inVal) % 10 39 | if rst == 0 { 40 | return 0 41 | } else { 42 | return 10 - rst 43 | } 44 | } 45 | 46 | // 反转字符串 47 | func reverseString(s string) string { 48 | runes := []rune(s) 49 | for from, to := 0, len(runes)-1; from < to; from, to = from+1, to-1 { 50 | runes[from], runes[to] = runes[to], runes[from] 51 | } 52 | return string(runes) 53 | } 54 | -------------------------------------------------------------------------------- /app/model/analyse/analyse.go: -------------------------------------------------------------------------------- 1 | package analyse 2 | 3 | //最近两天的数据结构 4 | type TwoDaysFlow struct { 5 | Iccid string `json:"iccid"` 6 | TodayUsage int64 `json:"today_usage"` 7 | YesterdayUsage int64 `json:"yesterday_usage"` 8 | } 9 | 10 | //计划内卡的信息结构 11 | type PlanSimCardInfo struct { 12 | Iccid string 13 | Flow int64 14 | PlanName string 15 | } 16 | 17 | //计费套餐计划信息结构 18 | type PlanInfo struct { 19 | PlanName string 20 | AllFlow int64 //流量池总流量 21 | UseFlow int64 //已用流量 22 | AveSimUseFlow int64 //每个卡已用平均流量 23 | AveDayFlow int64 //日均流量 24 | SurplusFlow int64 //剩余流量 25 | OutFlow int64 //超出流量 26 | ExpectFlow int64 //预计需要的流量 27 | SurplusDayNum int64 //剩余天数 28 | Num int 29 | } 30 | 31 | //指定卡两个月流量统计信息结构 32 | type TwoDaysBaySimCardFlow struct { 33 | DayName []int `json:"day_name"` 34 | Nowmonth []int64 `json:"nowmonth"` 35 | Beforemonth []int64 `json:"beforemonth"` 36 | } 37 | 38 | //两个月所有流量的统计信息结构 39 | type TwoMonthFlowCount struct { 40 | DayName []int `json:"day_name"` 41 | Nowmonth []int64 `json:"nowmonth"` 42 | NowmonthSimnum []int64 `json:"nowmonth_simnum"` 43 | NowmonthAvg []int64 `json:"nowmonth_avg"` 44 | Beforemonth []int64 `json:"beforemonth"` 45 | BeforeMonthSimnum []int64 `json:"before_month_simnum"` 46 | BeforeMonthAvg []int64 `json:"before_month_avg"` 47 | } 48 | -------------------------------------------------------------------------------- /app/api/operate/operate.go: -------------------------------------------------------------------------------- 1 | package operate 2 | 3 | import ( 4 | "github.com/gogf/gf/net/ghttp" 5 | "github.com/gogf/gf/util/gconv" 6 | serviceOperate "github.com/xinjiayu/SimServerUnicom/app/service/operate" 7 | "github.com/xinjiayu/SimServerUnicom/library/response" 8 | ) 9 | 10 | // Controller API管理对象 11 | type Controller struct{} 12 | 13 | // ChangePlan 跟据sim卡流量池使用情况进行自动设置平衡,平衡的顺序为1G池超出时,自动变更部分sim卡到2G池中, 14 | //当2G池流量超出时自动将部分sim卡变更到3G池。当3G池也超出时将1G池中部分sim卡变更到3G池 15 | func (c *Controller) ChangePlan(r *ghttp.Request) { 16 | 17 | as := new(serviceOperate.AutoChangePlan) 18 | err, num := as.AutoSetupPlan() 19 | if err != nil { 20 | response.Json(r, -1, err.Error(), "") 21 | 22 | } 23 | response.Json(r, 0, "SIM卡计费套餐变动数:"+gconv.String(num), "") 24 | 25 | } 26 | 27 | // ChangeInitPlan 自动初始化所有sim卡的流量套餐变更为1G套餐 28 | func (c *Controller) ChangeInitPlan(r *ghttp.Request) { 29 | 30 | as := new(serviceOperate.AutoChangePlan) 31 | if as.GetNot01PlanNum() > 0 { 32 | err, num := as.AutoSetupPlanInit() 33 | if err != nil { 34 | response.Json(r, -1, err.Error(), "") 35 | 36 | } 37 | response.Json(r, 0, "SIM卡计费套餐变动数:"+gconv.String(num), "") 38 | } 39 | 40 | } 41 | 42 | // 43 | func (c *Controller) Change19to20(r *ghttp.Request) { 44 | iccid := r.GetString("iccid") 45 | if iccid == "" { 46 | response.Json(r, -1, "19位的iccid号码不能为空!", "") 47 | } 48 | 49 | numberStr := serviceOperate.LuhnNext(iccid) 50 | 51 | response.Json(r, 0, "sim卡20位号码", iccid+gconv.String(numberStr)) 52 | 53 | } 54 | -------------------------------------------------------------------------------- /app/api/notice/notice.go: -------------------------------------------------------------------------------- 1 | package notice 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/xinjiayu/SimServerUnicom/app/model/datamodel" 6 | "github.com/xinjiayu/SimServerUnicom/app/model/unicommodel" 7 | "strconv" 8 | 9 | "github.com/gogf/gf/encoding/gxml" 10 | "github.com/gogf/gf/net/ghttp" 11 | "github.com/gogf/gf/os/glog" 12 | "github.com/gogf/gf/os/gtime" 13 | ) 14 | 15 | 16 | // Controller API管理对象 17 | type Controller struct{} 18 | 19 | // DataReceive 通知数据接收 20 | func (c *Controller) DataReceive(r *ghttp.Request) { 21 | eventId := r.GetString("eventId") 22 | eventType := r.GetString("eventType") 23 | timestamp := r.GetString("timestamp") 24 | //signature := r.GetString("signature") 25 | data := r.GetString("data") 26 | 27 | glog.Info("接收到的通知数据:",data) 28 | 29 | dataxml, err := gxml.ToJson([]byte(data)) 30 | if err != nil { 31 | glog.Error(err.Error()) 32 | } 33 | 34 | //包含过去 24 小时的用量超过指定数量的设备的 ICCID 和数据用量。 35 | //"PAST24H_DATA_USAGE_EXCEEDED": 36 | resultsData := new(unicommodel.ResultsData) 37 | 38 | Err := json.Unmarshal(dataxml, &resultsData) 39 | if Err != nil { 40 | glog.Error(Err) 41 | } 42 | iccid := "" 43 | dataUsage := 0 44 | switch eventType { 45 | case "PAST24H_DATA_USAGE_EXCEEDED": 46 | 47 | dataUsageStr := resultsData.Past24HDataUsage.DataUsage 48 | dataUsage, _ = strconv.Atoi(dataUsageStr) 49 | iccid = resultsData.Past24HDataUsage.Iccid 50 | 51 | case "CTD_USAGE": 52 | dataUsageStr := resultsData.CtdUsage.DataUsage 53 | dataUsage, _ = strconv.Atoi(dataUsageStr) 54 | iccid = resultsData.CtdUsage.Iccid 55 | } 56 | 57 | ttime := gtime.NewFromStr(timestamp).Second() 58 | strInt64 := strconv.FormatInt(ttime, 10) 59 | tt, _ := strconv.Atoi(strInt64) 60 | 61 | data1 := "" 62 | data2 := "" 63 | datamodel.UnicomNotice{}.Save(eventId, eventType, iccid, data1, data2, tt, dataUsage) 64 | } 65 | -------------------------------------------------------------------------------- /library/utils/utils.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "github.com/gogf/gf/os/gtime" 5 | "github.com/gogf/gf/util/gconv" 6 | "time" 7 | ) 8 | 9 | //1G计算单位 10 | const G1 int64 = 1024 * 1024 * 1024 11 | 12 | //1MB计算单位 13 | const MB1 int64 = 1024 * 1024 14 | 15 | //统一存储流量统计的时候所用的日期相关信息 16 | type FlowUseDate struct { 17 | Year string 18 | LastYear string 19 | Month string 20 | LastMonth string 21 | BeforeLastMonth string 22 | Today string 23 | Yesterday string 24 | BeforeYesterday string 25 | 26 | LastMonthDays string 27 | BeforeLastMonthDays string 28 | } 29 | 30 | //计算两个时间相差的天数 31 | func TimeSub(t1, t2 time.Time) int { 32 | t1 = time.Date(t1.Year(), t1.Month(), t1.Day(), 0, 0, 0, 0, time.Local) 33 | t2 = time.Date(t2.Year(), t2.Month(), t2.Day(), 0, 0, 0, 0, time.Local) 34 | return int(t1.Sub(t2).Hours() / 24) 35 | } 36 | 37 | //转为正数 38 | func Abs(x int64) int64 { 39 | if x < 0 { 40 | return -x 41 | } 42 | return x 43 | } 44 | 45 | //转换时间到时间戳格式 46 | func ChangeUnixTime(strTime string) string { 47 | t := gtime.NewFromStr(strTime).Second() 48 | return gconv.String(t) 49 | } 50 | //计算流量统计多处使用的相关日期信息 51 | func GetFlowUseDate() FlowUseDate { 52 | var fud = FlowUseDate{} 53 | fud.Year = gtime.Now().Format("Y") //当前年份 54 | fud.LastYear = gtime.Now().AddDate(-1, 0, 0).Format("Y") //上一年年份 55 | fud.Month = gtime.Now().Format("n") //当前月份 56 | fud.LastMonth = gtime.Now().AddDate(0, -1, 0).Format("n") //上个月 57 | fud.BeforeLastMonth = gtime.Now().AddDate(0, -2, 0).Format("n") //上上个月 58 | 59 | fud.Today = gtime.Now().Format("j") //今天 60 | fud.Yesterday = gtime.Now().AddDate(0, 0, -1).Format("j") //昨天 61 | fud.BeforeYesterday = gtime.Now().AddDate(0, 0, -2).Format("j") //前天 62 | 63 | fud.LastMonthDays = gtime.Now().AddDate(0, -1, 0).Format("t") //上个月的天数 64 | fud.LastMonthDays = gtime.Now().AddDate(0, -2, 0).Format("t") //上上个月的天数 65 | fud.BeforeLastMonthDays = gtime.Now().AddDate(0, -2, 0).Format("t") //上上个月的天数 66 | return fud 67 | } 68 | -------------------------------------------------------------------------------- /bin/curl.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | WORKSPACE=$(cd $(dirname $0)/; pwd) 4 | cd $WORKSPACE 5 | 6 | mkdir -p var 7 | 8 | app=simserver_unicom 9 | pidfile=var/app.pid 10 | logfile=var/app.log 11 | 12 | function check_pid() { 13 | if [ -f $pidfile ];then 14 | pid=`cat $pidfile` 15 | if [ -n $pid ]; then 16 | running=`ps -p $pid|grep -v "PID TTY" |wc -l` 17 | return $running 18 | fi 19 | fi 20 | return 0 21 | } 22 | 23 | 24 | function start(){ 25 | check_pid 26 | running=$? 27 | if [ $running -gt 0 ]; then 28 | echo -n "$app now is running already,pid=" 29 | cat $pidfile 30 | return 31 | fi 32 | 33 | nohup ./$app -L debug {option} &> $logfile & 34 | sleep 1 35 | running=`ps -p $! | grep -v "PID TTY" | wc -l` 36 | if [ $running -gt 0 ];then 37 | echo $! > $pidfile 38 | echo "$app started..., pid=$!" 39 | else 40 | echo "$app failed to start" 41 | return 1 42 | fi 43 | 44 | } 45 | 46 | function stop() { 47 | check_pid 48 | running=$? 49 | if [ $running -gt 0 ];then 50 | pid=`cat $pidfile` 51 | kill $pid 52 | rm -f $pidfile 53 | echo "$app stoped" 54 | else 55 | echo "$app already stoped" 56 | fi 57 | } 58 | 59 | function restart() { 60 | stop 61 | sleep 1 62 | start 63 | } 64 | 65 | function status() { 66 | check_pid 67 | running=$? 68 | if [ $running -gt 0 ];then 69 | echo "started" 70 | else 71 | echo "stoped" 72 | fi 73 | } 74 | 75 | function tailf() { 76 | tail -f var/* 77 | } 78 | 79 | function help() { 80 | echo "$0 pid|start|stop|restart|status|tail" 81 | } 82 | 83 | function pid() { 84 | cat $pidfile 85 | } 86 | 87 | if [ "$1" == "" ]; then 88 | help 89 | elif [ "$1" == "stop" ];then 90 | stop 91 | elif [ "$1" == "start" ];then 92 | start 93 | elif [ "$1" == "restart" ];then 94 | restart 95 | elif [ "$1" == "status" ];then 96 | status 97 | elif [ "$1" == "tail" ];then 98 | tailf 99 | elif [ "$1" == "pid" ];then 100 | pid 101 | else 102 | help 103 | fi 104 | -------------------------------------------------------------------------------- /app/model/datamodel/unicomnotice.go: -------------------------------------------------------------------------------- 1 | package datamodel 2 | 3 | import ( 4 | "github.com/gogf/gf/database/gdb" 5 | "github.com/gogf/gf/frame/g" 6 | "github.com/gogf/gf/os/glog" 7 | "github.com/xinjiayu/SimServerUnicom/app/model/basemodel" 8 | ) 9 | 10 | type UnicomNotice struct { 11 | Id int `json:"id"` 12 | Iccid string `json:"iccid"` 13 | EventId string `json:"event_id"` 14 | EventType string `json:"event_type"` 15 | Timestamp string `json:"timestamp"` 16 | DataUsage float64 `json:"datausage"` 17 | Data1 float64 `json:"data1"` 18 | Data2 float64 `json:"data1"` 19 | 20 | basemodel.BaseModel 21 | 22 | } 23 | // List 获取通知列表数据 24 | func (model UnicomNotice) List(where interface{}, args ...interface{}) []UnicomNotice { 25 | var resData []UnicomNotice 26 | err := model.dbModel().OrderBy("timestamp desc").Where(where,args).Structs(&resData) 27 | if err != nil { 28 | return []UnicomNotice{} 29 | } 30 | return resData 31 | } 32 | 33 | // Save 存储数据 34 | func (model UnicomNotice) Save(event_id, event_type, iccid, data1, data2 string, timestamp, data_usage int) error { 35 | newData := g.Map{"event_id": event_id, "event_type": event_type, "iccid": iccid, "data1": data1, "data2": data2, "timestamp": timestamp, "data_usage": data_usage} 36 | glog.Info("Data: ", newData) 37 | r, _ := model.dbModel().Where("event_id=?", event_id).One() 38 | if r != nil { 39 | //修改已有记录 40 | un := UnicomNotice{} 41 | if err := r.Struct(&un); err == nil { 42 | model.dbModel().Data(newData).Where("id=?", un.Id).Update() 43 | 44 | } else { 45 | glog.Error(err) 46 | } 47 | } else { 48 | //新增记录 49 | _, err := model.dbModel().Data(newData).Save() 50 | if err != nil { 51 | glog.Error(err) 52 | } 53 | 54 | } 55 | 56 | return nil 57 | } 58 | 59 | 60 | 61 | func (model UnicomNotice) PkVal() int { 62 | return model.Id 63 | } 64 | 65 | func (model UnicomNotice) TableName() string { 66 | table := g.Config().Get("datatable.notice_table") 67 | return table.(string) 68 | } 69 | 70 | func (model UnicomNotice) dbModel(alias ...string) *gdb.Model { 71 | var tmpAlias string 72 | if len(alias) > 0 { 73 | tmpAlias = " " + alias[0] 74 | } 75 | tableModel := g.DB().Table(model.TableName() + tmpAlias).Safe() 76 | return tableModel 77 | } -------------------------------------------------------------------------------- /app/api/analyse/analyse.go: -------------------------------------------------------------------------------- 1 | // 联通物联网卡流量数据分析API 2 | // 3 | // 对从联通物联网平台同步回来的数据进行统计与分析。 4 | // 5 | // Host: localhost 6 | // Version: 0.0.1 7 | // 8 | // swagger:meta 9 | package analyse 10 | 11 | import ( 12 | "github.com/gogf/gf/net/ghttp" 13 | "github.com/gogf/gf/util/gconv" 14 | serviceAnalyse "github.com/xinjiayu/SimServerUnicom/app/service/analyse" 15 | "github.com/xinjiayu/SimServerUnicom/library/response" 16 | ) 17 | 18 | // Controller API管理对象 19 | type Controller struct{} 20 | 21 | // TwoDaysSimCardFlow 获取最近两天流量的sim卡列表 22 | func (c *Controller) TwoDaysSimCardFlow(r *ghttp.Request) { 23 | rsData := serviceAnalyse.TwoFlow() 24 | numStr := gconv.String(len(rsData)) 25 | response.Json(r, 0, "SIM卡流量数据条数:"+numStr, rsData) 26 | } 27 | 28 | // AllSimFlowList 获取计费周期内所有sim卡用量信息 29 | // 参数:planName 计划名称,默可以为空,输出全部数据 30 | func (c *Controller) AllSimFlowList(r *ghttp.Request) { 31 | planName := r.GetString("planName") 32 | rsData := serviceAnalyse.SimList(planName) 33 | numStr := gconv.String(len(rsData)) 34 | response.Json(r, 0, "SIM卡流量数据条数:"+numStr, rsData) 35 | } 36 | 37 | // PlanInfoCountList 获取计费套餐计划的统计信息 38 | func (c *Controller) PlanInfoCountList(r *ghttp.Request) { 39 | rsData := serviceAnalyse.PlanInfoList() 40 | numStr := gconv.String(len(rsData)) 41 | response.Json(r, 0, "SIM卡流量套餐计划数:"+numStr, rsData) 42 | } 43 | 44 | // MonthSimFlowByIccid 获取指定sim卡最近两个月的流量 45 | // 参数:iccid ,sim卡的iccid号 46 | func (c *Controller) MonthSimFlowByIccid(r *ghttp.Request) { 47 | iccid := r.GetString("iccid") 48 | rsData := serviceAnalyse.MonthSimFlowListByIccid(gconv.String(iccid)) 49 | response.Json(r, 0, iccid+": SIM卡,流量信息:", rsData) 50 | } 51 | 52 | // MonthSimFlowCount 获取所有sim卡最近两个月的流量 53 | func (c *Controller) MonthSimFlowCount(r *ghttp.Request) { 54 | rsData := serviceAnalyse.MonthSimFlowList() 55 | response.Json(r, 0, ": SIM月度每日流量信息:", rsData) 56 | } 57 | 58 | // Notice 获取通知信息 59 | // 参数:event_type,字符类型,默认为空,将显示本周期内所有的通知信息。当前事件类型支持:PAST24H_DATA_USAGE_EXCEEDED 24小时内流量超过指定量的通知、CTD_USAGE 周期使用超过指定的通知 60 | func (c *Controller) Notice(r *ghttp.Request) { 61 | eventType := r.GetString("event_type") 62 | rsData := serviceAnalyse.GetNotice(eventType) 63 | if eventType==""{ 64 | eventType = "全部" 65 | } 66 | response.Json(r, 0, "通知信息,类型:"+eventType, rsData) 67 | 68 | } 69 | -------------------------------------------------------------------------------- /app/service/collect/common.go: -------------------------------------------------------------------------------- 1 | package collect 2 | 3 | import ( 4 | "encoding/base64" 5 | "encoding/json" 6 | "github.com/gogf/gf/frame/g" 7 | "github.com/gogf/gf/net/ghttp" 8 | "github.com/gogf/gf/os/glog" 9 | ) 10 | 11 | // API地址 12 | var ApiURL string 13 | 14 | func init() { 15 | api := g.Config().Get("unicom.api_url") 16 | ApiURL = api.(string) 17 | 18 | } 19 | 20 | /** 21 | 按照以下说明创建授权标头: 22 | 将用户名和 API 密钥组合到一个字符串中,用分号分隔各个值。例如,如果用户名是 starterkit,APIKey 是 d703f52b-1200-4318-ae0d-0f6092b2e6ab,则串联的字符串将是: 23 | starterkit:d703f52b-1200-4318-ae0d-0f6092b2e6ab 24 | 使用 Base64(即 RFC2045-MIME)对串联的字符串编码: 25 | c3RhcnRlcmtpdDpkNzAzZjUyYi0xMjAwLTQzMTgtYWUwZC0wZjYw 26 | OTJiMmU2YWI= 27 | 将授权标头值设置为 Basic,后跟第 2 步中的编码字符串。确保 Basic 与编码字符串之间有一个空格: 28 | Basic c3RhcnRlcmtpdDpkNzAzZjUyYi0xMjAwLTQzMTgtYWUwZC0wZjYwOTJiMmU2YWI= 29 | */ 30 | 31 | // 获取sign授权签名验证字符串 32 | func getSign() string { 33 | apiKey := g.Config().Get("unicom.api_key") 34 | apiUser := g.Config().Get("unicom.api_user") 35 | sign := apiUser.(string) + ":" + apiKey.(string) 36 | sign = base64.StdEncoding.EncodeToString([]byte(sign)) 37 | return sign 38 | } 39 | 40 | //getAPIDataBody 通过api获取数据 41 | func getAPIDataBody(APIURL string) ([]byte, error) { 42 | c := ghttp.NewClient() 43 | sgin := getSign() 44 | c.SetHeader("Authorization", "Basic "+sgin) 45 | c.SetHeader("Accept", "application/json") 46 | if res, e := c.Get(APIURL); e != nil { 47 | return nil, e 48 | } else { 49 | defer res.Close() 50 | body := []byte(res.ReadAllString()) 51 | return body, nil 52 | } 53 | } 54 | 55 | //getAPIData 通过api获取数据 56 | func getAPIData(APIURL string, dataModel interface{}) error { 57 | 58 | c := ghttp.NewClient() 59 | sgin := getSign() 60 | c.SetHeader("Authorization", "Basic "+sgin) 61 | c.SetHeader("Accept", "application/json") 62 | if res, e := c.Get(APIURL); e != nil { 63 | //panic(e) 64 | return e 65 | } else { 66 | defer res.Close() 67 | body := []byte(res.ReadAllString()) 68 | Err := json.Unmarshal(body, &dataModel) 69 | if Err != nil { 70 | glog.Error(Err) 71 | return Err 72 | } 73 | 74 | } 75 | return nil 76 | } 77 | 78 | //PutAPIData 通过API修改数据 79 | func PutAPIData(apiUrl, content string, dataModel interface{}) { 80 | 81 | c := ghttp.NewClient() 82 | sgin := getSign() 83 | c.SetHeader("Authorization", "Basic "+sgin) 84 | c.SetHeader("Accept", "application/json") 85 | c.SetHeader("Content-Type", "application/json") 86 | if r, e := c.Put(apiUrl, content); e != nil { 87 | panic(e) 88 | } else { 89 | defer r.Close() 90 | glog.Info(r.StatusCode) 91 | //glog.Info(r.Request.GetBody) 92 | body := []byte(r.ReadAllString()) 93 | glog.Info(apiUrl, body) 94 | Err := json.Unmarshal(body, &dataModel) 95 | if Err != nil { 96 | glog.Error(Err) 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /app/service/collect/getdata.go: -------------------------------------------------------------------------------- 1 | package collect 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/gogf/gf/os/glog" 6 | "github.com/gogf/gf/os/grpool" 7 | "github.com/gogf/gf/util/gconv" 8 | "github.com/qianlnk/pgbar" 9 | "github.com/xinjiayu/SimServerUnicom/app/model/datamodel" 10 | "github.com/xinjiayu/SimServerUnicom/app/model/unicommodel" 11 | "strconv" 12 | "sync" 13 | ) 14 | 15 | //获取指定sim卡的流量,并存入运营数据库 16 | func GetUsages(iccid string) interface{} { 17 | getURL := ApiURL + "devices/" + iccid + "/ctdUsages" 18 | dataModel := new(unicommodel.CtdUsages) 19 | err := getAPIData(getURL, dataModel) 20 | if err != nil { 21 | return nil 22 | } 23 | 24 | //存入数据库 25 | flowNum := dataModel.CtdDataUsage 26 | flow := gconv.String(flowNum) 27 | go datamodel.SimFlow{}.Save(dataModel.Iccid, flow, "cqunicom", "unicom", dataModel.RatePlan, dataModel.Status, dataModel.CtdSessionCount) 28 | return dataModel 29 | 30 | } 31 | 32 | // CardList 通过联通平台的devices接口获取设备卡列表 33 | func GetCardList() unicommodel.CardList { 34 | pageSize := "50" 35 | pageNumber := "1" 36 | status := "ACTIVATED" 37 | var page int = 0 38 | var simList unicommodel.CardList 39 | for { 40 | page++ 41 | //glog.Info(page, "=========页数=================") 42 | pageNumber = strconv.Itoa(page) 43 | getURL := ApiURL + "devices?modifiedSince=2016-04-18T17%3A31%3A34%2B00%3A00&pageSize=" + pageSize + "&pageNumber=" + pageNumber + "&status=" + status 44 | dataModel := new(unicommodel.CardList) 45 | getAPIData(getURL, dataModel) 46 | for _, v := range dataModel.Devices { 47 | simList.Devices = append(simList.Devices, v) 48 | } 49 | if dataModel.LastPage { 50 | break 51 | } 52 | } 53 | go getSimData(simList) //直接起个线程跳走,加快返回籹据的速度 54 | return simList 55 | } 56 | 57 | func getSimData(dd unicommodel.CardList) { 58 | pool := grpool.New(3) 59 | wg := sync.WaitGroup{} 60 | 61 | var vlist unicommodel.CardList 62 | b4 := pgbar.NewBar(0, "获取卡流量与信息", len(dd.Devices)) 63 | for _, v := range dd.Devices { 64 | wg.Add(1) 65 | vd := v //采用临时变量的形式来传递当前变量v的值 66 | pool.Add(func() { 67 | getURL := ApiURL + "devices/" + vd.Iccid + "/ctdUsages" 68 | dataModel := new(unicommodel.CtdUsages) 69 | body, err := getAPIDataBody(getURL) 70 | if err == nil { 71 | Err := json.Unmarshal(body, &dataModel) 72 | if Err != nil { 73 | glog.Error(Err) 74 | } 75 | 76 | flowNum := dataModel.CtdDataUsage 77 | flow := gconv.String(flowNum) 78 | if dataModel.Iccid != "" { 79 | //存入数据库 80 | datamodel.SimFlow{}.Save(dataModel.Iccid, flow, "cqunicom", "unicom", dataModel.RatePlan, dataModel.Status, dataModel.CtdSessionCount) 81 | //glog.Info("sim卡详细信息:",dataModel) 82 | getUnicomSimInfo(vd.Iccid, flowNum) 83 | } else { 84 | vlist.Devices = append(vlist.Devices, vd) 85 | } 86 | } 87 | b4.Add() 88 | wg.Done() 89 | }) 90 | } 91 | wg.Wait() 92 | 93 | //如果有获取失败的sim卡,继续获取 94 | if len(vlist.Devices) > 0 { 95 | getSimData(vlist) 96 | //glog.Info(vlist) 97 | vlistNum := len(vlist.Devices) 98 | glog.Infof("====未成功获取籹据的卡数:%d \n", vlistNum) 99 | 100 | } 101 | glog.Infof("sim卡数据获取结束!") 102 | 103 | } 104 | 105 | //获取sim卡的详细信息 106 | func getUnicomSimInfo(iccid string, flow int64) { 107 | getURL := ApiURL + "devices/" + iccid 108 | dataModel := new(unicommodel.SimInfo) 109 | err := getAPIData(getURL, dataModel) 110 | if err != nil { 111 | getAPIData(getURL, dataModel) 112 | } 113 | dataModel.CtdDataUsage = flow 114 | //存入数据库 115 | datamodel.SimUnicom{}.SaveUnicomSimInfo(*dataModel) 116 | 117 | } -------------------------------------------------------------------------------- /app/model/unicommodel/unicommodel.go: -------------------------------------------------------------------------------- 1 | //从联通服务接口获取的数据结构 2 | package unicommodel 3 | 4 | type CardList struct { 5 | PageNumber int `json:"pageNumber"` 6 | Devices []struct { 7 | Iccid string `json:"iccid"` 8 | Status string `json:"status"` 9 | RatePlan string `json:"ratePlan"` 10 | CommunicationPlan string `json:"communicationPlan"` 11 | } `json:"devices"` 12 | LastPage bool `json:"lastPage"` 13 | } 14 | 15 | type Device struct { 16 | Iccid string `json:"iccid"` 17 | Status string `json:"status"` 18 | RatePlan string `json:"ratePlan"` 19 | CommunicationPlan string `json:"communicationPlan"` 20 | } 21 | 22 | type CardListUsages []struct { 23 | CtdUsages 24 | } 25 | 26 | type CtdUsages struct { 27 | Iccid string `json:"iccid"` 28 | Imsi string `json:"imsi"` 29 | Msisdn string `json:"msisdn"` 30 | Imei string `json:"imei"` 31 | Status string `json:"status"` 32 | RatePlan string `json:"ratePlan"` 33 | CommunicationPlan string `json:"communicationPlan"` 34 | CtdDataUsage int64 `json:"ctdDataUsage"` 35 | CtdSMSUsage int `json:"ctdSMSUsage"` 36 | CtdVoiceUsage int `json:"ctdVoiceUsage"` 37 | CtdSessionCount int `json:"ctdSessionCount"` 38 | OverageLimitReached bool `json:"overageLimitReached"` 39 | OverageLimitOverride string `json:"overageLimitOverride"` 40 | } 41 | 42 | type SimInfo struct { 43 | Iccid string `json:"iccid"` 44 | Imsi string `json:"imsi"` 45 | Msisdn string `json:"msisdn"` 46 | Imei string `json:"imei"` 47 | Status string `json:"status"` 48 | RatePlan string `json:"rateplan"` 49 | CommunicationPlan string `json:"communicationplan"` 50 | Customer string `json:"customer"` 51 | EndConsumerID string `json:"endconsumerid"` 52 | DateActivated string `json:"dateactivated"` 53 | DateAdded string `json:"dateadded"` 54 | DateUpdated string `json:"dateupdated"` 55 | DateShipped string `json:"dateshipped"` 56 | AccountID string `json:"accountid"` 57 | FixedIPAddress string `json:"fixedipaddress"` 58 | AccountCustom1 string `json:"accountcustom1"` 59 | AccountCustom2 string `json:"accountcustom2"` 60 | AccountCustom3 string `json:"accountcustom3"` 61 | AccountCustom4 string `json:"accountcustom4"` 62 | AccountCustom5 string `json:"accountcustom5"` 63 | AccountCustom6 string `json:"accountcustom6"` 64 | AccountCustom7 string `json:"accountcustom7"` 65 | AccountCustom8 string `json:"accountcustom8"` 66 | AccountCustom9 string `json:"accountcustom9"` 67 | AccountCustom10 string `json:"accountcustom10"` 68 | SimNotes string `json:"simnotes"` 69 | DeviceID string `json:"deviceid"` 70 | ModemID string `json:"modemid"` 71 | GlobalSimType string `json:"globalsimtype"` 72 | CtdDataUsage int64 `json:"ctddatausage"` 73 | } 74 | 75 | type ResultsData struct { 76 | Past24HDataUsage struct { 77 | Xmlns string `json:"-xmlns"` 78 | DataUsage string `json:"dataUsage"` 79 | Iccid string `json:"iccid"` 80 | } `json:"Past24HDataUsage"` 81 | CtdUsage struct { 82 | Xmlns string `json:"-xmlns"` 83 | DataUsage string `json:"dataUsage"` 84 | Iccid string `json:"iccid"` 85 | } `json:"CtdUsage"` 86 | } 87 | 88 | type ObjValue struct { 89 | Xmlns string `json:"-xmlns"` 90 | DataUsage string `json:"dataUsage"` 91 | Iccid string `json:"iccid"` 92 | } 93 | 94 | 95 | type PutResultData struct { 96 | Iccid string `json:"iccid"` 97 | ErrorMessage string `json:"errorMessage"` 98 | ErrorCode string `json:"errorCode"` 99 | } 100 | -------------------------------------------------------------------------------- /app/service/analyse/monthsimflow.go: -------------------------------------------------------------------------------- 1 | package analyse 2 | 3 | import ( 4 | "github.com/gogf/gf/util/gconv" 5 | "github.com/xinjiayu/SimServerUnicom/app/model/analyse" 6 | "github.com/xinjiayu/SimServerUnicom/app/model/datamodel" 7 | "github.com/xinjiayu/SimServerUnicom/library/utils" 8 | ) 9 | 10 | func MonthSimFlowList() analyse.TwoMonthFlowCount { 11 | 12 | fud := utils.GetFlowUseDate() 13 | carrier := "unicom" //sim卡运营商 14 | 15 | //如果当前是1月份,后面取上一年的数据 16 | lastYear := fud.Year 17 | if fud.Month == "1" { 18 | lastYear = fud.LastYear 19 | } 20 | 21 | 22 | //接口返回数据的信息结构 23 | var twoMonthFlowCount analyse.TwoMonthFlowCount 24 | 25 | for i := 1; i < 32; i++ { 26 | //输出每天的数字 27 | twoMonthFlowCount.DayName = append(twoMonthFlowCount.DayName, i) 28 | } 29 | //上上个月的流量数据 30 | BeforeLastMonthFlow := datamodel.SimFlow{}.FlowList("year=? and month=? and carrier=?", lastYear, fud.BeforeLastMonth, carrier) 31 | BeforeLastMonthEndDayFlowData :=make(map[string]int64) 32 | for _, v := range BeforeLastMonthFlow { 33 | data := gconv.Map(v) 34 | //获取上上个月最后一天的数据 35 | BeforeLastMonthEndDayFlowData[v.Iccid] = gconv.Int64(data["d"+fud.BeforeLastMonthDays]) 36 | } 37 | 38 | //上个月的流量数据 =============================== 39 | lastMonthFlow := datamodel.SimFlow{}.FlowList("year=? and month=? and carrier=?", lastYear, fud.LastMonth, carrier) 40 | lastMonthEndDayFlowData :=make(map[string]int64) 41 | 42 | lastMonthFlowData := make([]int64,gconv.Int(fud.LastMonthDays)) //定义上个月每一天的流量数据表 43 | lastMonthSimcardNum := make([]int64,gconv.Int(fud.LastMonthDays)) //定义上个月有效卡数据 44 | lastMonthAvg := make([]int64,gconv.Int(fud.LastMonthDays)) //定义上个月的每天每卡平均流量数据表 45 | for _, v := range lastMonthFlow { 46 | data := gconv.Map(v) 47 | lastMonthEndDayFlowData[v.Iccid] = gconv.Int64(data["d"+fud.LastMonthDays]) //获取上个月最后一天的数据 48 | beforeFlowData := BeforeLastMonthEndDayFlowData[v.Iccid] //获取上上个月最后一天的数据 49 | dayData := countDaysFlow(beforeFlowData, data) 50 | for i:=0;i<=gconv.Int(fud.LastMonthDays)-1 ;i++ { 51 | lastMonthFlowData[i]=lastMonthFlowData[i]+dayData[i] 52 | if dayData[i] > 0 { 53 | lastMonthSimcardNum[i] = lastMonthSimcardNum[i] + 1 54 | } 55 | } 56 | 57 | } 58 | 59 | for i:=0;i<=gconv.Int(fud.LastMonthDays)-1 ;i++ { 60 | lastMonthAvg[i] = 0 61 | if lastMonthFlowData[i] >0 && lastMonthSimcardNum[i] >0 { 62 | lastMonthAvg[i] = lastMonthFlowData[i]/lastMonthSimcardNum[i] 63 | } 64 | } 65 | 66 | twoMonthFlowCount.Beforemonth = lastMonthFlowData 67 | twoMonthFlowCount.BeforeMonthSimnum = lastMonthSimcardNum 68 | twoMonthFlowCount.BeforeMonthAvg = lastMonthAvg 69 | 70 | 71 | 72 | //本月的流量数据 ================================ 73 | monthFlow := datamodel.SimFlow{}.FlowList("year=? and month=? and carrier=?", fud.Year, fud.Month, carrier) 74 | monthFlowData := make([]int64,gconv.Int(fud.Today)) //定义到今天的流量数据表 75 | monthSimcardNum := make([]int64,gconv.Int(fud.Today)) //定义到今天的每一天的有效卡数据 76 | monthAvg := make([]int64,gconv.Int(fud.Today)) //定义到今天的每天每卡平均流量数据表 77 | for _, v := range monthFlow { 78 | data := gconv.Map(v) 79 | beforeFlowData := lastMonthEndDayFlowData[v.Iccid] //获取上个月最后一天的数据 80 | dayData := countDaysFlow(beforeFlowData, data) 81 | for i:=0;i<=gconv.Int(fud.Today)-1 ;i++ { 82 | monthFlowData[i]=monthFlowData[i]+dayData[i] 83 | if dayData[i] > 0 { 84 | monthSimcardNum[i] = monthSimcardNum[i] + 1 85 | } 86 | } 87 | 88 | } 89 | for i:=0;i<=gconv.Int(fud.Today)-1 ;i++ { 90 | monthAvg[i] = 0 91 | if monthFlowData[i] >0 && monthSimcardNum[i] >0 { 92 | monthAvg[i] = monthFlowData[i]/monthSimcardNum[i] 93 | } 94 | } 95 | 96 | twoMonthFlowCount.Nowmonth = monthFlowData 97 | twoMonthFlowCount.NowmonthSimnum = monthSimcardNum 98 | twoMonthFlowCount.NowmonthAvg = monthAvg 99 | 100 | return twoMonthFlowCount 101 | } 102 | 103 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 联通物联网服务平台接口同步服务 2 | 3 | > 联通平台网址: https://cc1.10646.cn/ 4 | > 5 | >这个服务主要用于同步联通物联网卡流量相关的REST API 接口数据,便于本地化使用与分析 6 | > 7 | > 8 | 9 | ## 本地编译说明 10 | 11 | 1. 从git下载项目: git clone https://github.com/xinjiayu/SimServerUnicom 12 | 2. 安装mysql数据库,创建db,导入deploy下db.sql脚本 13 | 3. 修改config下config.example.toml为config.toml,并配置相关项 14 | 15 | 请跟据注释进行配置,包括服务相关配置,数据库配置,联通物联网平台相关配置 16 | ```toml 17 | # 数据库配置 18 | [database] 19 | [[database.default]] 20 | host = "127.0.0.1" 21 | 22 | ``` 23 | 4. go run main.go 24 | 25 | 26 | ## 编译生成环境说明 27 | 可通过交叉编译,生成目标平台的可执行版本。目前在linux、mac os 测试通过,其它平台未做过测试。 28 | 将编译生成的文件放到bin目录下,将bin目录下的文件放到目标服务器,执行`./curl.sh start` 运行。 29 | 30 | 注意:编译生成的文件名必须是simserver_unicom ,如果是其它名称,请自行修改curl.sh脚本文件中的app变量。 31 | 32 | ``` 33 | curl.sh脚本参数: 34 | 35 | start|stop|restart|status|tail 36 | 37 | 38 | ``` 39 | 40 | 41 | 可以使用gox工具 42 | `go get github.com/mitchellh/gox` 43 | 44 | 交叉编译:进入到mian函数文件所在目录下: 45 | 46 | `gox -os "windows linux" -arch amd64` 47 | 48 | `gox -os "linux" -arch amd64` 49 | 50 | 51 | 52 | ## 接口说明 53 | 54 | ### 数据采集接口 55 | 56 | 1、 CtdUsages 获取指定sim卡的流量接口,返回指定设备的周期累计用量信息。 57 | 调用地址:`/unicom/ctdusages` 58 | 59 | 2、CardList 通过联通平台的devices接口获取所有激活的sim卡流量数据 60 | 调用地址:`/unicom/cardlist` 61 | 62 | >注:从联通平台获取到的数据是当前计费周期内的流量使用数据。单位为字节。 63 | >这两个接口可以配合着定时任务使用,跟据需要定时拉取联通物联网平台的sim卡流量数据 64 | > 65 | > 66 | 67 | ### 数据分析输出接口 68 | 69 | 1、TwoDaysSimCardFlow 获取最近两天流量的sim卡列表 70 | 调用地址:`/unicom/analyse/twodayssimcardflow` 71 | 72 | 73 | 74 | 2、AllSimFlowList 获取计费周期内所有sim卡用量信息 75 | 调用地址:`/unicom/analyse/allsimflowlist` 76 | 参数:planName 计划名称,默可以为空,输出全部数据 77 | 78 | 79 | 3、PlanInfoCountList 获取计费套餐计划的统计信息 80 | 调用地址:`/unicom/analyse/planinfocountlist` 81 | 82 | 4、MonthSimFlowByIccid 获取指定sim卡最近两个月的流量 83 | 调用地址:`/unicom/analyse/monthsimflowbyiccid` 84 | 参数:iccid 必填 85 | 86 | 5、MonthSimFlowCount 获取所有sim卡最近两个月的流量 87 | 调用地址:`/unicom/analyse/monthsimflowcount` 88 | 89 | ### 远程操作接口 90 | 91 | 1、ChangePlan 跟据sim卡流量池使用情况进行自动设置平衡,平衡的顺序为1G池超出时,自动变更部分sim卡到2G池中, 92 | //当2G池流量超出时自动将部分sim卡变更到3G池。当3G池也超出时将1G池中部分sim卡变更到3G池 93 | 调用地址:`/unicom/op/changeplan` 94 | 95 | 说明:直接调用,后台自动执行。 96 | 97 | 2、ChangeInitPlan 自动初始化所有sim卡的流量套餐变更为1G套餐 98 | 调用地址:`/unicom/op/changeinitplan` 99 | 100 | 说明:直接调用,后台自动执行。 101 | 102 | 3、Change19to20 将sim卡从19位转换为20位的接口 103 | 调用地址`/unicom/op/change19to20` 104 | 谳用参数为:iccid 不能为空。 105 | 106 | 107 | ### 接收通知接口 108 | 1、DataReceive 通知数据接收 109 | 调用地址:`/unicom/notice/datareceive` 110 | 111 | 参数:event_type,字符类型,默认为空,将显示本周期内所有的通知信息。当前事件类型支持:PAST24H_DATA_USAGE_EXCEEDED 24小时内流量超过指定量的通知、CTD_USAGE 周期使用超过指定的通知 112 | 113 | ## 感谢 114 | 115 | 1. gf框架 [https://github.com/gogf/gf](https://github.com/gogf/gf) 116 | 117 | 118 | # Jasper API 策略和最佳做法 119 | 120 | Cisco Jasper 已经建立了一项 API 公平使用策略,确保所有 Control Center 用户都可以获得最佳的体验。任何使用 Control Center API 的客户端代码都必须遵守下述限制规定。 121 | 122 | ## 并发连接 123 | 124 | Cisco Jasper 建议企业避免进行并发处理,因为它可能会显著降低响应速度。我们建议仅在单线程应用程序或例程中使用 API。在多线程情况下使用 API 的账户可能会受到限流或其他限制,具体视环境而定。 125 | 126 | ***策略*:** **使用单线程代码 - 一次一项 API 调用**。 127 | 128 | 129 | 130 | ## 每秒调用数 131 | 132 | Cisco Jasper 通过限制账户每秒可以进行的 API 调用数来确保可靠的 API 性能。如果您的账户超出限制,则在测量期间(一秒)内的后续 API 调用将根据 API 类型获得下表中显示的响应。经过适当的延迟后,程序应该重试 API 调用。 133 | 134 | 135 | | API 类型 | 返回代码 | 消息 | 136 | | --- | --- | --- | 137 | | REST | 40000029 | 超出资费限制 | 138 | | SOAP | 400101 | 许可已超出 API 调用的资费限制 | 139 | 140 | 141 | 随着我们不断地调整优化平台以提高速度和可靠性,Cisco Jasper 可能会不时更新每秒调用数限制。如果您使用以下所述的最佳做法(单线程代码、动态速率限制等),这项限制将不会产生影响。需要时,运营商可以提供有关这项限制的更多详细信息。 142 | 143 | 请注意,此限制同时包括账户和账户的客户进行的 API 调用。例如,如果某账户有两位 API 用户,他们及其 5 位客户同时进行了 API 调用, Control Center 会将该账户的每秒调用总数计为 7 个。最初的 API 调用会成功,但是在达到限制之后,其余的 API 调用将返回上面显示的其中一条消息。 144 | 145 | 这个限制也适用于 REST 和 SOAP API 调用的组合。例如,假设某个账户同时进行了 1 次 REST API 调用和 1 次 SOAP API 调用,同时该账户的其中一位客户进行了一次 REST API 调用。Control Center 会将该账户的每秒调用数计为 3 个。 146 | 147 | **策略**。使用动态速率限制和其他最佳做法来遵守这项限制。 148 | 149 | 150 | 151 | -------------------------------------------------------------------------------- /app/model/datamodel/simflow.go: -------------------------------------------------------------------------------- 1 | package datamodel 2 | 3 | import ( 4 | "github.com/gogf/gf/database/gdb" 5 | "github.com/gogf/gf/errors/gerror" 6 | "github.com/gogf/gf/frame/g" 7 | "github.com/gogf/gf/os/glog" 8 | "github.com/gogf/gf/os/gtime" 9 | "github.com/xinjiayu/SimServerUnicom/app/model/basemodel" 10 | ) 11 | 12 | type SimFlow struct { 13 | Id int `json:"id"` 14 | Iccid string `json:"iccid"` 15 | Month string `json:"month"` 16 | Provider string `json:"provider"` 17 | Carrier string `json:"carrier"` 18 | D1 int64 `json:"d1"` 19 | D2 int64 `json:"d2"` 20 | D3 int64 `json:"d3"` 21 | D4 int64 `json:"d4"` 22 | D5 int64 `json:"d5"` 23 | D6 int64 `json:"d6"` 24 | D7 int64 `json:"d7"` 25 | D8 int64 `json:"d8"` 26 | D9 int64 `json:"d9"` 27 | D10 int64 `json:"d10"` 28 | D11 int64 `json:"d11"` 29 | D12 int64 `json:"d12"` 30 | D13 int64 `json:"d13"` 31 | D14 int64 `json:"d14"` 32 | D15 int64 `json:"d15"` 33 | D16 int64 `json:"d16"` 34 | D17 int64 `json:"d17"` 35 | D18 int64 `json:"d18"` 36 | D19 int64 `json:"d19"` 37 | D20 int64 `json:"d20"` 38 | D21 int64 `json:"d21"` 39 | D22 int64 `json:"d22"` 40 | D23 int64 `json:"d23"` 41 | D24 int64 `json:"d24"` 42 | D25 int64 `json:"d25"` 43 | D26 int64 `json:"d26"` 44 | D27 int64 `json:"d27"` 45 | D28 int64 `json:"d28"` 46 | D29 int64 `json:"d29"` 47 | D30 int64 `json:"d30"` 48 | D31 int64 `json:"d31"` 49 | 50 | basemodel.BaseModel 51 | } 52 | 53 | 54 | func (model SimFlow) Get() SimFlow { 55 | if model.Id <= 0 { 56 | glog.Error(model.TableName() + " get id error") 57 | return SimFlow{} 58 | } 59 | 60 | var resData SimFlow 61 | err := model.dbModel().Where(" id = ?", model.Id).Struct(&resData) 62 | if err != nil { 63 | glog.Error(model.TableName()+" get one error", err) 64 | return SimFlow{} 65 | } 66 | 67 | return resData 68 | } 69 | func (model SimFlow) GetByOne(where interface{}, args ...interface{}) SimFlow { 70 | var resData SimFlow 71 | r, err := model.dbModel().Where(where,args).One() 72 | if err != nil { 73 | return SimFlow{} 74 | } 75 | r.Struct(&resData) 76 | return resData 77 | } 78 | 79 | func (model SimFlow) FlowList(where interface{}, args ...interface{}) []SimFlow { 80 | var resData []SimFlow 81 | err := model.dbModel().Where(where,args).Structs(&resData) 82 | if err != nil { 83 | //glog.Error(model.TableName()+" list error", err) 84 | return []SimFlow{} 85 | } 86 | return resData 87 | } 88 | 89 | func (model SimFlow) GetSumFlowByOne(where interface{}, args ...interface{}) SimFlow { 90 | var resData SimFlow 91 | r, err := model.dbModel().Fields(model.sumColumns()).Where(where,args).One() 92 | if err != nil { 93 | return SimFlow{} 94 | } 95 | r.Struct(&resData) 96 | return resData 97 | } 98 | 99 | //Save 存储sim卡流量数据到月份表 100 | func (model SimFlow) Save(iccid, flow, provider, carrler, rate_plan, status string, ctd_session_count int) error { 101 | 102 | if iccid == "" { 103 | return gerror.New("iccid为空值!") 104 | } 105 | //获取当前的月份、第几天 106 | mtime := gtime.Now() 107 | year := mtime.Format("Y") 108 | day := mtime.Format("j") 109 | month := mtime.Format("n") 110 | //记录或是修改的时间 111 | ctime := gtime.Now().Format("U") 112 | 113 | r, _ := model.dbModel().Where("iccid=? and year=? and month=?", iccid, year, month).One() 114 | if r != nil { 115 | //修改已有记录 116 | sc := SimFlow{} 117 | if err := r.Struct(&sc); err == nil { 118 | data := g.Map{"d" + day: flow, "updatetime": ctime, "rate_plan": rate_plan, "status": status, "ctd_session_count": ctd_session_count} 119 | //glog.Info("data:", data) 120 | model.dbModel().Data(data).Where("id=?", sc.Id).Update() 121 | 122 | } else { 123 | glog.Error(err) 124 | } 125 | } else { 126 | //新增记录 127 | newData := g.Map{"iccid": iccid, "year": year, "month": month, "d" + day: flow, "provider": provider, "carrier": carrler, "createtime": ctime, "rate_plan": rate_plan, "status": status, "ctd_session_count": ctd_session_count} 128 | _, err := model.dbModel().Data(newData).Save() 129 | if err != nil { 130 | glog.Error(err) 131 | } 132 | 133 | } 134 | 135 | return nil 136 | } 137 | 138 | 139 | func (model SimFlow) PkVal() int { 140 | return model.Id 141 | } 142 | 143 | func (model SimFlow) TableName() string { 144 | table := g.Config().Get("datatable.flow_table") 145 | return table.(string) 146 | } 147 | 148 | func (model SimFlow) dbModel(alias ...string) *gdb.Model { 149 | var tmpAlias string 150 | if len(alias) > 0 { 151 | tmpAlias = " " + alias[0] 152 | } 153 | tableModel := g.DB().Table(model.TableName() + tmpAlias).Safe() 154 | return tableModel 155 | } 156 | 157 | func (model SimFlow) sumColumns() string { 158 | sqlColumns := "year,month,carrier,sum(d1) as d1,sum(d2) as d2,sum(d3) as d3,sum(d4) as d4,sum(d5) as d5,sum(d6) as d6,sum(d7) as d7,sum(d8) as d8,sum(d9) as d9,sum(d10) as d10,sum(d11) as d11,sum(d12) as d12,sum(d13) as d13,sum(d14) as d14,sum(d15) as d15,sum(d16) as d16,sum(d17) as d17,sum(d18) as d18,sum(d19) as d19,sum(d20) as d20,sum(d21) as d21,sum(d22) as d22,sum(d23) as d23,sum(d24) as d24,sum(d25) as d25,sum(d26) as d26,sum(d27) as d27,sum(d28) as d28,sum(d29) as d29,sum(d30) as d30,sum(d31) as d31" 159 | return sqlColumns 160 | } -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= 2 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 3 | github.com/clbanning/mxj v1.8.4 h1:HuhwZtbyvyOw+3Z1AowPkU87JkJUSv751ELWaiTpj8I= 4 | github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng= 5 | github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= 6 | github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= 7 | github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= 8 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 9 | github.com/gf-third/mysql v1.4.2 h1:f1M5CNFUG3WkE07UOomtu4o0n/KJKeuUUf5Nc9ZFXs4= 10 | github.com/gf-third/mysql v1.4.2/go.mod h1:+dd90V663ppI2fV5uQ6+rHk0u8KCyU6FkG8Um8Cx3ms= 11 | github.com/gf-third/yaml v1.0.1 h1:pqD4ix+65DqGphU1MDnToPZfGYk0tuuwRzuTSl3g0d0= 12 | github.com/gf-third/yaml v1.0.1/go.mod h1:t443vj0txEw3+E0MOtkr83kt+PrZg2I8SRuYfn85NM0= 13 | github.com/gogf/gf v1.9.10 h1:lPBf0EOxv6AXVWN46EKLID0GMHDGOrs4ZAi/RUJbt+c= 14 | github.com/gogf/gf v1.9.10/go.mod h1:sitJZHjmT9B+923N4mH5rkM19AugKG+BxI47R64bR08= 15 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 16 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 17 | github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= 18 | github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= 19 | github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= 20 | github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 21 | github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf h1:wIOAyJMMen0ELGiFzlmqxdcV1yGbkyHBAB6PolcNbLA= 22 | github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78= 23 | github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y= 24 | github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= 25 | github.com/olekukonko/tablewriter v0.0.1 h1:b3iUnf1v+ppJiOfNX4yxxqfWKMQPZR5yoh8urCTFX88= 26 | github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= 27 | github.com/qianlnk/pgbar v0.0.0-20190817080628-008abe846ec1/go.mod h1:4YWkn3EVkh8c1BDlVmw+Zh2QLhs+MbAg4xy4RqcKMsA= 28 | github.com/qianlnk/pgbar v0.0.0-20190929032005-46c23acad4ed h1:/nQRgal0OAvl64fVVo0IrwlMt8vXypxc/a+N0Is80VY= 29 | github.com/qianlnk/pgbar v0.0.0-20190929032005-46c23acad4ed/go.mod h1:4YWkn3EVkh8c1BDlVmw+Zh2QLhs+MbAg4xy4RqcKMsA= 30 | github.com/qianlnk/to v0.0.0-20180426070425-a52c7fda1751 h1:3EYaPrwMGOaFxBbiLlsfRGFNlSLJ3ETjkPbTfkG5IGQ= 31 | github.com/qianlnk/to v0.0.0-20180426070425-a52c7fda1751/go.mod h1:HYAQIJIdgW9cGr75BDsucQMgKREt00mECJHOskH5n5k= 32 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 33 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 34 | golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 35 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 36 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 37 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 38 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 39 | golang.org/x/net v0.0.0-20190916140828-c8589233b77d/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 40 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 41 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 42 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 43 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 44 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 45 | golang.org/x/sys v0.0.0-20190924092210-98129a5cf4a0 h1:wFKb4oFjFfHcg2hJGd5iawQsCWP5jTIE+5yNFcsMttM= 46 | golang.org/x/sys v0.0.0-20190924092210-98129a5cf4a0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 47 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 48 | golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= 49 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 50 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 51 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 52 | golang.org/x/tools v0.0.0-20190917215024-905c8ffbfa41/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 53 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 54 | google.golang.org/appengine v1.6.3 h1:hvZejVcIxAKHR8Pq2gXaDggf6CWT1QEqO+JEBeOKCG8= 55 | google.golang.org/appengine v1.6.3/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 56 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 57 | -------------------------------------------------------------------------------- /app/service/analyse/flow.go: -------------------------------------------------------------------------------- 1 | package analyse 2 | 3 | import ( 4 | "github.com/gogf/gf/os/gtime" 5 | "github.com/gogf/gf/util/gconv" 6 | "github.com/xinjiayu/SimServerUnicom/app/model/analyse" 7 | "github.com/xinjiayu/SimServerUnicom/app/model/datamodel" 8 | "github.com/xinjiayu/SimServerUnicom/app/service/operate" 9 | "github.com/xinjiayu/SimServerUnicom/library/utils" 10 | ) 11 | 12 | //分析所有卡最近两天的流量情况 13 | func TwoFlow() []analyse.TwoDaysFlow { 14 | 15 | fud := utils.GetFlowUseDate() 16 | 17 | carrier := "unicom" //sim卡运营商 18 | 19 | var twoDaysFlowList []analyse.TwoDaysFlow 20 | //本月流量 21 | monthFlowList := datamodel.SimFlow{}.FlowList("year=? and month=? and carrier=?", fud.Year, fud.Month, carrier) 22 | //如果当前是1月份,后面取上一年的数据 23 | 24 | year := fud.Year 25 | if fud.Month == "1" { 26 | year = fud.LastYear 27 | } 28 | //上个月流量 29 | lastMonthFlowList := datamodel.SimFlow{}.FlowList("year=? and month=? and carrier=?", year, fud.LastMonth, carrier) 30 | var lastMonthFlowSimList = make(map[string]datamodel.SimFlow) 31 | for _, v := range lastMonthFlowList { 32 | lastMonthFlowSimList[v.Iccid] = v 33 | } 34 | 35 | for _, v := range monthFlowList { 36 | 37 | twoDaysFlow := analyse.TwoDaysFlow{} 38 | twoDaysFlow.Iccid = v.Iccid 39 | 40 | flowData := gconv.Map(v) 41 | lastMonthFlowSim := gconv.Map(lastMonthFlowSimList[v.Iccid]) 42 | 43 | todayNum := gconv.Int(fud.Today) 44 | 45 | var d0 int64 = 0 46 | var d1 int64 = 0 47 | var d2 int64 = 0 48 | d0 = gconv.Int64(flowData["d"+fud.Today]) 49 | d1 = gconv.Int64(flowData["d"+fud.Yesterday]) 50 | d2 = gconv.Int64(flowData["d"+fud.BeforeYesterday]) 51 | 52 | switch todayNum { 53 | case 1: 54 | d1 = gconv.Int64(lastMonthFlowSim["d"+fud.Yesterday]) 55 | d2 = gconv.Int64(lastMonthFlowSim["d"+fud.BeforeYesterday]) 56 | case 2: 57 | d2 = gconv.Int64(lastMonthFlowSim["d"+fud.BeforeYesterday]) 58 | } 59 | 60 | //今天的流量 61 | twoDaysFlow.TodayUsage = (d0 - d1) / utils.MB1 62 | if fud.Today == "27" { 63 | twoDaysFlow.TodayUsage = d0 / utils.MB1 //如果是27号,不计算 64 | } 65 | 66 | if twoDaysFlow.TodayUsage < 0 { 67 | twoDaysFlow.TodayUsage = 0 68 | } 69 | twoDaysFlow.YesterdayUsage = (d1 - d2) / utils.MB1 //昨天的流量 70 | if twoDaysFlow.YesterdayUsage < 0 { 71 | twoDaysFlow.YesterdayUsage = 0 72 | } 73 | 74 | twoDaysFlowList = append(twoDaysFlowList, twoDaysFlow) 75 | } 76 | return twoDaysFlowList 77 | } 78 | 79 | //分析所有卡的流量列表,可以指定计费套餐 80 | func SimList(panName string) []analyse.PlanSimCardInfo { 81 | 82 | monthFlowList := datamodel.SimUnicom{}.FlowList(panName) 83 | var SimFlowList []analyse.PlanSimCardInfo 84 | for _, v := range monthFlowList { 85 | simFlow := analyse.PlanSimCardInfo{} 86 | 87 | simFlow.Iccid = v.Iccid 88 | simFlow.Flow = v.CtdDataUsage 89 | simFlow.PlanName = v.RatePlan 90 | SimFlowList = append(SimFlowList, simFlow) 91 | } 92 | 93 | return SimFlowList 94 | 95 | } 96 | 97 | //分析计费套餐使用情况 98 | func PlanInfoList() []analyse.PlanInfo { 99 | 100 | simCardList, _ := datamodel.SimUnicom{}.GetUnicomSimInfoList() 101 | var op = new(operate.AutoChangePlan) 102 | op.CountPlanFlow(simCardList) 103 | 104 | var planInfoList []analyse.PlanInfo 105 | planInfo1 := op.PlanInfo[datamodel.Plan01] 106 | if planInfo1.PlanName != "" { 107 | planInfoList = append(planInfoList, planInfo1) 108 | 109 | } 110 | planInfo2 := op.PlanInfo[datamodel.Plan02] 111 | if planInfo2.PlanName != "" { 112 | planInfoList = append(planInfoList, planInfo2) 113 | 114 | } 115 | planInfo3 := op.PlanInfo[datamodel.Plan03] 116 | if planInfo3.PlanName != "" { 117 | planInfoList = append(planInfoList, planInfo3) 118 | 119 | } 120 | 121 | return planInfoList 122 | } 123 | 124 | //分析指定sim卡的月度流量数据 125 | func MonthSimFlowListByIccid(iccid string) analyse.TwoDaysBaySimCardFlow { 126 | if iccid == "" { 127 | return analyse.TwoDaysBaySimCardFlow{} 128 | } 129 | fud := utils.GetFlowUseDate() 130 | 131 | carrier := "unicom" //sim卡运营商 132 | //本月流量 133 | monthFlow := datamodel.SimFlow{}.GetByOne("year=? and month=? and carrier=? and iccid=?", fud.Year, fud.Month, carrier, iccid) 134 | //如果当前是1月份,后面取上一年的数据 135 | year := fud.Year 136 | if fud.Month == "1" { 137 | year = fud.LastYear 138 | } 139 | //上个月流量 140 | lastMonthFlow := datamodel.SimFlow{}.GetByOne("year=? and month=? and carrier=? and iccid=?", year, fud.LastMonth, carrier, iccid) 141 | 142 | //上上个月流量 143 | BeforeLastMonthFlow := datamodel.SimFlow{}.GetByOne("year=? and month=? and carrier=? and iccid=?", year, fud.BeforeLastMonth, carrier, iccid) 144 | 145 | var twoDaysBaySimCardFlow analyse.TwoDaysBaySimCardFlow 146 | 147 | monthFlowData := gconv.Map(monthFlow) 148 | LastMonthFlowData := gconv.Map(lastMonthFlow) 149 | BeforeLastMonthFlowData := gconv.Map(BeforeLastMonthFlow) 150 | 151 | lastMonthFlowData := gconv.Int64(LastMonthFlowData["d"+fud.LastMonthDays]) //上个月最后一天的数据 152 | beforeLastMonthFlowData := gconv.Int64(BeforeLastMonthFlowData["d"+fud.BeforeLastMonthDays]) //上上个月最后一天的数据 153 | 154 | for i := 1; i < 32; i++ { 155 | //输出每天的数字 156 | twoDaysBaySimCardFlow.DayName = append(twoDaysBaySimCardFlow.DayName, i) 157 | } 158 | 159 | //本月流量统计 160 | twoDaysBaySimCardFlow.Nowmonth = countDaysFlow(lastMonthFlowData, monthFlowData) 161 | //上个月流量统计 162 | twoDaysBaySimCardFlow.Beforemonth = countDaysFlow(beforeLastMonthFlowData, LastMonthFlowData) 163 | 164 | return twoDaysBaySimCardFlow 165 | 166 | } 167 | 168 | //分析每天使用的流量 169 | func countDaysFlow(lastMonthFlowData int64, monthFlowData map[string]interface{}) []int64 { 170 | var dayFlows []int64 171 | for i := 1; i < 32; i++ { 172 | var d0 int64 = 0 173 | var d1 int64 = 0 174 | d0 = gconv.Int64(monthFlowData["d"+gconv.String(i)]) 175 | d1 = gconv.Int64(monthFlowData["d"+gconv.String(i-1)]) 176 | if i == 1 { 177 | d1 = lastMonthFlowData 178 | } 179 | //本天的流量 180 | dayFlow := d0 - d1 181 | if i == 27 { 182 | dayFlow = d0 //如果是27号,不计算 183 | } 184 | if dayFlow < 0 { 185 | dayFlow = 0 186 | } 187 | dayFlows = append(dayFlows, dayFlow/utils.MB1) 188 | } 189 | return dayFlows 190 | } 191 | 192 | //通过事件类型提取通知信息 193 | func GetNotice(eventType string) []datamodel.UnicomNotice { 194 | fud := utils.GetFlowUseDate() 195 | startDate := fud.Year + "-" + fud.LastMonth + "-27" 196 | if gconv.Int(fud.Month) == 1 && gconv.Int(fud.Today) < 27 { 197 | startDate = fud.LastYear + "-" + fud.LastMonth + "-27" 198 | } 199 | if gconv.Int(fud.Today) > 26 { 200 | startDate = fud.Year + "-" + fud.Month + "-27" 201 | } 202 | endDate := gtime.Now().Format("Y-m-d") 203 | 204 | startTime := gtime.NewFromStr(startDate).Second() 205 | endTime := gtime.NewFromStr(endDate).Second() 206 | 207 | if eventType == "" { 208 | return datamodel.UnicomNotice{}.List("timestamp > ? and timestamp < ?", startTime, endTime) 209 | } else { 210 | return datamodel.UnicomNotice{}.List("timestamp > ? and timestamp < ? and event_type = ?", startTime, endTime, eventType) 211 | 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /deploy/db.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE `sim_unicom` 2 | ( 3 | `id` int(11) NOT NULL, 4 | `iccid` varchar(100) NOT NULL, 5 | `imsi` varchar(100) DEFAULT NULL, 6 | `msisdn` varchar(100) DEFAULT NULL, 7 | `imei` varchar(100) DEFAULT NULL, 8 | `status` varchar(100) DEFAULT NULL, 9 | `rateplan` varchar(100) DEFAULT NULL, 10 | `communicationplan` varchar(100) DEFAULT NULL, 11 | `customer` varchar(100) DEFAULT NULL, 12 | `endconsumerid` varchar(100) DEFAULT NULL, 13 | `dateactivated` varchar(100) DEFAULT NULL, 14 | `dateadded` varchar(100) DEFAULT NULL, 15 | `dateupdated` varchar(100) DEFAULT NULL, 16 | `dateshipped` varchar(100) DEFAULT NULL, 17 | `accountid` varchar(100) DEFAULT NULL, 18 | `fixedipaddress` varchar(100) DEFAULT NULL, 19 | `accountcustom1` varchar(100) DEFAULT NULL, 20 | `accountcustom2` varchar(100) DEFAULT NULL, 21 | `accountcustom3` varchar(100) DEFAULT NULL, 22 | `accountcustom4` varchar(100) DEFAULT NULL, 23 | `accountcustom5` varchar(100) DEFAULT NULL, 24 | `accountcustom6` varchar(100) DEFAULT NULL, 25 | `accountcustom7` varchar(100) DEFAULT NULL, 26 | `accountcustom8` varchar(100) DEFAULT NULL, 27 | `accountcustom9` varchar(100) DEFAULT NULL, 28 | `accountcustom10` varchar(100) DEFAULT NULL, 29 | `simnotes` varchar(100) DEFAULT NULL, 30 | `deviceid` varchar(100) DEFAULT NULL, 31 | `modemid` varchar(100) DEFAULT NULL, 32 | `globalsimtype` varchar(100) DEFAULT NULL, 33 | `ctddatausage` bigint(19) NOT NULL DEFAULT '0' COMMENT '本期使用流量' 34 | ) ENGINE = InnoDB 35 | DEFAULT CHARSET = utf8 COMMENT ='联通sim卡信息表'; 36 | 37 | ALTER TABLE `sim_unicom` 38 | ADD PRIMARY KEY (`id`, `iccid`) USING BTREE; 39 | 40 | ALTER TABLE `sim_unicom` 41 | MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, 42 | AUTO_INCREMENT = 1; 43 | 44 | 45 | 46 | CREATE TABLE `sim_flow` 47 | ( 48 | `id` int(11) NOT NULL COMMENT 'ID', 49 | `iccid` varchar(30) NOT NULL COMMENT 'sim卡号', 50 | `year` int(4) NOT NULL COMMENT '年份', 51 | `month` tinyint(2) UNSIGNED NOT NULL DEFAULT '0' COMMENT '月份', 52 | `provider` varchar(30) NOT NULL DEFAULT '' COMMENT '提供商', 53 | `carrier` varchar(30) NOT NULL DEFAULT '' COMMENT '运营商', 54 | `d1` bigint(19) UNSIGNED DEFAULT '0' COMMENT '流量', 55 | `d2` bigint(19) UNSIGNED DEFAULT '0' COMMENT '流量', 56 | `d3` bigint(19) UNSIGNED DEFAULT '0' COMMENT '流量', 57 | `d4` bigint(19) UNSIGNED DEFAULT '0' COMMENT '流量', 58 | `d5` bigint(19) UNSIGNED DEFAULT '0' COMMENT '流量', 59 | `d6` bigint(19) UNSIGNED DEFAULT '0' COMMENT '流量', 60 | `d7` bigint(19) UNSIGNED DEFAULT '0' COMMENT '流量', 61 | `d8` bigint(19) UNSIGNED DEFAULT '0' COMMENT '流量', 62 | `d9` bigint(19) UNSIGNED DEFAULT '0' COMMENT '流量', 63 | `d10` bigint(19) UNSIGNED DEFAULT '0' COMMENT '流量', 64 | `d11` bigint(19) UNSIGNED DEFAULT '0' COMMENT '流量', 65 | `d12` bigint(19) UNSIGNED DEFAULT '0' COMMENT '流量', 66 | `d13` bigint(19) UNSIGNED DEFAULT '0' COMMENT '流量', 67 | `d14` bigint(19) UNSIGNED DEFAULT '0' COMMENT '流量', 68 | `d15` bigint(19) UNSIGNED DEFAULT '0' COMMENT '流量', 69 | `d16` bigint(19) UNSIGNED DEFAULT '0' COMMENT '流量', 70 | `d17` bigint(19) UNSIGNED DEFAULT '0' COMMENT '流量', 71 | `d18` bigint(19) UNSIGNED DEFAULT '0' COMMENT '流量', 72 | `d19` bigint(19) UNSIGNED DEFAULT '0' COMMENT '流量', 73 | `d20` bigint(19) UNSIGNED DEFAULT '0' COMMENT '流量', 74 | `d21` bigint(19) UNSIGNED DEFAULT '0' COMMENT '流量', 75 | `d22` bigint(19) UNSIGNED DEFAULT '0' COMMENT '流量', 76 | `d23` bigint(19) UNSIGNED DEFAULT '0' COMMENT '流量', 77 | `d24` bigint(19) UNSIGNED DEFAULT '0' COMMENT '流量', 78 | `d25` bigint(19) UNSIGNED DEFAULT '0' COMMENT '流量', 79 | `d26` bigint(19) UNSIGNED DEFAULT '0' COMMENT '流量', 80 | `d27` bigint(19) UNSIGNED DEFAULT '0' COMMENT '流量', 81 | `d28` bigint(19) UNSIGNED DEFAULT '0' COMMENT '流量', 82 | `d29` bigint(19) UNSIGNED DEFAULT '0' COMMENT '流量', 83 | `d30` bigint(19) UNSIGNED DEFAULT '0' COMMENT '流量', 84 | `d31` bigint(19) UNSIGNED DEFAULT '0' COMMENT '流量', 85 | `createtime` int(10) NOT NULL DEFAULT '0' COMMENT '创建时间', 86 | `updatetime` int(10) UNSIGNED NOT NULL DEFAULT '0' COMMENT '更新时间', 87 | `ctd_session_count` int(11) NOT NULL COMMENT '数据会话次数', 88 | `rate_plan` varchar(100) NOT NULL COMMENT '套餐计划名称', 89 | `status` varchar(50) NOT NULL COMMENT '状态' 90 | ) ENGINE = InnoDB 91 | DEFAULT CHARSET = utf8 COMMENT ='日流量表' 92 | ROW_FORMAT = COMPACT; 93 | 94 | 95 | ALTER TABLE `sim_flow` 96 | ADD PRIMARY KEY (`id`) USING BTREE, 97 | ADD UNIQUE KEY `iccid` (`iccid`, `month`, `year`) USING BTREE; 98 | 99 | ALTER TABLE `sim_flow` 100 | MODIFY `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'ID', 101 | AUTO_INCREMENT = 1; 102 | 103 | 104 | CREATE TABLE `unicom_notice` 105 | ( 106 | `id` int(10) NOT NULL COMMENT 'ID', 107 | `event_id` varchar(100) NOT NULL DEFAULT '' COMMENT '标识', 108 | `event_type` varchar(50) NOT NULL DEFAULT '' COMMENT '类型', 109 | `timestamp` int(10) NOT NULL DEFAULT '0' COMMENT '请求时间', 110 | `iccid` varchar(50) NOT NULL DEFAULT '' COMMENT 'ICCID', 111 | `data_usage` int(11) NOT NULL DEFAULT '0' COMMENT '流量', 112 | `data1` varchar(50) NOT NULL DEFAULT '' COMMENT '数据一', 113 | `data2` varchar(50) NOT NULL DEFAULT '' COMMENT '数据二' 114 | ) ENGINE = InnoDB 115 | DEFAULT CHARSET = utf8 COMMENT ='联通平台流量通知'; 116 | 117 | ALTER TABLE `unicom_notice` 118 | ADD PRIMARY KEY (`id`); 119 | 120 | ALTER TABLE `unicom_notice` 121 | MODIFY `id` int(10) NOT NULL AUTO_INCREMENT COMMENT 'ID', AUTO_INCREMENT=1; -------------------------------------------------------------------------------- /app/model/datamodel/simunicom.go: -------------------------------------------------------------------------------- 1 | package datamodel 2 | 3 | import ( 4 | "github.com/gogf/gf/database/gdb" 5 | "github.com/gogf/gf/errors/gerror" 6 | "github.com/gogf/gf/frame/g" 7 | "github.com/gogf/gf/os/glog" 8 | "github.com/xinjiayu/SimServerUnicom/app/model/basemodel" 9 | "github.com/xinjiayu/SimServerUnicom/app/model/unicommodel" 10 | "github.com/xinjiayu/SimServerUnicom/library/utils" 11 | ) 12 | 13 | type SimUnicom struct { 14 | Id int `json:"id"` 15 | Iccid string `json:"iccid"` 16 | Imsi string `json:"imsi"` 17 | Msisdn string `json:"msisdn"` 18 | Imei string `json:"imei"` 19 | Status string `json:"status"` 20 | RatePlan string `json:"rateplan"` 21 | CommunicationPlan string `json:"communicationplan"` 22 | Customer string `json:"customer"` 23 | EndConsumerID string `json:"endconsumerid"` 24 | DateActivated string `json:"dateactivated"` 25 | DateAdded string `json:"dateadded"` 26 | DateUpdated string `json:"dateupdated"` 27 | DateShipped string `json:"dateshipped"` 28 | AccountID string `json:"accountid"` 29 | FixedIPAddress string `json:"fixedipaddress"` 30 | AccountCustom1 string `json:"accountcustom1"` 31 | AccountCustom2 string `json:"accountcustom2"` 32 | AccountCustom3 string `json:"accountcustom3"` 33 | AccountCustom4 string `json:"accountcustom4"` 34 | AccountCustom5 string `json:"accountcustom5"` 35 | AccountCustom6 string `json:"accountcustom6"` 36 | AccountCustom7 string `json:"accountcustom7"` 37 | AccountCustom8 string `json:"accountcustom8"` 38 | AccountCustom9 string `json:"accountcustom9"` 39 | AccountCustom10 string `json:"accountcustom10"` 40 | SimNotes string `json:"simnotes"` 41 | DeviceID string `json:"deviceid"` 42 | ModemID string `json:"modemid"` 43 | GlobalSimType string `json:"globalsimtype"` 44 | CtdDataUsage int64 `json:"ctddatausage"` 45 | 46 | basemodel.BaseModel 47 | } 48 | 49 | const Plan01 = "831WLW016555_MON-FLEX_1024M_SP" 50 | const Plan02 = "831WLW016555_MON-FLEX_2048M_SP" 51 | const Plan03 = "831WLW016555_MON-FLEX_3072M_SP" 52 | 53 | func (model SimUnicom) Get() SimUnicom { 54 | if model.Id <= 0 { 55 | glog.Error(model.TableName() + " get id error") 56 | return SimUnicom{} 57 | } 58 | 59 | var resData SimUnicom 60 | err := model.dbModel().Where(" id = ?", model.Id).Struct(&resData) 61 | if err != nil { 62 | glog.Error(model.TableName()+" get one error", err) 63 | return SimUnicom{} 64 | } 65 | 66 | return resData 67 | } 68 | 69 | func (model SimUnicom) GetByIccid(iccid string) SimUnicom { 70 | if iccid == "" { 71 | return SimUnicom{} 72 | } 73 | 74 | var resData SimUnicom 75 | err := model.dbModel().Where(" iccid = ?", iccid).Struct(&resData) 76 | if err != nil { 77 | glog.Error(model.TableName()+" get one error", err) 78 | return SimUnicom{} 79 | } 80 | 81 | return resData 82 | } 83 | 84 | //计费周期内流量使用列表 85 | func (model SimUnicom) FlowList(rateplan string) []SimUnicom { 86 | var resData []SimUnicom 87 | var err error 88 | if rateplan != "" { 89 | err = model.dbModel().Where(" rateplan = ?", rateplan).OrderBy("ctddatausage desc").Structs(&resData) 90 | } else { 91 | err = model.dbModel().Where(" 1 = ?", 1).OrderBy("ctddatausage desc").Structs(&resData) 92 | } 93 | if err != nil { 94 | glog.Error(model.TableName()+" list error", err) 95 | return []SimUnicom{} 96 | } 97 | return resData 98 | } 99 | 100 | //func (model SimUnicom) PlanCountInfo(planName string) analyse.PlanInfo { 101 | // 102 | // var simList = make(map[string]analyse.PlanSimCardInfo) 103 | // var resData []SimUnicom 104 | // var err error 105 | // if planName != "" { 106 | // err = model.dbModel().Where(" rateplan = ?", planName).OrderBy("ctddatausage desc").Structs(&resData) 107 | // } else { 108 | // err = model.dbModel().Where(" 1 = ?", 1).OrderBy("ctddatausage desc").Structs(&resData) 109 | // } 110 | // if err != nil { 111 | // //glog.Error(model.TableName()+" list error", err) 112 | // return analyse.PlanInfo{} 113 | // } 114 | // 115 | // for _, v := range resData { 116 | // 117 | // planSimCardInfo := analyse.PlanSimCardInfo{} 118 | // planSimCardInfo.Flow = v.CtdDataUsage 119 | // planSimCardInfo.Iccid = v.Iccid 120 | // planSimCardInfo.PlanName = v.RatePlan 121 | // simList[v.Iccid] = planSimCardInfo 122 | // 123 | // } 124 | // 125 | // //计算每种计费套餐的流量池的真实大小。 126 | // planPoolFlow := countPlanFlow(resData) 127 | // planFlow := planPoolFlow[planName] 128 | // 129 | // //计算计费周期 130 | // yearNumStr := gtime.Now().Format("Y") 131 | // monthNum := gtime.Now().Format("n") 132 | // lastMonth := gtime.Now().AddDate(0, -1, 0).Format("n") //上个月 133 | // nextMonth := gtime.Now().AddDate(0, +1, 0).Format("n") //上个月 134 | // 135 | // //计费周期开始日期 136 | // startDayStr := yearNumStr + "-" + monthNum + "-27" 137 | // endDayStr := gconv.String(gtime.Now().Format("Y-m-d")) 138 | // 139 | // if gconv.Int(gtime.Now().Format("j")) < 27 { 140 | // startDayStr = yearNumStr + "-" + lastMonth + "-27" 141 | // //到本月26日 142 | // endDayStr = yearNumStr + "-" + gconv.String(monthNum) + "-26" 143 | // } else { 144 | // startDayStr = yearNumStr + "-" + monthNum + "-27" 145 | // //到下个月26日 146 | // endDayStr = yearNumStr + "-" + gconv.String(nextMonth) + "-26" 147 | // } 148 | // //glog.Info("开始日期:",startDayStr) 149 | // //glog.Info("结束日期:",endDayStr) 150 | // 151 | // dayStr := gtime.Now().AddDate(0, 0, 0).Format("Y-m-d") 152 | // //glog.Info("今天日期:",dayStr) 153 | // 154 | // //计算已使用的天数 155 | // a, _ := time.Parse("2006-01-02", dayStr) 156 | // b, _ := time.Parse("2006-01-02", startDayStr) 157 | // useDayNumtmp := utils.TimeSub(a, b) 158 | // //已使用的天数 159 | // useDayNum := gconv.Int64(useDayNumtmp) 160 | // //glog.Info("已用天数:",useDayNum) 161 | // 162 | // //计算剩余的天数 163 | // a2, _ := time.Parse("2006-01-02", endDayStr) 164 | // b2, _ := time.Parse("2006-01-02", dayStr) 165 | // remainderDayNumTmp := utils.TimeSub(a2, b2) 166 | // remainderDayNum := gconv.Int64(remainderDayNumTmp) 167 | // 168 | // //glog.Info("剩余天数:",remainderDayNum) 169 | // var useFlow int64 = 0 170 | // for _, v := range simList { 171 | // useFlow = useFlow + v.Flow 172 | // } 173 | // 174 | // //剩余流量 175 | // var surplusFlow int64 = 0 176 | // //超出流量 177 | // var outFlow int64 = 0 178 | // //计算剩余流量 179 | // surplusFlow = planFlow - useFlow 180 | // if surplusFlow < 0 { 181 | // outFlow = utils.Abs(surplusFlow) 182 | // surplusFlow = 0 183 | // } 184 | // 185 | // planInfo := analyse.PlanInfo{} 186 | // planInfo.PlanName = planName 187 | // planInfo.Num = len(simList) 188 | // planInfo.AllFlow = planFlow / utils.MB1 189 | // planInfo.UseFlow = useFlow / utils.MB1 190 | // planInfo.AveDayFlow = useFlow / useDayNum / utils.MB1 191 | // planInfo.AveSimUseFlow = useFlow / gconv.Int64(len(simList)) / utils.MB1 //这个留MB适合 192 | // planInfo.SurplusFlow = surplusFlow / utils.MB1 193 | // planInfo.OutFlow = outFlow / utils.MB1 194 | // planInfo.RemainderDayNum = remainderDayNum 195 | // planInfo.ExpectFlow = useFlow / useDayNum * remainderDayNum / utils.MB1 196 | // return planInfo 197 | //} 198 | 199 | //计算流量池的真实大小:流量池中,当前计费周期内新激活的卡,流量是从卡激活的日期到计费结束日按每天流量累计的流量。 200 | //func countPlanFlow(simList []SimUnicom) map[string]int64 { 201 | // 202 | // var retData = make(map[string]int64, 3) 203 | // monthNum := gconv.Int64(gtime.Now().Format("n")) 204 | // yearNum := gconv.Int64(gtime.Now().Format("Y")) 205 | // toDayNum := gconv.Int64(gtime.Now().Format("j")) 206 | // endYear := yearNum 207 | // endMonth := monthNum 208 | // if toDayNum > 26 { 209 | // endMonth = endMonth + 1 210 | // if monthNum == 12 { 211 | // endYear = endYear + 1 212 | // endMonth = 1 213 | // } 214 | // } 215 | // 216 | // var G1DayFlow int64 = utils.G1 / 30 217 | // var G2DayFlow int64 = utils.G1 * 2 / 30 218 | // var G3DayFlow int64 = utils.G1 * 3 / 30 219 | // 220 | // for _, v := range simList { 221 | // //dateActivated 222 | // var remainderDayNum int64 = 30 223 | // if v.DateActivated != "" { 224 | // tmpTime := gconv.Int64(v.DateActivated) 225 | // simCardActivatedYear := gconv.Int64(gtime.NewFromTimeStamp(tmpTime).Format("Y")) 226 | // simCardActivatedMonth := gconv.Int64(gtime.NewFromTimeStamp(tmpTime).Format("n")) 227 | // if simCardActivatedYear == yearNum && simCardActivatedMonth == monthNum { 228 | // simCardActivatedDate := gtime.NewFromTimeStamp(tmpTime).Format("Y-m-d") 229 | // endDayStr := gconv.String(endYear) + "-" + gconv.String(endMonth) + "-26" 230 | // //计算剩余的天数 231 | // a2, _ := time.Parse("2006-01-02", endDayStr) 232 | // b2, _ := time.Parse("2006-01-02", simCardActivatedDate) 233 | // remainderDayNumTmp := utils.TimeSub(a2, b2) 234 | // remainderDayNum = gconv.Int64(remainderDayNumTmp) 235 | // } 236 | // 237 | // } 238 | // 239 | // switch v.RatePlan { 240 | // case Plan01: 241 | // retData[Plan01] = retData[Plan01] + remainderDayNum*G1DayFlow 242 | // 243 | // case Plan02: 244 | // retData[Plan02] = retData[Plan02] + +remainderDayNum*G2DayFlow 245 | // 246 | // case Plan03: 247 | // retData[Plan03] = retData[Plan03] + +remainderDayNum*G3DayFlow 248 | // 249 | // } 250 | // } 251 | // return retData 252 | //} 253 | 254 | //SaveUnicomSimInfo 存储联通sim卡详细信息到数据表 255 | func (model SimUnicom) SaveUnicomSimInfo(sim unicommodel.SimInfo) error { 256 | if sim.Iccid == "" { 257 | return gerror.New("iccid为空值!") 258 | } 259 | 260 | if sim.DateActivated != "" { 261 | sim.DateActivated = utils.ChangeUnixTime(sim.DateActivated) 262 | 263 | } 264 | if sim.DateAdded != "" { 265 | sim.DateAdded = utils.ChangeUnixTime(sim.DateAdded) 266 | 267 | } 268 | if sim.DateShipped != "" { 269 | sim.DateShipped = utils.ChangeUnixTime(sim.DateShipped) 270 | 271 | } 272 | if sim.DateUpdated != "" { 273 | sim.DateUpdated = utils.ChangeUnixTime(sim.DateUpdated) 274 | 275 | } 276 | 277 | r, _ := model.dbModel().Where("iccid=?", sim.Iccid).One() 278 | if r != nil { 279 | //修改已有记录 280 | sc := unicommodel.SimInfo{} 281 | if err := r.Struct(&sc); err == nil { 282 | //glog.Info("data:", sim) 283 | model.dbModel().Data(sim).Where("iccid=?", sc.Iccid).Update() 284 | 285 | } else { 286 | glog.Error(err) 287 | } 288 | } else { 289 | //新增记录 290 | _, err := model.dbModel().Data(sim).Save() 291 | if err != nil { 292 | glog.Error(err) 293 | } 294 | } 295 | 296 | return nil 297 | } 298 | 299 | //获取所有资费计划的卡信息 300 | func (model SimUnicom) GetUnicomSimInfoList() (gdb.Result, error) { 301 | return model.dbModel().All() 302 | 303 | } 304 | 305 | //获取指定资费计划的卡信息 306 | func (model SimUnicom) GetUnicomSimInfoListByPlan(plan string) (gdb.Result, error) { 307 | return model.dbModel().Where("rateplan=?", plan).All() 308 | 309 | } 310 | 311 | func (model SimUnicom) PkVal() int { 312 | return model.Id 313 | } 314 | 315 | func (model SimUnicom) TableName() string { 316 | table := g.Config().Get("datatable.info_table") 317 | return table.(string) 318 | } 319 | 320 | func (model SimUnicom) dbModel(alias ...string) *gdb.Model { 321 | var tmpAlias string 322 | if len(alias) > 0 { 323 | tmpAlias = " " + alias[0] 324 | } 325 | tableModel := g.DB().Table(model.TableName() + tmpAlias).Safe() 326 | return tableModel 327 | } 328 | func getPlanNum(panName string) int64 { 329 | 330 | switch panName { 331 | case Plan01: 332 | return 1 333 | 334 | case Plan02: 335 | return 2 336 | 337 | case Plan03: 338 | return 3 339 | 340 | } 341 | return 1 342 | } 343 | -------------------------------------------------------------------------------- /app/service/operate/autoplan.go: -------------------------------------------------------------------------------- 1 | package operate 2 | 3 | import ( 4 | "github.com/gogf/gf/container/gmap" 5 | "github.com/gogf/gf/database/gdb" 6 | "github.com/gogf/gf/frame/g" 7 | "github.com/gogf/gf/os/glog" 8 | "github.com/gogf/gf/os/gtime" 9 | "github.com/gogf/gf/util/gconv" 10 | "github.com/xinjiayu/SimServerUnicom/app/model/analyse" 11 | "github.com/xinjiayu/SimServerUnicom/app/model/datamodel" 12 | "github.com/xinjiayu/SimServerUnicom/app/model/unicommodel" 13 | "github.com/xinjiayu/SimServerUnicom/app/service/collect" 14 | "github.com/xinjiayu/SimServerUnicom/library/utils" 15 | "time" 16 | ) 17 | 18 | const plan01 = "831WLW016555_MON-FLEX_1024M_SP" 19 | const plan02 = "831WLW016555_MON-FLEX_2048M_SP" 20 | const plan03 = "831WLW016555_MON-FLEX_3072M_SP" 21 | 22 | type AutoChangePlan struct { 23 | PlanInfo map[string]analyse.PlanInfo 24 | PlanListSimList *gmap.AnyAnyMap 25 | } 26 | 27 | //AutoSetupInit 自动初始化sim卡的设置 ,为1g套餐 28 | func (ac *AutoChangePlan) AutoSetupPlanInit() (error, int) { 29 | num := 0 30 | simCardList, err := datamodel.SimUnicom{}.GetUnicomSimInfoList() 31 | for _, v := range simCardList { 32 | simInfo := new(unicommodel.SimInfo) 33 | v.Struct(simInfo) 34 | if simInfo.RatePlan != plan01 { 35 | glog.Info(simInfo.Iccid, plan01) 36 | go startToChangePlan(simInfo.Iccid, plan01) 37 | num++ 38 | } 39 | } 40 | return err, num 41 | } 42 | 43 | func (ac *AutoChangePlan) GetNot01PlanNum() int { 44 | num := 0 45 | simCardList, _ := datamodel.SimUnicom{}.GetUnicomSimInfoList() 46 | for _, v := range simCardList { 47 | simInfo := new(unicommodel.SimInfo) 48 | v.Struct(simInfo) 49 | if simInfo.RatePlan != plan01 { 50 | num++ 51 | } 52 | } 53 | return num 54 | } 55 | 56 | //AutoSetupPlan 自动设置sim卡套餐 57 | func (ac *AutoChangePlan) toSetupPlan(planNum int) (map[string]datamodel.SimUnicom, int) { 58 | //需求调整的 59 | var simPlan02List = make(map[string]datamodel.SimUnicom) 60 | //是否继续进行调节 61 | continuePlan := 0 62 | aListData := ac.PlanListSimList.Get(getPlanName(planNum)) 63 | if aListData == nil { 64 | return nil, 0 65 | } 66 | aList := gconv.Map(aListData) 67 | 68 | planInfo := ac.PlanInfo[getPlanName(planNum)] 69 | 70 | if planInfo.OutFlow > 0 { 71 | var newListAllFlow int64 = 0 72 | var newListAllNum int64 = 0 73 | 74 | switch planNum { 75 | case 1: 76 | for k1, v1 := range aList { 77 | simInfo := datamodel.SimUnicom{} 78 | gconv.Struct(v1, &simInfo) 79 | 80 | f1 := simInfo.CtdDataUsage / utils.MB1 81 | if f1 > 3072 { 82 | simInfo.RatePlan = plan02 83 | simPlan02List[simInfo.Iccid] = simInfo 84 | newListAllNum++ 85 | newListAllFlow = newListAllFlow + simInfo.CtdDataUsage 86 | delete(aList, k1) 87 | } 88 | 89 | } 90 | p := ac.PlanInfo[getPlanName(planNum)] 91 | if p.OutFlow > 0 { 92 | continuePlan = 2 93 | } 94 | glog.Info("计划一:", getPlanName(1), p) 95 | 96 | case 2: 97 | for k1, v1 := range aList { 98 | simInfo := datamodel.SimUnicom{} 99 | gconv.Struct(v1, &simInfo) 100 | f1 := simInfo.CtdDataUsage / utils.MB1 101 | if f1 > 4096 { 102 | simInfo.RatePlan = plan03 103 | simPlan02List[simInfo.Iccid] = simInfo 104 | newListAllNum++ 105 | newListAllFlow = newListAllFlow + simInfo.CtdDataUsage 106 | delete(aList, k1) 107 | } 108 | 109 | } 110 | p := ac.PlanInfo[getPlanName(planNum)] 111 | if p.OutFlow > 0 { 112 | continuePlan = 3 113 | } 114 | glog.Info("计划二:", getPlanName(2), p) 115 | 116 | case 3: 117 | var simnum1 int64 = 1 118 | outFlowNumTmp1 := planInfo.OutFlow / utils.MB1 119 | outFlowNum1 := outFlowNumTmp1 / 3 120 | if outFlowNum1 > 0 { 121 | simnum1 = outFlowNum1 + 1 122 | } 123 | 124 | var cnt int64 = 0 125 | //a0, _ := ac.PlanListSimList[plan01] 126 | a1 := ac.PlanListSimList.Get(plan01) 127 | 128 | if a1 == nil { 129 | return nil, 0 130 | } 131 | aList := gconv.Map(a1) 132 | for k1, v1 := range aList { 133 | simInfo := datamodel.SimUnicom{} 134 | gconv.Struct(v1, &simInfo) 135 | if simInfo.CtdDataUsage/utils.MB1 < 500 { 136 | if cnt < simnum1 { 137 | simInfo.RatePlan = plan03 138 | simPlan02List[simInfo.Iccid] = simInfo 139 | delete(aList, k1) 140 | 141 | } 142 | 143 | cnt++ 144 | } 145 | 146 | } 147 | p := ac.PlanInfo[getPlanName(planNum)] 148 | if p.OutFlow > 0 { 149 | continuePlan = 3 150 | } 151 | glog.Info("计划三有超出:", getPlanName(3), p) 152 | 153 | } 154 | 155 | } 156 | 157 | return simPlan02List, continuePlan 158 | } 159 | 160 | func getPlanName(num int) string { 161 | 162 | switch num { 163 | case 1: 164 | return plan01 165 | 166 | case 2: 167 | return plan02 168 | 169 | case 3: 170 | return plan03 171 | 172 | } 173 | return "" 174 | } 175 | 176 | func (ac *AutoChangePlan) AutoSetupPlan() (error, int) { 177 | simCardList, _ := datamodel.SimUnicom{}.GetUnicomSimInfoList() 178 | ac.CountPlanFlow(simCardList) 179 | glog.Info("全部卡数:", len(simCardList)) 180 | 181 | var changeSimcardList = make(map[string]string) 182 | 183 | for a := 1; a < 4; a++ { 184 | aList, c := ac.toSetupPlan(a) 185 | for _, v := range aList { 186 | changeSimcardList[v.Iccid] = v.RatePlan 187 | } 188 | if c > 0 { 189 | bList, _ := ac.toSetupPlan(c) 190 | for _, v1 := range bList { 191 | changeSimcardList[v1.Iccid] = v1.RatePlan 192 | } 193 | 194 | } 195 | 196 | } 197 | for cid, plan := range changeSimcardList { 198 | glog.Info(cid, plan) 199 | go startToChangePlan(cid, plan) 200 | } 201 | 202 | glog.Info("sim卡计划变更数:", len(changeSimcardList)) 203 | 204 | return nil, len(changeSimcardList) 205 | } 206 | 207 | func toChangePlan(iccid, ratePlan string) *unicommodel.PutResultData { 208 | //延时处理 209 | apiurl := g.Config().Get("unicom.api_url") 210 | APIURL := apiurl.(string) 211 | getURL := APIURL + "devices/" + iccid 212 | searchStr := "{\"ratePlan\":\"" + ratePlan + "\"}" 213 | glog.Info("提交", searchStr) 214 | dataModel := new(unicommodel.PutResultData) 215 | collect.PutAPIData(getURL, searchStr, dataModel) 216 | return dataModel 217 | } 218 | 219 | func startToChangePlan(iccid, ratePlan string) { 220 | time.Sleep(1e9) 221 | prd := toChangePlan(iccid, ratePlan) 222 | if prd.ErrorCode == "40000029" { 223 | startToChangePlan(iccid, ratePlan) 224 | 225 | } 226 | 227 | } 228 | 229 | //根据流量排序 230 | func Sort(array []analyse.PlanSimCardInfo) []analyse.PlanSimCardInfo { 231 | for i := 0; i < len(array)-1; i++ { 232 | for j := 0; j < len(array)-1-i; j++ { 233 | //根据流量排序 234 | if array[j].Flow > array[j+1].Flow { // >升序 <降序 235 | array[j], array[j+1] = array[j+1], array[j] 236 | } 237 | } 238 | } 239 | return array 240 | } 241 | 242 | //计算流量池的真实大小:流量池中,当前计算周期新激活卡,是从卡激活的日期到计算结束日按每天流量累计的。 243 | func (ac *AutoChangePlan) CountPlanFlow(simList gdb.Result) { 244 | ac.PlanInfo = make(map[string]analyse.PlanInfo) 245 | //ac.PlanListSimList = make(map[string][]datamodel.SimUnicom) 246 | 247 | var simCordList1 = make(map[string]datamodel.SimUnicom) 248 | var simCordList2 = make(map[string]datamodel.SimUnicom) 249 | var simCordList3 = make(map[string]datamodel.SimUnicom) 250 | 251 | ac.PlanListSimList = gmap.NewAnyAnyMap() 252 | 253 | var flowPoolSize = make(map[string]int64) //池子总量 254 | var useFlow = make(map[string]int64) //池子使用量 255 | var simNum = make(map[string]int) //sim卡的数量 256 | 257 | monthNum := gconv.Int64(gtime.Now().Format("n")) 258 | yearNum := gconv.Int64(gtime.Now().Format("Y")) 259 | toDayNum := gconv.Int64(gtime.Now().Format("j")) 260 | 261 | startYear := yearNum //计费周期开始的年 262 | startMonth := monthNum - 1 //计划周期开始的月 263 | 264 | if monthNum == 1 { 265 | if toDayNum <= 26 { 266 | startYear = startYear - 1 267 | startMonth = 12 268 | } 269 | } 270 | 271 | endYear := yearNum //计费周期结束的年 272 | endMonth := monthNum //计划周期结束的月 273 | if toDayNum > 26 { 274 | endMonth = endMonth + 1 275 | if monthNum == 12 { 276 | endYear = endYear + 1 277 | endMonth = 1 278 | } 279 | } 280 | 281 | //计费周期开始日期 282 | startDayStr := gconv.String(startYear) + "-" + gconv.String(startMonth) + "-27" //计周期开始日期 283 | nowDayStr := gconv.String(gtime.Now().Format("Y-m-d")) //今天的日期 284 | endDayStr := gconv.String(endYear) + "-" + gconv.String(endMonth) + "-26" //计费周期结束的日期 285 | 286 | //计算计费周期还剩余的天数 287 | t1, _ := time.Parse("2006-01-02", endDayStr) 288 | t2, _ := time.Parse("2006-01-02", nowDayStr) 289 | surplusFlowDayNumTmp := utils.TimeSub(t1, t2) 290 | surplusDayNum := gconv.Int64(surplusFlowDayNumTmp) 291 | 292 | //计算已使用的天数 293 | a, _ := time.Parse("2006-01-02", nowDayStr) 294 | b, _ := time.Parse("2006-01-02", startDayStr) 295 | useDayNumtmp := utils.TimeSub(a, b) 296 | //已使用的天数 297 | useDayNum := gconv.Int64(useDayNumtmp) 298 | 299 | var G1DayFlow int64 = utils.G1 / 30 300 | var G2DayFlow int64 = utils.G1 * 2 / 30 301 | var G3DayFlow int64 = utils.G1 * 3 / 30 302 | 303 | for _, v := range simList { 304 | simInfo := datamodel.SimUnicom{} 305 | v.Struct(&simInfo) 306 | //计算当前周期内激活卡的池子流量 307 | var chargingDayNum int64 = 30 308 | tmpTime := gconv.Int64(simInfo.DateActivated) 309 | simCardActivatedYear := gconv.Int64(gtime.NewFromTimeStamp(tmpTime).Format("Y")) 310 | simCardActivatedMonth := gconv.Int64(gtime.NewFromTimeStamp(tmpTime).Format("n")) 311 | if simCardActivatedYear == yearNum && simCardActivatedMonth == monthNum { 312 | simCardActivatedDate := gtime.NewFromTimeStamp(tmpTime).Format("Y-m-d") 313 | //计算剩余的天数 314 | a2, _ := time.Parse("2006-01-02", endDayStr) 315 | b2, _ := time.Parse("2006-01-02", simCardActivatedDate) 316 | chargingDayNumTmp := utils.TimeSub(a2, b2) 317 | chargingDayNum = gconv.Int64(chargingDayNumTmp) 318 | } 319 | 320 | switch simInfo.RatePlan { 321 | case plan01: 322 | flowPoolSize[plan01] = flowPoolSize[plan01] + chargingDayNum*G1DayFlow 323 | useFlow[plan01] = useFlow[plan01] + simInfo.CtdDataUsage 324 | simCordList1[simInfo.Iccid] = simInfo 325 | ac.PlanListSimList.Set(plan01, simCordList1) 326 | simNum[plan01]++ 327 | 328 | case plan02: 329 | flowPoolSize[plan02] = flowPoolSize[plan02] + chargingDayNum*G2DayFlow 330 | useFlow[plan02] = useFlow[plan02] + simInfo.CtdDataUsage 331 | simCordList2[simInfo.Iccid] = simInfo 332 | ac.PlanListSimList.Set(plan02, simCordList2) 333 | simNum[plan02]++ 334 | 335 | case plan03: 336 | flowPoolSize[plan03] = flowPoolSize[plan03] + chargingDayNum*G3DayFlow 337 | useFlow[plan03] = useFlow[plan03] + simInfo.CtdDataUsage 338 | simCordList3[simInfo.Iccid] = simInfo 339 | ac.PlanListSimList.Set(plan03, simCordList3) 340 | simNum[plan03]++ 341 | 342 | } 343 | } 344 | 345 | //1G套餐统计 =============================== 346 | planInfo1 := analyse.PlanInfo{} 347 | planInfo1.PlanName = plan01 348 | planInfo1.AllFlow = flowPoolSize[plan01] / utils.MB1 //池子总量 349 | planInfo1.UseFlow = useFlow[plan01] / utils.MB1 //使用量 350 | //计算剩余流量 351 | surplusFlow := flowPoolSize[plan01] - useFlow[plan01] 352 | var outFlow int64 = 0 353 | if surplusFlow < 0 { 354 | outFlow = utils.Abs(surplusFlow) 355 | surplusFlow = 0 356 | } 357 | planInfo1.SurplusFlow = surplusFlow / utils.MB1 //余量 358 | planInfo1.OutFlow = outFlow / utils.MB1 //超出量 359 | planInfo1.AveDayFlow = planInfo1.UseFlow / useDayNum / utils.MB1 //每天的使用流量 360 | planInfo1.Num = simNum[plan01] //sim卡数量 361 | 362 | planInfo1.AveSimUseFlow = 0 363 | if simNum[plan01] > 0 { 364 | planInfo1.AveSimUseFlow = planInfo1.AveDayFlow / gconv.Int64(simNum[plan01]) / utils.MB1 //计算每天每卡的平均量 365 | } 366 | 367 | planInfo1.SurplusDayNum = surplusDayNum //剩余的天数 368 | planInfo1.ExpectFlow = planInfo1.AveDayFlow * surplusDayNum / utils.MB1 369 | ac.PlanInfo[plan01] = planInfo1 370 | 371 | //2G套餐统计 =============================== 372 | planInfo2 := analyse.PlanInfo{} 373 | planInfo2.PlanName = plan02 374 | planInfo2.AllFlow = flowPoolSize[plan02] / utils.MB1 //池子总量 375 | planInfo2.UseFlow = useFlow[plan02] / utils.MB1 //使用量 376 | //计算剩余流量 377 | surplusFlow2 := flowPoolSize[plan02] - useFlow[plan02] 378 | var outFlow2 int64 = 0 379 | if surplusFlow2 < 0 { 380 | outFlow2 = utils.Abs(surplusFlow2) 381 | surplusFlow2 = 0 382 | } 383 | planInfo2.SurplusFlow = surplusFlow2 / utils.MB1 //余量 384 | planInfo2.OutFlow = outFlow2 / utils.MB1 //超出量 385 | planInfo2.AveDayFlow = planInfo2.UseFlow / useDayNum / utils.MB1 //每天的使用流量 386 | planInfo2.Num = simNum[plan02] //sim卡数量 387 | 388 | planInfo2.AveSimUseFlow = 0 389 | if simNum[plan02] > 0 { 390 | planInfo2.AveSimUseFlow = planInfo2.AveDayFlow / gconv.Int64(simNum[plan02]) / utils.MB1 //计算每天每卡的平均量 391 | } 392 | 393 | planInfo2.SurplusDayNum = surplusDayNum //剩余的天数 394 | planInfo2.ExpectFlow = planInfo2.AveDayFlow * surplusDayNum / utils.MB1 395 | 396 | ac.PlanInfo[plan02] = planInfo2 397 | 398 | //3G套餐统计 =============================== 399 | planInfo3 := analyse.PlanInfo{} 400 | planInfo3.PlanName = plan03 401 | planInfo3.AllFlow = flowPoolSize[plan03] / utils.MB1 //池子总量 402 | planInfo3.UseFlow = useFlow[plan03] / utils.MB1 //使用量 403 | //计算剩余流量 404 | surplusFlow3 := flowPoolSize[plan03] - useFlow[plan03] 405 | var outFlow3 int64 = 0 406 | if surplusFlow3 < 0 { 407 | outFlow3 = utils.Abs(surplusFlow3) 408 | surplusFlow3 = 0 409 | } 410 | planInfo3.SurplusFlow = surplusFlow3 / utils.MB1 //余量 411 | planInfo3.OutFlow = outFlow3 / utils.MB1 //超出量 412 | planInfo3.AveDayFlow = planInfo3.UseFlow / useDayNum / utils.MB1 //每天的使用流量 413 | planInfo3.Num = simNum[plan03] //sim卡数量 414 | 415 | planInfo3.AveSimUseFlow = 0 416 | if simNum[plan03] > 0 { 417 | planInfo3.AveSimUseFlow = planInfo3.AveDayFlow / gconv.Int64(simNum[plan03]) / utils.MB1 //计算每天每卡的平均量 418 | } 419 | 420 | planInfo3.SurplusDayNum = surplusDayNum //剩余的天数 421 | planInfo3.ExpectFlow = planInfo3.AveDayFlow * surplusDayNum / utils.MB1 422 | 423 | ac.PlanInfo[plan03] = planInfo3 424 | } 425 | --------------------------------------------------------------------------------